From 200b94fc59a06a1c15e04f9b9ae0149cc94e756e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 9 Feb 2019 18:01:17 +0100 Subject: [PATCH 001/714] Initial commit --- radio/src/thirdparty/libopenui/LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/LICENSE diff --git a/radio/src/thirdparty/libopenui/LICENSE b/radio/src/thirdparty/libopenui/LICENSE new file mode 100644 index 000000000..4d5acc283 --- /dev/null +++ b/radio/src/thirdparty/libopenui/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 OpenTX + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From d7661960f51a6c23d23b33581dcd132a539c9c3f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 9 Feb 2019 18:13:00 +0100 Subject: [PATCH 002/714] Delete LICENSE --- radio/src/thirdparty/libopenui/LICENSE | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/LICENSE diff --git a/radio/src/thirdparty/libopenui/LICENSE b/radio/src/thirdparty/libopenui/LICENSE deleted file mode 100644 index 4d5acc283..000000000 --- a/radio/src/thirdparty/libopenui/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 OpenTX - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. From a95e23fc97e1813dae41cd35863f28a682582a35 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 9 Feb 2019 18:13:22 +0100 Subject: [PATCH 003/714] Create LICENSE --- radio/src/thirdparty/libopenui/LICENSE | 674 +++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/LICENSE diff --git a/radio/src/thirdparty/libopenui/LICENSE b/radio/src/thirdparty/libopenui/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/radio/src/thirdparty/libopenui/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From f8abbd423dc002d4e07efe30b4634ce8dd69397a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 9 Feb 2019 18:57:34 +0100 Subject: [PATCH 004/714] Update LICENSE --- radio/src/thirdparty/libopenui/LICENSE | 827 +++++-------------------- 1 file changed, 159 insertions(+), 668 deletions(-) diff --git a/radio/src/thirdparty/libopenui/LICENSE b/radio/src/thirdparty/libopenui/LICENSE index f288702d2..0a041280b 100644 --- a/radio/src/thirdparty/libopenui/LICENSE +++ b/radio/src/thirdparty/libopenui/LICENSE @@ -1,674 +1,165 @@ - GNU GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. From 9b14d177c579dff6599aa4fe1b9c49b7723c0004 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 2 Oct 2019 11:05:12 +0200 Subject: [PATCH 005/714] Initial commit --- radio/src/thirdparty/libopenui/CMakeLists.txt | 1 + .../thirdparty/libopenui/src/CMakeLists.txt | 31 + .../thirdparty/libopenui/src/basenumberedit.h | 104 ++ radio/src/thirdparty/libopenui/src/bitfield.h | 85 ++ .../thirdparty/libopenui/src/bitmapbuffer.cpp | 925 ++++++++++++++++++ .../thirdparty/libopenui/src/bitmapbuffer.h | 489 +++++++++ radio/src/thirdparty/libopenui/src/button.cpp | 138 +++ radio/src/thirdparty/libopenui/src/button.h | 180 ++++ .../src/thirdparty/libopenui/src/checkbox.cpp | 51 + radio/src/thirdparty/libopenui/src/checkbox.h | 58 ++ radio/src/thirdparty/libopenui/src/choice.cpp | 118 +++ radio/src/thirdparty/libopenui/src/choice.h | 74 ++ .../thirdparty/libopenui/src/coloredit.cpp | 85 ++ .../src/thirdparty/libopenui/src/coloredit.h | 39 + radio/src/thirdparty/libopenui/src/dialog.cpp | 49 + radio/src/thirdparty/libopenui/src/dialog.h | 38 + .../thirdparty/libopenui/src/filechoice.cpp | 158 +++ .../src/thirdparty/libopenui/src/filechoice.h | 57 ++ radio/src/thirdparty/libopenui/src/font.h | 30 + radio/src/thirdparty/libopenui/src/form.cpp | 120 +++ radio/src/thirdparty/libopenui/src/form.h | 197 ++++ .../src/thirdparty/libopenui/src/gridlayout.h | 123 +++ .../thirdparty/libopenui/src/keyboard_base.h | 77 ++ .../libopenui/src/keyboard_number.cpp | 97 ++ .../libopenui/src/keyboard_number.h | 54 + .../libopenui/src/keyboard_text.cpp | 228 +++++ .../thirdparty/libopenui/src/keyboard_text.h | 67 ++ .../libopenui/src/libopenui_defines.h | 125 +++ .../libopenui/src/libopenui_depends.h | 33 + .../libopenui/src/libopenui_file.cpp | 80 ++ .../thirdparty/libopenui/src/libopenui_file.h | 32 + .../libopenui/src/libopenui_globals.cpp | 23 + .../libopenui/src/libopenui_globals.h | 30 + .../libopenui/src/libopenui_helpers.cpp | 21 + .../libopenui/src/libopenui_helpers.h | 57 ++ .../libopenui/src/libopenui_types.h | 63 ++ .../thirdparty/libopenui/src/mainwindow.cpp | 111 +++ .../src/thirdparty/libopenui/src/mainwindow.h | 63 ++ radio/src/thirdparty/libopenui/src/menu.cpp | 148 +++ radio/src/thirdparty/libopenui/src/menu.h | 196 ++++ .../thirdparty/libopenui/src/menutoolbar.cpp | 32 + .../thirdparty/libopenui/src/menutoolbar.h | 180 ++++ .../thirdparty/libopenui/src/numberedit.cpp | 115 +++ .../src/thirdparty/libopenui/src/numberedit.h | 89 ++ radio/src/thirdparty/libopenui/src/page.cpp | 100 ++ radio/src/thirdparty/libopenui/src/page.h | 89 ++ .../src/thirdparty/libopenui/src/progress.cpp | 26 + radio/src/thirdparty/libopenui/src/progress.h | 51 + radio/src/thirdparty/libopenui/src/slider.cpp | 81 ++ radio/src/thirdparty/libopenui/src/slider.h | 72 ++ radio/src/thirdparty/libopenui/src/static.cpp | 31 + radio/src/thirdparty/libopenui/src/static.h | 107 ++ .../thirdparty/libopenui/src/tabsgroup.cpp | 194 ++++ .../src/thirdparty/libopenui/src/tabsgroup.h | 191 ++++ .../src/thirdparty/libopenui/src/textedit.cpp | 168 ++++ radio/src/thirdparty/libopenui/src/textedit.h | 100 ++ radio/src/thirdparty/libopenui/src/theme.h | 45 + radio/src/thirdparty/libopenui/src/window.cpp | 395 ++++++++ radio/src/thirdparty/libopenui/src/window.h | 323 ++++++ 59 files changed, 7044 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/CMakeLists.txt create mode 100644 radio/src/thirdparty/libopenui/src/CMakeLists.txt create mode 100644 radio/src/thirdparty/libopenui/src/basenumberedit.h create mode 100644 radio/src/thirdparty/libopenui/src/bitfield.h create mode 100644 radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp create mode 100644 radio/src/thirdparty/libopenui/src/bitmapbuffer.h create mode 100644 radio/src/thirdparty/libopenui/src/button.cpp create mode 100644 radio/src/thirdparty/libopenui/src/button.h create mode 100644 radio/src/thirdparty/libopenui/src/checkbox.cpp create mode 100644 radio/src/thirdparty/libopenui/src/checkbox.h create mode 100644 radio/src/thirdparty/libopenui/src/choice.cpp create mode 100644 radio/src/thirdparty/libopenui/src/choice.h create mode 100644 radio/src/thirdparty/libopenui/src/coloredit.cpp create mode 100644 radio/src/thirdparty/libopenui/src/coloredit.h create mode 100644 radio/src/thirdparty/libopenui/src/dialog.cpp create mode 100644 radio/src/thirdparty/libopenui/src/dialog.h create mode 100644 radio/src/thirdparty/libopenui/src/filechoice.cpp create mode 100644 radio/src/thirdparty/libopenui/src/filechoice.h create mode 100644 radio/src/thirdparty/libopenui/src/font.h create mode 100644 radio/src/thirdparty/libopenui/src/form.cpp create mode 100644 radio/src/thirdparty/libopenui/src/form.h create mode 100644 radio/src/thirdparty/libopenui/src/gridlayout.h create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_base.h create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_number.cpp create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_number.h create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_text.cpp create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_text.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_defines.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_depends.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_file.cpp create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_file.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_globals.cpp create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_globals.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_helpers.cpp create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_helpers.h create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_types.h create mode 100644 radio/src/thirdparty/libopenui/src/mainwindow.cpp create mode 100644 radio/src/thirdparty/libopenui/src/mainwindow.h create mode 100644 radio/src/thirdparty/libopenui/src/menu.cpp create mode 100644 radio/src/thirdparty/libopenui/src/menu.h create mode 100644 radio/src/thirdparty/libopenui/src/menutoolbar.cpp create mode 100644 radio/src/thirdparty/libopenui/src/menutoolbar.h create mode 100644 radio/src/thirdparty/libopenui/src/numberedit.cpp create mode 100644 radio/src/thirdparty/libopenui/src/numberedit.h create mode 100644 radio/src/thirdparty/libopenui/src/page.cpp create mode 100644 radio/src/thirdparty/libopenui/src/page.h create mode 100644 radio/src/thirdparty/libopenui/src/progress.cpp create mode 100644 radio/src/thirdparty/libopenui/src/progress.h create mode 100644 radio/src/thirdparty/libopenui/src/slider.cpp create mode 100644 radio/src/thirdparty/libopenui/src/slider.h create mode 100644 radio/src/thirdparty/libopenui/src/static.cpp create mode 100644 radio/src/thirdparty/libopenui/src/static.h create mode 100644 radio/src/thirdparty/libopenui/src/tabsgroup.cpp create mode 100644 radio/src/thirdparty/libopenui/src/tabsgroup.h create mode 100644 radio/src/thirdparty/libopenui/src/textedit.cpp create mode 100644 radio/src/thirdparty/libopenui/src/textedit.h create mode 100644 radio/src/thirdparty/libopenui/src/theme.h create mode 100644 radio/src/thirdparty/libopenui/src/window.cpp create mode 100644 radio/src/thirdparty/libopenui/src/window.h diff --git a/radio/src/thirdparty/libopenui/CMakeLists.txt b/radio/src/thirdparty/libopenui/CMakeLists.txt new file mode 100644 index 000000000..5f0cf266a --- /dev/null +++ b/radio/src/thirdparty/libopenui/CMakeLists.txt @@ -0,0 +1 @@ +include(src) diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt new file mode 100644 index 000000000..752f93b90 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -0,0 +1,31 @@ +set(LIBOPENUI_SRC + libopenui_globals.cpp + libopenui_file.cpp + bitmapbuffer.cpp + window.cpp + form.cpp + button.cpp + static.cpp + checkbox.cpp + slider.cpp + mainwindow.cpp + menu.cpp + menutoolbar.cpp + choice.cpp + filechoice.cpp + numberedit.cpp + textedit.cpp + coloredit.cpp + progress.cpp + page.cpp + dialog.cpp + tabsgroup.cpp + ) + +if(HARDWARE_TOUCH) + set(LIBOPENUI_SRC + ${LIBOPENUI_SRC} + keyboard_text.cpp + keyboard_number.cpp + ) +endif() diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h new file mode 100644 index 000000000..7b8c9549f --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BASENUMBEREDIT_H_ +#define _BASENUMBEREDIT_H_ + +#include "form.h" + +class BaseNumberEdit : public FormField { + public: + BaseNumberEdit(Window * parent, const rect_t &rect, int32_t vmin, int32_t vmax, + std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0): + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + _getValue(std::move(getValue)), + _setValue(std::move(setValue)), + flags(flags) + { + } + + void setMin(int32_t value) + { + vmin = value; + } + + void setMax(int32_t value) + { + vmax = value; + } + + void setDefault(int32_t value) + { + vdefault = value; + } + + int32_t getMin() const + { + return vmin; + } + + int32_t getMax() const + { + return vmax; + } + + int32_t getDefault() const + { + return vdefault; + } + + void setStep(int32_t value) + { + step = value; + } + + int32_t getStep() const + { + return step; + } + + void setValue(int32_t value) + { + _setValue(limit(vmin, value, vmax)); + invalidate(); + } + + void setSetValueHandler(std::function handler) + { + _setValue = handler; + } + + int32_t getValue() + { + return _getValue(); + } + + protected: + int32_t vdefault = 0; + int32_t vmin; + int32_t vmax; + int32_t step = 1; + std::function _getValue; + std::function _setValue; + LcdFlags flags; +}; + +#endif // _BASENUMBEREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/bitfield.h b/radio/src/thirdparty/libopenui/src/bitfield.h new file mode 100644 index 000000000..abb309ab1 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/bitfield.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BITFIELD_H +#define _BITFIELD_H + +#include + +// A set of bitfield handling macros + +template +inline T bfBit(uint8_t n) +{ + return T(1) << n; +} + +#define BFBIT_SET(y, mask) ( y |= (mask) ) +#define BFBIT_CLEAR(y, mask) ( y &= ~(mask) ) +#define BFBIT_FLIP(y, mask) ( y ^= (mask) ) +#define BF_SINGLE_BIT_SET(y, i) BFBIT_SET(y, bfBit(i)) + +//! Create a bitmask of length 'len'. +template +inline T bfBitmask(uint8_t len) +{ + return bfBit(len) - 1; +} + +//! Create a bitfield mask of length 'len' starting at bit 'start'. +template +inline T bfMask(uint8_t start, uint8_t len) +{ + return bfBitmask(len) << start; +} + +//! Prepare a bitmask for insertion or combining. +template +inline T bfPrep(T x, uint8_t start, uint8_t len) +{ + return (x & bfBitmask(len)) << start; +} + +//! Extract a bitfield of length 'len' starting at bit 'start' from 'y'. +template +inline T bfGet(T y, uint8_t start, uint8_t len) +{ + return ((y)>>(start)) & bfBitmask(len); +} + +//! Insert 'len' bits of 'x 'into 'y', starting at bit 'start' from 'y'. +template +inline T bfSet(T to, T from, uint8_t start, uint8_t len) +{ + return (to & ~bfMask(start, len)) | bfPrep(from, start, len); +} + +template +inline T bfBitGet(T y, T mask) +{ + return (y & mask); +} + +template +inline T bfSingleBitGet(T y, uint8_t i) +{ + return bfBitGet(y, bfBit(i)); +} + +#endif //BITFIELD_H diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp new file mode 100644 index 000000000..accb21fd8 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -0,0 +1,925 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include +#include "bitmapbuffer.h" +#include "libopenui_depends.h" +#include "libopenui_globals.h" +#include "libopenui_helpers.h" +#include "libopenui_file.h" +#include "font.h" + +void BitmapBuffer::drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color) +{ + if (opacity == OPACITY_MAX) { + drawPixel(p, color); + } + else if (opacity != 0) { + uint8_t bgWeight = OPACITY_MAX - opacity; + RGB_SPLIT(color, red, green, blue); + RGB_SPLIT(*p, bgRed, bgGreen, bgBlue); + uint16_t r = (bgRed * bgWeight + red * opacity) / OPACITY_MAX; + uint16_t g = (bgGreen * bgWeight + green * opacity) / OPACITY_MAX; + uint16_t b = (bgBlue * bgWeight + blue * opacity) / OPACITY_MAX; + drawPixel(p, RGB_JOIN(r, g, b)); + } +} + +void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags att) +{ + APPLY_OFFSET(); + + if (y >= height || y >= ymax || y < ymin) + return; + + if (x+w > width) { + w = width - x; + } + + pixel_t * p = getPixelPtr(x, y); + pixel_t color = lcdColorTable[COLOR_IDX(att)]; + uint8_t opacity = 0x0F - (att >> 24); + + if (pat == SOLID) { + while (w--) { + drawAlphaPixel(p, opacity, color); + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + else { + while (w--) { + if (pat & 1) { + drawAlphaPixel(p, opacity, color); + pat = (pat >> 1) | 0x80; + } + else { + pat = pat >> 1; + } + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } +} + +void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags att) +{ + APPLY_OFFSET(); + + if (x < xmin || x >= xmax) + return; + + if (h < 0) { + y += h; + h = -h; + } + + if (y < ymin) { + h += y-ymin; + y = ymin; + } + + if (y + h > ymax) { + h = ymax - y; + } + + if (h <= 0) + return; + + pixel_t color = lcdColorTable[COLOR_IDX(att)]; + uint8_t opacity = 0x0F - (att >> 24); + + if (pat == SOLID) { + while (h--) { + drawAlphaPixel(x, y, opacity, color); + y++; + } + } + else { + if (pat==DOTTED && !(y%2)) { + pat = ~pat; + } + while (h--) { + if (pat & 1) { + drawAlphaPixel(x, y, opacity, color); + pat = (pat >> 1) | 0x80; + } + else { + pat = pat >> 1; + } + y++; + } + } +} + +void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags att) +{ + for (int i=0; i= xmax || y >= ymax) + return; + + if (h < 0) { + y += h; + h = -h; + } + if (y < ymin) { + h += y-ymin; + y = ymin; + } + if (x < xmin) { + w += x - xmin; + x = xmin; + } + if (y + h > ymax) + h = ymax - y; + if (x + w > xmax) + w = xmax - x; + + if (!data || h<=0 || w<=0) + return; + + DMAFillRect(data, width, height, x, y, w, h, lcdColorTable[COLOR_IDX(flags)]); +} + +void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags att) +{ + APPLY_OFFSET(); + + if (x >= xmax || y >= ymax) + return; + + if (h < 0) { + y += h; + h = -h; + } + if (y < ymin) { + h += y-ymin; + y = ymin; + } + if (x < xmin) { + w += x - xmin; + x = xmin; + } + if (y + h > ymax) + h = ymax - y; + if (x + w > xmax) + w = xmax - x; + + for (coord_t i=y; i= 360 || endAngle <= 0) +// return false; +// +// if (startAngle == 0) { +// slopes[1] = 100000; +// slopes[2] = -100000; +// } +// else { +// float angle1 = float(startAngle) * (M_PI / 180.0f); +// if (startAngle >= 180) { +// slopes[1] = -100000; +// slopes[2] = cosf(angle1) * 100 / sinf(angle1); +// } +// else { +// slopes[1] = cosf(angle1) * 100 / sinf(angle1); +// slopes[2] = -100000; +// } +// } +// +// if (endAngle == 360) { +// slopes[0] = -100000; +// slopes[3] = 100000; +// } +// else { +// float angle2 = float(endAngle) * (M_PI / 180.0f); +// if (endAngle >= 180) { +// slopes[0] = -100000; +// slopes[3] = -cosf(angle2) * 100 / sinf(angle2); +// } +// else { +// slopes[0] = cosf(angle2) * 100 / sinf(angle2); +// slopes[3] = -100000; +// } +// } +// +// return true; +//} +// +//void BitmapBuffer::drawPie(int x0, int y0, int radius, int startAngle, int endAngle) +//{ +// int slopes[4]; +// if (!evalSlopes(slopes, startAngle, endAngle)) +// return; +// +// for (int y=0; y<=radius; y++) { +// for (int x=0; x<=radius; x++) { +// if (x*x+y*y <= radius*radius) { +// int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x); +// if (slope >= slopes[0] && slope < slopes[1]) { +// drawPixel(x0+x, y0-y, WHITE); +// } +// if (-slope >= slopes[0] && -slope < slopes[1]) { +// drawPixel(x0+x, y0+y, WHITE); +// } +// if (slope >= slopes[2] && slope < slopes[3]) { +// drawPixel(x0-x, y0-y, WHITE); +// } +// if (-slope >= slopes[2] && -slope < slopes[3]) { +// drawPixel(x0-x, y0+y, WHITE); +// } +// } +// } +// } +//} + +void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset, coord_t width) +{ + if (mask == nullptr) { + return; + } + + APPLY_OFFSET(); + + coord_t w = mask->getWidth(); + coord_t height = mask->getHeight(); + + if (!width || width > w) { + width = w; + } + + if (x + width > xmax) { + width = xmax - x; + } + + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { + return; + } + + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + + for (coord_t row=0; row= ymax) + continue; + pixel_t * p = getPixelPtr(x, y+row); + const pixel_t * q = mask->getPixelPtr(offset, row); + for (coord_t col=0; col w) { + width = w; + } + + if (x + width > xmax) { + width = xmax - x; + } + + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { + return; + } + + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + + for (coord_t row=0; row= ymax) + continue; + const uint8_t * q = bmp + 4 + row*w + offset; + for (coord_t col=0; col= xmin && xpixel < xmax) { + pixel_t * p = getPixelPtr(xpixel, ypixel); + if (p) + drawAlphaPixel(p, *q, color); + } + q++; + } + } +} + +uint8_t BitmapBuffer::drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags) +{ + coord_t offset = spec[index + 1]; + coord_t width = spec[index + 2] - offset; + if (width > 0) { + drawBitmapPattern(x, y, font, flags, offset, width); + } + return width; +} + +#define INCREMENT_POS(delta) do { if (flags & VERTICAL) y -= delta; else x += delta; } while(0) + +coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags) +{ + MOVE_OFFSET(); + + int height = getFontHeight(flags); + + if (y + height <= ymin || y >= ymax) { + RESTORE_OFFSET(); + return x; + } + + uint32_t fontindex = FONTINDEX(flags); + const unsigned char * font = fontsTable[fontindex]; + const uint16_t * fontspecs = fontspecsTable[fontindex]; + + if (flags & (RIGHT | CENTERED)) { + int width = getTextWidth(s, len, flags); + if (flags & RIGHT) { + INCREMENT_POS(-width); + } + else if (flags & CENTERED) { + INCREMENT_POS(-width / 2); + } + } + + coord_t & pos = (flags & VERTICAL) ? y : x; + const coord_t orig_pos = pos; + + while (len--) { + unsigned int c = uint8_t(*s); + + if (!c) { + break; + } + else if (c == 0x20) { + INCREMENT_POS(4); + } + else if (c >= 0xFE) { + s++; + c = uint8_t(*s) + ((c & 0x01) << 8) - 1; + if (c >= 0x101) + c -= 1; + c += 187; + uint8_t width = drawChar(x-1, y, font, fontspecs, c, flags); + INCREMENT_POS(width); + } + else if (c >= 0x20) { + uint8_t width = drawChar(x-1, y, font, fontspecs, getMappedChar(c), flags); + INCREMENT_POS(width); + } + else if (c == '\n') { + pos = orig_pos; + if (flags & VERTICAL) + x += height; + else + y += height; + } + + s++; + } + + RESTORE_OFFSET(); + + return pos - offsetX; +} + +void BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) +{ + char str[48+1]; // max=16 for the prefix, 16 chars for the number, 16 chars for the suffix + char *s = str+32; + *s = '\0'; + int idx = 0; + int mode = MODE(flags); + bool neg = false; + if (val < 0) { + val = -val; + neg = true; + } + do { + *--s = '0' + (val % 10); + ++idx; + val /= 10; + if (mode!=0 && idx==mode) { + mode = 0; + *--s = '.'; + if (val==0) + *--s = '0'; + } + } while (val!=0 || mode>0 || (mode==MODE(LEADING0) && idxdrawSolidFilledRect(x, y, thickness, h, att); + dc->drawSolidFilledRect(x+w-thickness, y, thickness, h, att); + dc->drawSolidFilledRect(x, y, w, thickness, att); + dc->drawSolidFilledRect(x, y+h-thickness, w, thickness, att); +} + +//void BitmapBuffer::drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle) +//{ +// const uint16_t * q = img; +// coord_t width = *q++; +// coord_t height = *q++; +// +// int slopes[4]; +// if (!evalSlopes(slopes, startAngle, endAngle)) +// return; +// +// int w2 = width/2; +// int h2 = height/2; +// +// for (int y=h2-1; y>=0; y--) { +// for (int x=w2-1; x>=0; x--) { +// int slope = (x==0 ? 99000 : y*100/x); +// if (slope >= slopes[0] && slope < slopes[1]) { +// *getPixelPtr(x0+w2+x, y0+h2-y) = q[(h2-y)*width + w2+x]; +// } +// if (-slope >= slopes[0] && -slope < slopes[1]) { +// *getPixelPtr(x0+w2+x, y0+h2+y) = q[(h2+y)*width + w2+x]; +// } +// if (slope >= slopes[2] && slope < slopes[3]) { +// *getPixelPtr(x0+w2-x, y0+h2-y) = q[(h2-y)*width + w2-x]; +// } +// if (-slope >= slopes[2] && -slope < slopes[3]) { +// *getPixelPtr(x0+w2-x, y0+h2+y) = q[(h2+y)*width + w2-x]; +// } +// } +// } +//} +// +//void BitmapBuffer::drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle) +//{ +// coord_t width = *((uint16_t *)img); +// coord_t height = *(((uint16_t *)img)+1); +// const uint8_t * q = img+4; +// +// int slopes[4]; +// if (!evalSlopes(slopes, startAngle, endAngle)) +// return; +// +// pixel_t color = lcdColorTable[COLOR_IDX(flags)]; +// +// int w2 = width/2; +// int h2 = height/2; +// +// for (int y=h2-1; y>=0; y--) { +// for (int x=w2-1; x>=0; x--) { +// int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x); +// if (slope >= slopes[0] && slope < slopes[1]) { +// drawAlphaPixel(x0+w2+x, y0+h2-y, q[(h2-y)*width + w2+x], color); +// } +// if (-slope >= slopes[0] && -slope < slopes[1]) { +// drawAlphaPixel(x0+w2+x, y0+h2+y, q[(h2+y)*width + w2+x], color); +// } +// if (slope >= slopes[2] && slope < slopes[3]) { +// drawAlphaPixel(x0+w2-x, y0+h2-y, q[(h2-y)*width + w2-x], color); +// } +// if (-slope >= slopes[2] && -slope < slopes[3]) { +// drawAlphaPixel(x0+w2-x, y0+h2+y, q[(h2+y)*width + w2-x], color); +// } +// } +// } +//} + +BitmapBuffer * BitmapBuffer::load(const char * filename) +{ + const char * ext = getFileExtension(filename); + if (ext && !strcmp(ext, ".bmp")) + return load_bmp(filename); + else + return load_stb(filename); +} + +BitmapBuffer * BitmapBuffer::loadMask(const char * filename) +{ + BitmapBuffer * bitmap = BitmapBuffer::load(filename); + if (bitmap) { + pixel_t * p = bitmap->getPixelPtr(0, 0); + for (int i = bitmap->getWidth() * bitmap->getHeight(); i > 0; i--) { + *((uint8_t *)p) = OPACITY_MAX - ((*p) >> 12); + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + return bitmap; +} + +BitmapBuffer * BitmapBuffer::loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background) +{ + BitmapBuffer * result = nullptr; + BitmapBuffer * mask = BitmapBuffer::loadMask(filename); + if (mask) { + result = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); + if (result) { + result->clear(background); + result->drawMask(0, 0, mask, foreground); + } + delete mask; + } + return result; +} + +FIL imgFile __DMA; + +BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) +{ + UINT read; + uint8_t palette[16]; + uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */ + uint8_t * buf = &bmpBuf[0]; + + FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); + if (result != FR_OK) { + return nullptr; + } + + if (f_size(&imgFile) < 14) { + f_close(&imgFile); + return nullptr; + } + + result = f_read(&imgFile, buf, 14, &read); + if (result != FR_OK || read != 14) { + f_close(&imgFile); + return nullptr; + } + + if (buf[0] != 'B' || buf[1] != 'M') { + f_close(&imgFile); + return nullptr; + } + + uint32_t fsize = *((uint32_t *)&buf[2]); + uint32_t hsize = *((uint32_t *)&buf[10]); /* header size */ + + uint32_t len = limit((uint32_t)4, (uint32_t)(hsize-14), (uint32_t)32); + result = f_read(&imgFile, buf, len, &read); + if (result != FR_OK || read != len) { + f_close(&imgFile); + return nullptr; + } + + uint32_t ihsize = *((uint32_t *)&buf[0]); /* more header size */ + + /* invalid header size */ + if (ihsize + 14 > hsize) { + f_close(&imgFile); + return nullptr; + } + + /* sometimes file size is set to some headers size, set a real size in that case */ + if (fsize == 14 || fsize == ihsize + 14) + fsize = f_size(&imgFile) - 2; + + /* declared file size less than header size */ + if (fsize <= hsize) { + f_close(&imgFile); + return nullptr; + } + + uint32_t w, h; + + switch (ihsize){ + case 40: // windib + case 56: // windib v3 + case 64: // OS/2 v2 + case 108: // windib v4 + case 124: // windib v5 + w = *((uint32_t *)&buf[4]); + h = *((uint32_t *)&buf[8]); + buf += 12; + break; + case 12: // OS/2 v1 + w = *((uint16_t *)&buf[4]); + h = *((uint16_t *)&buf[6]); + buf += 8; + break; + default: + f_close(&imgFile); + return nullptr; + } + + if (*((uint16_t *)&buf[0]) != 1) { /* planes */ + f_close(&imgFile); + return nullptr; + } + + uint16_t depth = *((uint16_t *)&buf[2]); + + buf = &bmpBuf[0]; + + if (depth == 4) { + if (f_lseek(&imgFile, hsize-64) != FR_OK || f_read(&imgFile, buf, 64, &read) != FR_OK || read != 64) { + f_close(&imgFile); + return nullptr; + } + for (uint8_t i=0; i<16; i++) { + palette[i] = buf[4*i]; + } + } + else { + if (f_lseek(&imgFile, hsize) != FR_OK) { + f_close(&imgFile); + return nullptr; + } + } + + BitmapBuffer * bmp = new BitmapBuffer(BMP_RGB565, w, h); + if (bmp == nullptr || bmp->getData() == nullptr) { + f_close(&imgFile); + return nullptr; + } + + uint32_t rowSize; + bool hasAlpha = false; + + switch (depth) { + case 32: + for (int i=h-1; i>=0; i--) { + pixel_t * dst = bmp->getPixelPtr(0, i); + for (unsigned int j=0; j> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + } + else { + if ((pixel & 0xff) == 0xff) { + *dst = RGB(pixel >> 24, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + } + else { + hasAlpha = true; + bmp->setFormat(BMP_ARGB4444); + for (pixel_t * p = bmp->getPixelPtr(j, i); p != bmp->getPixelPtr(0, h); MOVE_TO_NEXT_RIGHT_PIXEL(p)) { + pixel_t tmp = *p; + *p = ((tmp >> 1) & 0x0f) + (((tmp >> 7) & 0x0f) << 4) + (((tmp >> 12) & 0x0f) << 8); + } + *dst = ARGB(pixel & 0xff, (pixel >> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + } + } + MOVE_TO_NEXT_RIGHT_PIXEL(dst); + } + } + break; + + case 1: + break; + + case 4: + rowSize = ((4*w+31)/32)*4; + for (int32_t i=h-1; i>=0; i--) { + result = f_read(&imgFile, buf, rowSize, &read); + if (result != FR_OK || read != rowSize) { + f_close(&imgFile); + delete bmp; + return nullptr; + } + pixel_t * dst = bmp->getPixelPtr(0, i); + for (uint32_t j=0; j> ((j & 1) ? 0 : 4)) & 0x0F; + uint8_t val = palette[index]; + *dst = RGB(val, val, val); + MOVE_TO_NEXT_RIGHT_PIXEL(dst); + } + } + break; + + default: + f_close(&imgFile); + delete bmp; + return nullptr; + } + + f_close(&imgFile); + return bmp; +} + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_PNG +#define STBI_ONLY_JPEG +#define STBI_ONLY_BMP +#define STBI_ONLY_GIF +#define STBI_NO_STDIO +#define STBI_NO_HDR +#define STBI_NO_LINEAR + +// #define TRACE_STB_MALLOC + +#if defined(TRACE_STB_MALLOC) +#define STBI_MALLOC(sz) stb_malloc(sz) +#define STBI_REALLOC_SIZED(p,oldsz,newsz) stb_realloc(p,oldsz,newsz) +#define STBI_FREE(p) stb_free(p) + +void * stb_malloc(unsigned int size) +{ + void * res = malloc(size); + TRACE("malloc %d = %p", size, res); + return res; +} + +void stb_free(void *ptr) +{ + TRACE("free %p", ptr); + free(ptr); +} + +void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) +{ + void * res = realloc(ptr, newsz); + TRACE("realloc %p, %d -> %d = %p", ptr, oldsz, newsz, res); + return res; +} +#endif // #if defined(TRACE_STB_MALLOC) + +#include "thirdparty/Stb/stb_image.h" + +// fill 'data' with 'size' bytes. return number of bytes actually read +int stbc_read(void *user, char *data, int size) +{ + FIL * fp = (FIL *)user; + UINT br = 0; + FRESULT res = f_read(fp, data, size, &br); + if (res == FR_OK) { + return (int)br; + } + return 0; +} + +// skip the next 'n' bytes, or 'unget' the last -n bytes if negative +void stbc_skip(void *user, int n) +{ + FIL * fp = (FIL *)user; + f_lseek(fp, f_tell(fp) + n); +} + +// returns nonzero if we are at end of file/data +int stbc_eof(void *user) +{ + FIL * fp = (FIL *)user; + int res = f_eof(fp); + return res; +} + +// callbacks for stb-image +const stbi_io_callbacks stbCallbacks = { + stbc_read, + stbc_skip, + stbc_eof +}; + +BitmapBuffer * BitmapBuffer::load_stb(const char * filename) +{ + FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); + if (result != FR_OK) { + return nullptr; + } + + int w, h, n; + unsigned char * img = stbi_load_from_callbacks(&stbCallbacks, &imgFile, &w, &h, &n, 4); + f_close(&imgFile); + + if (!img) { + return nullptr; + } + + // convert to RGB565 or ARGB4444 format + BitmapBuffer * bmp = new BitmapBuffer(n == 4 ? BMP_ARGB4444 : BMP_RGB565, w, h); + if (bmp == NULL) { + TRACE("load_stb() malloc failed"); + stbi_image_free(img); + return nullptr; + } + +#if 0 + DMABitmapConvert(bmp->data, img, w, h, n == 4 ? DMA2D_ARGB4444 : DMA2D_RGB565); +#else + pixel_t * dest = bmp->getPixelPtr(0, 0); + const uint8_t * p = img; + if (n == 4) { + for (int row = 0; row < h; ++row) { + for (int col = 0; col < w; ++col) { + *dest = ARGB(p[3], p[0], p[1], p[2]); + MOVE_TO_NEXT_RIGHT_PIXEL(dest); + p += 4; + } + } + } + else { + for (int row = 0; row < h; ++row) { + for (int col = 0; col < w; ++col) { + *dest = RGB(p[0], p[1], p[2]); + MOVE_TO_NEXT_RIGHT_PIXEL(dest); + p += 4; + } + } + } +#endif + + stbi_image_free(img); + return bmp; +} diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h new file mode 100644 index 000000000..5a6631168 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -0,0 +1,489 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BITMAP_BUFFER_H_ +#define _BITMAP_BUFFER_H_ + +#include +#include +#include +#include "libopenui_types.h" +#include "libopenui_defines.h" +#include "libopenui_depends.h" +#include "debug.h" + +constexpr uint8_t SOLID = 0xFF; +constexpr uint8_t DOTTED = 0x55; +constexpr uint8_t STASHED = 0x33; + +#define MOVE_OFFSET() coord_t offsetX = this->offsetX; x += offsetX; this->offsetX = 0; coord_t offsetY = this->offsetY; y += offsetY; this->offsetY = 0 +#define APPLY_OFFSET() x += offsetX; y += offsetY +#define RESTORE_OFFSET() this->offsetX = offsetX, this->offsetY = offsetY + +#if defined(PCBX10) && !defined(SIMU) + #define MOVE_PIXEL_RIGHT(p, count) p -= count +#else + #define MOVE_PIXEL_RIGHT(p, count) p += count +#endif + +#define MOVE_TO_NEXT_RIGHT_PIXEL(p) MOVE_PIXEL_RIGHT(p, 1) + +#define USE_STB + +enum BitmapFormats +{ + BMP_RGB565, + BMP_ARGB4444 +}; + +template +class BitmapBufferBase +{ + public: + BitmapBufferBase(uint8_t format, uint16_t width, uint16_t height, T * data): + format(format), + width(width), + height(height), + xmax(width), + ymax(height), + data(data), + data_end(data + (width * height)) + { + } + + BitmapBufferBase(uint8_t format, T * data): + format(format), + width(*((uint16_t*)data)), + height(*(((uint16_t*)data) + 1)), + xmax(width), + ymax(height), + data(data + 2), + data_end(data + 2 + (width * height)) + { + } + + inline void clearClippingRect() + { + xmin = 0; + xmax = width; + ymin = 0; + ymax = height; + } + + inline void setClippingRect(coord_t xmin, coord_t xmax, coord_t ymin, coord_t ymax) + { + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + } + + inline void getClippingRect(coord_t & xmin, coord_t & xmax, coord_t & ymin, coord_t & ymax) + { + xmin = this->xmin; + xmax = this->xmax; + ymin = this->ymin; + ymax = this->ymax; + } + + inline void setOffset(coord_t offsetX, coord_t offsetY) + { + this->offsetX = offsetX; + this->offsetY = offsetY; + } + + coord_t getOffsetX() const + { + return offsetX; + } + + coord_t getOffsetY() const + { + return offsetY; + } + + inline uint8_t getFormat() const + { + return format; + } + + inline uint16_t getWidth() const + { + return width; + } + + inline uint16_t getHeight() const + { + return height; + } + + inline T * getData() const + { + return data; + } + + uint32_t getDataSize() const + { + return width * height * sizeof(T); + } + + inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const + { +#if defined(PCBX10) && !defined(SIMU) + x = width - x - 1; + y = height - y - 1; +#endif + return &data[y*width + x]; + } + + protected: + uint8_t format; + coord_t width; + coord_t height; + coord_t xmin = 0; + coord_t xmax; + coord_t ymin = 0; + coord_t ymax; + coord_t offsetX = 0; + coord_t offsetY = 0; + T * data; + T * data_end; +}; + +typedef BitmapBufferBase Bitmap; + +class RLEBitmap: + public BitmapBufferBase +{ + public: + RLEBitmap(uint8_t format, const uint8_t* rle_data) : + BitmapBufferBase(format, 0, 0, nullptr) + { + width = *((uint16_t *)rle_data); + height = *(((uint16_t *)rle_data)+1); + uint32_t pixels = width * height; + data = (uint16_t*)malloc(pixels * sizeof(uint16_t)); + decode((uint8_t *)data, pixels * sizeof(uint16_t), rle_data+4); + data_end = data + pixels; + } + + ~RLEBitmap() + { + free(data); + } + + static int decode(uint8_t * dest, unsigned int dest_size, const unsigned char* src) + { + //unsigned char count = 0; + unsigned char prev_byte = 0; + bool prev_byte_valid = false; + + const unsigned char * dest_end = dest + dest_size; + unsigned char * d = dest; + + while (d < dest_end) { + *d++ = *src; + if (prev_byte_valid && (*src == prev_byte)) { + src++; + + if (d + *src > dest + dest_size) { +// TRACE("rle_decode_8bit: destination overflow!\n"); + return -1; + } + + memset(d, prev_byte, *src); + d += *src++; + prev_byte_valid = false; + } + else { + prev_byte = *src++; + prev_byte_valid = true; + } + } + + return d - dest; + } +}; + +class BitmapBuffer: public BitmapBufferBase +{ + private: + bool dataAllocated; +#if defined(DEBUG) + bool leakReported; +#endif + + public: + + BitmapBuffer(uint8_t format, uint16_t width, uint16_t height): + BitmapBufferBase(format, width, height, nullptr), + dataAllocated(true) +#if defined(DEBUG) + , leakReported(false) +#endif + { + data = (uint16_t *)malloc(width*height*sizeof(uint16_t)); + data_end = data + (width * height); + } + + BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, uint16_t * data): + BitmapBufferBase(format, width, height, data), + dataAllocated(false) +#if defined(DEBUG) + , leakReported(false) +#endif + { + } + + ~BitmapBuffer() + { + if (dataAllocated) { + free(data); + } + } + + inline void setFormat(uint8_t format) + { + this->format = format; + } + + inline void clear(LcdFlags flags=0) + { + drawSolidFilledRect(0, 0, width - offsetX, height - offsetY, flags); + } + + inline void drawPixel(pixel_t * p, pixel_t value) + { + if (data && (data <= p || p < data_end)) { + *p = value; + } +#if defined(DEBUG) + else if (!leakReported) { + leakReported = true; + TRACE("BitmapBuffer(%p).drawPixel(): buffer overrun, data: %p, written at: %p", this, data, p); + } +#endif + } + + inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const + { +#if defined(PCBX10) && !defined(SIMU) + x = width - x - 1; + y = height - y - 1; +#endif + return &data[y*width + x]; + } + + inline pixel_t * getPixelPtr(coord_t x, coord_t y) + { +#if defined(PCBX10) && !defined(SIMU) + x = width - x - 1; + y = height - y - 1; +#endif + return &data[y*width + x]; + } + + inline void drawPixel(coord_t x, coord_t y, pixel_t value) + { + pixel_t * p = getPixelPtr(x, y); + drawPixel(p, value); + } + + void drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color); + + inline void drawAlphaPixel(coord_t x, coord_t y, uint8_t opacity, uint16_t color) + { + pixel_t * p = getPixelPtr(x, y); + drawAlphaPixel(p, opacity, color); + } + + void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat = SOLID, LcdFlags flags = 0); + + void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0); + + inline void drawSolidHorizontalLine(coord_t x, coord_t y, coord_t w, LcdFlags flags) + { + drawSolidFilledRect(x, y, w, 1, flags); + } + + inline void drawSolidVerticalLine(coord_t x, coord_t y, coord_t h, LcdFlags flags) + { + drawSolidFilledRect(x, y, 1, h, flags); + } + + void drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, uint8_t pat = SOLID, LcdFlags flags = 0); + + inline void drawSolidRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, LcdFlags flags = 0) + { + drawSolidFilledRect(x, y, thickness, h, flags); + drawSolidFilledRect(x+w-thickness, y, thickness, h, flags); + drawSolidFilledRect(x, y, w, thickness, flags); + drawSolidFilledRect(x, y+h-thickness, w, thickness, flags); + } + + void drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); + + void drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0); + + void invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); + + void drawCircle(int x0, int y0, int radius); + + void drawPie(int x0, int y0, int radius, int startAngle, int endAngle); + + void drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle); + + void drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle); + + static BitmapBuffer * load(const char * filename); + + static BitmapBuffer * loadMask(const char * filename); + + static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background); + + void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset=0, coord_t width=0); + + void drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset=0, coord_t width=0); + + coord_t drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags=0); + + void drawText(coord_t x, coord_t y, const char * s, LcdFlags flags = 0) + { + drawSizedText(x, y, s, 255, flags); + } + + void drawTextAtIndex(coord_t x, coord_t y, const char * s, uint8_t idx, LcdFlags flags = 0) + { + char length = *s++; + drawSizedText(x, y, s+length*idx, length, flags); + } + + void drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); + + template + void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0) + { + APPLY_OFFSET(); + + if (x >= xmax) return; + if (y >= ymax) return; + + if (!data || !bmp) + return; + + coord_t srcw = bmp->getWidth(); + coord_t srch = bmp->getHeight(); + + if (w == 0) + w = srcw; + if (h == 0) + h = srch; + if (srcx + w > srcw) + w = srcw - srcx; + if (srcy + h > srch) + h = srch - srcy; + + if (x < xmin) { + w += x-xmin; + srcx -= x-xmin; + x = xmin; + } + + if (y < ymin) { + h += y-ymin; + srcy -= y-ymin; + y = ymin; + } + + if (x + w > xmax) { + w = xmax - x; + } + + if (y + h > ymax) { + h = ymax - y; + } + + if (w <= 0 || h <= 0) + return; + + // TRACE("x=%d xmin=%d xmax=%d w=%d", x, xmin, xmax, w); + + if (scale == 0) { + /*if (x + w > width) { + w = width - x; + } + if (y + h > height) { + h = height - y; + }*/ + if (bmp->getFormat() == BMP_ARGB4444) { + DMACopyAlphaBitmap(data, width, height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); + } + else { + DMACopyBitmap(data, width, height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); + } + } + else { + int scaledw = w * scale; + int scaledh = h * scale; + + if (x + scaledw > width) + scaledw = width - x; + if (y + scaledh > height) + scaledh = height - y; + + for (int i = 0; i < scaledh; i++) { + pixel_t * p = getPixelPtr(x, y + i); + const pixel_t * qstart = bmp->getPixelPtr(srcx, srcy + int(i / scale)); + for (int j = 0; j < scaledw; j++) { + const pixel_t * q = qstart; + MOVE_PIXEL_RIGHT(q, int(j / scale)); + if (bmp->getFormat() == BMP_ARGB4444) { + ARGB_SPLIT(*q, a, r, g, b); + drawAlphaPixel(p, a, RGB_JOIN(r<<1, g<<2, b<<1)); + } + else { + drawPixel(p, *q); + } + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + } + } + + template + void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h) + { + float vscale = float(h) / bitmap->getHeight(); + float hscale = float(w) / bitmap->getWidth(); + float scale = vscale < hscale ? vscale : hscale; + + int xshift = (w - (bitmap->getWidth() * scale)) / 2; + int yshift = (h - (bitmap->getHeight() * scale)) / 2; + drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); + } + + protected: + static BitmapBuffer * load_bmp(const char * filename); + static BitmapBuffer * load_stb(const char * filename); + uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); +}; + +extern BitmapBuffer * lcd; + +#endif // _BITMAP_BUFFER_H_ diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp new file mode 100644 index 000000000..84b33b2db --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "libopenui_config.h" +#include "button.h" +#include "font.h" +#include "theme.h" + +void Button::onPress() +{ + bool check = (pressHandler && pressHandler()); + if (check != bool(flags & BUTTON_CHECKED)) { + flags ^= BUTTON_CHECKED; + invalidate(); + } +} + +#if defined(HARDWARE_KEYS) +void Button::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + onPress(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Button::onTouchEnd(coord_t x, coord_t y) +{ + if (enabled()) { + onPress(); + if (!(flags & BUTTON_NOFOCUS)) { + setFocus(); + } + } + return true; +} +#endif + +void Button::checkEvents() +{ + Window::checkEvents(); + if (checkHandler) + checkHandler(); +} + +void TextButton::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor = TEXT_COLOR; + + if (checked()) { + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, TEXT_INVERTED_BGCOLOR); + } + else { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, TEXT_INVERTED_BGCOLOR); + } + textColor = TEXT_INVERTED_COLOR; + } + else { + if (flags & BUTTON_BACKGROUND) { + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, CURVE_AXIS_COLOR); + } + else { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, CURVE_AXIS_COLOR); + } + } + else { + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } + } + } + + dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | textColor); +} + +void IconButton::paint(BitmapBuffer * dc) +{ + dc->drawBitmap(0, 0, theme->getIcon(icon, checked() ? STATE_PRESSED : STATE_DEFAULT)); +} + + +const uint8_t __alpha_button_on[] { +#include "alpha_button_on.lbm" +}; +Bitmap ALPHA_BUTTON_ON(BMP_ARGB4444, (const uint16_t*)__alpha_button_on); + +const uint8_t __alpha_button_off[] { +#include "alpha_button_off.lbm" +}; +Bitmap ALPHA_BUTTON_OFF(BMP_ARGB4444, (const uint16_t*)__alpha_button_off); + + +FabIconButton::FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags): + Button(parent, { x - 34, y - 34, 68, 68 }, pressHandler, flags), + icon(icon) +{ +} + +void FabIconButton::paint(BitmapBuffer * dc) +{ + dc->drawBitmap(0, 0, checked() ? &ALPHA_BUTTON_ON : &ALPHA_BUTTON_OFF); + const BitmapBuffer * mask = theme->getIconMask(icon); + if (mask) { + dc->drawMask((68 - mask->getWidth()) / 2, (68 - mask->getHeight()) / 2, mask, TEXT_BGCOLOR); + } +} diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h new file mode 100644 index 000000000..c72910107 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BUTTON_H_ +#define _BUTTON_H_ + +#include "form.h" +#include + +enum ButtonFlags { + BUTTON_BACKGROUND = 1, + BUTTON_CHECKED = 2, + BUTTON_NOFOCUS = 4, + BUTTON_CHECKED_ON_FOCUS = 8, + BUTTON_DISABLED = 16, +}; + +class Button : public FormField { + public: + Button(Window * parent, const rect_t & rect, std::function pressHandler=nullptr, uint8_t flags=0): + FormField(parent, rect), + pressHandler(pressHandler), + flags(flags) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Button"; + } +#endif + + void enable(bool enabled=true) + { + if (!enabled != bool(flags & BUTTON_DISABLED)) { + flags ^= BUTTON_DISABLED; + invalidate(); + } + } + + void disable() + { + enable(false); + } + + void check(bool checked=true) + { + if (checked != bool(flags & BUTTON_CHECKED)) { + flags ^= BUTTON_CHECKED; + invalidate(); + } + } + + bool enabled() + { + return !(flags & BUTTON_DISABLED); + } + + bool checked() + { + if (flags & BUTTON_CHECKED_ON_FOCUS) + return hasFocus(); + else + return flags & BUTTON_CHECKED; + } + + void setPressHandler(std::function handler) + { + pressHandler = std::move(handler); + } + + void setCheckHandler(std::function handler) + { + checkHandler = std::move(handler); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event); +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void checkEvents() override; + + protected: + std::function pressHandler; + std::function checkHandler; + uint8_t flags; + + void onPress(); +}; + +class TextButton : public Button { + public: + TextButton(Window * parent, const rect_t & rect, std::string text, std::function pressHandler=nullptr, uint8_t flags=BUTTON_BACKGROUND): + Button(parent, rect, pressHandler, flags), + text(std::move(text)) + { + windowFlags = OPAQUE; + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextButton \"" + text + "\""; + } +#endif + + void setText(std::string value) + { + if (value != text) { + text = std::move(value); + invalidate(); + } + } + + void paint(BitmapBuffer * dc) override; + + protected: + std::string text; +}; + +class IconButton: public Button { + public: + IconButton(Window * parent, const rect_t & rect, uint8_t icon, std::function pressHandler, uint8_t flags = 0): + Button(parent, rect, pressHandler, flags), + icon(icon) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "IconButton(" + std::to_string(icon) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + protected: + uint8_t icon; +}; + +class FabIconButton: public Button { + public: + FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags=0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FabIconButton(" + std::to_string(icon) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + protected: + uint8_t icon; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp new file mode 100644 index 000000000..9b0da61d5 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "checkbox.h" +#include "theme.h" + +void CheckBox::paint(BitmapBuffer * dc) +{ + theme->drawCheckBox(dc, getValue(), hasFocus()); +} + +#if defined(HARDWARE_KEYS) +void CheckBox::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + setValue(!getValue()); + invalidate({0, 0, 20, 20}); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool CheckBox::onTouchEnd(coord_t x, coord_t y) +{ + setValue(!getValue()); + setFocus(); + invalidate({0, 0, 20, 20}); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h new file mode 100644 index 000000000..0e199400f --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _CHECKBOX_H_ +#define _CHECKBOX_H_ + +#include "form.h" + +class CheckBox : public FormField { + public: + CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, + LcdFlags flags = 0) : + FormField(parent, rect), + getValue(getValue), + setValue(setValue) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "CheckBox"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + const char * label; + std::function getValue; + std::function setValue; +}; + +#endif // _CHECKBOX_H_ diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp new file mode 100644 index 000000000..2c3fe383f --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "choice.h" +#include "menu.h" + +const uint8_t LBM_DROPDOWN[] = { +#include "mask_dropdown.lbm" +}; + +Choice::Choice(Window * parent, const rect_t &rect, const char * values, int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue, LcdFlags flags) : + FormField(parent, rect), + values(values), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)), + flags(flags) +{ +} + +void Choice::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else + textColor = 0; + + if (textHandler) + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, textHandler(getValue()).c_str(), textColor); + else + dc->drawTextAtIndex(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, values, getValue() - vmin, flags | textColor); + + dc->drawBitmapPattern(rect.w - 14, (rect.h - 5) / 2, LBM_DROPDOWN, textColor); +} + +#if defined(HARDWARE_KEYS) +void Choice::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + invalidate(); + openMenu(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +void Choice::openMenu() +{ + auto menu = new Menu(); + auto value = getValue(); + int count = 0; + int current = -1; + + for (int i = vmin; i <= vmax; ++i) { + if (isValueAvailable && !isValueAvailable(i)) + continue; + if (textHandler) { + menu->addLine(textHandler(i), [=]() { + setValue(i); + }); + } + else { + menu->addLine(TEXT_AT_INDEX(values, i - vmin), [=]() { + setValue(i); + }); + } + if (value == i) { + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } + + menu->setCloseHandler([=]() { + editMode = false; + setFocus(); + }); +} + +#if defined(HARDWARE_TOUCH) +bool Choice::onTouchEnd(coord_t, coord_t) +{ + openMenu(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h new file mode 100644 index 000000000..2f644a6cc --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _CHOICE_H_ +#define _CHOICE_H_ + +#include +#include "form.h" + +class Choice : public FormField { + public: + Choice(Window * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Choice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void setSetValueHandler(std::function handler) + { + setValue = std::move(handler); + } + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + void setTextHandler(std::function handler) + { + textHandler = std::move(handler); + } + + protected: + const char * values; + int16_t vmin; + int16_t vmax; + std::function getValue; + std::function setValue; + std::function isValueAvailable; + std::function textHandler; + LcdFlags flags; + void openMenu(); +}; + +#endif // _CHOICE_H_ diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp new file mode 100644 index 000000000..326d79fc0 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "coloredit.h" +#include "numberedit.h" +#include "bitfield.h" + +constexpr uint8_t PART_BITS[3][2] = { {0, 5}, {5, 6}, {11, 5} }; + +class ColorBox: public Window { + public: + ColorBox(Window * parent, const rect_t & rect, LcdFlags color): + Window(parent, rect), + color(color) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ColorBox"; + } +#endif + + void paint(BitmapBuffer * dc) override + { + lcdSetColor(color); + dc->drawSolidFilledRect(0, 0, width(), height(), TEXT_COLOR); + dc->drawSolidFilledRect(1, 1, width() - 2, height() - 2, CUSTOM_COLOR); + } + + void setColor(LcdFlags value) + { + color = value; + invalidate(); + } + + protected: + LcdFlags color; +}; + +ColorEdit::ColorEdit(Window * parent, const rect_t & rect, std::function getValue, std::function setValue): + Window(parent, rect, FORWARD_SCROLL) +{ + auto width = rect.w / 4 - 5; + + // The color box + auto box = new ColorBox(this, {0, 0, rect.w - 3 * width, rect.h}, getValue()); + + // The 3 parts of RGB + for (uint8_t part = 0; part < 3; part++) { + new NumberEdit(this, {rect.w - (3 - part) * width + 5, 0, width - 5, rect.h}, 0, (1 << PART_BITS[part][1]) - 1, + [=]() { + return bfGet(getValue(), PART_BITS[part][0], PART_BITS[part][1]); + }, + [=](uint16_t newValue) { + uint16_t value = getValue(); + value = bfSet(value, newValue, PART_BITS[part][0], PART_BITS[part][1]); + setValue(value); + box->setColor(value); + }); + } + +} + +ColorEdit::~ColorEdit() +{ + deleteChildren(); +} diff --git a/radio/src/thirdparty/libopenui/src/coloredit.h b/radio/src/thirdparty/libopenui/src/coloredit.h new file mode 100644 index 000000000..e78d91785 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/coloredit.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _COLOREDIT_H_ +#define _COLOREDIT_H_ + +#include "window.h" + +class ColorEdit : public Window { + public: + ColorEdit(Window * parent, const rect_t & rect, std::function getValue, std::function setValue = nullptr); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ColorEdit"; + } +#endif + + ~ColorEdit() override; +}; + +#endif // _COLOREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp new file mode 100644 index 000000000..9bef8a708 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "dialog.h" +#include "mainwindow.h" +#include "form.h" + +Dialog::Dialog(std::string title, const rect_t rect): + Window(&mainWindow, rect, OPAQUE), + title(std::move(title)), + previousFocus(focusWindow) +{ + bringToTop(); + FormField::clearCurrentField(); + setFocus(); +} + +void Dialog::paint(BitmapBuffer * dc) +{ + dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, TEXT_COLOR); + dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, TITLE_BGCOLOR); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), MENU_TITLE_COLOR); + dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, TEXT_BGCOLOR_INDEX); +} + +void Dialog::deleteLater() +{ + if (previousFocus) { + previousFocus->setFocus(); + } + + Window::deleteLater(); +} diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h new file mode 100644 index 000000000..5ec3bf8aa --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _DIALOG_H_ +#define _DIALOG_H_ + +#include "window.h" + +class Dialog: public Window { + public: + Dialog(std::string title, const rect_t rect); + + void paint(BitmapBuffer * dc) override; + + void deleteLater(); + + protected: + std::string title; + Window * previousFocus; +}; + +#endif // _CONFIRMATION_H_ diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp new file mode 100644 index 000000000..2dc4fd580 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "filechoice.h" +#include "libopenui_file.h" +#include "menu.h" + +const uint8_t LBM_FOLDER[] = { +#include "mask_folder.lbm" +}; + +// comparison, not case sensitive. +bool compare_nocase(const std::string &first, const std::string &second) +{ + return strcasecmp(first.c_str(), second.c_str()) < 0; +} + +FileChoice::FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): + FormField(parent, rect), + folder(std::move(folder)), + extension(extension), + maxlen(maxlen), + getValue(std::move(getValue)), + setValue(std::move(setValue)) +{ +} + +void FileChoice::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + const char * displayedValue = getValue().c_str(); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else if (displayedValue[0] == '\0') + textColor = CURVE_AXIS_COLOR; + else + textColor = TEXT_COLOR; + + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue[0] == '\0' ? "---" : displayedValue, textColor); + dc->drawBitmapPattern(rect.w - 20, (rect.h - 11) / 2, LBM_FOLDER, textColor); +} + +void FileChoice::openMenu() +{ + FILINFO fno; + DIR dir; + std::list files; + const char * fnExt; + uint8_t fnLen, extLen; + + FRESULT res = f_opendir(&dir, folder.c_str()); // Open the directory + if (res == FR_OK) { + bool firstTime = true; + for (;;) { + res = sdReadDir(&dir, &fno, firstTime); + if (res != FR_OK || fno.fname[0] == 0) + break; // break on error or end of dir + if (fno.fattrib & AM_DIR) + continue; // skip subfolders + if (fno.fattrib & AM_HID) + continue; // skip hidden files + if (fno.fattrib & AM_SYS) + continue; // skip system files + + fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); + fnLen -= extLen; + + if (!fnLen || fnLen > maxlen) + continue; // wrong size + if (extension && !isExtensionMatching(fnExt, extension)) + continue; // wrong extension + + files.emplace_back(fno.fname, fnLen); + } + + if (files.size() > 0) { + // sort files + files.sort(compare_nocase); + + files.push_front(""); + + auto menu = new Menu(); + int count = 0; + int current = -1; + std::string value = getValue(); + for (const auto &file: files) { + menu->addLine(file, [=]() { + setValue(file); + }); + // TRACE("%s %d %s %d", value.c_str(), value.size(), file.c_str(), file.size()); + if (value.compare(file) == 0) { + // TRACE("OK"); + current = count; + } + ++count; + } + + if (current >= 0) { + menu->select(current); + } + + menu->setCloseHandler([=]() { + editMode = false; + setFocus(); + }); + } + else { + TRACE("NO FILES !!!!!"); + // TODO popup "NO FILES" + } + } +} + +#if defined(HARDWARE_KEYS) +void FileChoice::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + invalidate(); + openMenu(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool FileChoice::onTouchEnd(coord_t, coord_t) +{ + openMenu(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h new file mode 100644 index 000000000..7c5b1f958 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _FILECHOICE_H_ +#define _FILECHOICE_H_ + +#include "form.h" +#include + +class FileChoice: public FormField { + public: + FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FileChoice"; + } +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + std::string folder; + const char * extension; + int maxlen; + std::function getValue; + std::function setValue; + + void openMenu(); +}; + +#endif // _FILECHOICE_H_ diff --git a/radio/src/thirdparty/libopenui/src/font.h b/radio/src/thirdparty/libopenui/src/font.h new file mode 100644 index 000000000..6503e3c78 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/font.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _FONT_H_ +#define _FONT_H_ + +#include "libopenui_types.h" + +uint8_t getMappedChar(uint8_t c); +uint8_t getFontHeight(LcdFlags flags); +int getCharWidth(uint8_t c, const uint16_t * spec); +int getTextWidth(const char * s, int len = 0, LcdFlags flags = 0); + +#endif // _FONT_H_ diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp new file mode 100644 index 000000000..96712696e --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "form.h" +#include "bitmapbuffer.h" +#include "libopenui_config.h" + +FormField * FormField::current = nullptr; + +FormField::FormField(Window *parent, const rect_t &rect, uint8_t flags) : + Window(parent, rect, flags) +{ + if (current) { + setPreviousField(current); + current->setNextField(this); + } + + FormWindow * form = dynamic_cast(parent); + if (form && !form->getFirstField()) + form->setFirstField(this); + + current = this; +} + +#if defined(HARDWARE_KEYS) +void FormWindow::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_EXIT) && first && getFocus() != first) { + first->setFocus(); + } + else { + Window::onKeyEvent(event); + } +} + +void FormField::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", FormField::getWindowDebugString().c_str(), event); + + if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { + if (next) { + next->setFocus(); + } + } + else if (event == EVT_ROTARY_LEFT/*EVT_KEY_BREAK(KEY_UP)*/) { + if (previous) { + previous->setFocus(); + } + } + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = !editMode; + invalidate(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { + editMode = false; + invalidate(); + } + else { + Window::onKeyEvent(event); + } +} + +void FormGroup::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + first->setFocus(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { + editMode = false; + setFocus(); + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +void FormField::paint(BitmapBuffer * dc) +{ + if (editMode) { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, TEXT_INVERTED_BGCOLOR); + } + else if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } +} + +void FormGroup::paint(BitmapBuffer * dc) +{ + if (!editMode && hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + } + else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + } +} diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h new file mode 100644 index 000000000..c6087d5d4 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -0,0 +1,197 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _FORM_H_ +#define _FORM_H_ + +#include "window.h" + +class FormField: public Window { + friend class FormWindow; + + public: + FormField(Window * parent, const rect_t & rect, uint8_t flags=0); + + inline void setNextField(FormField *field) + { + next = field; + } + + inline void setPreviousField(FormField * field) + { + previous = field; + } + + inline static void setCurrentField(FormField * field) + { + current = field; + } + + inline static void clearCurrentField() + { + setCurrentField(nullptr); + }; + + inline static FormField * getCurrentField() + { + return current; + }; + + inline FormField * getPreviousField() + { + return previous; + }; + + inline FormField * getNextField() + { + return next; + }; + + inline static void link(FormField * previous, FormField * next) + { + previous->setNextField(next); + next->setPreviousField(previous); + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + protected: + static FormField * current; + FormField * next = nullptr; + FormField * previous = nullptr; + bool editMode = false; +}; + +class FormGroup: public FormField { + public: + FormGroup(Window * parent, const rect_t & rect, uint8_t flags=0) : + FormField(parent, rect, flags) + { + FormField::current = nullptr; + } + + ~FormGroup() + { + deleteChildren(); + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FormGroup"; + } +#endif + + void clear() + { + Window::clear(); + first = nullptr; + FormField::current = nullptr; + } + + inline void setFirstField(FormField * field) + { + first = field; + } + + inline void setLastField(FormField * field = nullptr) + { + // by default the current field is the last one + if (!field) + field = getCurrentField(); + + FormField::link(field, first); + + // now the current field is the group, it will be linked to the next one + FormField::setCurrentField(this); + } + + FormField * getFirstField() + { + return first; + } + + protected: + FormField * first = nullptr; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override; +}; + +class FormWindow: public Window { + public: + FormWindow(Window * parent, const rect_t & rect) : + Window(parent, rect) + { + FormField::current = nullptr; + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FormWindow"; + } +#endif + + void clear() + { + first = nullptr; + Window::clear(); + } + + FormField * getFirstField() + { + return first; + } + + inline void setFirstField(FormField * field) + { + first = field; + field->setFocus(); + } + + inline void setLastField(FormField * field = nullptr) + { + if (!field) + field = FormField::getCurrentField(); + FormField::link(field, first); + } + + protected: + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override + { + } + + protected: + FormField * first = nullptr; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/gridlayout.h b/radio/src/thirdparty/libopenui/src/gridlayout.h new file mode 100644 index 000000000..9134b7c72 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/gridlayout.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _GRIDLAYOUT_H_ +#define _GRIDLAYOUT_H_ + +#include "window.h" + +class GridLayout { + public: + GridLayout(coord_t width): + width(width) + { + } + + GridLayout(Window * window): + width(window->width()) + { + } + + rect_t getSlot(uint8_t count = 1, uint8_t index = 0) const + { + coord_t width = (this->width - (count - 1) * PAGE_LINE_SPACING) / count; + coord_t left = (width + PAGE_LINE_SPACING) * index; + return {left, currentY, width, PAGE_LINE_HEIGHT}; + } + + void spacer(coord_t height=PAGE_LINE_SPACING) + { + currentY += height; + } + + void nextLine(coord_t height=PAGE_LINE_HEIGHT) + { + spacer(height + PAGE_LINE_SPACING); + } + + void addWindow(Window * window) + { + window->adjustHeight(); + currentY += window->rect.h + PAGE_LINE_SPACING; + } + + coord_t getWindowHeight() const + { + return currentY; + } + + protected: + coord_t width; + coord_t currentY = 0; +}; + +class FormGridLayout: public GridLayout { + public: + FormGridLayout(coord_t width = LCD_W): + GridLayout(width) + { + } + + void setLabelWidth(coord_t value) + { + labelWidth = value; + } + + void setMarginLeft(coord_t value) + { + lineMarginLeft = value; + } + + void setMarginRight(coord_t value) + { + lineMarginRight = value; + } + + rect_t getLineSlot() + { + return { lineMarginLeft, currentY, this->width - lineMarginRight - lineMarginLeft, PAGE_LINE_HEIGHT }; + } + + rect_t getCenteredSlot(coord_t width = 0 ) const + { + if (width == 0) + width = this->width; + return { lineMarginLeft + (this->width - width)/2, currentY, min(width, this->width - lineMarginRight - lineMarginLeft), PAGE_LINE_HEIGHT }; + } + + rect_t getLabelSlot(bool indent = false) const + { + coord_t left = indent ? lineMarginLeft + PAGE_INDENT_WIDTH : lineMarginLeft; + return { left, currentY, labelWidth - left, PAGE_LINE_HEIGHT }; + } + + rect_t getFieldSlot(uint8_t count = 1, uint8_t index = 0) const + { + coord_t width = (this->width - labelWidth - lineMarginRight - (count - 1) * PAGE_LINE_SPACING) / count; + coord_t left = labelWidth + (width + PAGE_LINE_SPACING) * index; + return {left, currentY, width, PAGE_LINE_HEIGHT}; + } + + protected: + coord_t labelWidth = PAGE_LABEL_WIDTH; + coord_t lineMarginLeft = PAGE_PADDING; + coord_t lineMarginRight = 10; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h new file mode 100644 index 000000000..3fdbf0d5a --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _KEYBOARD_BASE_H_ +#define _KEYBOARD_BASE_H_ + +#include "mainwindow.h" +#include "libopenui_types.h" + +class Page; +class TabsGroup; + +template +class Keyboard: public Window { + public: + explicit Keyboard(coord_t height): + Window(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) + { + } + + void setField(T * newField) + { + field = newField; + attach(&mainWindow); + Window * w = getPageBody(); + w->setHeight(LCD_H - height() - w->top()); + w->scrollTo(newField); + invalidate(); + } + + void disable(bool resizePage) + { + detach(); + if (field) { + Window * w = getPageBody(); + w->setHeight(LCD_H - 0 - w->top()); + field = nullptr; + } + } + + T * getField() const + { + return field; + } + + protected: + T * field = nullptr; + Window * getPageBody() + { + Window * parent = field; + while (true) { + Window * tmp = parent->getParent(); + if ((tmp->getWindowFlags() & OPAQUE) && tmp->width() == LCD_W && tmp->height() == LCD_H) { + return parent; + } + parent = tmp; + } + } +}; + +#endif // _KEYBOARD_BASE_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp new file mode 100644 index 000000000..ead6f270b --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "keyboard_number.h" +#include "button.h" +#include "basenumberedit.h" + +constexpr coord_t KEYBOARD_HEIGHT = 90; + +NumberKeyboard * NumberKeyboard::_instance = nullptr; + +NumberKeyboard::NumberKeyboard(): + Keyboard(KEYBOARD_HEIGHT) +{ + new TextButton(this, { LCD_W/2 - 115, 10, 50, 30 }, "<<", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() - 10 * field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 55, 10, 50, 30 }, "-", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() - field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 5, 10, 50, 30 }, "+", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() + field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 65, 10, 50, 30 }, ">>", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getValue() + 10 * field->getStep()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 115, 50, 50, 30 }, "MIN", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getMin()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 + 65, 50, 50, 30 }, "MAX", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getMax()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + + new TextButton(this, { LCD_W/2 - 55, 50, 110, 30 }, "DEFAULT", + [=]() -> uint8_t { + if (field) { + field->setValue(field->getDefault()); + } + return 0; + }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); +} + +NumberKeyboard::~NumberKeyboard() +{ + _instance = nullptr; +} + +void NumberKeyboard::paint(BitmapBuffer * dc) +{ + lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + dc->clear(CUSTOM_COLOR); +} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h new file mode 100644 index 000000000..2d6cf63e0 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _KEYBOARD_NUMBER_H_ +#define _KEYBOARD_NUMBER_H_ + +#include "keyboard_base.h" +#include "basenumberedit.h" + +class NumberKeyboard : public Keyboard { + friend class BaseNumberEdit; + + public: + NumberKeyboard(); + + ~NumberKeyboard() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "NumberKeyboard"; + } +#endif + + static NumberKeyboard * instance() + { + if (!_instance) + _instance = new NumberKeyboard(); + return _instance; + } + + void paint(BitmapBuffer * dc) override; + + protected: + static NumberKeyboard * _instance; +}; + +#endif // _KEYBOARD_NUMBER_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp new file mode 100644 index 000000000..378e95758 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "keyboard_text.h" +#include "libopenui_globals.h" +#include "textedit.h" +#include "font.h" + +constexpr coord_t KEYBOARD_HEIGHT = 160; + +TextKeyboard * TextKeyboard::_instance = nullptr; + +const uint8_t LBM_KEY_UPPERCASE[] = { +#include "mask_key_uppercase.lbm" +}; + +const uint8_t LBM_KEY_LOWERCASE[] = { +#include "mask_key_lowercase.lbm" +}; + +const uint8_t LBM_KEY_BACKSPACE[] = { +#include "mask_key_backspace.lbm" +}; + +const uint8_t LBM_KEY_LETTERS[] = { +#include "mask_key_letters.lbm" +}; + +const uint8_t LBM_KEY_NUMBERS[] = { +#include "mask_key_numbers.lbm" +}; + +const uint8_t LBM_KEY_SPACEBAR[] = { +#include "mask_key_spacebar.lbm" +}; + +const uint8_t * const LBM_SPECIAL_KEYS[] = { + LBM_KEY_BACKSPACE, + LBM_KEY_UPPERCASE, + LBM_KEY_LOWERCASE, + LBM_KEY_LETTERS, + LBM_KEY_NUMBERS, +}; + +const char * const KEYBOARD_LOWERCASE[] = { + "qwertyuiop", + " asdfghjkl", + "\201zxcvbnm\200", + "\204\t\n" +}; + +const char * const KEYBOARD_UPPERCASE[] = { + "QWERTYUIOP", + " ASDFGHJKL", + "\202ZXCVBNM\200", + "\204\t\n" +}; + +const char * const KEYBOARD_NUMBERS[] = { + "1234567890", + "_-", + " \200", + "\203\t\n" +}; + +const char * const * const KEYBOARD_LAYOUTS[] = { + KEYBOARD_UPPERCASE, + KEYBOARD_LOWERCASE, + KEYBOARD_LOWERCASE, + KEYBOARD_NUMBERS, +}; + +TextKeyboard::TextKeyboard(): + Keyboard(KEYBOARD_HEIGHT), + layout(KEYBOARD_LOWERCASE) +{ +} + +TextKeyboard::~TextKeyboard() +{ + _instance = nullptr; +} + +void TextKeyboard::setCursorPos(coord_t x) +{ + if (!field) + return; + + uint8_t size = field->getMaxLength(); + char * data = field->getData(); + coord_t rest = x; + for (cursorIndex = 0; cursorIndex < size; cursorIndex++) { + if (data[cursorIndex] == '\0') + break; + char c = data[cursorIndex]; + uint8_t w = getCharWidth(c, fontspecsTable[0]); + if (rest < w) + break; + rest -= w; + } + cursorPos = x - rest; + field->invalidate(); +} + +void TextKeyboard::paint(BitmapBuffer * dc) +{ + lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + dc->clear(CUSTOM_COLOR); + + for (uint8_t i=0; i<4; i++) { + coord_t y = 15 + i * 40; + coord_t x = 15; + const char * c = layout[i]; + while(*c) { + if (*c == ' ') { + x += 15; + } + else if (*c == '\t') { + // spacebar + dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, TEXT_COLOR); + x += 135; + } + else if (*c == '\n') { + // enter + dc->drawSolidFilledRect(x, y-2, 80, 25, TEXT_DISABLE_COLOR); + dc->drawText(x+40, y, "ENTER", CENTERED); + x += 80; + } + else if (int8_t(*c) < 0) { + dc->drawBitmapPattern(x, y, LBM_SPECIAL_KEYS[uint8_t(*c - 128)], TEXT_COLOR); + x += 45; + } + else { + dc->drawSizedText(x, y, c, 1); + x += 30; + } + c++; + } + } +} + +bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) +{ + if (!field) + return false; + + uint8_t size = field->getMaxLength(); + char * data = field->getData(); + + char c = 0; + + uint8_t row = max(0, y - 5) / 40; + const char * key = layout[row]; + while(*key) { + if (*key == ' ') { + x -= 15; + } + else if (*key == '\t') { + if (x <= 135) { + c = ' '; + break; + } + x -= 135; + } + else if (*key == '\n') { + if (x <= 80) { + // enter + disable(true); + return true; + } + x -= 80; + } + else if (int8_t(*key) < 0) { + if (x <= 45) { + uint8_t specialKey = *key; + if (specialKey == 128) { + // backspace + if (cursorIndex > 0) { + char c = data[cursorIndex - 1]; + memmove(data + cursorIndex - 1, data + cursorIndex, size - cursorIndex); + data[size - 1] = '\0'; + cursorPos -= getCharWidth(c, fontspecsTable[0]); + --cursorIndex; + } + } + else { + layout = KEYBOARD_LAYOUTS[specialKey - 129]; + invalidate(); + } + break; + } + x -= 45; + } + else { + if (x <= 30) { + c = *key; + break; + } + x -= 30; + } + key++; + } + + if (c && size - cursorIndex > 1) { + memmove(data + cursorIndex + 1, data + cursorIndex, size - cursorIndex - 1); + data[cursorIndex++] = c; + cursorPos += getCharWidth(c, fontspecsTable[0]); + } + + field->invalidate(); + return true; +} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h new file mode 100644 index 000000000..36daf8672 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _KEYBOARD_TEXT_H_ +#define _KEYBOARD_TEXT_H_ + +#include "keyboard_base.h" +#include "textedit.h" + +class TextKeyboard : public Keyboard { + friend class TextEdit; + + public: + TextKeyboard(); + + ~TextKeyboard() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextKeyboard"; + } +#endif + + static TextKeyboard * instance() { + if (!_instance) + _instance = new TextKeyboard(); + return _instance; + } + + coord_t getCursorPos() const + { + return cursorPos; + } + + void setCursorPos(coord_t x); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + static TextKeyboard * _instance; + coord_t cursorPos = 0; + uint8_t cursorIndex = 0; + const char * const * layout; +}; + +#endif // _KEYBOARD_TEXT_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h new file mode 100644 index 000000000..a65f59d36 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_DEFINES_H_ +#define _LIBOPEUI_DEFINES_H_ + +#include "libopenui_types.h" + +/* lcd common flags */ +#define BLINK 0x01 + +/* drawText flags */ +#define INVERS 0x02 +#define LEFT 0x00 /* align left */ +#define CENTERED 0x04 /* align center */ +#define RIGHT 0x08 /* align right */ +#define SHADOWED 0x80 /* black copy at +1 +1 */ + +/* drawNumber flags */ +#define LEADING0 0x10 +#define PREC1 0x20 +#define PREC2 0x30 +#define MODE(flags) ((((int8_t)(flags) & 0x30) - 0x10) >> 4) + +/* rect, square flags */ +#define ROUND 0x04 + +/* telemetry flags */ +#define NO_UNIT 0x40 + +enum FontSizeIndex +{ + STDSIZE_INDEX, + TINSIZE_INDEX, + SMLSIZE_INDEX, + MIDSIZE_INDEX, + DBLSIZE_INDEX, + XXLSIZE_INDEX, + SPARE6_INDEX, + SPARE7_INDEX, + STDSIZE_BOLD_INDEX, + SPARE9_INDEX, + SPAREa_INDEX, + SPAREb_INDEX, + SPAREc_INDEX, + SPAREd_INDEX, + SPAREe_INDEX, + SPAREf_INDEX, +}; + +#define STDSIZE (STDSIZE_INDEX << 8) +#define TINSIZE (TINSIZE_INDEX << 8) +#define SMLSIZE (SMLSIZE_INDEX << 8) +#define MIDSIZE (MIDSIZE_INDEX << 8) +#define DBLSIZE (DBLSIZE_INDEX << 8) +#define XXLSIZE (XXLSIZE_INDEX << 8) +#define BOLD (STDSIZE_BOLD_INDEX << 8) +#define FONTSIZE_MASK 0x0f00 + +#if !defined(BOOT) +#define FONTSIZE(flags) ((flags) & FONTSIZE_MASK) +#define FONTINDEX(flags) (FONTSIZE(flags) >> 8) +#else +#define FONTSIZE(flags) STDSIZE +#define FONTINDEX(flags) STDSIZE_INDEX +#endif + +#define TIMEBLINK 0x1000 +#define TIMEHOUR 0x2000 +#define EXPANDED 0x2000 +#define VERTICAL 0x4000 + +#define ARGB_SPLIT(color, a, r, g, b) \ + uint16_t a = ((color) & 0xF000) >> 12; \ + uint16_t r = ((color) & 0x0F00) >> 8; \ + uint16_t g = ((color) & 0x00F0) >> 4; \ + uint16_t b = ((color) & 0x000F) + +#define RGB_SPLIT(color, r, g, b) \ + uint16_t r = ((color) & 0xF800) >> 11; \ + uint16_t g = ((color) & 0x07E0) >> 5; \ + uint16_t b = ((color) & 0x001F) + +#define RGB_JOIN(r, g, b) \ + (((r) << 11) + ((g) << 5) + (b)) + +#define GET_RED(color) \ + (((color) & 0xF800) >> 8) + +#define GET_GREEN(color) \ + (((color) & 0x07E0) >> 3) + +#define GET_BLUE(color) \ + (((color) & 0x001F) << 3) + +// remove windows default definitions +#undef OPAQUE +#undef RGB + +#define OPACITY_MAX 0x0F +#define OPACITY(x) ((x)<<24) + +#define RGB(r, g, b) (uint16_t)((((r) & 0xF8) << 8) + (((g) & 0xFC) << 3) + (((b) & 0xF8) >> 3)) +#define ARGB(a, r, g, b) (uint16_t)((((a) & 0xF0) << 8) + (((r) & 0xF0) << 4) + (((g) & 0xF0) << 0) + (((b) & 0xF0) >> 4)) + +#define COLOR(index) LcdFlags((index) << 16) +#define COLOR_IDX(att) uint8_t((att) >> 16) + +#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h new file mode 100644 index 000000000..e2c529126 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_DEPENDS_H_ +#define _LIBOPEUI_DEPENDS_H_ + +#include +#include "libopenui_config.h" + +void lcdNextLayer(); +void lcdSetColor(uint16_t color); +void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void onKeyPress(); +void onKeyError(); + +#endif // _LIBOPEUI_DEPENDS_H_ diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp new file mode 100644 index 000000000..d01a1db3c --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include +#include +#include "libopenui_file.h" + +const char * getFileExtension(const char * filename, uint8_t size, uint8_t extMaxLen, uint8_t * fnlen, uint8_t * extlen) +{ + int len = size; + if (!size) { + len = strlen(filename); + } + if (!extMaxLen) { + extMaxLen = LEN_FILE_EXTENSION_MAX; + } + if (fnlen != nullptr) { + *fnlen = (uint8_t)len; + } + for (int i=len-1; i >= 0 && len-i <= extMaxLen; --i) { + if (filename[i] == '.') { + if (extlen) { + *extlen = len-i; + } + return &filename[i]; + } + } + if (extlen != nullptr) { + *extlen = 0; + } + return nullptr; +} + +// returns true if current working dir is at the root level +bool isCwdAtRoot() +{ + char path[10]; + if (f_getcwd(path, sizeof(path)-1) == FR_OK) { + return (strcasecmp("/", path) == 0); + } + return false; +} + +/* + Wrapper around the f_readdir() function which + also returns ".." entry for sub-dirs. (FatFS 0.12 does + not return ".", ".." dirs anymore) +*/ +FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime) +{ + FRESULT res; + if (firstTime && !isCwdAtRoot()) { + // fake parent directory entry + strcpy(fno->fname, ".."); + fno->fattrib = AM_DIR; + res = FR_OK; + } + else { + res = f_readdir(dir, fno); /* Read a directory item */ + } + firstTime = false; + return res; +} + diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h new file mode 100644 index 000000000..0aca1a8cf --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_FILE_H_ +#define _LIBOPEUI_FILE_H_ + +#include +#include "ff.h" + +constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the dot, excluding null term. + +const char * getFileExtension(const char * filename, uint8_t size = 0, uint8_t extMaxLen = 0, uint8_t * fnlen = nullptr, uint8_t * extlen = nullptr); +bool isExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); +FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime); + +#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp b/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp new file mode 100644 index 000000000..90cefa0ba --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "libopenui_globals.h" + +uint16_t lcdColorTable[LCD_COLOR_COUNT]; + diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.h b/radio/src/thirdparty/libopenui/src/libopenui_globals.h new file mode 100644 index 000000000..b4900e5ac --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_GLOBALS_H_ +#define _LIBOPEUI_GLOBALS_H_ + +#include +#include "libopenui_config.h" + +extern uint16_t lcdColorTable[LCD_COLOR_COUNT]; +extern const uint16_t * const fontspecsTable[FONT_TABLE_SIZE]; +extern const uint8_t * fontsTable[FONT_TABLE_SIZE]; + +#endif // _LIBOPEUI_GLOBALS_H_ diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.cpp b/radio/src/thirdparty/libopenui/src/libopenui_helpers.cpp new file mode 100644 index 000000000..196771132 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "libopenui_helpers.h" + diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h new file mode 100644 index 000000000..ed70ddd07 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_HELPERS_H_ +#define _LIBOPEUI_HELPERS_H_ + +#include + +template +inline T min(T a, T b) +{ + return a < b ? a : b; +} + +template +inline T max(T a, T b) +{ + return a > b ? a : b; +} + +template +inline T limit(T vmin, T x, T vmax) +{ + return min(max(vmin, x), vmax); +} + +inline int divRoundClosest(const int n, const int d) +{ + if (d == 0) + return 0; + else + return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d); +} + +inline std::string TEXT_AT_INDEX(const char * val, uint8_t idx) +{ + const char * start = val + 1 + idx * val[0]; + return std::string(start, min(val[0], strlen(start))); +} + +#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_types.h b/radio/src/thirdparty/libopenui/src/libopenui_types.h new file mode 100644 index 000000000..5cf077897 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_types.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _LIBOPEUI_TYPES_H_ +#define _LIBOPEUI_TYPES_H_ + +#include + +typedef uint16_t pixel_t; + +typedef int coord_t; + +struct point_t +{ + coord_t x; + coord_t y; +}; + +struct rect_t +{ + coord_t x, y, w, h; + + constexpr coord_t left() const + { + return x; + } + + constexpr coord_t right() const + { + return x + w; + } + + constexpr coord_t top() const + { + return y; + } + + constexpr coord_t bottom() const + { + return y + h; + } +}; + +typedef uint32_t LcdFlags; +typedef uint16_t event_t; + +#endif // _LIBOPEUI_TYPES_H_ diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp new file mode 100644 index 000000000..a0fe22406 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "mainwindow.h" + +#if defined(HARDWARE_TOUCH) +#include "touch.h" +#endif + +MainWindow mainWindow; + +#if defined(HARDWARE_TOUCH) +STRUCT_TOUCH touchState; +#endif + +void MainWindow::emptyTrash() +{ + for (auto window: trash) { + delete window; + } + trash.clear(); +} + +void MainWindow::checkEvents(bool trash) +{ +#if defined(HARDWARE_TOUCH) + if (touchState.Event == TE_DOWN) { + onTouchStart(touchState.X + scrollPositionX, touchState.Y + scrollPositionY); + // touchState.Event = TE_NONE; + } + else if (touchState.Event == TE_UP) { + touchState.Event = TE_NONE; + onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); + } + else if (touchState.Event == TE_SLIDE) { + coord_t x = touchState.X - touchState.lastX; + coord_t y = touchState.Y - touchState.lastY; + onTouchSlide(touchState.X, touchState.Y, touchState.startX, touchState.startY, x, y); + touchState.lastX = touchState.X; + touchState.lastY = touchState.Y; + } +#endif + + Window::checkEvents(); + + if (trash) { + emptyTrash(); + } +} + +void MainWindow::invalidate(const rect_t & rect) +{ + if (invalidatedRect.w) { + coord_t left = min(invalidatedRect.left(), rect.left()); + coord_t right = max(invalidatedRect.right(), rect.right()); + coord_t top = min(invalidatedRect.top(), rect.top()); + coord_t bottom = max(invalidatedRect.bottom(), rect.bottom()); + invalidatedRect = {left, top, right - left, bottom - top}; + } + else { + invalidatedRect = rect; + } +} + +bool MainWindow::refresh() +{ + if (invalidatedRect.w) { + if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { + TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); + BitmapBuffer * previous = lcd; + lcdNextLayer(); + lcdCopy(lcd->getData(), previous->getData()); + } + else { + TRACE_WINDOWS("Refresh full screen"); + lcdNextLayer(); + } + lcd->setOffset(0, 0); + lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); + fullPaint(lcd); + invalidatedRect.w = 0; + return true; + } + else { + return false; + } +} + +void MainWindow::run(bool trash) +{ + checkEvents(trash); + if (refresh()) { + lcdRefresh(); + } +} diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h new file mode 100644 index 000000000..31bae69df --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MAINWINDOW_H_ +#define _MAINWINDOW_H_ + +#include +#include "window.h" +#include "bitmapbuffer.h" + +class MainWindow: public Window { + public: + MainWindow(): + Window(nullptr, {0, 0, LCD_W, LCD_H}), + invalidatedRect(rect) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "MainWindow"; + } +#endif + + void checkEvents(bool trash=true); + + void invalidate() + { + invalidate({0, 0, rect.w, rect.h}); + } + + void invalidate(const rect_t & rect) override; + + bool refresh(); + + void run(bool trash=true); + + protected: + void emptyTrash(); + rect_t invalidatedRect; + const char * shutdown = nullptr; +}; + +extern MainWindow mainWindow; + +#endif // _MAINWINDOW_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp new file mode 100644 index 000000000..f4c1bf3d8 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "menu.h" + +MenuWindow::MenuWindow(Menu * parent): + Window(parent, {LCD_W / 2 - 100, LCD_H / 2, 200, 0}, OPAQUE) +{ + setFocus(); +} + +void MenuWindow::select(int index) +{ + selectedIndex = index; + if (innerHeight > height()) { + setScrollPositionY(lineHeight * index - 3 * lineHeight); + } + invalidate(); +} + +#if defined(HARDWARE_KEYS) +void MenuWindow::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_ROTARY_RIGHT) { + select((selectedIndex + 1) % lines.size()); + } + else if (event == EVT_ROTARY_LEFT) { + select(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1); + } + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + Window::onKeyEvent(event); // the window above will be closed on event + lines[selectedIndex].onPress(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + if (onCancel) + onCancel(); + Window::onKeyEvent(event); + } + else { + Window::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool MenuWindow::onTouchEnd(coord_t x, coord_t y) +{ + int index = y / lineHeight; + lines[index].onPress(); + return false; // = close the menu (inverted so that click outside the menu closes it) +} +#endif + +void MenuWindow::paint(BitmapBuffer * dc) +{ + int width = (innerHeight > height() ? 195 : 200); + dc->clear(HEADER_BGCOLOR); + for (unsigned i = 0; i < lines.size(); i++) { + auto & line = lines[i]; + LcdFlags flags = (selectedIndex == (int) i ? WARNING_COLOR : MENU_TITLE_COLOR); + if (line.drawLine) { + line.drawLine(dc, 0, i * lineHeight /*+ (lineHeight - 20) / 2*/, flags); + } + else { + const char *text = line.text.data(); + dc->drawText(10, i * lineHeight + (lineHeight - 20) / 2, text[0] == '\0' ? "---" : text, flags); + } + if (i > 0) { + dc->drawSolidHorizontalLine(0, i * lineHeight - 1, width, CURVE_AXIS_COLOR); + } + } +} + +void Menu::updatePosition() +{ + if (!toolbar) { + // there is no navigation bar at the left, we may center the window on screen + int count = min(MenuWindow::maxLines, menuWindow.lines.size()); + coord_t h = count * MenuWindow::lineHeight - 1; + menuWindow.setTop((LCD_H - h) / 2 + 20); + menuWindow.setHeight(h); + } + menuWindow.setInnerHeight(menuWindow.lines.size() * MenuWindow::lineHeight - 1); +} + +void Menu::addLine(const std::string & text, std::function onPress) +{ + menuWindow.addLine(text, std::move(onPress)); + updatePosition(); +} + +void Menu::addLine(std::function drawLine, std::function onPress) +{ + menuWindow.addLine(drawLine, std::move(onPress)); + updatePosition(); +} + +void Menu::removeLines() +{ + menuWindow.removeLines(); + updatePosition(); +} + +#if defined(HARDWARE_KEYS) +void Menu::onKeyEvent(event_t event) +{ + if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN))) { + toolbar->onKeyEvent(event); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { + deleteLater(); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Menu::onTouchEnd(coord_t x, coord_t y) +{ + if (!Window::onTouchEnd(x, y)) { + deleteLater(); + } + return true; +} + +bool Menu::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h new file mode 100644 index 000000000..25e7f7184 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MENU_H_ +#define _MENU_H_ + +#include +#include +#include +#include +#include "mainwindow.h" + +class Menu; + +class MenuWindow: public Window { + friend class Menu; + + class MenuLine { + friend class MenuWindow; + + public: + MenuLine(std::string text, std::function onPress): + text(std::move(text)), + onPress(std::move(onPress)) + { + } + + MenuLine(std::function drawLine, std::function onPress): + drawLine(std::move(drawLine)), + onPress(std::move(onPress)) + { + } + + MenuLine(MenuLine &) = delete; + + MenuLine(MenuLine &&) = default; + + protected: + std::string text; + std::function drawLine; + std::function onPress; + }; + + public: + MenuWindow(Menu * parent); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "MenuWindow"; + } +#endif + + void setCancelHandler(std::function handler) + { + onCancel = handler; + } + + void addLine(const std::string & text, std::function onPress) + { + lines.emplace_back(text, onPress); + invalidate(); + } + + void addLine(std::function drawLine, std::function onPress) + { + lines.emplace_back(drawLine, onPress); + invalidate(); + } + + void removeLines() + { + lines.clear(); + invalidate(); + } + + void select(int index); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + std::vector lines; +#if defined(HARDWARE_TOUCH) + int selectedIndex = -1; +#else + int selectedIndex = 0; +#endif + std::function onCancel; + static constexpr uint8_t maxLines = 7; // TODO NV14 was 8 + static constexpr uint8_t lineHeight = 30; // TODO NV14 was 40 +}; + +class Menu : public Window { + public: + Menu() : + Window(&mainWindow, {0, 0, LCD_W, LCD_H}, TRANSPARENT), +#if !defined(HARDWARE_TOUCH) + previousFocus(focusWindow), +#endif + menuWindow(this) + { + } + + ~Menu() override + { + delete toolbar; + } + + void setCancelHandler(std::function handler) + { + menuWindow.setCancelHandler(handler); + } + + void deleteLater() + { +#if !defined(HARDWARE_TOUCH) + if (previousFocus) { + previousFocus->setFocus(); + } +#endif + Window::deleteLater(); + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Menu"; + } +#endif + + void setToolbar(Window * window) + { + toolbar = window; + menuWindow.setLeft(toolbar->right()); + menuWindow.setTop(toolbar->top()); + menuWindow.setHeight(toolbar->height()); + } + + void addLine(const std::string & text, std::function onPress); + + void addLine(std::function drawLine, std::function onPress); + + void removeLines(); + + void select(int index) + { + menuWindow.select(index); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override + { + return true; + } + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; +#endif + + protected: + Window * previousFocus; + MenuWindow menuWindow; + Window * toolbar = nullptr; + void updatePosition(); +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp new file mode 100644 index 000000000..4834d56ac --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "menutoolbar.h" +#include "font.h" + +void MenuToolbarButton::paint(BitmapBuffer * dc) +{ + if (checked()) { + dc->drawSolidFilledRect(MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, HEADER_BGCOLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | MENU_TITLE_COLOR); + } + else { + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | TEXT_COLOR); + } +} \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h new file mode 100644 index 000000000..4ca1de84c --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MENUTOOLBAR_H_ +#define _MENUTOOLBAR_H_ + +#include "button.h" +#include "menu.h" + +int getFirstAvailable(int min, int max, std::functionisValueAvailable); + +class MenuToolbarButton: public Button { + public: + MenuToolbarButton(Window * window, const rect_t & rect, char picto): +#if defined(HARDWARE_TOUCH) + Button(window, rect, nullptr, BUTTON_CHECKED_ON_FOCUS), +#else + Button(window, rect, nullptr), +#endif + picto(picto) + { + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override + { + if (hasFocus()) { + check(false); + clearFocus(); + } + else { + setFocus(); + } + onPress(); + return true; + } +#endif + + void check(bool checked=true) + { + Button::check(checked); + onPress(); + Button::check(checked); + } + + protected: + char picto; +}; + +template +class MenuToolbar: public Window { + friend T; + + public: + MenuToolbar(T * choice, Menu * menu): + Window(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR), + choice(choice), + menu(menu) + { + FormField::clearCurrentField(); + } + + ~MenuToolbar() override + { + deleteChildren(); + } + + void paint(BitmapBuffer * dc) override + { + dc->clear(CURVE_AXIS_COLOR); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override + { + if (event == EVT_KEY_BREAK(KEY_PGDN)) { + if (current == children.end()) { + current = children.begin(); + } + else { + static_cast(*current)->check(false); + ++current; + } + if (current != children.end()) { + auto button = static_cast(*current); + button->check(true); + scrollTo(button); + } + else { + setScrollPositionY(0); + } + } + else if (event == EVT_KEY_LONG(KEY_PGDN)) { + killEvents(event); + if (current == children.end()) { + --current; + } + else { + static_cast(*current)->check(false); + if (current == children.begin()) + current = children.end(); + else + --current; + } + if (current != children.end()) { + auto button = static_cast(*current); + button->check(true); + scrollTo(button); + } + else { + setScrollPositionY(0); + } + } + } +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override + { + Window::onTouchEnd(x, y); + return true; // = don't close the menu (inverted so that click outside the menu closes it) + } +#endif + + protected: + std::list::iterator current = children.end(); + T * choice; + Menu * menu; + coord_t y = 0; // TODO NV14 was 5; + + void addButton(char picto, int16_t filtermin, int16_t filtermax) + { + int vmin = choice->vmin; + int vmax = choice->vmax; + + if (vmin > filtermin || vmax < filtermin) + return; + + if (choice->isValueAvailable && getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == 0) + return; + + auto button = new MenuToolbarButton(this, {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}, picto); + button->setPressHandler([=]() { + if (button->checked()) { + choice->fillMenu(menu, [=](int16_t index) { + return index >= filtermin && index <= filtermax; + }); + } + else { + choice->fillMenu(menu); + } + return 1; + }); + + y += 30; + + setInnerHeight(y); + } +}; + +#endif // _MENUTOOLBAR_H_ + diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp new file mode 100644 index 000000000..d268d8513 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "numberedit.h" + +#if defined(HARDWARE_TOUCH) +#include "keyboard_number.h" +#endif + +NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, LcdFlags flags): + BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), flags) +{ +} + +void NumberEdit::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + LcdFlags textColor; + if (editMode) + textColor = TEXT_INVERTED_COLOR; + else if (hasFocus()) + textColor = TEXT_INVERTED_BGCOLOR; + else if (enabled) + textColor = TEXT_COLOR; + else + textColor = CURVE_AXIS_COLOR; + + int32_t value = getValue(); + if (displayFunction) + displayFunction(dc, textColor, value); + else if (value == 0 && !zeroText.empty()) + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | flags); + else + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | flags, 0, prefix.c_str(), suffix.c_str()); +} + +#if defined(HARDWARE_KEYS) +void NumberEdit::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + if (event == EVT_ROTARY_RIGHT) { + int value = getValue(); + do { + value += rotencSpeed * step; + } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); + if (value <= vmax) + setValue(value); + else + onKeyError(); + return; + } + else if (event == EVT_ROTARY_LEFT) { + int value = getValue(); + do { + value -= rotencSpeed * step; + } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); + if (value >= vmin) + setValue(value); + else + onKeyError(); + return; + } + } + + FormField::onKeyEvent(event); +} +#endif + +#if defined(HARDWARE_TOUCH) +bool NumberEdit::onTouchEnd(coord_t, coord_t) +{ + if (!enabled) { + return true; + } + + if (!hasFocus()) { + setFocus(); + } + + NumberKeyboard * keyboard = NumberKeyboard::instance(); + if (keyboard->getField() != this) { + keyboard->setField(this); + } + + return true; +} +#endif + +void NumberEdit::onFocusLost() +{ +#if defined(HARDWARE_TOUCH) + NumberKeyboard::instance()->disable(true); +#endif + + FormField::onFocusLost(); +} diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h new file mode 100644 index 000000000..d8b51d805 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _NUMBEREDIT_H_ +#define _NUMBEREDIT_H_ + +#include "basenumberedit.h" +#include + +class NumberEdit : public BaseNumberEdit { + public: + NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, + std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "NumberEdit(" + std::to_string(getValue()) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + void enable(bool enabled) + { + this->enabled = enabled; + } + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + void setPrefix(std::string value) + { + prefix = std::move(value); + } + + void setSuffix(std::string value) + { + suffix = std::move(value); + } + + void setZeroText(std::string value) + { + zeroText = std::move(value); + } + + void setDisplayHandler(std::function function) + { + displayFunction = std::move(function); + } + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + void onFocusLost() override; + + protected: + std::function displayFunction; + std::string prefix; + std::string suffix; + std::string zeroText; + bool enabled = true; + std::function isValueAvailable; +}; + +#endif // _NUMBEREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/page.cpp b/radio/src/thirdparty/libopenui/src/page.cpp new file mode 100644 index 000000000..6f86287ad --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/page.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "page.h" +#include "mainwindow.h" +#include "keyboard_number.h" +#include "keyboard_text.h" +#include "keyboard_curve.h" +#include "opentx.h" // TODO for constants + +PageHeader::PageHeader(Page * parent, uint8_t icon): + Window(parent, { 0, 0, LCD_W, MENU_HEADER_HEIGHT }, OPAQUE), + icon(icon) +#if defined(HARDWARE_TOUCH) + , back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, + [=]() -> uint8_t { + parent->deleteLater(); + return 0; + }, BUTTON_NOFOCUS) +#endif +{ +} + +void PageHeader::paint(BitmapBuffer * dc) +{ + theme->drawPageHeader(dc, this); +} + +Page::Page(unsigned icon): + Window(&mainWindow, {0, 0, LCD_W, LCD_H}, OPAQUE), + header(this, icon), + body(this, { 0, MENU_HEADER_HEIGHT, LCD_W, LCD_H - MENU_HEADER_HEIGHT }), + previousFocus(focusWindow) +{ + setFocus(); +} + +Page::~Page() +{ +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif +} + +void Page::deleteLater() +{ + if (previousFocus) { + previousFocus->setFocus(); + } + + Window::deleteLater(); +} + +void Page::paint(BitmapBuffer * dc) +{ + dc->clear(TEXT_BGCOLOR); +} + +#if defined(HARDWARE_KEYS) +void Page::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { + killEvents(event); + deleteLater(); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Page::onTouchEnd(coord_t x, coord_t y) +{ + if (Window::onTouchEnd(x, y)) + return true; + + TextKeyboard::instance()->disable(true); + NumberKeyboard::instance()->disable(true); + CurveKeyboard::instance()->disable(true); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/page.h b/radio/src/thirdparty/libopenui/src/page.h new file mode 100644 index 000000000..a43d054a3 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/page.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _PAGE_H_ +#define _PAGE_H_ + +#include "window.h" +#include "button.h" + +class Page; + +class PageHeader: public Window { + public: + PageHeader(Page * parent, uint8_t icon); + + void paint(BitmapBuffer * dc) override; + + uint8_t getIcon() const + { + return icon; + } + + protected: + uint8_t icon; +#if defined(HARDWARE_TOUCH) + IconButton back; +#endif +}; + +class Page: public Window { + public: + explicit Page(unsigned icon); + + ~Page() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Page"; + } +#endif + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override + { + Window::onTouchStart(x, y); + return true; + } + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override + { + Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + return true; + } +#endif + + void deleteLater(); + + void paint(BitmapBuffer * dc) override; + + protected: + PageHeader header; + FormWindow body; + Window * previousFocus; +}; + +#endif // _PAGE_H_ diff --git a/radio/src/thirdparty/libopenui/src/progress.cpp b/radio/src/thirdparty/libopenui/src/progress.cpp new file mode 100644 index 000000000..a29707719 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/progress.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "progress.h" +#include "theme.h" + +void Progress::paint(BitmapBuffer * dc) +{ + theme->drawProgressBar(dc, 0, 0, width(), height(), value); +} diff --git a/radio/src/thirdparty/libopenui/src/progress.h b/radio/src/thirdparty/libopenui/src/progress.h new file mode 100644 index 000000000..e135b3715 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/progress.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _PROGRESS_H_ +#define _PROGRESS_H_ + +#include "window.h" + +class Progress : public Window { + public: + Progress(Window * parent, const rect_t & rect): + Window(parent, rect) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Progress"; + } +#endif + + void setValue(int value) + { + this->value = value; + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + + protected: + int value; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp new file mode 100644 index 000000000..ff981bdcf --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "slider.h" +#include "theme.h" + +void Slider::paint(BitmapBuffer * dc) +{ + theme->drawSlider(dc, vmin, vmax, getValue(), rect, editMode, hasFocus()); +} + +int Slider::value(coord_t x) const +{ + return vmin + ((vmax - vmin) * x + (rect.w / 2)) / rect.w; +} + +#if defined(HARDWARE_KEYS) +void Slider::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + if (event == EVT_ROTARY_RIGHT) { + setValue(getValue() + rotencSpeed); + return; + } + else if (event == EVT_ROTARY_LEFT) { + setValue(getValue() - rotencSpeed); + return; + } + } + + FormField::onKeyEvent(event); +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Slider::onTouchStart(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(); + } + sliding = (value(x) == getValue()); + return true; +} + +bool Slider::onTouchEnd(coord_t x, coord_t y) +{ + setValue(value(x)); + invalidate(); + return true; +} + +bool Slider::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + if (hasFocus() && sliding) { + int newValue = value(x); + if (getValue() != newValue) { + setValue(newValue); + invalidate(); + } + } + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h new file mode 100644 index 000000000..3087a8682 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _SLIDER_H_ +#define _SLIDER_H_ + +#include "form.h" + +class Slider : public FormField { + public: + Slider(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue): + FormField(parent, rect), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + _setValue(std::move(setValue)) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Slider"; + } +#endif + + void setValue(int32_t value) + { + _setValue(limit(vmin, value, vmax)); + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override; + + bool onTouchEnd(coord_t x, coord_t y) override; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; +#endif + + protected: + int value(coord_t x) const; + int32_t vmin; + int32_t vmax; + bool sliding = false; + std::function getValue; + std::function _setValue; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp new file mode 100644 index 000000000..3be2d73de --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "static.h" +#include "font.h" + +void StaticText::paint(BitmapBuffer * dc) +{ + if (flags & BUTTON_BACKGROUND) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, CURVE_AXIS_COLOR); + if (flags & CENTERED) + dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | flags); + else + dc->drawText(0, 0, text.c_str(), flags); +} diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h new file mode 100644 index 000000000..9bbcde75d --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _STATIC_H_ +#define _STATIC_H_ + +#include +#include "window.h" +#include "button.h" // TODO just for BUTTON_BACKGROUND + +class StaticText : public Window { + public: + StaticText(Window * parent, const rect_t & rect, std::string text = "", LcdFlags flags = 0) : + Window(parent, rect), + text(std::move(text)), + flags(flags) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "StaticText \"" + text + "\""; + } +#endif + + void setFlags(LcdFlags flags) + { + this->flags = flags; + invalidate(); + } + + void paint(BitmapBuffer * dc) override; + + void setText(std::string value) + { + text = std::move(value); + invalidate(); + } + + protected: + std::string text; + LcdFlags flags; +}; + +class Subtitle: public StaticText { + public: + Subtitle(Window * parent, const rect_t & rect, const char * text): + StaticText(parent, rect, text, BOLD) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Subtitle \"" + text + "\""; + } +#endif +}; + +class StaticBitmap: public Window { + public: + StaticBitmap(Window * parent, const rect_t & rect, const char * filename): + Window(parent, rect), + bitmap(BitmapBuffer::load(filename)) + { + } + + StaticBitmap(Window * parent, const rect_t & rect, const BitmapBuffer * bitmap): + Window(parent, rect), + bitmap(bitmap) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "StaticBitmap"; + } +#endif + + void paint(BitmapBuffer * dc) override + { + dc->drawBitmap(0, 0, bitmap); + } + + protected: + const BitmapBuffer * bitmap; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.cpp b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp new file mode 100644 index 000000000..2c2b31365 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "tabsgroup.h" +#include "mainwindow.h" + +#if defined(HARDWARE_TOUCH) +#include "keyboard_number.h" +#include "keyboard_text.h" +#include "keyboard_curve.h" +#endif + +#include "opentx.h" // TODO for constants... +#include "view_main.h" + +TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): + Window(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE), +#if defined(HARDWARE_TOUCH) + back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, + [=]() -> uint8_t { + parent->deleteLater(); + return 1; + }, BUTTON_NOFOCUS), +#endif + icon(icon), + carousel(this, parent) +{ +} + +void TabsGroupHeader::paint(BitmapBuffer * dc) +{ + static_cast(theme)->drawMenuBackground(dc, icon, title); +} + +TabsCarousel::TabsCarousel(Window * parent, TabsGroup * menu): + Window(parent, { MENU_HEADER_BUTTONS_LEFT, 0, LCD_W - MENU_HEADER_BUTTONS_LEFT, MENU_HEADER_HEIGHT + 10 }, TRANSPARENT), + menu(menu) +{ +} + +void TabsCarousel::updateInnerWidth() +{ + setInnerWidth(padding_left + MENU_HEADER_BUTTON_WIDTH * menu->tabs.size()); +} + +void TabsCarousel::paint(BitmapBuffer * dc) +{ + static_cast(theme)->drawMenuHeader(dc, menu->tabs, currentIndex); +} + +#if defined(HARDWARE_TOUCH) +bool TabsCarousel::onTouchEnd(coord_t x, coord_t y) +{ + unsigned index = (x - padding_left) / MENU_HEADER_BUTTON_WIDTH; + menu->setCurrentTab(index); + setCurrentIndex(index); + return true; +} +#endif + +TabsGroup::TabsGroup(uint8_t icon): + Window(&mainWindow, { 0, 0, LCD_W, LCD_H }, OPAQUE), + header(this, icon), + body(this, { 0, MENU_BODY_TOP, LCD_W, MENU_BODY_HEIGHT }) +{ +} + +TabsGroup::~TabsGroup() +{ +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif + + for (auto tab: tabs) { + delete tab; + } + + body.deleteChildren(); +} + +void TabsGroup::addTab(PageTab * page) +{ + tabs.push_back(page); + if (!currentTab) { + setCurrentTab(0); + } + header.carousel.updateInnerWidth(); +} + +void TabsGroup::removeTab(unsigned index) +{ + if (currentTab == tabs[index]) { + setCurrentTab(max(0, index - 1)); + } + tabs.erase(tabs.begin() + index); + header.carousel.updateInnerWidth(); +} + +void TabsGroup::removeAllTabs() +{ + for (auto * tab: tabs) { + delete tab; + } + tabs.clear(); + currentTab = nullptr; + header.carousel.updateInnerWidth(); +} + +void TabsGroup::setVisibleTab(PageTab * tab) +{ + if (tab != currentTab) { + setFocus(); + FormField::clearCurrentField(); + body.clear(); +#if defined(HARDWARE_TOUCH) + TextKeyboard::instance()->disable(false); + NumberKeyboard::instance()->disable(false); + CurveKeyboard::instance()->disable(false); +#endif + currentTab = tab; + tab->build(&body); + header.setTitle(tab->title.c_str()); + invalidate(); + } +} + +void TabsGroup::checkEvents() +{ + Window::checkEvents(); + if (currentTab) { + currentTab->checkEvents(); + } +} + +#if defined(HARDWARE_KEYS) +void TabsGroup::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_PGDN)) { + uint8_t current = header.carousel.getCurrentIndex() + 1; + setCurrentTab(current >= tabs.size() ? 0 : current); + } + else if (event == EVT_KEY_LONG(KEY_PGDN)) { + killEvents(event); + uint8_t current = header.carousel.getCurrentIndex(); + setCurrentTab(current == 0 ? tabs.size() - 1 : current - 1); + } + else if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { + killEvents(event); + ViewMain::instance->setFocus(); + deleteLater(); + } + else if (parent) { + parent->onKeyEvent(event); + } +} +#endif + +void TabsGroup::paint(BitmapBuffer * dc) +{ + dc->clear(TEXT_BGCOLOR); +} + +#if defined(HARDWARE_TOUCH) +bool TabsGroup::onTouchEnd(coord_t x, coord_t y) +{ + if (Window::onTouchEnd(x, y)) + return true; + + TextKeyboard::instance()->disable(true); + NumberKeyboard::instance()->disable(true); + CurveKeyboard::instance()->disable(true); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.h b/radio/src/thirdparty/libopenui/src/tabsgroup.h new file mode 100644 index 000000000..182bab6bc --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/tabsgroup.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _TABSGROUP_H_ +#define _TABSGROUP_H_ + +#include +#include "button.h" +#include "form.h" + +class TabsGroup; + +class PageTab { + friend class TabsCarousel; + friend class TabsGroup; + + public: + PageTab(std::string title, unsigned icon): + title(std::move(title)), + icon(icon) + { + } + + virtual ~PageTab() = default; + + virtual void build(FormWindow * window) = 0; + + virtual void checkEvents() + { + } + + void destroy() + { + if (onPageDestroyed) { + onPageDestroyed(); + } + delete this; + } + + void setOnPageDestroyedHandler(std::function handler) + { + onPageDestroyed = std::move(handler); + } + + void setTitle(std::string value) + { + title = std::move(value); + } + + unsigned getIcon() const + { + return icon; + } + + protected: + std::string title; + unsigned icon; + std::function onPageDestroyed; +}; + +class TabsCarousel: public Window { + public: + TabsCarousel(Window * parent, TabsGroup * menu); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsCarousel"; + } +#endif + + inline void setCurrentIndex(uint8_t index) + { + currentIndex = index; + } + + inline uint8_t getCurrentIndex() + { + return currentIndex; + } + + void updateInnerWidth(); + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + constexpr static uint8_t padding_left = 3; + TabsGroup * menu; + uint8_t currentIndex = 0; +}; + +class TabsGroupHeader: public Window { + friend class TabsGroup; + + public: + TabsGroupHeader(TabsGroup * menu, uint8_t icon); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsGroupHeader"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + void setTitle(const char * value) + { + title = value; + } + + protected: +#if defined(HARDWARE_TOUCH) + IconButton back; +#endif + uint8_t icon; + TabsCarousel carousel; + const char * title = nullptr; +}; + +class TabsGroup: public Window { + friend class TabsCarousel; + + public: + explicit TabsGroup(uint8_t icon); + + ~TabsGroup() override; + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TabsGroup"; + } +#endif + + void addTab(PageTab * page); + + void removeTab(unsigned index); + + void removeAllTabs(); + + void setCurrentTab(unsigned index) + { + if (index < tabs.size()) { + header.carousel.setCurrentIndex(index); + setVisibleTab(tabs[index]); + } + } + + void checkEvents() override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + + protected: + TabsGroupHeader header; + FormWindow body; + std::vector tabs; + PageTab * currentTab = nullptr; + + void setVisibleTab(PageTab * tab); +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp new file mode 100644 index 000000000..39dbc4ee9 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "textedit.h" +#include "font.h" + +#if defined(HARDWARE_TOUCH) +#include "keyboard_text.h" +#endif + +void TextEdit::paint(BitmapBuffer * dc) +{ + FormField::paint(dc); + + if (editMode) { + dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, TEXT_INVERTED_COLOR); + coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); + char s[] = { value[cursorPos], '\0' }; + dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP + 1, getTextWidth(s, 1) + 1, height() - 2, TEXT_INVERTED_COLOR); + dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, TEXT_COLOR); + } + else { + const char * displayedValue = value; + LcdFlags textColor; + if (strlen(value) == 0) { + displayedValue = "---"; + textColor = hasFocus() ? TEXT_INVERTED_BGCOLOR : CURVE_AXIS_COLOR; + } + else if (hasFocus()) { + textColor = TEXT_INVERTED_BGCOLOR; + } + else { + textColor = TEXT_COLOR; + } + dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); + } + +#if defined(HARDWARE_TOUCH) + auto keyboard = TextKeyboard::instance(); + if (hasFocus() && keyboard->getField() == this) { + coord_t cursorPos = keyboard->getCursorPos(); + dc->drawSolidFilledRect(cursorPos + 2, FIELD_PADDING_TOP, 2, 21, 0); // TEXT_INVERTED_BGCOLOR); + } +#endif +} + +#if defined(HARDWARE_KEYS) +void TextEdit::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (editMode) { + int c = value[cursorPos]; + int v = c; + + switch (event) { + case EVT_ROTARY_RIGHT: + for (int i = 0; i < rotencSpeed; i++) { + v = getNextChar(v); + } + break; + + case EVT_ROTARY_LEFT: + for (int i = 0; i < rotencSpeed; i++) { + v = getPreviousChar(v); + } + break; + + case EVT_KEY_BREAK(KEY_LEFT): + if (cursorPos > 0) { + cursorPos--; + invalidate(); + } + break; + + case EVT_KEY_BREAK(KEY_RIGHT): + if (cursorPos < length - 1) { + cursorPos++; + invalidate(); + } + break; + + case EVT_KEY_BREAK(KEY_ENTER): + if (cursorPos < length - 1) { + cursorPos++; + invalidate(); + } + else { + FormField::onKeyEvent(event); + } + break; + + case EVT_KEY_BREAK(KEY_EXIT): + cursorPos = 0; + FormField::onKeyEvent(event); + break; + + case EVT_KEY_LONG(KEY_ENTER): + if (v == 0) { + killEvents(event); + FormField::onKeyEvent(EVT_KEY_BREAK(KEY_ENTER)); + break; + } + // no break + + case EVT_KEY_LONG(KEY_LEFT): + case EVT_KEY_LONG(KEY_RIGHT): + if (v >= -26 && v <= 26) { + v = -v; // toggle case + if (event == EVT_KEY_LONG(KEY_LEFT)) { + killEvents(KEY_LEFT); + } + } + break; + } + + if (c != v) { + // TRACE("value[%d] = %d", cursorPos, v); + value[cursorPos] = v; + invalidate(); + // TODO storageDirty(...); + } + } + else { + FormField::onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool TextEdit::onTouchEnd(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(); + } + + auto keyboard = TextKeyboard::instance(); + if (keyboard->getField() != this) { + keyboard->setField(this); + } + + keyboard->setCursorPos(x); + + return true; +} + +void TextEdit::onFocusLost() +{ + TextKeyboard::instance()->disable(true); + // TODO storageDirty(...); +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h new file mode 100644 index 000000000..b30230cd6 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _TEXTEDIT_H_ +#define _TEXTEDIT_H_ + +#include "form.h" + +class TextEdit : public FormField { + friend class TextKeyboard; + + public: + TextEdit(Window * parent, const rect_t &rect, char * value, uint8_t length, LcdFlags flags = 0) : + FormField(parent, rect), + value(value), + length(length) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TextEdit"; + } +#endif + + uint8_t getMaxLength() + { + return length; + } + + char * getData() + { + return value; + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; + + void onFocusLost() override; +#endif + + protected: + char * value; + uint8_t length; + uint8_t cursorPos = 0; + + static uint8_t getPreviousChar(uint8_t c) + { + if (c == 'A') + return ' '; + else if (c == 'a') + return 'Z'; + else if (c == '0') + return 'z'; + else if (c == ' ') + return '>'; + else + return c - 1; + } + + static uint8_t getNextChar(uint8_t c) + { + if (c == ' ') + return 'A'; + else if (c == 'Z') + return 'a'; + else if (c == 'z') + return '0'; + else if (c == '>') + return ' '; + else + return c + 1; + } + +}; + +#endif // _TEXTEDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h new file mode 100644 index 000000000..a57a3be06 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _THEME_H_ +#define _THEME_H_ + +#include "bitmapbuffer.h" + +class PageHeader; + +enum IconState { + STATE_DEFAULT, + STATE_PRESSED, +}; + +class Theme { + public: + virtual void drawMessageBox(const char * title, const char * text, const char * action, LcdFlags flags) const = 0; + virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const = 0; + virtual void drawCheckBox(BitmapBuffer * dc, bool value, bool focus) const = 0; + virtual void drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const = 0; + virtual void drawPageHeader(BitmapBuffer * dc, const PageHeader * page) const = 0; + virtual const BitmapBuffer * getIcon(uint8_t index, IconState state) const = 0; + virtual const BitmapBuffer * getIconMask(uint8_t index) const = 0; +}; + +extern Theme * theme; + +#endif // _THEME_H_ diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp new file mode 100644 index 000000000..ed0805ee8 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "window.h" + +Window * Window::focusWindow = nullptr; +std::list Window::trash; + +Window::Window(Window * parent, const rect_t & rect, uint8_t flags): + parent(parent), + rect(rect), + innerWidth(rect.w), + innerHeight(rect.h), + windowFlags(flags) +{ + if (parent) { + parent->addChild(this); + if (!(flags & TRANSPARENT)) { + invalidate(); + } + } +} + +Window::~Window() +{ + TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); + + if (focusWindow == this) { + focusWindow = nullptr; + } +} + +void Window::attach(Window * window) +{ + if (parent) + detach(); + parent = window; + parent->addChild(this); +} + +void Window::detach() +{ + if (parent) { + parent->removeChild(this); + parent = nullptr; + } +} + +void Window::deleteLater(bool detach) +{ + TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); + + if (static_cast(focusWindow) == static_cast(this)) { + focusWindow = nullptr; + } + + if (detach) + this->detach(); + else + parent = nullptr; + + if (closeHandler) { + closeHandler(); + } + + trash.push_back(this); +} + +void Window::clear() +{ + scrollPositionX = 0; + scrollPositionY = 0; + innerWidth = rect.w; + innerHeight = rect.h; + deleteChildren(); + invalidate(); +} + +void Window::deleteChildren() +{ + for (auto window: children) { + window->deleteLater(false); + } + children.clear(); +} + +void Window::clearFocus() +{ + if (focusWindow) { + focusWindow->onFocusLost(); + focusWindow = nullptr; + } +} + +void Window::setFocus() +{ + TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); + + if (focusWindow != this) { + clearFocus(); + focusWindow = this; + if (focusHandler) { + focusHandler(); + } + } + + Window * parent = this->parent; + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + + if (parent) { + parent->scrollTo(this); + invalidate(); + } +} + +void Window::setScrollPositionX(coord_t value) +{ + coord_t newScrollPosition = max(0, min(innerWidth - width(), value)); + if (newScrollPosition != scrollPositionX) { + scrollPositionX = newScrollPosition; + invalidate(); + } +} + +void Window::setScrollPositionY(coord_t value) +{ + coord_t newScrollPosition = max(0, min(innerHeight - height(), value)); + if (newScrollPosition != scrollPositionY) { + scrollPositionY = newScrollPosition; + invalidate(); + } +} + +void Window::scrollTo(Window * child) +{ + coord_t offsetY = 0; + Window * parent = child->getParent(); + while (parent && parent != this) { + offsetY += parent->top(); + parent = parent->getParent(); + } + + coord_t top = offsetY + child->top(); + coord_t bottom = offsetY + child->bottom(); + + if (top < scrollPositionY) { + setScrollPositionY(top - 5); + } + else if (bottom > scrollPositionY + height() - 5) { + setScrollPositionY(bottom - height() + 5); + } +} + +void Window::fullPaint(BitmapBuffer * dc) +{ + TRACE_WINDOWS("%s", getWindowDebugString().c_str()); + + if (windowFlags & PAINT_CHILDREN_FIRST) { + coord_t xmin, xmax, ymin, ymax; + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + dc->getClippingRect(xmin, xmax, ymin, ymax); + paintChildren(dc); + dc->setOffset(x, y); + dc->setClippingRect(xmin, xmax, ymin, ymax); + } + + paint(dc); + + if (!(windowFlags & NO_SCROLLBAR)) { + drawVerticalScrollbar(dc); + } + + if (!(windowFlags & PAINT_CHILDREN_FIRST)) { + paintChildren(dc); + } +} + +bool Window::isChildFullSize(Window * child) +{ + return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width(); +} + +bool Window::isChildVisible(Window * window) +{ + for (auto rit = children.rbegin(); rit != children.rend(); rit++) { + auto child = *rit; + if (child == window) { + return true; + } + if ((child->getWindowFlags() & OPAQUE) & isChildFullSize(child)) { + return false; + } + } + return false; +} + +void Window::paintChildren(BitmapBuffer * dc) +{ + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + + auto it = children.end(); + + while(it != children.begin()) { + auto child = *(--it); + if ((child->windowFlags & OPAQUE) && isChildFullSize(child)) { + break; + } + } + + for (; it != children.end(); it++) { + auto child = *it; + + coord_t child_xmin = x + child->rect.x; + if (child_xmin >= xmax) + continue; + coord_t child_ymin = y + child->rect.y; + if (child_ymin >= ymax) + continue; + coord_t child_xmax = child_xmin + child->rect.w; + if (child_xmax <= xmin) + continue; + coord_t child_ymax = child_ymin + child->rect.h; + if (child_ymax <= ymin) + continue; + + dc->setOffset(x + child->rect.x - child->scrollPositionX, y + child->rect.y - child->scrollPositionY); + dc->setClippingRect(max(xmin, x + child->rect.left()), + min(xmax, x + child->rect.right()), + max(ymin, y + child->rect.top()), + min(ymax, y + child->rect.bottom())); + child->fullPaint(dc); + } +} + +void Window::checkEvents() +{ + auto copy = children; + for (auto child: copy) { + child->checkEvents(); + } + +#if defined(HARDWARE_KEYS) + if (this == focusWindow) { + event_t event = getEvent(false); + if (event) { + TRACE_WINDOWS("Event 0x%x received ...", event); + this->onKeyEvent(event); + } + } +#endif + + if (windowFlags & REFRESH_ALWAYS) { + invalidate(); + } +} + +#if defined(HARDWARE_KEYS) +void Window::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString().c_str(), event); + if (parent) { + parent->onKeyEvent(event); + } +} +#endif + +#if defined(HARDWARE_TOUCH) +bool Window::onTouchStart(coord_t x, coord_t y) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(x, y, child->rect)) { + if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + return true; + } + } + } + + return false; +} + +bool Window::onTouchEnd(coord_t x, coord_t y) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(x, y, child->rect)) { + if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + return true; + } + } + } + + return false; +} + +bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (pointInRect(startX, startY, child->rect)) { + if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { + return true; + } + } + } + + if (slideY && innerHeight > rect.h) { + setScrollPositionY(scrollPositionY - slideY); + return true; + } + + if (slideX && innerWidth > rect.w) { + setScrollPositionX(scrollPositionX - slideX); + return true; + } + + return false; +} +#endif + +void Window::adjustInnerHeight() +{ + innerHeight = 0; + for (auto child: children) { + innerHeight = max(innerHeight, child->rect.y + child->rect.h); + } +} + +coord_t Window::adjustHeight() +{ + adjustInnerHeight(); + coord_t old = rect.h; + rect.h = innerHeight; + return rect.h - old; +} + +void Window::moveWindowsTop(coord_t y, coord_t delta) +{ + for (auto child: children) { + if (child->rect.y > y) { + child->rect.y += delta; + } + } + innerHeight += delta; +} + +void Window::invalidate(const rect_t & rect) +{ + if (isVisible()) { + parent->invalidate({this->rect.x + rect.x - parent->scrollPositionX, this->rect.y + rect.y - parent->scrollPositionY, rect.w, rect.h}); + } +} + +void Window::drawVerticalScrollbar(BitmapBuffer * dc) +{ + if (innerHeight > rect.h) { + coord_t x = rect.w - 3; + coord_t y = scrollPositionY + 3; + coord_t h = rect.h - 6; + // lcd->drawSolidFilledRect(x, y, 1, h, LINE_COLOR); + coord_t yofs = (h*scrollPositionY + innerHeight/2) / innerHeight; + coord_t yhgt = (h*rect.h + innerHeight/2) / innerHeight; + if (yhgt + yofs > h) + yhgt = h - yofs; + if (yhgt < 15) { + yhgt = 15; + if (y + yofs + yhgt > innerHeight) { + yofs = innerHeight - y - yhgt; + } + } + dc->drawSolidFilledRect(x-1, y + yofs, 3, yhgt, SCROLLBOX_COLOR); + } +} diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h new file mode 100644 index 000000000..d99644216 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _WINDOW_H_ +#define _WINDOW_H_ + +#include +#include +#include +#include +#include +#include +#include "bitmapbuffer.h" +#include "libopenui_defines.h" +#include "libopenui_helpers.h" +#include "libopenui_config.h" + +// OPAQUE/TRANSPARENT defined in WinGDI +#ifndef OPAQUE + #define OPAQUE 1 + #define TRANSPARENT 2 +#endif + +constexpr uint8_t NO_SCROLLBAR = 4; +constexpr uint8_t FORWARD_SCROLL = 8; +constexpr uint8_t REFRESH_ALWAYS = 16; +constexpr uint8_t BORDER_FOCUS_ONLY = 32; +constexpr uint8_t PAINT_CHILDREN_FIRST = 64; + +class Window { + friend class GridLayout; + + public: + Window(Window * parent, const rect_t & rect, uint8_t flags=0); + + virtual ~Window(); + +#if defined(DEBUG_WINDOWS) + virtual std::string getName() + { + return "Window"; + } + + std::string getRectString() + { + char result[32]; + sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); + return result; + } + + std::string getIndentString() + { + std::string result; + auto tmp = parent; + while (tmp) { + result += " "; + tmp = tmp->getParent(); + } + return result; + } + + std::string getWindowDebugString() + { + return getIndentString() + getName() + " " + getRectString(); + } +#endif + + Window * getParent() const + { + return parent; + } + + uint8_t getWindowFlags() const + { + return windowFlags; + } + + void setWindowFlags(uint8_t flags) + { + windowFlags = flags; + } + + void setCloseHandler(std::function handler) + { + closeHandler = std::move(handler); + } + + void setFocusHandler(std::function handler) + { + focusHandler = std::move(handler); + } + + void deleteLater(bool detach=true); + + void clear(); + + void deleteChildren(); + + bool hasFocus() const + { + return focusWindow == this; + } + + static Window * getFocus() + { + return focusWindow; + } + + void scrollTo(Window * child); + + static void clearFocus(); + + void setFocus(); + + void setRect(rect_t rect) + { + this->rect = rect; + invalidate(); + } + + void setWidth(coord_t value) + { + rect.w = value; + invalidate(); + } + + void setHeight(coord_t value) + { + rect.h = value; + if (innerHeight <= value) { + setScrollPositionY(0); + } + invalidate(); + } + + coord_t getHeight() + { + return rect.h; + } + + void setLeft(coord_t x) + { + rect.x = x; + invalidate(); + } + + void setTop(coord_t y) + { + rect.y = y; + invalidate(); + } + + coord_t left() const + { + return rect.x; + } + + coord_t right() const + { + return rect.x + rect.w; + } + + coord_t top() const + { + return rect.y; + } + + coord_t bottom() const + { + return rect.y + rect.h; + } + + coord_t width() const + { + return rect.w; + } + + coord_t height() const + { + return rect.h; + } + + rect_t getRect() const + { + return rect; + } + + void setInnerWidth(coord_t w) + { + innerWidth = w; + if (width() >= w) { + scrollPositionX = 0; + } + } + + void setInnerHeight(coord_t h) + { + innerHeight = h; + if (height() >= h) { + scrollPositionY = 0; + } + } + + coord_t getScrollPositionY() const + { + return scrollPositionY; + } + + void setScrollPositionX(coord_t value); + + void setScrollPositionY(coord_t value); + + bool isChildVisible(Window * window); + + bool isChildFullSize(Window * window); + + bool isVisible() + { + return parent && parent->isChildVisible(this); + } + + virtual void paint(BitmapBuffer * dc) + { + } + + void drawVerticalScrollbar(BitmapBuffer * dc); + + void paintChildren(BitmapBuffer * dc); + + void fullPaint(BitmapBuffer * dc); + + bool pointInRect(coord_t x, coord_t y, rect_t & rect) + { + return (x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h); + } + + virtual void onFocusLost() + { + TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); + invalidate(); + }; + +#if defined(HARDWARE_KEYS) + virtual void onKeyEvent(event_t event); +#endif + +#if defined(HARDWARE_TOUCH) + virtual bool onTouchStart(coord_t x, coord_t y); + + virtual bool onTouchEnd(coord_t x, coord_t y); + + virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); +#endif + + void adjustInnerHeight(); + + coord_t adjustHeight(); + + void moveWindowsTop(coord_t y, coord_t delta); + + void invalidate() + { + invalidate({0, 0, rect.w, rect.h}); + } + + void bringToTop() + { + attach(parent); // does a detach + attach + } + + virtual void checkEvents(); + + protected: + Window * parent; + std::list children; + rect_t rect; + coord_t innerWidth; + coord_t innerHeight; + coord_t scrollPositionX = 0; + coord_t scrollPositionY = 0; + uint8_t windowFlags; + + static Window * focusWindow; + static std::list trash; + + std::function closeHandler; + std::function focusHandler; + + void attach(Window * window); + + void detach(); + + void addChild(Window * window) + { + children.push_back(window); + } + + void removeChild(Window * window) + { + children.remove(window); + invalidate(); + } + + virtual void invalidate(const rect_t & rect); +}; + +#endif // _WINDOW_H_ From e967f0b3b3029d8c13c84a67eaabcbfe6011e490 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 2 Oct 2019 17:56:04 +0200 Subject: [PATCH 006/714] Compilation fix --- radio/src/thirdparty/libopenui/src/libopenui_types.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_types.h b/radio/src/thirdparty/libopenui/src/libopenui_types.h index 5cf077897..1dbcc4086 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_types.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_types.h @@ -22,8 +22,6 @@ #include -typedef uint16_t pixel_t; - typedef int coord_t; struct point_t From 19a164f8736dab47eee2e0437ee50ca4194a7f78 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 3 Oct 2019 00:16:13 +0200 Subject: [PATCH 007/714] Compilation fixes --- radio/src/thirdparty/libopenui/src/choice.h | 1 - radio/src/thirdparty/libopenui/src/coloredit.cpp | 1 + radio/src/thirdparty/libopenui/src/keyboard_number.cpp | 1 + radio/src/thirdparty/libopenui/src/libopenui_depends.h | 1 - radio/src/thirdparty/libopenui/src/libopenui_globals.h | 5 +++++ radio/src/thirdparty/libopenui/src/menu.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/menu.h | 5 ++--- radio/src/thirdparty/libopenui/src/static.h | 1 - 8 files changed, 11 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 2f644a6cc..306ff1aed 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -20,7 +20,6 @@ #ifndef _CHOICE_H_ #define _CHOICE_H_ -#include #include "form.h" class Choice : public FormField { diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index 326d79fc0..1374ce85f 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -18,6 +18,7 @@ */ #include "coloredit.h" +#include "libopenui_globals.h" #include "numberedit.h" #include "bitfield.h" diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index ead6f270b..41ec31a31 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -20,6 +20,7 @@ #include "keyboard_number.h" #include "button.h" #include "basenumberedit.h" +#include "libopenui_globals.h" constexpr coord_t KEYBOARD_HEIGHT = 90; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index e2c529126..f9dd8308e 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -24,7 +24,6 @@ #include "libopenui_config.h" void lcdNextLayer(); -void lcdSetColor(uint16_t color); void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void onKeyPress(); diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.h b/radio/src/thirdparty/libopenui/src/libopenui_globals.h index b4900e5ac..ebcc1d9ff 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_globals.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.h @@ -27,4 +27,9 @@ extern uint16_t lcdColorTable[LCD_COLOR_COUNT]; extern const uint16_t * const fontspecsTable[FONT_TABLE_SIZE]; extern const uint8_t * fontsTable[FONT_TABLE_SIZE]; +inline void lcdSetColor(uint16_t color) +{ + lcdColorTable[CUSTOM_COLOR_INDEX] = color; +} + #endif // _LIBOPEUI_GLOBALS_H_ diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index f4c1bf3d8..077f433b8 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -107,9 +107,9 @@ void Menu::addLine(const std::string & text, std::function onPress) updatePosition(); } -void Menu::addLine(std::function drawLine, std::function onPress) +void Menu::addCustomLine(std::function drawLine, std::function onPress) { - menuWindow.addLine(drawLine, std::move(onPress)); + menuWindow.addCustomLine(drawLine, std::move(onPress)); updatePosition(); } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 25e7f7184..f6703407a 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -23,7 +23,6 @@ #include #include #include -#include #include "mainwindow.h" class Menu; @@ -78,7 +77,7 @@ class MenuWindow: public Window { invalidate(); } - void addLine(std::function drawLine, std::function onPress) + void addCustomLine(std::function drawLine, std::function onPress) { lines.emplace_back(drawLine, onPress); invalidate(); @@ -162,7 +161,7 @@ class Menu : public Window { void addLine(const std::string & text, std::function onPress); - void addLine(std::function drawLine, std::function onPress); + void addCustomLine(std::function drawLine, std::function onPress); void removeLines(); diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 9bbcde75d..e824afbe4 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -20,7 +20,6 @@ #ifndef _STATIC_H_ #define _STATIC_H_ -#include #include "window.h" #include "button.h" // TODO just for BUTTON_BACKGROUND From 0199a7d6d514437e1dda66bb4f4772866d67e772 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 3 Oct 2019 23:03:52 +0200 Subject: [PATCH 008/714] FabIconButton splitted from Button --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + radio/src/thirdparty/libopenui/src/button.cpp | 27 - radio/src/thirdparty/libopenui/src/button.h | 17 - .../libopenui/src/fab_icon_button.cpp | 49 + .../libopenui/src/fab_icon_button.h | 42 + .../libopenui/src/thirdparty/Stb/stb_image.h | 6710 +++++++++++++++++ 6 files changed, 6802 insertions(+), 44 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/fab_icon_button.cpp create mode 100644 radio/src/thirdparty/libopenui/src/fab_icon_button.h create mode 100644 radio/src/thirdparty/libopenui/src/thirdparty/Stb/stb_image.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 752f93b90..ec2cdd6e0 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -5,6 +5,7 @@ set(LIBOPENUI_SRC window.cpp form.cpp button.cpp + fab_icon_button.cpp static.cpp checkbox.cpp slider.cpp diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 84b33b2db..88f63d3a9 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -109,30 +109,3 @@ void IconButton::paint(BitmapBuffer * dc) { dc->drawBitmap(0, 0, theme->getIcon(icon, checked() ? STATE_PRESSED : STATE_DEFAULT)); } - - -const uint8_t __alpha_button_on[] { -#include "alpha_button_on.lbm" -}; -Bitmap ALPHA_BUTTON_ON(BMP_ARGB4444, (const uint16_t*)__alpha_button_on); - -const uint8_t __alpha_button_off[] { -#include "alpha_button_off.lbm" -}; -Bitmap ALPHA_BUTTON_OFF(BMP_ARGB4444, (const uint16_t*)__alpha_button_off); - - -FabIconButton::FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags): - Button(parent, { x - 34, y - 34, 68, 68 }, pressHandler, flags), - icon(icon) -{ -} - -void FabIconButton::paint(BitmapBuffer * dc) -{ - dc->drawBitmap(0, 0, checked() ? &ALPHA_BUTTON_ON : &ALPHA_BUTTON_OFF); - const BitmapBuffer * mask = theme->getIconMask(icon); - if (mask) { - dc->drawMask((68 - mask->getWidth()) / 2, (68 - mask->getHeight()) / 2, mask, TEXT_BGCOLOR); - } -} diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index c72910107..03091f67a 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -160,21 +160,4 @@ class IconButton: public Button { uint8_t icon; }; -class FabIconButton: public Button { - public: - FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags=0); - -#if defined(DEBUG_WINDOWS) - std::string getName() override - { - return "FabIconButton(" + std::to_string(icon) + ")"; - } -#endif - - void paint(BitmapBuffer * dc) override; - - protected: - uint8_t icon; -}; - #endif diff --git a/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp b/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp new file mode 100644 index 000000000..99435d4a6 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "libopenui_config.h" +#include "fab_icon_button.h" +#include "font.h" +#include "theme.h" + +const uint8_t __alpha_button_on[] { +#include "alpha_button_on.lbm" +}; +Bitmap ALPHA_BUTTON_ON(BMP_ARGB4444, (const uint16_t*)__alpha_button_on); + +const uint8_t __alpha_button_off[] { +#include "alpha_button_off.lbm" +}; +Bitmap ALPHA_BUTTON_OFF(BMP_ARGB4444, (const uint16_t*)__alpha_button_off); + + +FabIconButton::FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags): + Button(parent, { x - 34, y - 34, 68, 68 }, pressHandler, flags), + icon(icon) +{ +} + +void FabIconButton::paint(BitmapBuffer * dc) +{ + dc->drawBitmap(0, 0, checked() ? &ALPHA_BUTTON_ON : &ALPHA_BUTTON_OFF); + const BitmapBuffer * mask = theme->getIconMask(icon); + if (mask) { + dc->drawMask((68 - mask->getWidth()) / 2, (68 - mask->getHeight()) / 2, mask, TEXT_BGCOLOR); + } +} diff --git a/radio/src/thirdparty/libopenui/src/fab_icon_button.h b/radio/src/thirdparty/libopenui/src/fab_icon_button.h new file mode 100644 index 000000000..3a467b6cb --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/fab_icon_button.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _FAB_ICON_BUTTON_H_ +#define _FAB_ICON_BUTTON_H_ + +#include "button.h" + +class FabIconButton: public Button { + public: + FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags=0); + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "FabIconButton(" + std::to_string(icon) + ")"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + protected: + uint8_t icon; +}; + +#endif diff --git a/radio/src/thirdparty/libopenui/src/thirdparty/Stb/stb_image.h b/radio/src/thirdparty/libopenui/src/thirdparty/Stb/stb_image.h new file mode 100644 index 000000000..836ad182a --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/thirdparty/Stb/stb_image.h @@ -0,0 +1,6710 @@ +/* stb_image - v2.10 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB + + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) + optimize PNG + fix bug in interlaced PNG with user-specified channel count + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + urraka@github (animated gif) Junggon Kim (PNM comments) + Daniel Gibson (16-bit TGA) + + Optimizations & bugfixes + Fabian "ryg" Giesen + Arseny Kapoulkine + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson + Dave Moore Roy Eltham Hayaki Saito Phil Jordan + Won Chun Luke Graham Johan Duparc Nathan Reed + the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis + Janez Zemva John Bartholomew Michal Cichon svdijk@github + Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson + Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github + Aruelien Pocheville Thibault Reuille Cass Everitt + Ryamond Barbiero Paul Du Bois Engin Manap + Blazej Dariusz Roszkowski + Michaelangel007@github + + +LICENSE + +This software is in the public domain. Where that dedication is not +recognized, you are granted a perpetual, irrevocable license to copy, +distribute, and modify this file as you see fit. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// The output of the JPEG decoder is slightly different from versions where +// SIMD support was introduced (that is, for versions before 1.49). The +// difference is only +-1 in the 8-bit RGB channels, and only on a small +// fraction of pixels. You can force the pre-1.49 behavior by defining +// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path +// and hence cost some performance. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// + + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; +#endif +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } + + return result; +} + +#ifndef STBI_NO_HDR +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_flip(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_flip(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + + +#ifndef STBI_NO_HDR +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } +#endif // STBI_NO_HDR + + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc(req_comp * x * y); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) (dest[0]=src[0], dest[1]=255); + break; + CASE(1,3) (dest[0]=dest[1]=dest[2]=src[0]); + break; + CASE(1,4) (dest[0]=dest[1]=dest[2]=src[0], dest[3]=255); + break; + CASE(2,1) (dest[0]=src[0]); + break; + CASE(2,3) (dest[0]=dest[1]=dest[2]=src[0]); + break; + CASE(2,4) (dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]); + break; + CASE(3,4) (dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255); + break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); + break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; + break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); + break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; + break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; + break; + default: + STBI_ASSERT(0); + } + #undef CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (powf(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) powf(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi_uc dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (-1 << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + stbi__skip(z->s, stbi__get16be(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = stbi__get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return stbi__err("bad component ID","Corrupt JPEG"); + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + if (z->progressive) { + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char * r; + stbi__jpeg * j; + j = (stbi__jpeg *) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return 0; + j->s = s; + stbi__setup_jpeg(j); + r = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg * j; + j = (stbi__jpeg *) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return 0; + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int r; + stbi__jpeg * j; + j = (stbi__jpeg *) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return 0; + j->s = s; + r = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return r; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncomperssed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncomperssed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + char * r; + stbi__zbuf * a; + a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); + if (!a) return 0; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) { + STBI_FREE(a); + return NULL; + } + a->zbuffer = (stbi_uc *) buffer; + a->zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a->zout - a->zout_start); + r = a->zout_start; + STBI_FREE(a); + return r; + } else { + STBI_FREE(a->zout_start); + STBI_FREE(a); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + char * r; + stbi__zbuf * a; + a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); + if (!a) return 0; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) { + STBI_FREE(a); + return NULL; + } + a->zbuffer = (stbi_uc *) buffer; + a->zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a->zout - a->zout_start); + r = a->zout_start; + STBI_FREE(a); + return r; + } else { + STBI_FREE(a->zout_start); + STBI_FREE(a); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + int r; + stbi__zbuf * a; + a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); + if (!a) return -1; + a->zbuffer = (stbi_uc *) ibuffer; + a->zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(a, obuffer, olen, 0, 1)) { + r = (int) (a->zout - a->zout_start); + STBI_FREE(a); + return r; + } + else { + STBI_FREE(a); + return -1; + } +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + char * r; + stbi__zbuf * a; + a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); + if (!a) return NULL; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) { + STBI_FREE(a); + return NULL; + } + a->zbuffer = (stbi_uc *) buffer; + a->zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a->zout - a->zout_start); + r = a->zout_start; + STBI_FREE(a); + return r; + } else { + STBI_FREE(a->zout_start); + STBI_FREE(a); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + int r; + stbi__zbuf * a; + a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); + if (!a) return -1; + a->zbuffer = (stbi_uc *) ibuffer; + a->zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(a, obuffer, olen, 0, 0)) { + r = (int) (a->zout - a->zout_start); + STBI_FREE(a); + return r; + } + else { + STBI_FREE(a); + return -1; + } +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + s->img_out_n = 3; + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior = cur - stride; + int filter = *raw++; + int filter_bytes = img_n; + int width = x; + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*img_n; + #define CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; + CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; + CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; + CASE(STBI__F_paeth) {cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; + CASE(STBI__F_avg_first) {cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; + CASE(STBI__F_paeth_first) {cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; + } + #undef CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(STBI__F_none) cur[k] = raw[k]; + break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); + break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); + break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); + break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); + break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); + break; + } + #undef CASE + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; + if (has_trans) + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; +#if 0 + if (req_comp && req_comp != p->s->img_out_n) { + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } +#endif + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_out_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + info->mr = info->mg = info->mb = 0; + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + s->img_n = 3; + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + s->img_n = 3; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) { + uint8_t alpha = STBI__BYTECAST(a); + if (alpha != 255) s->img_n = 4; + out[z++] = alpha; + } + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if(is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // else: fall-through + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fall-through + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (r * 255)/31; + out[1] = (g * 255)/31; + out[2] = (b * 255)/31; + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int bitdepth; + int w,h; + stbi_uc *out; + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; + if (channel >= channelCount) { + // Fill this channel with default data. + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } else { + // Read the data. + if (bitdepth == 16) { + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + + if (req_comp && req_comp != 4) { + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif * g; + g = (stbi__gif *) stbi__malloc(sizeof(stbi__gif)); + if (!g) return 0; + if (!stbi__gif_header(s, g, comp, 1)) { + stbi__rewind( s ); + STBI_FREE(g); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *prev_out = 0; + + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int prev_trans = -1; + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } + + STBI_NOTUSED(req_comp); +} + +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *u = 0; + stbi__gif * g; + g = (stbi__gif *) stbi__malloc(sizeof(stbi__gif)); + if (!g) return 0; + memset(g, 0, sizeof(stbi__gif)); + + u = stbi__gif_load_next(s, g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g->w; + *y = g->h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g->w, g->h); + } + else if (g->out) + STBI_FREE(g->out); + + STBI_FREE(g); + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s); + stbi__rewind(s); + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = info.ma ? 4 : 3; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + stbi__pic_packet packets[10]; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv; + char c, p, t; + + stbi__rewind( s ); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ From 17cff9546fabcf5e25d15ed83e4259195334c5af Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Oct 2019 11:45:31 +0200 Subject: [PATCH 009/714] More code moved to the theme --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + radio/src/thirdparty/libopenui/src/choice.cpp | 41 +++++++++--------- radio/src/thirdparty/libopenui/src/choice.h | 32 +++++++++++--- radio/src/thirdparty/libopenui/src/dialog.cpp | 2 +- .../thirdparty/libopenui/src/filechoice.cpp | 43 +++++++------------ .../src/thirdparty/libopenui/src/filechoice.h | 6 +-- radio/src/thirdparty/libopenui/src/form.h | 13 ++++-- .../libopenui/src/libopenui_defines.h | 32 ++++++++++++++ .../libopenui/src/message_dialog.cpp | 38 ++++++++++++++++ .../thirdparty/libopenui/src/message_dialog.h | 34 +++++++++++++++ radio/src/thirdparty/libopenui/src/theme.h | 2 + 11 files changed, 182 insertions(+), 62 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/message_dialog.cpp create mode 100644 radio/src/thirdparty/libopenui/src/message_dialog.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index ec2cdd6e0..999f4e888 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -20,6 +20,7 @@ set(LIBOPENUI_SRC progress.cpp page.cpp dialog.cpp + message_dialog.cpp tabsgroup.cpp ) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 2c3fe383f..860275291 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -19,14 +19,11 @@ #include "choice.h" #include "menu.h" +#include "theme.h" -const uint8_t LBM_DROPDOWN[] = { -#include "mask_dropdown.lbm" -}; - -Choice::Choice(Window * parent, const rect_t &rect, const char * values, int16_t vmin, int16_t vmax, +Choice::Choice(Window * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue, LcdFlags flags) : - FormField(parent, rect), + ChoiceBase(parent, rect), values(values), vmin(vmin), vmax(vmax), @@ -39,21 +36,23 @@ Choice::Choice(Window * parent, const rect_t &rect, const char * values, int16_t void Choice::paint(BitmapBuffer * dc) { FormField::paint(dc); - - LcdFlags textColor; - if (editMode) - textColor = TEXT_INVERTED_COLOR; - else if (hasFocus()) - textColor = TEXT_INVERTED_BGCOLOR; - else - textColor = 0; - - if (textHandler) - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, textHandler(getValue()).c_str(), textColor); - else - dc->drawTextAtIndex(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, values, getValue() - vmin, flags | textColor); - - dc->drawBitmapPattern(rect.w - 14, (rect.h - 5) / 2, LBM_DROPDOWN, textColor); + theme->drawChoice(dc, this, textHandler ? textHandler(getValue()).c_str() : TEXT_AT_INDEX(values, getValue() - vmin).c_str()); +// +// +// LcdFlags textColor; +// if (editMode) +// textColor = TEXT_INVERTED_COLOR; +// else if (hasFocus()) +// textColor = TEXT_INVERTED_BGCOLOR; +// else +// textColor = 0; +// +// if (textHandler) +// dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, , textColor); +// else +// dc->drawTextAtIndex(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, values, getValue() - vmin, flags | textColor); +// +// dc->drawBitmapPattern(rect.w - 14, (rect.h - 5) / 2, LBM_DROPDOWN, textColor); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 306ff1aed..6eaf48a99 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -22,7 +22,29 @@ #include "form.h" -class Choice : public FormField { +enum ChoiceType { + CHOICE_TYPE_DROPOWN, + CHOICE_TYPE_FOLDER, +}; + +class ChoiceBase : public FormField { + public: + ChoiceBase(Window * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN): + FormField(parent, rect), + type(type) + { + } + + inline ChoiceType getType() const + { + return type; + } + + protected: + ChoiceType type; +}; + +class Choice : public ChoiceBase { public: Choice(Window * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); @@ -59,14 +81,14 @@ class Choice : public FormField { } protected: - const char * values; - int16_t vmin; - int16_t vmax; + const char * values = nullptr; + int16_t vmin = 0; + int16_t vmax = 0; std::function getValue; std::function setValue; std::function isValueAvailable; std::function textHandler; - LcdFlags flags; + LcdFlags flags = 0; void openMenu(); }; diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 9bef8a708..fdcb4c1b0 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -36,7 +36,7 @@ void Dialog::paint(BitmapBuffer * dc) dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, TEXT_COLOR); dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, TITLE_BGCOLOR); dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), MENU_TITLE_COLOR); - dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, TEXT_BGCOLOR_INDEX); + dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, TEXT_BGCOLOR); } void Dialog::deleteLater() diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 2dc4fd580..8e25c26ac 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -20,10 +20,8 @@ #include "filechoice.h" #include "libopenui_file.h" #include "menu.h" - -const uint8_t LBM_FOLDER[] = { -#include "mask_folder.lbm" -}; +#include "theme.h" +#include "message_dialog.h" // comparison, not case sensitive. bool compare_nocase(const std::string &first, const std::string &second) @@ -32,7 +30,7 @@ bool compare_nocase(const std::string &first, const std::string &second) } FileChoice::FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): - FormField(parent, rect), + ChoiceBase(parent, rect, CHOICE_TYPE_FOLDER), folder(std::move(folder)), extension(extension), maxlen(maxlen), @@ -44,24 +42,10 @@ FileChoice::FileChoice(Window * parent, const rect_t & rect, std::string folder, void FileChoice::paint(BitmapBuffer * dc) { FormField::paint(dc); - - const char * displayedValue = getValue().c_str(); - - LcdFlags textColor; - if (editMode) - textColor = TEXT_INVERTED_COLOR; - else if (hasFocus()) - textColor = TEXT_INVERTED_BGCOLOR; - else if (displayedValue[0] == '\0') - textColor = CURVE_AXIS_COLOR; - else - textColor = TEXT_COLOR; - - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue[0] == '\0' ? "---" : displayedValue, textColor); - dc->drawBitmapPattern(rect.w - 20, (rect.h - 11) / 2, LBM_FOLDER, textColor); + theme->drawChoice(dc, this, getValue().c_str()); } -void FileChoice::openMenu() +bool FileChoice::openMenu() { FILINFO fno; DIR dir; @@ -124,12 +108,14 @@ void FileChoice::openMenu() editMode = false; setFocus(); }); - } - else { - TRACE("NO FILES !!!!!"); - // TODO popup "NO FILES" + + return true; } } + + new MessageDialog("Files", "No files on SD"); + + return false; } #if defined(HARDWARE_KEYS) @@ -138,9 +124,10 @@ void FileChoice::onKeyEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { - editMode = true; - invalidate(); - openMenu(); + if (openMenu()) { + editMode = true; + invalidate(); + } } else { FormField::onKeyEvent(event); diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index 7c5b1f958..dd6b6b442 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -20,10 +20,10 @@ #ifndef _FILECHOICE_H_ #define _FILECHOICE_H_ -#include "form.h" +#include "choice.h" #include -class FileChoice: public FormField { +class FileChoice: public ChoiceBase { public: FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); @@ -51,7 +51,7 @@ class FileChoice: public FormField { std::function getValue; std::function setValue; - void openMenu(); + bool openMenu(); }; #endif // _FILECHOICE_H_ diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index c6087d5d4..d940591ac 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -46,22 +46,27 @@ class FormField: public Window { inline static void clearCurrentField() { setCurrentField(nullptr); - }; + } inline static FormField * getCurrentField() { return current; - }; + } inline FormField * getPreviousField() { return previous; - }; + } inline FormField * getNextField() { return next; - }; + } + + inline bool isEditMode() const + { + return editMode; + } inline static void link(FormField * previous, FormField * next) { diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index a65f59d36..a9b140780 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -122,4 +122,36 @@ enum FontSizeIndex #define COLOR(index) LcdFlags((index) << 16) #define COLOR_IDX(att) uint8_t((att) >> 16) +//#define TEXT_COLOR COLOR(TEXT_COLOR_INDEX) +//#define TEXT_BGCOLOR COLOR(TEXT_BGCOLOR_INDEX) +//#define TEXT_INVERTED_COLOR COLOR(TEXT_INVERTED_COLOR_INDEX) +//#define TEXT_INVERTED_BGCOLOR COLOR(TEXT_INVERTED_BGCOLOR_INDEX) +//#define TEXT_STATUSBAR_COLOR COLOR(TEXT_STATUSBAR_COLOR_INDEX) +//#define LINE_COLOR COLOR(LINE_COLOR_INDEX) +#define SCROLLBOX_COLOR COLOR(SCROLLBOX_COLOR_INDEX) +//#define HEADER_SEPARATOR_COLOR COLOR(HEADER_SEPARATOR_COLOR_INDEX) +//#define MENU_TITLE_BGCOLOR COLOR(MENU_TITLE_BGCOLOR_INDEX) +//#define MENU_TITLE_COLOR COLOR(MENU_TITLE_COLOR_INDEX) +//#define MENU_TITLE_DISABLE_COLOR COLOR(MENU_TITLE_DISABLE_COLOR_INDEX) +//#define HEADER_COLOR COLOR(HEADER_COLOR_INDEX) +//#define ALARM_COLOR COLOR(ALARM_COLOR_INDEX) +//#define WARNING_COLOR COLOR(WARNING_COLOR_INDEX) +//#define TEXT_DISABLE_COLOR COLOR(TEXT_DISABLE_COLOR_INDEX) +//#define CURVE_AXIS_COLOR COLOR(CURVE_AXIS_COLOR_INDEX) +//#define CURVE_COLOR COLOR(CURVE_COLOR_INDEX) +//#define CURVE_CURSOR_COLOR COLOR(CURVE_CURSOR_COLOR_INDEX) +//#define TITLE_BGCOLOR COLOR(TITLE_BGCOLOR_INDEX) +//#define TRIM_BGCOLOR COLOR(TRIM_BGCOLOR_INDEX) +//#define TRIM_SHADOW_COLOR COLOR(TRIM_SHADOW_COLOR_INDEX) +//#define HEADER_BGCOLOR COLOR(HEADER_BGCOLOR_INDEX) +//#define HEADER_ICON_BGCOLOR COLOR(HEADER_ICON_BGCOLOR_INDEX) +//#define HEADER_CURRENT_BGCOLOR COLOR(HEADER_CURRENT_BGCOLOR_INDEX) +//#define MAINVIEW_PANES_COLOR COLOR(MAINVIEW_PANES_COLOR_INDEX) +//#define MAINVIEW_GRAPHICS_COLOR COLOR(MAINVIEW_GRAPHICS_COLOR_INDEX) +//#define OVERLAY_COLOR COLOR(OVERLAY_COLOR_INDEX) +//#define BARGRAPH1_COLOR COLOR(BARGRAPH1_COLOR_INDEX) +//#define BARGRAPH2_COLOR COLOR(BARGRAPH2_COLOR_INDEX) +//#define BARGRAPH_BGCOLOR COLOR(BARGRAPH_BGCOLOR_INDEX) +//#define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) + #endif diff --git a/radio/src/thirdparty/libopenui/src/message_dialog.cpp b/radio/src/thirdparty/libopenui/src/message_dialog.cpp new file mode 100644 index 000000000..bf8ce52ba --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/message_dialog.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "message_dialog.h" +#include "static.h" + +MessageDialog::MessageDialog(const char * title, const char * message): + Dialog(title, {50, 73, LCD_W - 100, LCD_H - 146}) +{ + new StaticText(this, {0, coord_t(height() - PAGE_LINE_HEIGHT) / 2, width(), PAGE_LINE_HEIGHT}, message, CENTERED); +} + +#if defined(HARDWARE_KEYS) +void MessageDialog::onKeyEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { + deleteLater(); + } +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/message_dialog.h b/radio/src/thirdparty/libopenui/src/message_dialog.h new file mode 100644 index 000000000..56027f45f --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/message_dialog.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MESSAGE_DIALOG_H_ +#define _MESSAGE_DIALOG_H_ + +#include "dialog.h" + +class MessageDialog: public Dialog { + public: + MessageDialog(const char * title, const char * message); + +#if defined(HARDWARE_KEYS) + void onKeyEvent(event_t event) override; +#endif +}; + +#endif // _MESSAGE_DIALOG_H_ diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index a57a3be06..cac336d6f 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -23,6 +23,7 @@ #include "bitmapbuffer.h" class PageHeader; +class ChoiceBase; enum IconState { STATE_DEFAULT, @@ -34,6 +35,7 @@ class Theme { virtual void drawMessageBox(const char * title, const char * text, const char * action, LcdFlags flags) const = 0; virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const = 0; virtual void drawCheckBox(BitmapBuffer * dc, bool value, bool focus) const = 0; + virtual void drawChoice(BitmapBuffer * dc, ChoiceBase * choice, const char * str) const = 0; virtual void drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const = 0; virtual void drawPageHeader(BitmapBuffer * dc, const PageHeader * page) const = 0; virtual const BitmapBuffer * getIcon(uint8_t index, IconState state) const = 0; From fbc8e019827a6f2fa27e21bba213528d68e50820 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Oct 2019 15:14:43 +0200 Subject: [PATCH 010/714] Updates --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 2 ++ radio/src/thirdparty/libopenui/src/checkbox.h | 1 - radio/src/thirdparty/libopenui/src/slider.cpp | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 9b0da61d5..d143e1704 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -31,6 +31,7 @@ void CheckBox::onKeyEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { + onKeyPress(); setValue(!getValue()); invalidate({0, 0, 20, 20}); } @@ -43,6 +44,7 @@ void CheckBox::onKeyEvent(event_t event) #if defined(HARDWARE_TOUCH) bool CheckBox::onTouchEnd(coord_t x, coord_t y) { + onKeyPress(); setValue(!getValue()); setFocus(); invalidate({0, 0, 20, 20}); diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 0e199400f..dc87446c7 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -50,7 +50,6 @@ class CheckBox : public FormField { #endif protected: - const char * label; std::function getValue; std::function setValue; }; diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index ff981bdcf..c138f6921 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -38,10 +38,12 @@ void Slider::onKeyEvent(event_t event) if (editMode) { if (event == EVT_ROTARY_RIGHT) { setValue(getValue() + rotencSpeed); + onKeyPress(); return; } else if (event == EVT_ROTARY_LEFT) { setValue(getValue() - rotencSpeed); + onKeyPress(); return; } } @@ -54,6 +56,7 @@ void Slider::onKeyEvent(event_t event) bool Slider::onTouchStart(coord_t x, coord_t y) { if (!hasFocus()) { + onKeyPress(); setFocus(); } sliding = (value(x) == getValue()); @@ -64,6 +67,7 @@ bool Slider::onTouchEnd(coord_t x, coord_t y) { setValue(value(x)); invalidate(); + onKeyPress(); return true; } @@ -74,6 +78,7 @@ bool Slider::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, if (getValue() != newValue) { setValue(newValue); invalidate(); + onKeyPress(); } } return true; From 8e462fe9b55af93ea92170c6d3551d391ec43546 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Oct 2019 18:59:38 +0200 Subject: [PATCH 011/714] Updates --- radio/src/thirdparty/libopenui/src/button.cpp | 22 +++++++++---------- .../src/thirdparty/libopenui/src/checkbox.cpp | 6 ++--- radio/src/thirdparty/libopenui/src/checkbox.h | 13 +++++++---- radio/src/thirdparty/libopenui/src/choice.cpp | 4 ++-- .../thirdparty/libopenui/src/coloredit.cpp | 2 +- radio/src/thirdparty/libopenui/src/dialog.cpp | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 10 ++++----- .../libopenui/src/keyboard_text.cpp | 4 ++-- .../libopenui/src/libopenui_defines.h | 8 +++---- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- .../thirdparty/libopenui/src/menutoolbar.cpp | 2 +- .../thirdparty/libopenui/src/menutoolbar.h | 2 +- .../thirdparty/libopenui/src/numberedit.cpp | 8 +++---- radio/src/thirdparty/libopenui/src/static.cpp | 2 +- .../src/thirdparty/libopenui/src/textedit.cpp | 14 ++++++------ radio/src/thirdparty/libopenui/src/theme.h | 7 +++++- 16 files changed, 59 insertions(+), 49 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 88f63d3a9..3203a9d14 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -69,35 +69,35 @@ void TextButton::paint(BitmapBuffer * dc) { FormField::paint(dc); - LcdFlags textColor = TEXT_COLOR; + LcdFlags textColor = DEFAULT_COLOR; if (checked()) { if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); - dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, TEXT_INVERTED_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, FOCUS_BGCOLOR); } else { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, TEXT_INVERTED_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, FOCUS_BGCOLOR); } - textColor = TEXT_INVERTED_COLOR; + textColor = FOCUS_COLOR; } else { if (flags & BUTTON_BACKGROUND) { if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, CURVE_AXIS_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, DISABLE_COLOR); } else { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, CURVE_AXIS_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); } } else { if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } else { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); } } } diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index d143e1704..9c8c06219 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -22,7 +22,7 @@ void CheckBox::paint(BitmapBuffer * dc) { - theme->drawCheckBox(dc, getValue(), hasFocus()); + theme->drawCheckBox(dc, this); } #if defined(HARDWARE_KEYS) @@ -32,7 +32,7 @@ void CheckBox::onKeyEvent(event_t event) if (event == EVT_KEY_BREAK(KEY_ENTER)) { onKeyPress(); - setValue(!getValue()); + _setValue(!getValue()); invalidate({0, 0, 20, 20}); } else { @@ -45,7 +45,7 @@ void CheckBox::onKeyEvent(event_t event) bool CheckBox::onTouchEnd(coord_t x, coord_t y) { onKeyPress(); - setValue(!getValue()); + _setValue(!getValue()); setFocus(); invalidate({0, 0, 20, 20}); return true; diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index dc87446c7..2604038a2 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -27,8 +27,8 @@ class CheckBox : public FormField { CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, LcdFlags flags = 0) : FormField(parent, rect), - getValue(getValue), - setValue(setValue) + _getValue(getValue), + _setValue(setValue) { } @@ -49,9 +49,14 @@ class CheckBox : public FormField { bool onTouchEnd(coord_t x, coord_t y) override; #endif + uint8_t getValue() const + { + return _getValue(); + } + protected: - std::function getValue; - std::function setValue; + std::function _getValue; + std::function _setValue; }; #endif // _CHECKBOX_H_ diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 860275291..5195c4f4b 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -41,9 +41,9 @@ void Choice::paint(BitmapBuffer * dc) // // LcdFlags textColor; // if (editMode) -// textColor = TEXT_INVERTED_COLOR; +// textColor = FOCUS_COLOR; // else if (hasFocus()) -// textColor = TEXT_INVERTED_BGCOLOR; +// textColor = FOCUS_BGCOLOR; // else // textColor = 0; // diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index 1374ce85f..1e085e576 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -42,7 +42,7 @@ class ColorBox: public Window { void paint(BitmapBuffer * dc) override { lcdSetColor(color); - dc->drawSolidFilledRect(0, 0, width(), height(), TEXT_COLOR); + dc->drawSolidFilledRect(0, 0, width(), height(), DEFAULT_COLOR); dc->drawSolidFilledRect(1, 1, width() - 2, height() - 2, CUSTOM_COLOR); } diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index fdcb4c1b0..4f4f2e2be 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -33,7 +33,7 @@ Dialog::Dialog(std::string title, const rect_t rect): void Dialog::paint(BitmapBuffer * dc) { - dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, TEXT_COLOR); + dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, DEFAULT_COLOR); dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, TITLE_BGCOLOR); dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), MENU_TITLE_COLOR); dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, TEXT_BGCOLOR); diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 96712696e..d0e2c5af7 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -99,22 +99,22 @@ void FormGroup::onKeyEvent(event_t event) void FormField::paint(BitmapBuffer * dc) { if (editMode) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FOCUS_BGCOLOR); } else if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } else if (!(windowFlags & BORDER_FOCUS_ONLY)) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); } } void FormGroup::paint(BitmapBuffer * dc) { if (!editMode && hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, TEXT_INVERTED_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } else if (!(windowFlags & BORDER_FOCUS_ONLY)) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, CURVE_AXIS_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); } } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 378e95758..c4e9afc50 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -133,7 +133,7 @@ void TextKeyboard::paint(BitmapBuffer * dc) } else if (*c == '\t') { // spacebar - dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, TEXT_COLOR); + dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, DEFAULT_COLOR); x += 135; } else if (*c == '\n') { @@ -143,7 +143,7 @@ void TextKeyboard::paint(BitmapBuffer * dc) x += 80; } else if (int8_t(*c) < 0) { - dc->drawBitmapPattern(x, y, LBM_SPECIAL_KEYS[uint8_t(*c - 128)], TEXT_COLOR); + dc->drawBitmapPattern(x, y, LBM_SPECIAL_KEYS[uint8_t(*c - 128)], DEFAULT_COLOR); x += 45; } else { diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index a9b140780..c9a2f95c3 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -122,10 +122,10 @@ enum FontSizeIndex #define COLOR(index) LcdFlags((index) << 16) #define COLOR_IDX(att) uint8_t((att) >> 16) -//#define TEXT_COLOR COLOR(TEXT_COLOR_INDEX) +//#define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) //#define TEXT_BGCOLOR COLOR(TEXT_BGCOLOR_INDEX) -//#define TEXT_INVERTED_COLOR COLOR(TEXT_INVERTED_COLOR_INDEX) -//#define TEXT_INVERTED_BGCOLOR COLOR(TEXT_INVERTED_BGCOLOR_INDEX) +//#define FOCUS_COLOR COLOR(FOCUS_COLOR_INDEX) +//#define FOCUS_BGCOLOR COLOR(FOCUS_BGCOLOR_INDEX) //#define TEXT_STATUSBAR_COLOR COLOR(TEXT_STATUSBAR_COLOR_INDEX) //#define LINE_COLOR COLOR(LINE_COLOR_INDEX) #define SCROLLBOX_COLOR COLOR(SCROLLBOX_COLOR_INDEX) @@ -137,7 +137,7 @@ enum FontSizeIndex //#define ALARM_COLOR COLOR(ALARM_COLOR_INDEX) //#define WARNING_COLOR COLOR(WARNING_COLOR_INDEX) //#define TEXT_DISABLE_COLOR COLOR(TEXT_DISABLE_COLOR_INDEX) -//#define CURVE_AXIS_COLOR COLOR(CURVE_AXIS_COLOR_INDEX) +//#define DISABLE_COLOR COLOR(DISABLE_COLOR_INDEX) //#define CURVE_COLOR COLOR(CURVE_COLOR_INDEX) //#define CURVE_CURSOR_COLOR COLOR(CURVE_CURSOR_COLOR_INDEX) //#define TITLE_BGCOLOR COLOR(TITLE_BGCOLOR_INDEX) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 077f433b8..3bef8b906 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -84,7 +84,7 @@ void MenuWindow::paint(BitmapBuffer * dc) dc->drawText(10, i * lineHeight + (lineHeight - 20) / 2, text[0] == '\0' ? "---" : text, flags); } if (i > 0) { - dc->drawSolidHorizontalLine(0, i * lineHeight - 1, width, CURVE_AXIS_COLOR); + dc->drawSolidHorizontalLine(0, i * lineHeight - 1, width, DISABLE_COLOR); } } } diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 4834d56ac..38f28417d 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -27,6 +27,6 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | MENU_TITLE_COLOR); } else { - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | TEXT_COLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | DEFAULT_COLOR); } } \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 4ca1de84c..d1c7bb319 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -85,7 +85,7 @@ class MenuToolbar: public Window { void paint(BitmapBuffer * dc) override { - dc->clear(CURVE_AXIS_COLOR); + dc->clear(DISABLE_COLOR); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index d268d8513..d192cdd4f 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -34,13 +34,13 @@ void NumberEdit::paint(BitmapBuffer * dc) LcdFlags textColor; if (editMode) - textColor = TEXT_INVERTED_COLOR; + textColor = FOCUS_COLOR; else if (hasFocus()) - textColor = TEXT_INVERTED_BGCOLOR; + textColor = FOCUS_BGCOLOR; else if (enabled) - textColor = TEXT_COLOR; + textColor = DEFAULT_COLOR; else - textColor = CURVE_AXIS_COLOR; + textColor = DISABLE_COLOR; int32_t value = getValue(); if (displayFunction) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 3be2d73de..199787138 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -23,7 +23,7 @@ void StaticText::paint(BitmapBuffer * dc) { if (flags & BUTTON_BACKGROUND) - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, CURVE_AXIS_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); if (flags & CENTERED) dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | flags); else diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 39dbc4ee9..09759944e 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -29,24 +29,24 @@ void TextEdit::paint(BitmapBuffer * dc) FormField::paint(dc); if (editMode) { - dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, TEXT_INVERTED_COLOR); + dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, FOCUS_COLOR); coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); char s[] = { value[cursorPos], '\0' }; - dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP + 1, getTextWidth(s, 1) + 1, height() - 2, TEXT_INVERTED_COLOR); - dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, TEXT_COLOR); + dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP + 1, getTextWidth(s, 1) + 1, height() - 2, FOCUS_COLOR); + dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, DEFAULT_COLOR); } else { const char * displayedValue = value; LcdFlags textColor; if (strlen(value) == 0) { displayedValue = "---"; - textColor = hasFocus() ? TEXT_INVERTED_BGCOLOR : CURVE_AXIS_COLOR; + textColor = hasFocus() ? FOCUS_BGCOLOR : DISABLE_COLOR; } else if (hasFocus()) { - textColor = TEXT_INVERTED_BGCOLOR; + textColor = FOCUS_BGCOLOR; } else { - textColor = TEXT_COLOR; + textColor = DEFAULT_COLOR; } dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); } @@ -55,7 +55,7 @@ void TextEdit::paint(BitmapBuffer * dc) auto keyboard = TextKeyboard::instance(); if (hasFocus() && keyboard->getField() == this) { coord_t cursorPos = keyboard->getCursorPos(); - dc->drawSolidFilledRect(cursorPos + 2, FIELD_PADDING_TOP, 2, 21, 0); // TEXT_INVERTED_BGCOLOR); + dc->drawSolidFilledRect(cursorPos + 2, FIELD_PADDING_TOP, 2, 21, 0); // FOCUS_BGCOLOR); } #endif } diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index cac336d6f..b0a432cdf 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -21,6 +21,7 @@ #define _THEME_H_ #include "bitmapbuffer.h" +#include "checkbox.h" class PageHeader; class ChoiceBase; @@ -34,7 +35,11 @@ class Theme { public: virtual void drawMessageBox(const char * title, const char * text, const char * action, LcdFlags flags) const = 0; virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const = 0; - virtual void drawCheckBox(BitmapBuffer * dc, bool value, bool focus) const = 0; + virtual void drawCheckBox(BitmapBuffer * dc, CheckBox * checkBox) const + { + return drawCheckBox(dc, checkBox->getValue(), 0, 0, checkBox->hasFocus()); + } + virtual void drawCheckBox(BitmapBuffer * dc, bool checked, coord_t x, coord_t y, bool focus = false) const = 0; virtual void drawChoice(BitmapBuffer * dc, ChoiceBase * choice, const char * str) const = 0; virtual void drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const = 0; virtual void drawPageHeader(BitmapBuffer * dc, const PageHeader * page) const = 0; From 39b1b3eeecef0a50d1cdcace0e7f4249881874d0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Oct 2019 19:03:39 +0200 Subject: [PATCH 012/714] Cosmetics --- radio/src/thirdparty/libopenui/src/choice.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 5195c4f4b..0b9cc422b 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -37,22 +37,6 @@ void Choice::paint(BitmapBuffer * dc) { FormField::paint(dc); theme->drawChoice(dc, this, textHandler ? textHandler(getValue()).c_str() : TEXT_AT_INDEX(values, getValue() - vmin).c_str()); -// -// -// LcdFlags textColor; -// if (editMode) -// textColor = FOCUS_COLOR; -// else if (hasFocus()) -// textColor = FOCUS_BGCOLOR; -// else -// textColor = 0; -// -// if (textHandler) -// dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, , textColor); -// else -// dc->drawTextAtIndex(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, values, getValue() - vmin, flags | textColor); -// -// dc->drawBitmapPattern(rect.w - 14, (rect.h - 5) / 2, LBM_DROPDOWN, textColor); } #if defined(HARDWARE_KEYS) From b07267d64523a30e53e48ceda648510c396bf485 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Oct 2019 19:05:32 +0200 Subject: [PATCH 013/714] Cosmetics --- radio/src/thirdparty/libopenui/src/dialog.cpp | 2 +- radio/src/thirdparty/libopenui/src/fab_icon_button.cpp | 2 +- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 2 +- radio/src/thirdparty/libopenui/src/page.cpp | 2 +- radio/src/thirdparty/libopenui/src/tabsgroup.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 4f4f2e2be..f203b3084 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -36,7 +36,7 @@ void Dialog::paint(BitmapBuffer * dc) dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, DEFAULT_COLOR); dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, TITLE_BGCOLOR); dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), MENU_TITLE_COLOR); - dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, TEXT_BGCOLOR); + dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, DEFAULT_BGCOLOR); } void Dialog::deleteLater() diff --git a/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp b/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp index 99435d4a6..4e8062c25 100644 --- a/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp +++ b/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp @@ -44,6 +44,6 @@ void FabIconButton::paint(BitmapBuffer * dc) dc->drawBitmap(0, 0, checked() ? &ALPHA_BUTTON_ON : &ALPHA_BUTTON_OFF); const BitmapBuffer * mask = theme->getIconMask(icon); if (mask) { - dc->drawMask((68 - mask->getWidth()) / 2, (68 - mask->getHeight()) / 2, mask, TEXT_BGCOLOR); + dc->drawMask((68 - mask->getWidth()) / 2, (68 - mask->getHeight()) / 2, mask, DEFAULT_BGCOLOR); } } diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index c9a2f95c3..a1caeb96a 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -123,7 +123,7 @@ enum FontSizeIndex #define COLOR_IDX(att) uint8_t((att) >> 16) //#define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) -//#define TEXT_BGCOLOR COLOR(TEXT_BGCOLOR_INDEX) +//#define DEFAULT_BGCOLOR COLOR(DEFAULT_BGCOLOR_INDEX) //#define FOCUS_COLOR COLOR(FOCUS_COLOR_INDEX) //#define FOCUS_BGCOLOR COLOR(FOCUS_BGCOLOR_INDEX) //#define TEXT_STATUSBAR_COLOR COLOR(TEXT_STATUSBAR_COLOR_INDEX) diff --git a/radio/src/thirdparty/libopenui/src/page.cpp b/radio/src/thirdparty/libopenui/src/page.cpp index 6f86287ad..021818ab5 100644 --- a/radio/src/thirdparty/libopenui/src/page.cpp +++ b/radio/src/thirdparty/libopenui/src/page.cpp @@ -71,7 +71,7 @@ void Page::deleteLater() void Page::paint(BitmapBuffer * dc) { - dc->clear(TEXT_BGCOLOR); + dc->clear(DEFAULT_BGCOLOR); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.cpp b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp index 2c2b31365..3563feb32 100644 --- a/radio/src/thirdparty/libopenui/src/tabsgroup.cpp +++ b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp @@ -177,7 +177,7 @@ void TabsGroup::onKeyEvent(event_t event) void TabsGroup::paint(BitmapBuffer * dc) { - dc->clear(TEXT_BGCOLOR); + dc->clear(DEFAULT_BGCOLOR); } #if defined(HARDWARE_TOUCH) From 442a9ad84caa30bfbbde8733bdaf170a79d9b8b3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Oct 2019 19:32:10 +0200 Subject: [PATCH 014/714] Cosmetics --- .../libopenui/src/libopenui_defines.h | 15 +++++----- .../libopenui/src/libopenui_file.cpp | 30 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index a1caeb96a..093fe1f80 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -122,13 +122,16 @@ enum FontSizeIndex #define COLOR(index) LcdFlags((index) << 16) #define COLOR_IDX(att) uint8_t((att) >> 16) -//#define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) -//#define DEFAULT_BGCOLOR COLOR(DEFAULT_BGCOLOR_INDEX) -//#define FOCUS_COLOR COLOR(FOCUS_COLOR_INDEX) -//#define FOCUS_BGCOLOR COLOR(FOCUS_BGCOLOR_INDEX) +#define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) +#define DEFAULT_BGCOLOR COLOR(DEFAULT_BGCOLOR_INDEX) +#define FOCUS_COLOR COLOR(FOCUS_COLOR_INDEX) +#define FOCUS_BGCOLOR COLOR(FOCUS_BGCOLOR_INDEX) +#define DISABLE_COLOR COLOR(DISABLE_COLOR_INDEX) +#define CHECKBOX_COLOR COLOR(CHECKBOX_COLOR_INDEX) +#define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) + //#define TEXT_STATUSBAR_COLOR COLOR(TEXT_STATUSBAR_COLOR_INDEX) //#define LINE_COLOR COLOR(LINE_COLOR_INDEX) -#define SCROLLBOX_COLOR COLOR(SCROLLBOX_COLOR_INDEX) //#define HEADER_SEPARATOR_COLOR COLOR(HEADER_SEPARATOR_COLOR_INDEX) //#define MENU_TITLE_BGCOLOR COLOR(MENU_TITLE_BGCOLOR_INDEX) //#define MENU_TITLE_COLOR COLOR(MENU_TITLE_COLOR_INDEX) @@ -137,7 +140,6 @@ enum FontSizeIndex //#define ALARM_COLOR COLOR(ALARM_COLOR_INDEX) //#define WARNING_COLOR COLOR(WARNING_COLOR_INDEX) //#define TEXT_DISABLE_COLOR COLOR(TEXT_DISABLE_COLOR_INDEX) -//#define DISABLE_COLOR COLOR(DISABLE_COLOR_INDEX) //#define CURVE_COLOR COLOR(CURVE_COLOR_INDEX) //#define CURVE_CURSOR_COLOR COLOR(CURVE_CURSOR_COLOR_INDEX) //#define TITLE_BGCOLOR COLOR(TITLE_BGCOLOR_INDEX) @@ -152,6 +154,5 @@ enum FontSizeIndex //#define BARGRAPH1_COLOR COLOR(BARGRAPH1_COLOR_INDEX) //#define BARGRAPH2_COLOR COLOR(BARGRAPH2_COLOR_INDEX) //#define BARGRAPH_BGCOLOR COLOR(BARGRAPH_BGCOLOR_INDEX) -//#define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) #endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp index d01a1db3c..83bfa1caa 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp @@ -47,6 +47,36 @@ const char * getFileExtension(const char * filename, uint8_t size, uint8_t extMa return nullptr; } +/** + Check if given extension exists in a list of extensions. + @param extension The extension to search for, including leading period. + @param pattern One or more file extensions concatenated together, including the periods. + The list is searched backwards and the first match, if any, is returned. + eg: ".gif.jpg.jpeg.png" + @param match Optional container to hold the matched file extension (wide enough to hold LEN_FILE_EXTENSION_MAX + 1). + @retval true if a extension was found in the lost, false otherwise. +*/ +bool isExtensionMatching(const char * extension, const char * pattern, char * match) +{ + const char *ext; + uint8_t extlen, fnlen; + int plen; + + ext = getFileExtension(pattern, 0, 0, &fnlen, &extlen); + plen = (int)fnlen; + while (plen > 0 && ext) { + if (!strncasecmp(extension, ext, extlen)) { + if (match != nullptr) strncat(&(match[0]='\0'), ext, extlen); + return true; + } + plen -= extlen; + if (plen > 0) { + ext = getFileExtension(pattern, plen, 0, nullptr, &extlen); + } + } + return false; +} + // returns true if current working dir is at the root level bool isCwdAtRoot() { diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index ed0805ee8..902f512d5 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -390,6 +390,6 @@ void Window::drawVerticalScrollbar(BitmapBuffer * dc) yofs = innerHeight - y - yhgt; } } - dc->drawSolidFilledRect(x-1, y + yofs, 3, yhgt, SCROLLBOX_COLOR); + dc->drawSolidFilledRect(x-1, y + yofs, 3, yhgt, CHECKBOX_COLOR); } } From 08329dd6a43f47e85be5fdaee606612c7935aef3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Oct 2019 19:37:23 +0200 Subject: [PATCH 015/714] Cosmetics --- radio/src/thirdparty/libopenui/src/page.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/page.cpp b/radio/src/thirdparty/libopenui/src/page.cpp index 021818ab5..762f2536d 100644 --- a/radio/src/thirdparty/libopenui/src/page.cpp +++ b/radio/src/thirdparty/libopenui/src/page.cpp @@ -22,7 +22,7 @@ #include "keyboard_number.h" #include "keyboard_text.h" #include "keyboard_curve.h" -#include "opentx.h" // TODO for constants +#include "theme.h" PageHeader::PageHeader(Page * parent, uint8_t icon): Window(parent, { 0, 0, LCD_W, MENU_HEADER_HEIGHT }, OPAQUE), From 701e48772b7b30d842d0afa6c75abc465a4fe2a3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Oct 2019 19:49:14 +0200 Subject: [PATCH 016/714] Cosmetics --- radio/src/thirdparty/libopenui/src/choice.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 0b9cc422b..56648895f 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -94,6 +94,7 @@ void Choice::openMenu() #if defined(HARDWARE_TOUCH) bool Choice::onTouchEnd(coord_t, coord_t) { + onKeyPress(); openMenu(); setFocus(); return true; From 3c5cfce3b4a07a40ba4cd6a54a2e76e3f83a0cdb Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 5 Oct 2019 08:21:06 +0200 Subject: [PATCH 017/714] Compilation fix --- .../libopenui/src/keyboard_text.cpp | 34 ++++++++++++------- radio/src/thirdparty/libopenui/src/page.cpp | 1 + 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index c4e9afc50..0227fddbc 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -58,25 +58,33 @@ const uint8_t * const LBM_SPECIAL_KEYS[] = { LBM_KEY_NUMBERS, }; +#define KEYBOARD_SPACE "\t" +#define KEYBOARD_ENTER "\n" +#define KEYBOARD_BACKSPACE "\200" +#define KEYBOARD_SET_UPPERCASE "\201" +#define KEYBOARD_SET_LOWERCASE "\202" +#define KEYBOARD_SET_LETTERS "\203" +#define KEYBOARD_SET_NUMBERS "\204" + const char * const KEYBOARD_LOWERCASE[] = { "qwertyuiop", " asdfghjkl", - "\201zxcvbnm\200", - "\204\t\n" + KEYBOARD_SET_UPPERCASE "zxcvbnm" KEYBOARD_BACKSPACE, + KEYBOARD_SET_NUMBERS KEYBOARD_SPACE KEYBOARD_ENTER }; const char * const KEYBOARD_UPPERCASE[] = { "QWERTYUIOP", " ASDFGHJKL", - "\202ZXCVBNM\200", - "\204\t\n" + KEYBOARD_SET_LOWERCASE "ZXCVBNM" KEYBOARD_BACKSPACE, + KEYBOARD_SET_NUMBERS KEYBOARD_SPACE KEYBOARD_ENTER }; const char * const KEYBOARD_NUMBERS[] = { "1234567890", "_-", - " \200", - "\203\t\n" + " " KEYBOARD_BACKSPACE, + KEYBOARD_SET_LETTERS KEYBOARD_SPACE KEYBOARD_ENTER }; const char * const * const KEYBOARD_LAYOUTS[] = { @@ -127,16 +135,16 @@ void TextKeyboard::paint(BitmapBuffer * dc) coord_t y = 15 + i * 40; coord_t x = 15; const char * c = layout[i]; - while(*c) { + while (*c) { if (*c == ' ') { x += 15; } - else if (*c == '\t') { + else if (*c == KEYBOARD_SPACE[0]) { // spacebar dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, DEFAULT_COLOR); x += 135; } - else if (*c == '\n') { + else if (*c == KEYBOARD_ENTER[0]) { // enter dc->drawSolidFilledRect(x, y-2, 80, 25, TEXT_DISABLE_COLOR); dc->drawText(x+40, y, "ENTER", CENTERED); @@ -160,6 +168,8 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) if (!field) return false; + onKeyPress(); + uint8_t size = field->getMaxLength(); char * data = field->getData(); @@ -167,18 +177,18 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) uint8_t row = max(0, y - 5) / 40; const char * key = layout[row]; - while(*key) { + while (*key) { if (*key == ' ') { x -= 15; } - else if (*key == '\t') { + else if (*key == KEYBOARD_SPACE[0]) { if (x <= 135) { c = ' '; break; } x -= 135; } - else if (*key == '\n') { + else if (*key == KEYBOARD_ENTER[0]) { if (x <= 80) { // enter disable(true); diff --git a/radio/src/thirdparty/libopenui/src/page.cpp b/radio/src/thirdparty/libopenui/src/page.cpp index 762f2536d..927e1fa5f 100644 --- a/radio/src/thirdparty/libopenui/src/page.cpp +++ b/radio/src/thirdparty/libopenui/src/page.cpp @@ -23,6 +23,7 @@ #include "keyboard_text.h" #include "keyboard_curve.h" #include "theme.h" +#include "opentx.h" PageHeader::PageHeader(Page * parent, uint8_t icon): Window(parent, { 0, 0, LCD_W, MENU_HEADER_HEIGHT }, OPAQUE), From a9bce97bdddb0c925fb7f98c28f5bb7f687592a4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 5 Oct 2019 14:28:49 +0200 Subject: [PATCH 018/714] Cosmetics --- radio/src/thirdparty/libopenui/src/button.cpp | 4 +- radio/src/thirdparty/libopenui/src/button.h | 2 +- .../src/thirdparty/libopenui/src/checkbox.cpp | 4 +- radio/src/thirdparty/libopenui/src/checkbox.h | 2 +- radio/src/thirdparty/libopenui/src/choice.cpp | 6 +- radio/src/thirdparty/libopenui/src/choice.h | 2 +- .../thirdparty/libopenui/src/filechoice.cpp | 5 +- .../src/thirdparty/libopenui/src/filechoice.h | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 12 +-- radio/src/thirdparty/libopenui/src/form.h | 11 ++- .../thirdparty/libopenui/src/keyboard_base.h | 58 +++++++----- .../libopenui/src/keyboard_number.cpp | 61 +++++-------- .../libopenui/src/keyboard_number.h | 9 +- .../libopenui/src/keyboard_text.cpp | 64 ++----------- .../thirdparty/libopenui/src/keyboard_text.h | 25 +++-- .../thirdparty/libopenui/src/mainwindow.cpp | 2 + .../src/thirdparty/libopenui/src/mainwindow.h | 2 +- radio/src/thirdparty/libopenui/src/menu.cpp | 12 +-- radio/src/thirdparty/libopenui/src/menu.h | 4 +- .../thirdparty/libopenui/src/menutoolbar.h | 2 +- .../libopenui/src/message_dialog.cpp | 2 +- .../thirdparty/libopenui/src/message_dialog.h | 2 +- .../thirdparty/libopenui/src/numberedit.cpp | 91 ++++++++++++------- .../src/thirdparty/libopenui/src/numberedit.h | 4 +- radio/src/thirdparty/libopenui/src/page.cpp | 14 +-- radio/src/thirdparty/libopenui/src/page.h | 2 +- radio/src/thirdparty/libopenui/src/slider.cpp | 4 +- radio/src/thirdparty/libopenui/src/slider.h | 2 +- .../thirdparty/libopenui/src/tabsgroup.cpp | 22 ++--- .../src/thirdparty/libopenui/src/tabsgroup.h | 2 +- .../src/thirdparty/libopenui/src/textedit.cpp | 82 +++++++++++------ radio/src/thirdparty/libopenui/src/textedit.h | 4 +- radio/src/thirdparty/libopenui/src/window.cpp | 10 +- radio/src/thirdparty/libopenui/src/window.h | 4 +- 34 files changed, 259 insertions(+), 275 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 3203a9d14..ff5caef45 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -32,7 +32,7 @@ void Button::onPress() } #if defined(HARDWARE_KEYS) -void Button::onKeyEvent(event_t event) +void Button::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -40,7 +40,7 @@ void Button::onKeyEvent(event_t event) onPress(); } else { - FormField::onKeyEvent(event); + FormField::onEvent(event); } } #endif diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 03091f67a..7af9a52a9 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -92,7 +92,7 @@ class Button : public FormField { } #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event); + void onEvent(event_t event); #endif #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 9c8c06219..dd6dd0f85 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -26,7 +26,7 @@ void CheckBox::paint(BitmapBuffer * dc) } #if defined(HARDWARE_KEYS) -void CheckBox::onKeyEvent(event_t event) +void CheckBox::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -36,7 +36,7 @@ void CheckBox::onKeyEvent(event_t event) invalidate({0, 0, 20, 20}); } else { - FormField::onKeyEvent(event); + FormField::onEvent(event); } } #endif diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 2604038a2..d61605881 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -42,7 +42,7 @@ class CheckBox : public FormField { void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 56648895f..da77b1b41 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -36,11 +36,12 @@ Choice::Choice(Window * parent, const rect_t & rect, const char * values, int16_ void Choice::paint(BitmapBuffer * dc) { FormField::paint(dc); + theme->drawChoice(dc, this, textHandler ? textHandler(getValue()).c_str() : TEXT_AT_INDEX(values, getValue() - vmin).c_str()); } #if defined(HARDWARE_KEYS) -void Choice::onKeyEvent(event_t event) +void Choice::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -50,7 +51,7 @@ void Choice::onKeyEvent(event_t event) openMenu(); } else { - FormField::onKeyEvent(event); + FormField::onEvent(event); } } #endif @@ -96,6 +97,7 @@ bool Choice::onTouchEnd(coord_t, coord_t) { onKeyPress(); openMenu(); + setEditMode(true); setFocus(); return true; } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 6eaf48a99..a7f2365e4 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -58,7 +58,7 @@ class Choice : public ChoiceBase { void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 8e25c26ac..49b0723e1 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -119,7 +119,7 @@ bool FileChoice::openMenu() } #if defined(HARDWARE_KEYS) -void FileChoice::onKeyEvent(event_t event) +void FileChoice::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -130,7 +130,7 @@ void FileChoice::onKeyEvent(event_t event) } } else { - FormField::onKeyEvent(event); + FormField::onEvent(event); } } #endif @@ -140,6 +140,7 @@ bool FileChoice::onTouchEnd(coord_t, coord_t) { openMenu(); setFocus(); + setEditMode(true); return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index dd6b6b442..2a28ea3a9 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -37,7 +37,7 @@ class FileChoice: public ChoiceBase { void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index d0e2c5af7..7fa8dd593 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -39,7 +39,7 @@ FormField::FormField(Window *parent, const rect_t &rect, uint8_t flags) : } #if defined(HARDWARE_KEYS) -void FormWindow::onKeyEvent(event_t event) +void FormWindow::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -47,11 +47,11 @@ void FormWindow::onKeyEvent(event_t event) first->setFocus(); } else { - Window::onKeyEvent(event); + Window::onEvent(event); } } -void FormField::onKeyEvent(event_t event) +void FormField::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", FormField::getWindowDebugString().c_str(), event); @@ -74,11 +74,11 @@ void FormField::onKeyEvent(event_t event) invalidate(); } else { - Window::onKeyEvent(event); + Window::onEvent(event); } } -void FormGroup::onKeyEvent(event_t event) +void FormGroup::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -91,7 +91,7 @@ void FormGroup::onKeyEvent(event_t event) setFocus(); } else { - FormField::onKeyEvent(event); + FormField::onEvent(event); } } #endif diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index d940591ac..20e5713d8 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -68,6 +68,11 @@ class FormField: public Window { return editMode; } + inline void setEditMode(bool newEditMode) + { + editMode = newEditMode; + } + inline static void link(FormField * previous, FormField * next) { previous->setNextField(next); @@ -77,7 +82,7 @@ class FormField: public Window { void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif protected: @@ -140,7 +145,7 @@ class FormGroup: public FormField { FormField * first = nullptr; #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif void paint(BitmapBuffer * dc) override; @@ -188,7 +193,7 @@ class FormWindow: public Window { protected: #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif void paint(BitmapBuffer * dc) override diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 3fdbf0d5a..7a91c2890 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -21,12 +21,8 @@ #define _KEYBOARD_BASE_H_ #include "mainwindow.h" -#include "libopenui_types.h" +#include "form.h" -class Page; -class TabsGroup; - -template class Keyboard: public Window { public: explicit Keyboard(coord_t height): @@ -34,34 +30,50 @@ class Keyboard: public Window { { } - void setField(T * newField) + static void hide() { - field = newField; - attach(&mainWindow); - Window * w = getPageBody(); - w->setHeight(LCD_H - height() - w->top()); - w->scrollTo(newField); - invalidate(); + if (activeKeyboard) { + activeKeyboard->clearField(); + activeKeyboard = nullptr; + } } - void disable(bool resizePage) + protected: + static Keyboard * activeKeyboard; + FormField * field = nullptr; + Window * fieldContainer = nullptr; + + void setField(FormField * newField) + { + if (activeKeyboard) { + if (activeKeyboard == this) + return; + activeKeyboard->clearField(); + } + activeKeyboard = this; + attach(&mainWindow); + fieldContainer = getFieldContainer(newField); + fieldContainer->setHeight(LCD_H - height() - fieldContainer->top()); + fieldContainer->scrollTo(newField); + invalidate(); + newField->setEditMode(true); + field = newField; + } + + void clearField() { detach(); + if (fieldContainer) { + fieldContainer->setHeight(LCD_H - 0 - fieldContainer->top()); + fieldContainer = nullptr; + } if (field) { - Window * w = getPageBody(); - w->setHeight(LCD_H - 0 - w->top()); + field->setEditMode(false); field = nullptr; } } - T * getField() const - { - return field; - } - - protected: - T * field = nullptr; - Window * getPageBody() + Window * getFieldContainer(FormField * field) { Window * parent = field; while (true) { diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index 41ec31a31..90767690a 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -19,70 +19,55 @@ #include "keyboard_number.h" #include "button.h" -#include "basenumberedit.h" #include "libopenui_globals.h" constexpr coord_t KEYBOARD_HEIGHT = 90; NumberKeyboard * NumberKeyboard::_instance = nullptr; -NumberKeyboard::NumberKeyboard(): - Keyboard(KEYBOARD_HEIGHT) +NumberKeyboard::NumberKeyboard() : + Keyboard(KEYBOARD_HEIGHT) { - new TextButton(this, { LCD_W/2 - 115, 10, 50, 30 }, "<<", + new TextButton(this, {LCD_W / 2 - 115, 10, 50, 30}, "<<", [=]() -> uint8_t { - if (field) { - field->setValue(field->getValue() - 10 * field->getStep()); - } - return 0; + putEvent(EVT_VIRTUAL_KEY_BACKWARD); + return 0; }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); - new TextButton(this, { LCD_W/2 - 55, 10, 50, 30 }, "-", + new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", [=]() -> uint8_t { - if (field) { - field->setValue(field->getValue() - field->getStep()); - } - return 0; + putEvent(EVT_VIRTUAL_KEY_MINUS); + return 0; }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); - new TextButton(this, { LCD_W/2 + 5, 10, 50, 30 }, "+", + new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", [=]() -> uint8_t { - if (field) { - field->setValue(field->getValue() + field->getStep()); - } - return 0; + putEvent(EVT_VIRTUAL_KEY_PLUS); + return 0; }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); - new TextButton(this, { LCD_W/2 + 65, 10, 50, 30 }, ">>", + new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", [=]() -> uint8_t { - if (field) { - field->setValue(field->getValue() + 10 * field->getStep()); - } - return 0; + putEvent(EVT_VIRTUAL_KEY_FORWARD); + return 0; }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); - new TextButton(this, { LCD_W/2 - 115, 50, 50, 30 }, "MIN", + new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", [=]() -> uint8_t { - if (field) { - field->setValue(field->getMin()); - } - return 0; + putEvent(EVT_VIRTUAL_KEY_MIN); + return 0; }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); - new TextButton(this, { LCD_W/2 + 65, 50, 50, 30 }, "MAX", + new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", [=]() -> uint8_t { - if (field) { - field->setValue(field->getMax()); - } - return 0; + putEvent(EVT_VIRTUAL_KEY_MAX); + return 0; }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); - new TextButton(this, { LCD_W/2 - 55, 50, 110, 30 }, "DEFAULT", + new TextButton(this, {LCD_W / 2 - 55, 50, 110, 30}, "DEFAULT", [=]() -> uint8_t { - if (field) { - field->setValue(field->getDefault()); - } - return 0; + putEvent(EVT_VIRTUAL_KEY_DEFAULT); + return 0; }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h index 2d6cf63e0..52e195658 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -21,11 +21,8 @@ #define _KEYBOARD_NUMBER_H_ #include "keyboard_base.h" -#include "basenumberedit.h" - -class NumberKeyboard : public Keyboard { - friend class BaseNumberEdit; +class NumberKeyboard : public Keyboard { public: NumberKeyboard(); @@ -38,11 +35,11 @@ class NumberKeyboard : public Keyboard { } #endif - static NumberKeyboard * instance() + static void show(FormField * field) { if (!_instance) _instance = new NumberKeyboard(); - return _instance; + _instance->setField(field); } void paint(BitmapBuffer * dc) override; diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 0227fddbc..67eabe4b2 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -58,14 +58,6 @@ const uint8_t * const LBM_SPECIAL_KEYS[] = { LBM_KEY_NUMBERS, }; -#define KEYBOARD_SPACE "\t" -#define KEYBOARD_ENTER "\n" -#define KEYBOARD_BACKSPACE "\200" -#define KEYBOARD_SET_UPPERCASE "\201" -#define KEYBOARD_SET_LOWERCASE "\202" -#define KEYBOARD_SET_LETTERS "\203" -#define KEYBOARD_SET_NUMBERS "\204" - const char * const KEYBOARD_LOWERCASE[] = { "qwertyuiop", " asdfghjkl", @@ -95,7 +87,7 @@ const char * const * const KEYBOARD_LAYOUTS[] = { }; TextKeyboard::TextKeyboard(): - Keyboard(KEYBOARD_HEIGHT), + Keyboard(KEYBOARD_HEIGHT), layout(KEYBOARD_LOWERCASE) { } @@ -105,27 +97,6 @@ TextKeyboard::~TextKeyboard() _instance = nullptr; } -void TextKeyboard::setCursorPos(coord_t x) -{ - if (!field) - return; - - uint8_t size = field->getMaxLength(); - char * data = field->getData(); - coord_t rest = x; - for (cursorIndex = 0; cursorIndex < size; cursorIndex++) { - if (data[cursorIndex] == '\0') - break; - char c = data[cursorIndex]; - uint8_t w = getCharWidth(c, fontspecsTable[0]); - if (rest < w) - break; - rest -= w; - } - cursorPos = x - rest; - field->invalidate(); -} - void TextKeyboard::paint(BitmapBuffer * dc) { lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); @@ -165,16 +136,8 @@ void TextKeyboard::paint(BitmapBuffer * dc) bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) { - if (!field) - return false; - onKeyPress(); - uint8_t size = field->getMaxLength(); - char * data = field->getData(); - - char c = 0; - uint8_t row = max(0, y - 5) / 40; const char * key = layout[row]; while (*key) { @@ -183,15 +146,15 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) } else if (*key == KEYBOARD_SPACE[0]) { if (x <= 135) { - c = ' '; - break; + putEvent(EVT_VIRTUAL_KEY(' ')); + return true; } x -= 135; } else if (*key == KEYBOARD_ENTER[0]) { if (x <= 80) { // enter - disable(true); + hide(); return true; } x -= 80; @@ -201,13 +164,7 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) uint8_t specialKey = *key; if (specialKey == 128) { // backspace - if (cursorIndex > 0) { - char c = data[cursorIndex - 1]; - memmove(data + cursorIndex - 1, data + cursorIndex, size - cursorIndex); - data[size - 1] = '\0'; - cursorPos -= getCharWidth(c, fontspecsTable[0]); - --cursorIndex; - } + putEvent(EVT_VIRTUAL_KEY(KEYBOARD_BACKSPACE[0])); } else { layout = KEYBOARD_LAYOUTS[specialKey - 129]; @@ -219,20 +176,13 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) } else { if (x <= 30) { - c = *key; - break; + putEvent(EVT_VIRTUAL_KEY(*key)); + return true; } x -= 30; } key++; } - if (c && size - cursorIndex > 1) { - memmove(data + cursorIndex + 1, data + cursorIndex, size - cursorIndex - 1); - data[cursorIndex++] = c; - cursorPos += getCharWidth(c, fontspecsTable[0]); - } - - field->invalidate(); return true; } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index 36daf8672..bc333904e 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -21,11 +21,16 @@ #define _KEYBOARD_TEXT_H_ #include "keyboard_base.h" -#include "textedit.h" -class TextKeyboard : public Keyboard { - friend class TextEdit; +#define KEYBOARD_SPACE "\t" +#define KEYBOARD_ENTER "\n" +#define KEYBOARD_BACKSPACE "\200" +#define KEYBOARD_SET_UPPERCASE "\201" +#define KEYBOARD_SET_LOWERCASE "\202" +#define KEYBOARD_SET_LETTERS "\203" +#define KEYBOARD_SET_NUMBERS "\204" +class TextKeyboard : public Keyboard { public: TextKeyboard(); @@ -38,19 +43,13 @@ class TextKeyboard : public Keyboard { } #endif - static TextKeyboard * instance() { + static void show(FormField * field) + { if (!_instance) _instance = new TextKeyboard(); - return _instance; + _instance->setField(field); } - coord_t getCursorPos() const - { - return cursorPos; - } - - void setCursorPos(coord_t x); - void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_TOUCH) @@ -59,8 +58,6 @@ class TextKeyboard : public Keyboard { protected: static TextKeyboard * _instance; - coord_t cursorPos = 0; - uint8_t cursorIndex = 0; const char * const * layout; }; diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index a0fe22406..260683d6c 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -18,6 +18,7 @@ */ #include "mainwindow.h" +#include "keyboard_base.h" #if defined(HARDWARE_TOUCH) #include "touch.h" @@ -27,6 +28,7 @@ MainWindow mainWindow; #if defined(HARDWARE_TOUCH) STRUCT_TOUCH touchState; +Keyboard * Keyboard::activeKeyboard = nullptr; #endif void MainWindow::emptyTrash() diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 31bae69df..86d51e8c9 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -39,7 +39,7 @@ class MainWindow: public Window { } #endif - void checkEvents(bool trash=true); + void checkEvents(bool trash = true); void invalidate() { diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 3bef8b906..6a49501d8 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -35,7 +35,7 @@ void MenuWindow::select(int index) } #if defined(HARDWARE_KEYS) -void MenuWindow::onKeyEvent(event_t event) +void MenuWindow::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -46,16 +46,16 @@ void MenuWindow::onKeyEvent(event_t event) select(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1); } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - Window::onKeyEvent(event); // the window above will be closed on event + Window::onEvent(event); // the window above will be closed on event lines[selectedIndex].onPress(); } else if (event == EVT_KEY_BREAK(KEY_EXIT)) { if (onCancel) onCancel(); - Window::onKeyEvent(event); + Window::onEvent(event); } else { - Window::onKeyEvent(event); + Window::onEvent(event); } } #endif @@ -120,10 +120,10 @@ void Menu::removeLines() } #if defined(HARDWARE_KEYS) -void Menu::onKeyEvent(event_t event) +void Menu::onEvent(event_t event) { if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN))) { - toolbar->onKeyEvent(event); + toolbar->onEvent(event); } else if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { deleteLater(); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index f6703407a..b76d1f477 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -94,7 +94,7 @@ class MenuWindow: public Window { void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) @@ -171,7 +171,7 @@ class Menu : public Window { } #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index d1c7bb319..3cf902dd0 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -89,7 +89,7 @@ class MenuToolbar: public Window { } #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override + void onEvent(event_t event) override { if (event == EVT_KEY_BREAK(KEY_PGDN)) { if (current == children.end()) { diff --git a/radio/src/thirdparty/libopenui/src/message_dialog.cpp b/radio/src/thirdparty/libopenui/src/message_dialog.cpp index bf8ce52ba..71a301f65 100644 --- a/radio/src/thirdparty/libopenui/src/message_dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/message_dialog.cpp @@ -27,7 +27,7 @@ MessageDialog::MessageDialog(const char * title, const char * message): } #if defined(HARDWARE_KEYS) -void MessageDialog::onKeyEvent(event_t event) +void MessageDialog::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); diff --git a/radio/src/thirdparty/libopenui/src/message_dialog.h b/radio/src/thirdparty/libopenui/src/message_dialog.h index 56027f45f..755a0fe1f 100644 --- a/radio/src/thirdparty/libopenui/src/message_dialog.h +++ b/radio/src/thirdparty/libopenui/src/message_dialog.h @@ -27,7 +27,7 @@ class MessageDialog: public Dialog { MessageDialog(const char * title, const char * message); #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif }; diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index d192cdd4f..8435a95d5 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -51,39 +51,72 @@ void NumberEdit::paint(BitmapBuffer * dc) dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | flags, 0, prefix.c_str(), suffix.c_str()); } -#if defined(HARDWARE_KEYS) -void NumberEdit::onKeyEvent(event_t event) +void NumberEdit::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (editMode) { - if (event == EVT_ROTARY_RIGHT) { - int value = getValue(); - do { - value += rotencSpeed * step; - } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); - if (value <= vmax) - setValue(value); - else - onKeyError(); - return; - } - else if (event == EVT_ROTARY_LEFT) { - int value = getValue(); - do { - value -= rotencSpeed * step; - } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); - if (value >= vmin) - setValue(value); - else - onKeyError(); - return; + switch (event) { +#if defined(HARDWARE_KEYS) + case EVT_ROTARY_RIGHT: { + int value = getValue(); + do { + value += rotencSpeed * step; + } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); + if (value <= vmax) + setValue(value); + else + onKeyError(); + return; + } + + case EVT_ROTARY_LEFT: { + int value = getValue(); + do { + value -= rotencSpeed * step; + } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); + if (value >= vmin) + setValue(value); + else + onKeyError(); + return; + } +#endif + +#if defined(HARDWARE_TOUCH) + case EVT_VIRTUAL_KEY_PLUS: + setValue(getValue() + getStep()); + break; + + case EVT_VIRTUAL_KEY_MINUS: + setValue(getValue() - getStep()); + break; + + case EVT_VIRTUAL_KEY_FORWARD: + setValue(getValue() + 10 * getStep()); + break; + + case EVT_VIRTUAL_KEY_BACKWARD: + setValue(getValue() - 10 * getStep()); + break; + + case EVT_VIRTUAL_KEY_DEFAULT: + setValue(getDefault()); + break; + + case EVT_VIRTUAL_KEY_MAX: + setValue(getMax()); + break; + + case EVT_VIRTUAL_KEY_MIN: + setValue(getMin()); + break; +#endif } } - FormField::onKeyEvent(event); + FormField::onEvent(event); } -#endif #if defined(HARDWARE_TOUCH) bool NumberEdit::onTouchEnd(coord_t, coord_t) @@ -96,10 +129,7 @@ bool NumberEdit::onTouchEnd(coord_t, coord_t) setFocus(); } - NumberKeyboard * keyboard = NumberKeyboard::instance(); - if (keyboard->getField() != this) { - keyboard->setField(this); - } + NumberKeyboard::show(this); return true; } @@ -108,8 +138,7 @@ bool NumberEdit::onTouchEnd(coord_t, coord_t) void NumberEdit::onFocusLost() { #if defined(HARDWARE_TOUCH) - NumberKeyboard::instance()->disable(true); + Keyboard::hide(); #endif - FormField::onFocusLost(); } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index d8b51d805..7bf689344 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -67,9 +67,7 @@ class NumberEdit : public BaseNumberEdit { displayFunction = std::move(function); } -#if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; -#endif + void onEvent(event_t event) override; #if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t x, coord_t y) override; diff --git a/radio/src/thirdparty/libopenui/src/page.cpp b/radio/src/thirdparty/libopenui/src/page.cpp index 927e1fa5f..a4d1c3a11 100644 --- a/radio/src/thirdparty/libopenui/src/page.cpp +++ b/radio/src/thirdparty/libopenui/src/page.cpp @@ -19,9 +19,7 @@ #include "page.h" #include "mainwindow.h" -#include "keyboard_number.h" -#include "keyboard_text.h" -#include "keyboard_curve.h" +#include "keyboard_base.h" #include "theme.h" #include "opentx.h" @@ -55,9 +53,7 @@ Page::Page(unsigned icon): Page::~Page() { #if defined(HARDWARE_TOUCH) - TextKeyboard::instance()->disable(false); - NumberKeyboard::instance()->disable(false); - CurveKeyboard::instance()->disable(false); + Keyboard::hide(); #endif } @@ -76,7 +72,7 @@ void Page::paint(BitmapBuffer * dc) } #if defined(HARDWARE_KEYS) -void Page::onKeyEvent(event_t event) +void Page::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -93,9 +89,7 @@ bool Page::onTouchEnd(coord_t x, coord_t y) if (Window::onTouchEnd(x, y)) return true; - TextKeyboard::instance()->disable(true); - NumberKeyboard::instance()->disable(true); - CurveKeyboard::instance()->disable(true); + Keyboard::hide(); return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/page.h b/radio/src/thirdparty/libopenui/src/page.h index a43d054a3..03c714cf5 100644 --- a/radio/src/thirdparty/libopenui/src/page.h +++ b/radio/src/thirdparty/libopenui/src/page.h @@ -57,7 +57,7 @@ class Page: public Window { #endif #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index c138f6921..3284e884a 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -31,7 +31,7 @@ int Slider::value(coord_t x) const } #if defined(HARDWARE_KEYS) -void Slider::onKeyEvent(event_t event) +void Slider::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -48,7 +48,7 @@ void Slider::onKeyEvent(event_t event) } } - FormField::onKeyEvent(event); + FormField::onEvent(event); } #endif diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index 3087a8682..7f00b9837 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -49,7 +49,7 @@ class Slider : public FormField { void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.cpp b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp index 3563feb32..95813c445 100644 --- a/radio/src/thirdparty/libopenui/src/tabsgroup.cpp +++ b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp @@ -19,15 +19,13 @@ #include "tabsgroup.h" #include "mainwindow.h" +#include "view_main.h" #if defined(HARDWARE_TOUCH) -#include "keyboard_number.h" -#include "keyboard_text.h" -#include "keyboard_curve.h" +#include "keyboard_base.h" #endif #include "opentx.h" // TODO for constants... -#include "view_main.h" TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): Window(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE), @@ -84,9 +82,7 @@ TabsGroup::TabsGroup(uint8_t icon): TabsGroup::~TabsGroup() { #if defined(HARDWARE_TOUCH) - TextKeyboard::instance()->disable(false); - NumberKeyboard::instance()->disable(false); - CurveKeyboard::instance()->disable(false); + Keyboard::hide(); #endif for (auto tab: tabs) { @@ -131,9 +127,7 @@ void TabsGroup::setVisibleTab(PageTab * tab) FormField::clearCurrentField(); body.clear(); #if defined(HARDWARE_TOUCH) - TextKeyboard::instance()->disable(false); - NumberKeyboard::instance()->disable(false); - CurveKeyboard::instance()->disable(false); + Keyboard::hide(); #endif currentTab = tab; tab->build(&body); @@ -151,7 +145,7 @@ void TabsGroup::checkEvents() } #if defined(HARDWARE_KEYS) -void TabsGroup::onKeyEvent(event_t event) +void TabsGroup::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -170,7 +164,7 @@ void TabsGroup::onKeyEvent(event_t event) deleteLater(); } else if (parent) { - parent->onKeyEvent(event); + parent->onEvent(event); } } #endif @@ -186,9 +180,7 @@ bool TabsGroup::onTouchEnd(coord_t x, coord_t y) if (Window::onTouchEnd(x, y)) return true; - TextKeyboard::instance()->disable(true); - NumberKeyboard::instance()->disable(true); - CurveKeyboard::instance()->disable(true); + Keyboard::hide(); return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.h b/radio/src/thirdparty/libopenui/src/tabsgroup.h index 182bab6bc..8f36bd2bd 100644 --- a/radio/src/thirdparty/libopenui/src/tabsgroup.h +++ b/radio/src/thirdparty/libopenui/src/tabsgroup.h @@ -170,7 +170,7 @@ class TabsGroup: public Window { void checkEvents() override; #if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; + void onEvent(event_t event) override; #endif void paint(BitmapBuffer * dc) override; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 09759944e..8eaf7c6a4 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -19,6 +19,7 @@ #include "textedit.h" #include "font.h" +#include "libopenui_globals.h" #if defined(HARDWARE_TOUCH) #include "keyboard_text.h" @@ -31,40 +32,57 @@ void TextEdit::paint(BitmapBuffer * dc) if (editMode) { dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, FOCUS_COLOR); coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); +#if defined(HARDWARE_TOUCH) + dc->drawSolidFilledRect(left + 2, FIELD_PADDING_TOP + 1, 2, 20, FOCUS_COLOR); +#else char s[] = { value[cursorPos], '\0' }; dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP + 1, getTextWidth(s, 1) + 1, height() - 2, FOCUS_COLOR); dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, DEFAULT_COLOR); +#endif } else { const char * displayedValue = value; LcdFlags textColor; - if (strlen(value) == 0) { - displayedValue = "---"; - textColor = hasFocus() ? FOCUS_BGCOLOR : DISABLE_COLOR; - } - else if (hasFocus()) { + if (hasFocus()) { textColor = FOCUS_BGCOLOR; } else { - textColor = DEFAULT_COLOR; + if (strlen(value) == 0) { + displayedValue = "---"; + textColor = DISABLE_COLOR; + } + else { + textColor = DEFAULT_COLOR; + } } dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); } - -#if defined(HARDWARE_TOUCH) - auto keyboard = TextKeyboard::instance(); - if (hasFocus() && keyboard->getField() == this) { - coord_t cursorPos = keyboard->getCursorPos(); - dc->drawSolidFilledRect(cursorPos + 2, FIELD_PADDING_TOP, 2, 21, 0); // FOCUS_BGCOLOR); - } -#endif } -#if defined(HARDWARE_KEYS) -void TextEdit::onKeyEvent(event_t event) +void TextEdit::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); +#if defined(HARDWARE_TOUCH) + if (IS_VIRTUAL_KEY_EVENT(event)) { + uint8_t c = event & 0xFF; + if (c == (uint8_t)KEYBOARD_BACKSPACE[0]) { + if (cursorPos > 0) { + memmove(value + cursorPos - 1, value + cursorPos, length - cursorPos); + value[length - 1] = '\0'; + --cursorPos; + invalidate(); + } + } + else { + memmove(value + cursorPos + 1, value + cursorPos, length - cursorPos - 1); + value[cursorPos++] = c; + invalidate(); + } + } +#endif + +#if defined(HARDWARE_KEYS) if (editMode) { int c = value[cursorPos]; int v = c; @@ -102,19 +120,19 @@ void TextEdit::onKeyEvent(event_t event) invalidate(); } else { - FormField::onKeyEvent(event); + FormField::onEvent(event); } break; case EVT_KEY_BREAK(KEY_EXIT): cursorPos = 0; - FormField::onKeyEvent(event); + FormField::onEvent(event); break; case EVT_KEY_LONG(KEY_ENTER): if (v == 0) { killEvents(event); - FormField::onKeyEvent(EVT_KEY_BREAK(KEY_ENTER)); + FormField::onEvent(EVT_KEY_BREAK(KEY_ENTER)); break; } // no break @@ -138,10 +156,10 @@ void TextEdit::onKeyEvent(event_t event) } } else { - FormField::onKeyEvent(event); + FormField::onEvent(event); } -} #endif +} #if defined(HARDWARE_TOUCH) bool TextEdit::onTouchEnd(coord_t x, coord_t y) @@ -150,19 +168,29 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) setFocus(); } - auto keyboard = TextKeyboard::instance(); - if (keyboard->getField() != this) { - keyboard->setField(this); + TextKeyboard::show(this); + + coord_t rest = x; + for (cursorPos = 0; cursorPos < length; cursorPos++) { + char c = value[cursorPos]; + if (c == '\0') + break; + uint8_t w = getCharWidth(c, fontspecsTable[0]); + if (rest < w) + break; + rest -= w; } - keyboard->setCursorPos(x); - + invalidate(); return true; } void TextEdit::onFocusLost() { - TextKeyboard::instance()->disable(true); +#if defined(HARDWARE_TOUCH) + TextKeyboard::hide(); +#endif // TODO storageDirty(...); + FormField::onFocusLost(); } #endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index b30230cd6..dded4b8cd 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -52,9 +52,7 @@ class TextEdit : public FormField { void paint(BitmapBuffer * dc) override; -#if defined(HARDWARE_KEYS) - void onKeyEvent(event_t event) override; -#endif + void onEvent(event_t event) override; #if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t x, coord_t y) override; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 902f512d5..3ecacb353 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -261,30 +261,26 @@ void Window::checkEvents() child->checkEvents(); } -#if defined(HARDWARE_KEYS) if (this == focusWindow) { event_t event = getEvent(false); if (event) { TRACE_WINDOWS("Event 0x%x received ...", event); - this->onKeyEvent(event); + this->onEvent(event); } } -#endif if (windowFlags & REFRESH_ALWAYS) { invalidate(); } } -#if defined(HARDWARE_KEYS) -void Window::onKeyEvent(event_t event) +void Window::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString().c_str(), event); if (parent) { - parent->onKeyEvent(event); + parent->onEvent(event); } } -#endif #if defined(HARDWARE_TOUCH) bool Window::onTouchStart(coord_t x, coord_t y) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index d99644216..e1cf2134c 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -256,9 +256,7 @@ class Window { invalidate(); }; -#if defined(HARDWARE_KEYS) - virtual void onKeyEvent(event_t event); -#endif + virtual void onEvent(event_t event); #if defined(HARDWARE_TOUCH) virtual bool onTouchStart(coord_t x, coord_t y); From f23021226d0e35f230c2a9862046060caf5b0d69 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 5 Oct 2019 19:01:56 +0200 Subject: [PATCH 019/714] New UI continued --- .../src/thirdparty/libopenui/src/textedit.cpp | 2 +- radio/src/thirdparty/libopenui/src/textedit.h | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 8eaf7c6a4..65b95834f 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -74,7 +74,7 @@ void TextEdit::onEvent(event_t event) invalidate(); } } - else { + else if (cursorPos < length) { memmove(value + cursorPos + 1, value + cursorPos, length - cursorPos - 1); value[cursorPos++] = c; invalidate(); diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index dded4b8cd..e160dd865 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -65,6 +65,20 @@ class TextEdit : public FormField { uint8_t length; uint8_t cursorPos = 0; + static uint8_t getNextChar(uint8_t c) + { + if (c == ' ' || c == 0) + return 'A'; + else if (c == 'Z') + return 'a'; + else if (c == 'z') + return '0'; + else if (c == '>') + return ' '; + else + return c + 1; + } + static uint8_t getPreviousChar(uint8_t c) { if (c == 'A') @@ -79,20 +93,6 @@ class TextEdit : public FormField { return c - 1; } - static uint8_t getNextChar(uint8_t c) - { - if (c == ' ') - return 'A'; - else if (c == 'Z') - return 'a'; - else if (c == 'z') - return '0'; - else if (c == '>') - return ' '; - else - return c + 1; - } - }; #endif // _TEXTEDIT_H_ From e7773df205b99b3c6596dddae7d206dee17c3d98 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 5 Oct 2019 19:35:52 +0200 Subject: [PATCH 020/714] Cosmetics --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 5 +++-- radio/src/thirdparty/libopenui/src/textedit.cpp | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 8435a95d5..33b6e4fa9 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -32,17 +32,18 @@ void NumberEdit::paint(BitmapBuffer * dc) { FormField::paint(dc); + auto value = getValue(); + LcdFlags textColor; if (editMode) textColor = FOCUS_COLOR; else if (hasFocus()) textColor = FOCUS_BGCOLOR; - else if (enabled) + else if (enabled && (value != 0 || zeroText.empty())) textColor = DEFAULT_COLOR; else textColor = DISABLE_COLOR; - int32_t value = getValue(); if (displayFunction) displayFunction(dc, textColor, value); else if (value == 0 && !zeroText.empty()) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 65b95834f..e808dcc55 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -44,6 +44,9 @@ void TextEdit::paint(BitmapBuffer * dc) const char * displayedValue = value; LcdFlags textColor; if (hasFocus()) { + if (strlen(value) == 0) { + displayedValue = "---"; + } textColor = FOCUS_BGCOLOR; } else { From b1f351b7ca3ef02714617b0ce9571ec40a9673b6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 10 Oct 2019 10:23:10 +0200 Subject: [PATCH 021/714] External functions declared --- radio/src/thirdparty/libopenui/src/libopenui_depends.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index f9dd8308e..6da9fa0d8 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -28,5 +28,7 @@ void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void onKeyPress(); void onKeyError(); +void killEvents(event_t event); +event_t getWindowEvent(); #endif // _LIBOPEUI_DEPENDS_H_ From 55975630a75c4a2d5706460adf70e975cc7d618d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 10 Oct 2019 13:58:15 +0200 Subject: [PATCH 022/714] libopenui latest updates --- .../thirdparty/libopenui/src/CMakeLists.txt | 3 - radio/src/thirdparty/libopenui/src/dialog.cpp | 4 +- .../libopenui/src/fab_icon_button.cpp | 49 ----- .../libopenui/src/fab_icon_button.h | 42 ---- .../libopenui/src/libopenui_defines.h | 28 +-- radio/src/thirdparty/libopenui/src/menu.cpp | 4 +- .../thirdparty/libopenui/src/menutoolbar.cpp | 4 +- radio/src/thirdparty/libopenui/src/page.cpp | 95 --------- radio/src/thirdparty/libopenui/src/page.h | 89 -------- .../thirdparty/libopenui/src/tabsgroup.cpp | 186 ----------------- .../src/thirdparty/libopenui/src/tabsgroup.h | 191 ------------------ radio/src/thirdparty/libopenui/src/theme.h | 1 - radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 13 files changed, 10 insertions(+), 688 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/src/fab_icon_button.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/fab_icon_button.h delete mode 100644 radio/src/thirdparty/libopenui/src/page.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/page.h delete mode 100644 radio/src/thirdparty/libopenui/src/tabsgroup.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/tabsgroup.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 999f4e888..1227244e7 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -5,7 +5,6 @@ set(LIBOPENUI_SRC window.cpp form.cpp button.cpp - fab_icon_button.cpp static.cpp checkbox.cpp slider.cpp @@ -18,10 +17,8 @@ set(LIBOPENUI_SRC textedit.cpp coloredit.cpp progress.cpp - page.cpp dialog.cpp message_dialog.cpp - tabsgroup.cpp ) if(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index f203b3084..955417da9 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -34,8 +34,8 @@ Dialog::Dialog(std::string title, const rect_t rect): void Dialog::paint(BitmapBuffer * dc) { dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, DEFAULT_COLOR); - dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, TITLE_BGCOLOR); - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), MENU_TITLE_COLOR); + dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, FOCUS_BGCOLOR); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), FOCUS_COLOR); dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, DEFAULT_BGCOLOR); } diff --git a/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp b/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp deleted file mode 100644 index 4e8062c25..000000000 --- a/radio/src/thirdparty/libopenui/src/fab_icon_button.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "libopenui_config.h" -#include "fab_icon_button.h" -#include "font.h" -#include "theme.h" - -const uint8_t __alpha_button_on[] { -#include "alpha_button_on.lbm" -}; -Bitmap ALPHA_BUTTON_ON(BMP_ARGB4444, (const uint16_t*)__alpha_button_on); - -const uint8_t __alpha_button_off[] { -#include "alpha_button_off.lbm" -}; -Bitmap ALPHA_BUTTON_OFF(BMP_ARGB4444, (const uint16_t*)__alpha_button_off); - - -FabIconButton::FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags): - Button(parent, { x - 34, y - 34, 68, 68 }, pressHandler, flags), - icon(icon) -{ -} - -void FabIconButton::paint(BitmapBuffer * dc) -{ - dc->drawBitmap(0, 0, checked() ? &ALPHA_BUTTON_ON : &ALPHA_BUTTON_OFF); - const BitmapBuffer * mask = theme->getIconMask(icon); - if (mask) { - dc->drawMask((68 - mask->getWidth()) / 2, (68 - mask->getHeight()) / 2, mask, DEFAULT_BGCOLOR); - } -} diff --git a/radio/src/thirdparty/libopenui/src/fab_icon_button.h b/radio/src/thirdparty/libopenui/src/fab_icon_button.h deleted file mode 100644 index 3a467b6cb..000000000 --- a/radio/src/thirdparty/libopenui/src/fab_icon_button.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#ifndef _FAB_ICON_BUTTON_H_ -#define _FAB_ICON_BUTTON_H_ - -#include "button.h" - -class FabIconButton: public Button { - public: - FabIconButton(Window * parent, coord_t x, coord_t y, uint8_t icon, std::function pressHandler, uint8_t flags=0); - -#if defined(DEBUG_WINDOWS) - std::string getName() override - { - return "FabIconButton(" + std::to_string(icon) + ")"; - } -#endif - - void paint(BitmapBuffer * dc) override; - - protected: - uint8_t icon; -}; - -#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 093fe1f80..a1f3444c7 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -127,32 +127,10 @@ enum FontSizeIndex #define FOCUS_COLOR COLOR(FOCUS_COLOR_INDEX) #define FOCUS_BGCOLOR COLOR(FOCUS_BGCOLOR_INDEX) #define DISABLE_COLOR COLOR(DISABLE_COLOR_INDEX) +#define HIGHLIGHT_COLOR COLOR(HIGHLIGHT_COLOR_INDEX) #define CHECKBOX_COLOR COLOR(CHECKBOX_COLOR_INDEX) +#define MENU_COLOR COLOR(MENU_COLOR_INDEX) +#define MENU_BGCOLOR COLOR(MENU_BGCOLOR_INDEX) #define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) -//#define TEXT_STATUSBAR_COLOR COLOR(TEXT_STATUSBAR_COLOR_INDEX) -//#define LINE_COLOR COLOR(LINE_COLOR_INDEX) -//#define HEADER_SEPARATOR_COLOR COLOR(HEADER_SEPARATOR_COLOR_INDEX) -//#define MENU_TITLE_BGCOLOR COLOR(MENU_TITLE_BGCOLOR_INDEX) -//#define MENU_TITLE_COLOR COLOR(MENU_TITLE_COLOR_INDEX) -//#define MENU_TITLE_DISABLE_COLOR COLOR(MENU_TITLE_DISABLE_COLOR_INDEX) -//#define HEADER_COLOR COLOR(HEADER_COLOR_INDEX) -//#define ALARM_COLOR COLOR(ALARM_COLOR_INDEX) -//#define WARNING_COLOR COLOR(WARNING_COLOR_INDEX) -//#define TEXT_DISABLE_COLOR COLOR(TEXT_DISABLE_COLOR_INDEX) -//#define CURVE_COLOR COLOR(CURVE_COLOR_INDEX) -//#define CURVE_CURSOR_COLOR COLOR(CURVE_CURSOR_COLOR_INDEX) -//#define TITLE_BGCOLOR COLOR(TITLE_BGCOLOR_INDEX) -//#define TRIM_BGCOLOR COLOR(TRIM_BGCOLOR_INDEX) -//#define TRIM_SHADOW_COLOR COLOR(TRIM_SHADOW_COLOR_INDEX) -//#define HEADER_BGCOLOR COLOR(HEADER_BGCOLOR_INDEX) -//#define HEADER_ICON_BGCOLOR COLOR(HEADER_ICON_BGCOLOR_INDEX) -//#define HEADER_CURRENT_BGCOLOR COLOR(HEADER_CURRENT_BGCOLOR_INDEX) -//#define MAINVIEW_PANES_COLOR COLOR(MAINVIEW_PANES_COLOR_INDEX) -//#define MAINVIEW_GRAPHICS_COLOR COLOR(MAINVIEW_GRAPHICS_COLOR_INDEX) -//#define OVERLAY_COLOR COLOR(OVERLAY_COLOR_INDEX) -//#define BARGRAPH1_COLOR COLOR(BARGRAPH1_COLOR_INDEX) -//#define BARGRAPH2_COLOR COLOR(BARGRAPH2_COLOR_INDEX) -//#define BARGRAPH_BGCOLOR COLOR(BARGRAPH_BGCOLOR_INDEX) - #endif diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 6a49501d8..c4150b65d 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -72,10 +72,10 @@ bool MenuWindow::onTouchEnd(coord_t x, coord_t y) void MenuWindow::paint(BitmapBuffer * dc) { int width = (innerHeight > height() ? 195 : 200); - dc->clear(HEADER_BGCOLOR); + dc->clear(MENU_BGCOLOR); for (unsigned i = 0; i < lines.size(); i++) { auto & line = lines[i]; - LcdFlags flags = (selectedIndex == (int) i ? WARNING_COLOR : MENU_TITLE_COLOR); + LcdFlags flags = (selectedIndex == (int) i ? HIGHLIGHT_COLOR : MENU_COLOR); if (line.drawLine) { line.drawLine(dc, 0, i * lineHeight /*+ (lineHeight - 20) / 2*/, flags); } diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 38f28417d..4ec0a27c1 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -23,8 +23,8 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) { if (checked()) { - dc->drawSolidFilledRect(MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, HEADER_BGCOLOR); - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | MENU_TITLE_COLOR); + dc->drawSolidFilledRect(MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, FOCUS_BGCOLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | FOCUS_COLOR); } else { dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | DEFAULT_COLOR); diff --git a/radio/src/thirdparty/libopenui/src/page.cpp b/radio/src/thirdparty/libopenui/src/page.cpp deleted file mode 100644 index a4d1c3a11..000000000 --- a/radio/src/thirdparty/libopenui/src/page.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "page.h" -#include "mainwindow.h" -#include "keyboard_base.h" -#include "theme.h" -#include "opentx.h" - -PageHeader::PageHeader(Page * parent, uint8_t icon): - Window(parent, { 0, 0, LCD_W, MENU_HEADER_HEIGHT }, OPAQUE), - icon(icon) -#if defined(HARDWARE_TOUCH) - , back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, - [=]() -> uint8_t { - parent->deleteLater(); - return 0; - }, BUTTON_NOFOCUS) -#endif -{ -} - -void PageHeader::paint(BitmapBuffer * dc) -{ - theme->drawPageHeader(dc, this); -} - -Page::Page(unsigned icon): - Window(&mainWindow, {0, 0, LCD_W, LCD_H}, OPAQUE), - header(this, icon), - body(this, { 0, MENU_HEADER_HEIGHT, LCD_W, LCD_H - MENU_HEADER_HEIGHT }), - previousFocus(focusWindow) -{ - setFocus(); -} - -Page::~Page() -{ -#if defined(HARDWARE_TOUCH) - Keyboard::hide(); -#endif -} - -void Page::deleteLater() -{ - if (previousFocus) { - previousFocus->setFocus(); - } - - Window::deleteLater(); -} - -void Page::paint(BitmapBuffer * dc) -{ - dc->clear(DEFAULT_BGCOLOR); -} - -#if defined(HARDWARE_KEYS) -void Page::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { - killEvents(event); - deleteLater(); - } -} -#endif - -#if defined(HARDWARE_TOUCH) -bool Page::onTouchEnd(coord_t x, coord_t y) -{ - if (Window::onTouchEnd(x, y)) - return true; - - Keyboard::hide(); - return true; -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/page.h b/radio/src/thirdparty/libopenui/src/page.h deleted file mode 100644 index 03c714cf5..000000000 --- a/radio/src/thirdparty/libopenui/src/page.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#ifndef _PAGE_H_ -#define _PAGE_H_ - -#include "window.h" -#include "button.h" - -class Page; - -class PageHeader: public Window { - public: - PageHeader(Page * parent, uint8_t icon); - - void paint(BitmapBuffer * dc) override; - - uint8_t getIcon() const - { - return icon; - } - - protected: - uint8_t icon; -#if defined(HARDWARE_TOUCH) - IconButton back; -#endif -}; - -class Page: public Window { - public: - explicit Page(unsigned icon); - - ~Page() override; - -#if defined(DEBUG_WINDOWS) - std::string getName() override - { - return "Page"; - } -#endif - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - -#if defined(HARDWARE_TOUCH) - bool onTouchStart(coord_t x, coord_t y) override - { - Window::onTouchStart(x, y); - return true; - } - - bool onTouchEnd(coord_t x, coord_t y) override; - - bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override - { - Window::onTouchSlide(x, y, startX, startY, slideX, slideY); - return true; - } -#endif - - void deleteLater(); - - void paint(BitmapBuffer * dc) override; - - protected: - PageHeader header; - FormWindow body; - Window * previousFocus; -}; - -#endif // _PAGE_H_ diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.cpp b/radio/src/thirdparty/libopenui/src/tabsgroup.cpp deleted file mode 100644 index 95813c445..000000000 --- a/radio/src/thirdparty/libopenui/src/tabsgroup.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "tabsgroup.h" -#include "mainwindow.h" -#include "view_main.h" - -#if defined(HARDWARE_TOUCH) -#include "keyboard_base.h" -#endif - -#include "opentx.h" // TODO for constants... - -TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): - Window(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE), -#if defined(HARDWARE_TOUCH) - back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, - [=]() -> uint8_t { - parent->deleteLater(); - return 1; - }, BUTTON_NOFOCUS), -#endif - icon(icon), - carousel(this, parent) -{ -} - -void TabsGroupHeader::paint(BitmapBuffer * dc) -{ - static_cast(theme)->drawMenuBackground(dc, icon, title); -} - -TabsCarousel::TabsCarousel(Window * parent, TabsGroup * menu): - Window(parent, { MENU_HEADER_BUTTONS_LEFT, 0, LCD_W - MENU_HEADER_BUTTONS_LEFT, MENU_HEADER_HEIGHT + 10 }, TRANSPARENT), - menu(menu) -{ -} - -void TabsCarousel::updateInnerWidth() -{ - setInnerWidth(padding_left + MENU_HEADER_BUTTON_WIDTH * menu->tabs.size()); -} - -void TabsCarousel::paint(BitmapBuffer * dc) -{ - static_cast(theme)->drawMenuHeader(dc, menu->tabs, currentIndex); -} - -#if defined(HARDWARE_TOUCH) -bool TabsCarousel::onTouchEnd(coord_t x, coord_t y) -{ - unsigned index = (x - padding_left) / MENU_HEADER_BUTTON_WIDTH; - menu->setCurrentTab(index); - setCurrentIndex(index); - return true; -} -#endif - -TabsGroup::TabsGroup(uint8_t icon): - Window(&mainWindow, { 0, 0, LCD_W, LCD_H }, OPAQUE), - header(this, icon), - body(this, { 0, MENU_BODY_TOP, LCD_W, MENU_BODY_HEIGHT }) -{ -} - -TabsGroup::~TabsGroup() -{ -#if defined(HARDWARE_TOUCH) - Keyboard::hide(); -#endif - - for (auto tab: tabs) { - delete tab; - } - - body.deleteChildren(); -} - -void TabsGroup::addTab(PageTab * page) -{ - tabs.push_back(page); - if (!currentTab) { - setCurrentTab(0); - } - header.carousel.updateInnerWidth(); -} - -void TabsGroup::removeTab(unsigned index) -{ - if (currentTab == tabs[index]) { - setCurrentTab(max(0, index - 1)); - } - tabs.erase(tabs.begin() + index); - header.carousel.updateInnerWidth(); -} - -void TabsGroup::removeAllTabs() -{ - for (auto * tab: tabs) { - delete tab; - } - tabs.clear(); - currentTab = nullptr; - header.carousel.updateInnerWidth(); -} - -void TabsGroup::setVisibleTab(PageTab * tab) -{ - if (tab != currentTab) { - setFocus(); - FormField::clearCurrentField(); - body.clear(); -#if defined(HARDWARE_TOUCH) - Keyboard::hide(); -#endif - currentTab = tab; - tab->build(&body); - header.setTitle(tab->title.c_str()); - invalidate(); - } -} - -void TabsGroup::checkEvents() -{ - Window::checkEvents(); - if (currentTab) { - currentTab->checkEvents(); - } -} - -#if defined(HARDWARE_KEYS) -void TabsGroup::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_KEY_BREAK(KEY_PGDN)) { - uint8_t current = header.carousel.getCurrentIndex() + 1; - setCurrentTab(current >= tabs.size() ? 0 : current); - } - else if (event == EVT_KEY_LONG(KEY_PGDN)) { - killEvents(event); - uint8_t current = header.carousel.getCurrentIndex(); - setCurrentTab(current == 0 ? tabs.size() - 1 : current - 1); - } - else if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { - killEvents(event); - ViewMain::instance->setFocus(); - deleteLater(); - } - else if (parent) { - parent->onEvent(event); - } -} -#endif - -void TabsGroup::paint(BitmapBuffer * dc) -{ - dc->clear(DEFAULT_BGCOLOR); -} - -#if defined(HARDWARE_TOUCH) -bool TabsGroup::onTouchEnd(coord_t x, coord_t y) -{ - if (Window::onTouchEnd(x, y)) - return true; - - Keyboard::hide(); - return true; -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/tabsgroup.h b/radio/src/thirdparty/libopenui/src/tabsgroup.h deleted file mode 100644 index 8f36bd2bd..000000000 --- a/radio/src/thirdparty/libopenui/src/tabsgroup.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#ifndef _TABSGROUP_H_ -#define _TABSGROUP_H_ - -#include -#include "button.h" -#include "form.h" - -class TabsGroup; - -class PageTab { - friend class TabsCarousel; - friend class TabsGroup; - - public: - PageTab(std::string title, unsigned icon): - title(std::move(title)), - icon(icon) - { - } - - virtual ~PageTab() = default; - - virtual void build(FormWindow * window) = 0; - - virtual void checkEvents() - { - } - - void destroy() - { - if (onPageDestroyed) { - onPageDestroyed(); - } - delete this; - } - - void setOnPageDestroyedHandler(std::function handler) - { - onPageDestroyed = std::move(handler); - } - - void setTitle(std::string value) - { - title = std::move(value); - } - - unsigned getIcon() const - { - return icon; - } - - protected: - std::string title; - unsigned icon; - std::function onPageDestroyed; -}; - -class TabsCarousel: public Window { - public: - TabsCarousel(Window * parent, TabsGroup * menu); - -#if defined(DEBUG_WINDOWS) - std::string getName() override - { - return "TabsCarousel"; - } -#endif - - inline void setCurrentIndex(uint8_t index) - { - currentIndex = index; - } - - inline uint8_t getCurrentIndex() - { - return currentIndex; - } - - void updateInnerWidth(); - - void paint(BitmapBuffer * dc) override; - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif - - protected: - constexpr static uint8_t padding_left = 3; - TabsGroup * menu; - uint8_t currentIndex = 0; -}; - -class TabsGroupHeader: public Window { - friend class TabsGroup; - - public: - TabsGroupHeader(TabsGroup * menu, uint8_t icon); - -#if defined(DEBUG_WINDOWS) - std::string getName() override - { - return "TabsGroupHeader"; - } -#endif - - void paint(BitmapBuffer * dc) override; - - void setTitle(const char * value) - { - title = value; - } - - protected: -#if defined(HARDWARE_TOUCH) - IconButton back; -#endif - uint8_t icon; - TabsCarousel carousel; - const char * title = nullptr; -}; - -class TabsGroup: public Window { - friend class TabsCarousel; - - public: - explicit TabsGroup(uint8_t icon); - - ~TabsGroup() override; - -#if defined(DEBUG_WINDOWS) - std::string getName() override - { - return "TabsGroup"; - } -#endif - - void addTab(PageTab * page); - - void removeTab(unsigned index); - - void removeAllTabs(); - - void setCurrentTab(unsigned index) - { - if (index < tabs.size()) { - header.carousel.setCurrentIndex(index); - setVisibleTab(tabs[index]); - } - } - - void checkEvents() override; - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - - void paint(BitmapBuffer * dc) override; - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif - - protected: - TabsGroupHeader header; - FormWindow body; - std::vector tabs; - PageTab * currentTab = nullptr; - - void setVisibleTab(PageTab * tab); -}; - -#endif diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index b0a432cdf..7b9233d41 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -42,7 +42,6 @@ class Theme { virtual void drawCheckBox(BitmapBuffer * dc, bool checked, coord_t x, coord_t y, bool focus = false) const = 0; virtual void drawChoice(BitmapBuffer * dc, ChoiceBase * choice, const char * str) const = 0; virtual void drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const = 0; - virtual void drawPageHeader(BitmapBuffer * dc, const PageHeader * page) const = 0; virtual const BitmapBuffer * getIcon(uint8_t index, IconState state) const = 0; virtual const BitmapBuffer * getIconMask(uint8_t index) const = 0; }; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 3ecacb353..c1516de7e 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -262,7 +262,7 @@ void Window::checkEvents() } if (this == focusWindow) { - event_t event = getEvent(false); + event_t event = getWindowEvent(); if (event) { TRACE_WINDOWS("Event 0x%x received ...", event); this->onEvent(event); From cd702443c76dfecc837af3325c7837ddc3696f4f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 11 Oct 2019 18:30:24 +0200 Subject: [PATCH 023/714] Latest libopenui updates --- radio/src/thirdparty/libopenui/CMakeLists.txt | 1 - radio/src/thirdparty/libopenui/src/CMakeLists.txt | 14 +++++++++++++- radio/src/thirdparty/libopenui/src/numberedit.cpp | 7 ++++--- radio/src/thirdparty/libopenui/src/numberedit.h | 2 +- radio/src/thirdparty/libopenui/src/textedit.h | 2 ++ 5 files changed, 20 insertions(+), 6 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/CMakeLists.txt diff --git a/radio/src/thirdparty/libopenui/CMakeLists.txt b/radio/src/thirdparty/libopenui/CMakeLists.txt deleted file mode 100644 index 5f0cf266a..000000000 --- a/radio/src/thirdparty/libopenui/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -include(src) diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 1227244e7..e6c7c273f 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -1,3 +1,15 @@ +if(HARDWARE_KEYS) + add_definitions(-DHARDWARE_KEYS) +endif() + +if(HARWARE_TOUCH) + add_definitions(-DHARDWARE_TOUCH) +endif() + +if(SOFTWARE_KEYBOARD) + add_definitions(-DSOFTWARE_KEYBOARD) +endif() + set(LIBOPENUI_SRC libopenui_globals.cpp libopenui_file.cpp @@ -21,7 +33,7 @@ set(LIBOPENUI_SRC message_dialog.cpp ) -if(HARDWARE_TOUCH) +if(SOFTWARE_KEYBOARD) set(LIBOPENUI_SRC ${LIBOPENUI_SRC} keyboard_text.cpp diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 33b6e4fa9..4e2dd4d96 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -19,7 +19,7 @@ #include "numberedit.h" -#if defined(HARDWARE_TOUCH) +#if defined(SOFTWARE_KEYBOARD) #include "keyboard_number.h" #endif @@ -119,7 +119,7 @@ void NumberEdit::onEvent(event_t event) FormField::onEvent(event); } -#if defined(HARDWARE_TOUCH) +#if defined(SOFTWARE_KEYBOARD) bool NumberEdit::onTouchEnd(coord_t, coord_t) { if (!enabled) { @@ -138,8 +138,9 @@ bool NumberEdit::onTouchEnd(coord_t, coord_t) void NumberEdit::onFocusLost() { -#if defined(HARDWARE_TOUCH) +#if defined(SOFTWARE_KEYBOARD) Keyboard::hide(); #endif + FormField::onFocusLost(); } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 7bf689344..793df432e 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -69,7 +69,7 @@ class NumberEdit : public BaseNumberEdit { void onEvent(event_t event) override; -#if defined(HARDWARE_TOUCH) +#if defined(SOFTWARE_KEYBOARD) bool onTouchEnd(coord_t x, coord_t y) override; #endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index e160dd865..8f7d3f05f 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -56,7 +56,9 @@ class TextEdit : public FormField { #if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t x, coord_t y) override; +#endif +#if defined(SOFTWARE_KEYBOARD) void onFocusLost() override; #endif From ce3d612faf43266e6888a547fa0614e4c64e0bd6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 11 Oct 2019 19:44:06 +0200 Subject: [PATCH 024/714] Latest libopenui updates --- .../thirdparty/libopenui/src/bitmapbuffer.h | 8 +++---- radio/src/thirdparty/libopenui/src/checkbox.h | 11 ++++++++++ .../thirdparty/libopenui/src/mainwindow.cpp | 22 +++++++++---------- .../src/thirdparty/libopenui/src/textedit.cpp | 7 ++++-- radio/src/thirdparty/libopenui/src/theme.h | 5 ++++- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 5a6631168..de0a0e69b 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -363,15 +363,15 @@ class BitmapBuffer: public BitmapBufferBase coord_t drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags=0); - void drawText(coord_t x, coord_t y, const char * s, LcdFlags flags = 0) + coord_t drawText(coord_t x, coord_t y, const char * s, LcdFlags flags = 0) { - drawSizedText(x, y, s, 255, flags); + return drawSizedText(x, y, s, 255, flags); } - void drawTextAtIndex(coord_t x, coord_t y, const char * s, uint8_t idx, LcdFlags flags = 0) + coord_t drawTextAtIndex(coord_t x, coord_t y, const char * s, uint8_t idx, LcdFlags flags = 0) { char length = *s++; - drawSizedText(x, y, s+length*idx, length, flags); + return drawSizedText(x, y, s+length*idx, length, flags); } void drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index d61605881..c0b2193d9 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -39,6 +39,16 @@ class CheckBox : public FormField { } #endif + const char * getLabel() const + { + return label.c_str(); + } + + void setLabel(std::string newLabel) + { + label = std::move(newLabel); + } + void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_KEYS) @@ -55,6 +65,7 @@ class CheckBox : public FormField { } protected: + std::string label; std::function _getValue; std::function _setValue; }; diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 260683d6c..48aec258b 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -27,7 +27,7 @@ MainWindow mainWindow; #if defined(HARDWARE_TOUCH) -STRUCT_TOUCH touchState; +TouchState touchState; Keyboard * Keyboard::activeKeyboard = nullptr; #endif @@ -42,20 +42,20 @@ void MainWindow::emptyTrash() void MainWindow::checkEvents(bool trash) { #if defined(HARDWARE_TOUCH) - if (touchState.Event == TE_DOWN) { - onTouchStart(touchState.X + scrollPositionX, touchState.Y + scrollPositionY); + if (touchState.event == TE_DOWN) { + onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); // touchState.Event = TE_NONE; } - else if (touchState.Event == TE_UP) { - touchState.Event = TE_NONE; + else if (touchState.event == TE_UP) { + touchState.event = TE_NONE; onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); } - else if (touchState.Event == TE_SLIDE) { - coord_t x = touchState.X - touchState.lastX; - coord_t y = touchState.Y - touchState.lastY; - onTouchSlide(touchState.X, touchState.Y, touchState.startX, touchState.startY, x, y); - touchState.lastX = touchState.X; - touchState.lastY = touchState.Y; + else if (touchState.event == TE_SLIDE) { + coord_t x = touchState.x - touchState.lastX; + coord_t y = touchState.y - touchState.lastY; + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, x, y); + touchState.lastX = touchState.x; + touchState.lastY = touchState.y; } #endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index e808dcc55..27006581b 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -171,7 +171,9 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) setFocus(); } +#if defined(SOFTWARE_KEYBOARD) TextKeyboard::show(this); +#endif coord_t rest = x; for (cursorPos = 0; cursorPos < length; cursorPos++) { @@ -187,12 +189,13 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) invalidate(); return true; } +#endif +#if defined(SOFTWARE_KEYBOARD) void TextEdit::onFocusLost() { -#if defined(HARDWARE_TOUCH) TextKeyboard::hide(); -#endif + // TODO storageDirty(...); FormField::onFocusLost(); } diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index 7b9233d41..18241e250 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -37,7 +37,10 @@ class Theme { virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const = 0; virtual void drawCheckBox(BitmapBuffer * dc, CheckBox * checkBox) const { - return drawCheckBox(dc, checkBox->getValue(), 0, 0, checkBox->hasFocus()); + drawCheckBox(dc, checkBox->getValue(), 0, FIELD_PADDING_TOP, checkBox->hasFocus()); + const char * label = checkBox->getLabel(); + if (label) + dc->drawText(22, FIELD_PADDING_TOP, label); } virtual void drawCheckBox(BitmapBuffer * dc, bool checked, coord_t x, coord_t y, bool focus = false) const = 0; virtual void drawChoice(BitmapBuffer * dc, ChoiceBase * choice, const char * str) const = 0; From bf430e8b56392b355ab0480e9574aaae8bc080e8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Oct 2019 13:40:32 +0200 Subject: [PATCH 025/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index accb21fd8..d223e1ac5 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -468,7 +468,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ return pos - offsetX; } -void BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) +coord_t BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) { char str[48+1]; // max=16 for the prefix, 16 chars for the number, 16 chars for the suffix char *s = str+32; @@ -505,7 +505,7 @@ void BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, strncpy(&str[32], suffix, 16); } flags &= ~LEADING0; - drawText(x, y, s, flags); + return drawText(x, y, s, flags); } void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, LcdFlags att) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index de0a0e69b..8677f7864 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -374,7 +374,7 @@ class BitmapBuffer: public BitmapBufferBase return drawSizedText(x, y, s+length*idx, length, flags); } - void drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); + coord_t drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); template void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0) From 843f2466b55556204dcfe201f91fd9ded1efa710 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 16 Oct 2019 08:18:36 +0200 Subject: [PATCH 026/714] Latest updates --- radio/src/thirdparty/libopenui/src/button.h | 2 +- radio/src/thirdparty/libopenui/src/choice.cpp | 1 - radio/src/thirdparty/libopenui/src/choice.h | 3 ++- radio/src/thirdparty/libopenui/src/form.h | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 7af9a52a9..8b160c6ca 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -111,7 +111,7 @@ class Button : public FormField { class TextButton : public Button { public: - TextButton(Window * parent, const rect_t & rect, std::string text, std::function pressHandler=nullptr, uint8_t flags=BUTTON_BACKGROUND): + TextButton(Window * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, uint8_t flags = BUTTON_BACKGROUND): Button(parent, rect, pressHandler, flags), text(std::move(text)) { diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index da77b1b41..9f5d3a5a9 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -36,7 +36,6 @@ Choice::Choice(Window * parent, const rect_t & rect, const char * values, int16_ void Choice::paint(BitmapBuffer * dc) { FormField::paint(dc); - theme->drawChoice(dc, this, textHandler ? textHandler(getValue()).c_str() : TEXT_AT_INDEX(values, getValue() - vmin).c_str()); } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index a7f2365e4..ea12c55a2 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -89,7 +89,8 @@ class Choice : public ChoiceBase { std::function isValueAvailable; std::function textHandler; LcdFlags flags = 0; - void openMenu(); + + virtual void openMenu(); }; #endif // _CHOICE_H_ diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 20e5713d8..bb6898834 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -53,12 +53,12 @@ class FormField: public Window { return current; } - inline FormField * getPreviousField() + virtual FormField * getPreviousField() { return previous; } - inline FormField * getNextField() + virtual FormField * getNextField() { return next; } From e9b822aa2f8e2b69364ebc760592224dacbb400e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 16 Oct 2019 17:46:38 +0200 Subject: [PATCH 027/714] Bitmaps encoder script rewritten --- .../libopenui/tools/encode-bitmap.py | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100755 radio/src/thirdparty/libopenui/tools/encode-bitmap.py diff --git a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py new file mode 100755 index 000000000..1448ae737 --- /dev/null +++ b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python + + +import argparse +from PIL import Image + + +class RawMixin: + def encode_byte(self, byte): + self.write(byte) + + def encode_end(self): + pass + + +class RleMixin: + RLE_BYTE = 0 + RLE_SEQ = 1 + + def __init__(self): + self.state = self.RLE_BYTE + self.count = 0 + self.prev_byte = None + + def eq_prev_byte(self, byte): + if self.prev_byte is None: + return False + return byte == self.prev_byte + + def encode_byte(self, byte): + if self.state == self.RLE_BYTE: + self.write(byte) + if self.eq_prev_byte(byte): + self.state = self.RLE_SEQ + self.count = 0 + else: + self.prev_byte = byte + elif self.state == self.RLE_SEQ: + if self.eq_prev_byte(byte): + self.count += 1 + if self.count == 255: + self.write(self.count) + self.prev_byte = None + self.state = self.RLE_BYTE + else: + self.write(self.count) + self.write(byte) + self.prev_byte = byte + self.state = self.RLE_BYTE + + def encode_end(self): + if self.state == self.RLE_SEQ: + self.write(self.count) + + +class ImageEncoder: + def __init__(self, filename, size_format, reverse=False): + self.f = open(filename, "w") + self.size_format = size_format + self.reverse = reverse + + def write(self, value): + self.f.write("0x%02x," % value) + + def write_size(self, width, height): + if self.size_format == 2: + self.f.write("%d,%d,%d,%d,\n" % (width % 256, width // 256, height % 256, height // 256)) + else: + self.f.write("%d,%d,\n" % (width, height)) + + def encode_1bit(self, image, rows): + image = image.convert(mode='1') + width, height = image.size + self.write_size(width, height // rows) + for y in range(0, height, 8): + for x in range(width): + value = 0 + for z in range(8): + if y + z < height: + if image.format == "XBM": + if image.getpixel((x, y + z)) > 0: + value += 1 << z + else: + if image.getpixel((x, y + z)) == 0: + value += 1 << z + self.encode_byte(value) + self.f.write("\n") + self.encode_end() + + def encode_4bits(self, image): + image = image.convert(mode='L') + width, height = image.size + self.write_size(width, height) + for y in range(0, height, 2): + for x in range(width): + value = 0xFF + gray1 = self.get_pixel(image, x, y) + if y + 1 < height: + gray2 = self.get_pixel(image, x, y + 1) + else: + gray2 = 255 + for i in range(4): + if gray1 & (1 << (4 + i)): + value -= 1 << i + if gray2 & (1 << (4 + i)): + value -= 1 << (4 + i) + self.encode_byte(value) + self.f.write("\n") + self.encode_end() + + def encode_8bits(self, image): + image = image.convert(mode='L') + width, height = image.size + self.write_size(width, height) + for y in range(height): + for x in range(width): + value = self.get_pixel(image, x, y) + value = 0x0f - (value >> 4) + self.encode_byte(value) + self.f.write("\n") + self.encode_end() + + def encode_16bits(self, image): + width, height = image.size + self.write_size(width, height) + for y in range(height): + for x in range(width): + pixel = self.get_pixel(image, x, y) + val = ((pixel[0] >> 3) << 11) + ((pixel[1] >> 2) << 5) + ((pixel[2] >> 3) << 0) + self.encode_byte(val & 255) + self.encode_byte(val >> 8) + self.encode_end() + + def encode_16bits_alpha(self, image): + width, height = image.size + self.write_size(width, height) + for y in range(height): + for x in range(width): + pixel = self.get_pixel(image, x, y) + val = ((pixel[3] // 16) << 12) + ((pixel[0] // 16) << 8) + ((pixel[1] // 16) << 4) + ((pixel[2] // 16) << 0) + self.encode_byte(val & 255) + self.encode_byte(val >> 8) + self.encode_end() + + def get_pixel(self, image, x, y): + if self.reverse: + return image.getpixel((image.width - x - 1, image.height - y - 1)) + else: + return image.getpixel((x, y)) + + @staticmethod + def create(filename, size_format=1, reverse=False, encode_mixin=RawMixin): + class ResultClass(ImageEncoder, encode_mixin): + def __init__(self, *args, **kwargs): + ImageEncoder.__init__(self, *args, **kwargs) + encode_mixin.__init__(self) + return ResultClass(filename, size_format, reverse) + + +def main(): + parser = argparse.ArgumentParser(description='Bitmaps encoder') + parser.add_argument('input', action="store", help="Input file name") + parser.add_argument('output', action="store", help="Output file name") + parser.add_argument('--format', action="store", help="Output format") + parser.add_argument("--reverse", help="Invert pixels order", action="store_true") + parser.add_argument("--rle", help="Enable RLE compression", action="store_true") + parser.add_argument("--rows", help="Image rows count (for 1bit format)", type=int, default=1) + parser.add_argument("--size-format", help="Header image size format (1 or 2 bytes)", type=int, default=1) + + args = parser.parse_args() + + image = Image.open(args.input) + output = args.output + encoder = ImageEncoder.create(output, args.size_format, args.reverse, RleMixin if args.rle else RawMixin) + + if args.format == "1bit": + encoder.encode_1bit(image, args.rows) + elif args.format == "4bits": + encoder.encode_4bits(image) + elif args.format == "8bits": + encoder.encode_8bits(image) + elif args.format == "4/4/4/4": + encoder.encode_16bits_alpha(image) + elif args.format == "5/6/5": + encoder.encode_16bits(image) + + +if __name__ == "__main__": + main() From a98b0a4f7971ec7da113e8d2f4175aed1c6b3743 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 18 Oct 2019 22:13:49 +0200 Subject: [PATCH 028/714] Forms refactoring --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 8 +- radio/src/thirdparty/libopenui/src/button.cpp | 8 +- radio/src/thirdparty/libopenui/src/button.h | 41 +++-- radio/src/thirdparty/libopenui/src/choice.cpp | 7 +- radio/src/thirdparty/libopenui/src/choice.h | 9 +- radio/src/thirdparty/libopenui/src/dialog.cpp | 4 +- radio/src/thirdparty/libopenui/src/dialog.h | 4 +- .../thirdparty/libopenui/src/filechoice.cpp | 2 +- .../src/thirdparty/libopenui/src/filechoice.h | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 147 ++++++++++++------ radio/src/thirdparty/libopenui/src/form.h | 103 ++++-------- .../thirdparty/libopenui/src/menutoolbar.cpp | 4 +- .../thirdparty/libopenui/src/menutoolbar.h | 7 +- radio/src/thirdparty/libopenui/src/static.cpp | 8 +- radio/src/thirdparty/libopenui/src/static.h | 16 +- radio/src/thirdparty/libopenui/src/window.cpp | 9 +- radio/src/thirdparty/libopenui/src/window.h | 36 +++-- 17 files changed, 204 insertions(+), 211 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index d223e1ac5..7f78b1702 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -444,12 +444,12 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ c = uint8_t(*s) + ((c & 0x01) << 8) - 1; if (c >= 0x101) c -= 1; - c += 187; - uint8_t width = drawChar(x-1, y, font, fontspecs, c, flags); + c += CJK_FIRST_LETTER_INDEX; + uint8_t width = drawChar(x, y, font, fontspecs, c, flags); INCREMENT_POS(width); } - else if (c >= 0x20) { - uint8_t width = drawChar(x-1, y, font, fontspecs, getMappedChar(c), flags); + else if (c > 0x20) { + uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); INCREMENT_POS(width); } else if (c == '\n') { diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index ff5caef45..d9b97bead 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -25,8 +25,8 @@ void Button::onPress() { bool check = (pressHandler && pressHandler()); - if (check != bool(flags & BUTTON_CHECKED)) { - flags ^= BUTTON_CHECKED; + if (check != bool(windowFlags & BUTTON_CHECKED)) { + windowFlags ^= BUTTON_CHECKED; invalidate(); } } @@ -83,7 +83,7 @@ void TextButton::paint(BitmapBuffer * dc) textColor = FOCUS_COLOR; } else { - if (flags & BUTTON_BACKGROUND) { + if (windowFlags & BUTTON_BACKGROUND) { if (hasFocus()) { dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, DISABLE_COLOR); @@ -102,7 +102,7 @@ void TextButton::paint(BitmapBuffer * dc) } } - dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | textColor); + dc->drawText(rect.w / 2, (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), CENTERED | textColor); } void IconButton::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 8b160c6ca..075eb2f59 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -23,20 +23,17 @@ #include "form.h" #include -enum ButtonFlags { - BUTTON_BACKGROUND = 1, - BUTTON_CHECKED = 2, - BUTTON_NOFOCUS = 4, - BUTTON_CHECKED_ON_FOCUS = 8, - BUTTON_DISABLED = 16, -}; +constexpr WindowFlags BUTTON_BACKGROUND = FORM_FLAGS_LAST << 1; +constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2; +constexpr WindowFlags BUTTON_NOFOCUS = FORM_FLAGS_LAST << 3; +constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 4; +constexpr WindowFlags BUTTON_DISABLED = FORM_FLAGS_LAST << 5; class Button : public FormField { public: - Button(Window * parent, const rect_t & rect, std::function pressHandler=nullptr, uint8_t flags=0): - FormField(parent, rect), - pressHandler(pressHandler), - flags(flags) + Button(FormGroup * parent, const rect_t & rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0): + FormField(parent, rect, windowFlags), + pressHandler(pressHandler) { } @@ -49,8 +46,8 @@ class Button : public FormField { void enable(bool enabled=true) { - if (!enabled != bool(flags & BUTTON_DISABLED)) { - flags ^= BUTTON_DISABLED; + if (!enabled != bool(windowFlags & BUTTON_DISABLED)) { + windowFlags ^= BUTTON_DISABLED; invalidate(); } } @@ -62,23 +59,23 @@ class Button : public FormField { void check(bool checked=true) { - if (checked != bool(flags & BUTTON_CHECKED)) { - flags ^= BUTTON_CHECKED; + if (checked != bool(windowFlags & BUTTON_CHECKED)) { + windowFlags ^= BUTTON_CHECKED; invalidate(); } } bool enabled() { - return !(flags & BUTTON_DISABLED); + return !(windowFlags & BUTTON_DISABLED); } bool checked() { - if (flags & BUTTON_CHECKED_ON_FOCUS) + if (windowFlags & BUTTON_CHECKED_ON_FOCUS) return hasFocus(); else - return flags & BUTTON_CHECKED; + return windowFlags & BUTTON_CHECKED; } void setPressHandler(std::function handler) @@ -104,18 +101,16 @@ class Button : public FormField { protected: std::function pressHandler; std::function checkHandler; - uint8_t flags; void onPress(); }; class TextButton : public Button { public: - TextButton(Window * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, uint8_t flags = BUTTON_BACKGROUND): - Button(parent, rect, pressHandler, flags), + TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND): + Button(parent, rect, pressHandler, windowFlags | OPAQUE), text(std::move(text)) { - windowFlags = OPAQUE; } #if defined(DEBUG_WINDOWS) @@ -141,7 +136,7 @@ class TextButton : public Button { class IconButton: public Button { public: - IconButton(Window * parent, const rect_t & rect, uint8_t icon, std::function pressHandler, uint8_t flags = 0): + IconButton(FormGroup * parent, const rect_t & rect, uint8_t icon, std::function pressHandler, WindowFlags flags = 0): Button(parent, rect, pressHandler, flags), icon(icon) { diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 9f5d3a5a9..9185653f7 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -21,15 +21,14 @@ #include "menu.h" #include "theme.h" -Choice::Choice(Window * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, - std::function getValue, std::function setValue, LcdFlags flags) : +Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue) : ChoiceBase(parent, rect), values(values), vmin(vmin), vmax(vmax), getValue(std::move(getValue)), - setValue(std::move(setValue)), - flags(flags) + setValue(std::move(setValue)) { } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index ea12c55a2..6930f8192 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -29,9 +29,9 @@ enum ChoiceType { class ChoiceBase : public FormField { public: - ChoiceBase(Window * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN): - FormField(parent, rect), - type(type) + ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN): + FormField(parent, rect), + type(type) { } @@ -46,7 +46,7 @@ class ChoiceBase : public FormField { class Choice : public ChoiceBase { public: - Choice(Window * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr); #if defined(DEBUG_WINDOWS) std::string getName() override @@ -88,7 +88,6 @@ class Choice : public ChoiceBase { std::function setValue; std::function isValueAvailable; std::function textHandler; - LcdFlags flags = 0; virtual void openMenu(); }; diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 955417da9..26b540477 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -19,15 +19,13 @@ #include "dialog.h" #include "mainwindow.h" -#include "form.h" Dialog::Dialog(std::string title, const rect_t rect): - Window(&mainWindow, rect, OPAQUE), + FormGroup(&mainWindow, rect, OPAQUE), title(std::move(title)), previousFocus(focusWindow) { bringToTop(); - FormField::clearCurrentField(); setFocus(); } diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 5ec3bf8aa..6807705a4 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -20,9 +20,9 @@ #ifndef _DIALOG_H_ #define _DIALOG_H_ -#include "window.h" +#include "form.h" -class Dialog: public Window { +class Dialog: public FormGroup { public: Dialog(std::string title, const rect_t rect); diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 49b0723e1..ddca3da82 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -29,7 +29,7 @@ bool compare_nocase(const std::string &first, const std::string &second) return strcasecmp(first.c_str(), second.c_str()) < 0; } -FileChoice::FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): +FileChoice::FileChoice(FormGroup * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): ChoiceBase(parent, rect, CHOICE_TYPE_FOLDER), folder(std::move(folder)), extension(extension), diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index 2a28ea3a9..ebed4d8d4 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -25,7 +25,7 @@ class FileChoice: public ChoiceBase { public: - FileChoice(Window * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); + FileChoice(FormGroup * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); #if defined(DEBUG_WINDOWS) std::string getName() override diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 7fa8dd593..77f195603 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -21,33 +21,12 @@ #include "bitmapbuffer.h" #include "libopenui_config.h" -FormField * FormField::current = nullptr; - -FormField::FormField(Window *parent, const rect_t &rect, uint8_t flags) : - Window(parent, rect, flags) +FormField::FormField(Window *parent, const rect_t & rect, WindowFlags windowFlags) : + Window(parent, rect, windowFlags) { - if (current) { - setPreviousField(current); - current->setNextField(this); - } - - FormWindow * form = dynamic_cast(parent); - if (form && !form->getFirstField()) - form->setFirstField(this); - - current = this; -} - -#if defined(HARDWARE_KEYS) -void FormWindow::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_KEY_BREAK(KEY_EXIT) && first && getFocus() != first) { - first->setFocus(); - } - else { - Window::onEvent(event); + FormGroup * form = dynamic_cast(parent); + if (form) { + form->addField(this); } } @@ -57,12 +36,12 @@ void FormField::onEvent(event_t event) if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { if (next) { - next->setFocus(); + next->setFocus(SET_FOCUS_FORWARD); } } else if (event == EVT_ROTARY_LEFT/*EVT_KEY_BREAK(KEY_UP)*/) { if (previous) { - previous->setFocus(); + previous->setFocus(SET_FOCUS_BACKWARD); } } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { @@ -78,24 +57,6 @@ void FormField::onEvent(event_t event) } } -void FormGroup::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - editMode = true; - first->setFocus(); - } - else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { - editMode = false; - setFocus(); - } - else { - FormField::onEvent(event); - } -} -#endif - void FormField::paint(BitmapBuffer * dc) { if (editMode) { @@ -109,12 +70,96 @@ void FormField::paint(BitmapBuffer * dc) } } -void FormGroup::paint(BitmapBuffer * dc) +void FormGroup::addField(FormField * field) { - if (!editMode && hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + if (field->getWindowFlags() & FORM_DETACHED) + return; + + if (!first) { + first = field; + if (windowFlags & FORM_FORWARD_FOCUS) + link(previous, field); } - else if (!(windowFlags & BORDER_FOCUS_ONLY)) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); + if (last) + link(last, field); + last = field; + if (!(windowFlags & FORM_FORWARD_FOCUS)) + link(field, first); + else + field->setNextField(this); + if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) + field->setFocus(); +} + +void FormGroup::setFocus(uint8_t flag) +{ + if (windowFlags & FORM_FORWARD_FOCUS) { + switch (flag) { + case SET_FOCUS_FIRST: + first->setFocus(SET_FOCUS_FIRST); + break; + case SET_FOCUS_BACKWARD: + if (focusWindow == first) + previous->setFocus(SET_FOCUS_BACKWARD); + else + last->setFocus(SET_FOCUS_BACKWARD); + break; + default: + if (focusWindow == previous) + first->setFocus(); + else + next->setFocus(); + break; + } + } + else { + FormField::setFocus(); } } + +#if defined(HARDWARE_KEYS) +void FormGroup::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + editMode = true; + first->setFocus(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { + editMode = false; + setFocus(SET_FOCUS_DEFAULT); + } + else { + FormField::onEvent(event); + } +} +#endif + +void FormGroup::paint(BitmapBuffer * dc) +{ + if (!(windowFlags & FORM_FORWARD_FOCUS)) { + if (!editMode && hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + } + else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); + } + } +} + +#if defined(HARDWARE_KEYS) +void FormWindow::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_EXIT) && first) { + Window * currentFocus = getFocus(); + first->setFocus(SET_FOCUS_FIRST); + if (getFocus() != currentFocus) + return; + } + + Window::onEvent(event); +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index bb6898834..cd9397252 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -22,11 +22,13 @@ #include "window.h" -class FormField: public Window { - friend class FormWindow; +constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 1; +constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 2; +constexpr WindowFlags FORM_FLAGS_LAST = FORM_DETACHED; +class FormField: public Window { public: - FormField(Window * parent, const rect_t & rect, uint8_t flags=0); + FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0); inline void setNextField(FormField *field) { @@ -38,27 +40,12 @@ class FormField: public Window { previous = field; } - inline static void setCurrentField(FormField * field) - { - current = field; - } - - inline static void clearCurrentField() - { - setCurrentField(nullptr); - } - - inline static FormField * getCurrentField() - { - return current; - } - - virtual FormField * getPreviousField() + inline FormField * getPreviousField() { return previous; } - virtual FormField * getNextField() + inline FormField * getNextField() { return next; } @@ -86,7 +73,6 @@ class FormField: public Window { #endif protected: - static FormField * current; FormField * next = nullptr; FormField * previous = nullptr; bool editMode = false; @@ -94,13 +80,12 @@ class FormField: public Window { class FormGroup: public FormField { public: - FormGroup(Window * parent, const rect_t & rect, uint8_t flags=0) : - FormField(parent, rect, flags) + FormGroup(Window * parent, const rect_t & rect, WindowFlags windowflags = 0) : + FormField(parent, rect, windowflags) { - FormField::current = nullptr; } - ~FormGroup() + ~FormGroup() override { deleteChildren(); } @@ -116,33 +101,26 @@ class FormGroup: public FormField { { Window::clear(); first = nullptr; - FormField::current = nullptr; + last = nullptr; } - inline void setFirstField(FormField * field) - { - first = field; - } + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT) override; - inline void setLastField(FormField * field = nullptr) - { - // by default the current field is the last one - if (!field) - field = getCurrentField(); + void addField(FormField * field); - FormField::link(field, first); - - // now the current field is the group, it will be linked to the next one - FormField::setCurrentField(this); - } - - FormField * getFirstField() + inline FormField * getFirstField() { return first; } + inline FormField * getLastField() + { + return last; + } + protected: FormField * first = nullptr; + FormField * last = nullptr; #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; @@ -151,12 +129,11 @@ class FormGroup: public FormField { void paint(BitmapBuffer * dc) override; }; -class FormWindow: public Window { +class FormWindow: public FormGroup { public: - FormWindow(Window * parent, const rect_t & rect) : - Window(parent, rect) + FormWindow(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0) : + FormGroup(parent, rect, windowFlags) { - FormField::current = nullptr; } #if defined(DEBUG_WINDOWS) @@ -166,42 +143,14 @@ class FormWindow: public Window { } #endif - void clear() - { - first = nullptr; - Window::clear(); - } - - FormField * getFirstField() - { - return first; - } - - inline void setFirstField(FormField * field) - { - first = field; - field->setFocus(); - } - - inline void setLastField(FormField * field = nullptr) - { - if (!field) - field = FormField::getCurrentField(); - FormField::link(field, first); - } - protected: - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - void paint(BitmapBuffer * dc) override { } - protected: - FormField * first = nullptr; +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif }; #endif diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 4ec0a27c1..37174cdf4 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -24,9 +24,9 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) { if (checked()) { dc->drawSolidFilledRect(MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, FOCUS_BGCOLOR); - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | FOCUS_COLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(STDSIZE)) / 2 + 1, &picto, 1, CENTERED | FOCUS_COLOR); } else { - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2 + 1, &picto, 1, CENTERED | DEFAULT_COLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(STDSIZE)) / 2 + 1, &picto, 1, CENTERED | DEFAULT_COLOR); } } \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 3cf902dd0..81720d339 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -27,7 +27,7 @@ int getFirstAvailable(int min, int max, std::functionisValueAvailable class MenuToolbarButton: public Button { public: - MenuToolbarButton(Window * window, const rect_t & rect, char picto): + MenuToolbarButton(FormGroup * window, const rect_t & rect, char picto): #if defined(HARDWARE_TOUCH) Button(window, rect, nullptr, BUTTON_CHECKED_ON_FOCUS), #else @@ -66,16 +66,15 @@ class MenuToolbarButton: public Button { }; template -class MenuToolbar: public Window { +class MenuToolbar: public FormGroup { friend T; public: MenuToolbar(T * choice, Menu * menu): - Window(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR), + FormGroup(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR), choice(choice), menu(menu) { - FormField::clearCurrentField(); } ~MenuToolbar() override diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 199787138..5b38a12b1 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -22,10 +22,10 @@ void StaticText::paint(BitmapBuffer * dc) { - if (flags & BUTTON_BACKGROUND) + if (windowFlags & BUTTON_BACKGROUND) dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); - if (flags & CENTERED) - dc->drawText(rect.w / 2, (rect.h - getFontHeight(flags)) / 2, text.c_str(), CENTERED | flags); + if (textFlags & CENTERED) + dc->drawText(rect.w / 2, (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); else - dc->drawText(0, 0, text.c_str(), flags); + dc->drawText(0, 0, text.c_str(), textFlags); } diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index e824afbe4..20c843d30 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -25,10 +25,9 @@ class StaticText : public Window { public: - StaticText(Window * parent, const rect_t & rect, std::string text = "", LcdFlags flags = 0) : - Window(parent, rect), - text(std::move(text)), - flags(flags) + StaticText(Window * parent, const rect_t & rect, std::string text = "", WindowFlags windowFlags = 0, LcdFlags textFlags = 0) : + Window(parent, rect, windowFlags, textFlags), + text(std::move(text)) { } @@ -39,12 +38,6 @@ class StaticText : public Window { } #endif - void setFlags(LcdFlags flags) - { - this->flags = flags; - invalidate(); - } - void paint(BitmapBuffer * dc) override; void setText(std::string value) @@ -55,13 +48,12 @@ class StaticText : public Window { protected: std::string text; - LcdFlags flags; }; class Subtitle: public StaticText { public: Subtitle(Window * parent, const rect_t & rect, const char * text): - StaticText(parent, rect, text, BOLD) + StaticText(parent, rect, text, 0, BOLD) { } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index c1516de7e..80896e6ad 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -22,16 +22,17 @@ Window * Window::focusWindow = nullptr; std::list Window::trash; -Window::Window(Window * parent, const rect_t & rect, uint8_t flags): +Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): parent(parent), rect(rect), innerWidth(rect.w), innerHeight(rect.h), - windowFlags(flags) + windowFlags(windowFlags), + textFlags(textFlags) { if (parent) { parent->addChild(this); - if (!(flags & TRANSPARENT)) { + if (!(windowFlags & TRANSPARENT)) { invalidate(); } } @@ -108,7 +109,7 @@ void Window::clearFocus() } } -void Window::setFocus() +void Window::setFocus(uint8_t flag) { TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index e1cf2134c..75e55493a 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -31,23 +31,33 @@ #include "libopenui_helpers.h" #include "libopenui_config.h" +typedef uint32_t WindowFlags; + // OPAQUE/TRANSPARENT defined in WinGDI #ifndef OPAQUE #define OPAQUE 1 #define TRANSPARENT 2 #endif -constexpr uint8_t NO_SCROLLBAR = 4; -constexpr uint8_t FORWARD_SCROLL = 8; -constexpr uint8_t REFRESH_ALWAYS = 16; -constexpr uint8_t BORDER_FOCUS_ONLY = 32; -constexpr uint8_t PAINT_CHILDREN_FIRST = 64; +constexpr WindowFlags NO_SCROLLBAR = 4; +constexpr WindowFlags FORWARD_SCROLL = 8; +constexpr WindowFlags REFRESH_ALWAYS = 16; +constexpr WindowFlags BORDER_FOCUS_ONLY = 32; +constexpr WindowFlags PAINT_CHILDREN_FIRST = 64; +constexpr WindowFlags WINDOW_FLAGS_LAST = PAINT_CHILDREN_FIRST; + +enum SetFocusFlag { + SET_FOCUS_DEFAULT, + SET_FOCUS_FORWARD, + SET_FOCUS_BACKWARD, + SET_FOCUS_FIRST +}; class Window { friend class GridLayout; public: - Window(Window * parent, const rect_t & rect, uint8_t flags=0); + Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); virtual ~Window(); @@ -86,16 +96,21 @@ class Window { return parent; } - uint8_t getWindowFlags() const + WindowFlags getWindowFlags() const { return windowFlags; } - void setWindowFlags(uint8_t flags) + void setWindowFlags(WindowFlags flags) { windowFlags = flags; } + void setTextFlags(LcdFlags flags) + { + textFlags = flags; + } + void setCloseHandler(std::function handler) { closeHandler = std::move(handler); @@ -126,7 +141,7 @@ class Window { static void clearFocus(); - void setFocus(); + virtual void setFocus(uint8_t flag = SET_FOCUS_DEFAULT); void setRect(rect_t rect) { @@ -292,7 +307,8 @@ class Window { coord_t innerHeight; coord_t scrollPositionX = 0; coord_t scrollPositionY = 0; - uint8_t windowFlags; + WindowFlags windowFlags; + LcdFlags textFlags; static Window * focusWindow; static std::list trash; From 48de6ca188f7485608945a543328cca4925cb12c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 18 Oct 2019 22:26:20 +0200 Subject: [PATCH 029/714] Forms refactoring --- radio/src/thirdparty/libopenui/src/button.cpp | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 2 ++ radio/src/thirdparty/libopenui/src/keyboard_base.h | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index d9b97bead..83af6d83f 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -50,7 +50,7 @@ bool Button::onTouchEnd(coord_t x, coord_t y) { if (enabled()) { onPress(); - if (!(flags & BUTTON_NOFOCUS)) { + if (!(windowFlags & BUTTON_NOFOCUS)) { setFocus(); } } diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 77f195603..1a4b3b1e7 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -30,6 +30,7 @@ FormField::FormField(Window *parent, const rect_t & rect, WindowFlags windowFlag } } +#if defined(HARDWARE_KEYS) void FormField::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", FormField::getWindowDebugString().c_str(), event); @@ -56,6 +57,7 @@ void FormField::onEvent(event_t event) Window::onEvent(event); } } +#endif void FormField::paint(BitmapBuffer * dc) { diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 7a91c2890..540c228cc 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -23,10 +23,10 @@ #include "mainwindow.h" #include "form.h" -class Keyboard: public Window { +class Keyboard: public FormWindow { public: explicit Keyboard(coord_t height): - Window(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) + FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) { } From d2eecac722dd7ca297ab2aab0d5fe5003437b722 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 19 Oct 2019 13:01:04 +0200 Subject: [PATCH 030/714] One TODO added --- radio/src/thirdparty/libopenui/src/form.h | 2 +- radio/src/thirdparty/libopenui/src/window.cpp | 2 ++ radio/src/thirdparty/libopenui/src/window.h | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index cd9397252..cd612ff0b 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -87,7 +87,7 @@ class FormGroup: public FormField { ~FormGroup() override { - deleteChildren(); + deleteChildren(); // TODO move it to ~Window() } #if defined(DEBUG_WINDOWS) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 80896e6ad..8c371e2ce 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -45,6 +45,8 @@ Window::~Window() if (focusWindow == this) { focusWindow = nullptr; } + + // TODO deleteChildren(); (remove from FormGroup) } void Window::attach(Window * window) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 75e55493a..0eeb051ba 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -299,6 +299,8 @@ class Window { virtual void checkEvents(); + void detach(); + protected: Window * parent; std::list children; @@ -318,8 +320,6 @@ class Window { void attach(Window * window); - void detach(); - void addChild(Window * window) { children.push_back(window); From 4bcce12d76b561d91021f012aceb80133d9627d7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 19 Oct 2019 19:07:03 +0200 Subject: [PATCH 031/714] Screen Setup fixed --- radio/src/thirdparty/libopenui/src/coloredit.cpp | 5 ++--- radio/src/thirdparty/libopenui/src/coloredit.h | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index 1e085e576..6ea5936c0 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -56,8 +56,8 @@ class ColorBox: public Window { LcdFlags color; }; -ColorEdit::ColorEdit(Window * parent, const rect_t & rect, std::function getValue, std::function setValue): - Window(parent, rect, FORWARD_SCROLL) +ColorEdit::ColorEdit(FormGroup * parent, const rect_t & rect, std::function getValue, std::function setValue): + FormGroup(parent, rect, FORWARD_SCROLL | FORM_FORWARD_FOCUS) { auto width = rect.w / 4 - 5; @@ -77,7 +77,6 @@ ColorEdit::ColorEdit(Window * parent, const rect_t & rect, std::functionsetColor(value); }); } - } ColorEdit::~ColorEdit() diff --git a/radio/src/thirdparty/libopenui/src/coloredit.h b/radio/src/thirdparty/libopenui/src/coloredit.h index e78d91785..4568a7dc5 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.h +++ b/radio/src/thirdparty/libopenui/src/coloredit.h @@ -20,11 +20,11 @@ #ifndef _COLOREDIT_H_ #define _COLOREDIT_H_ -#include "window.h" +#include "form.h" -class ColorEdit : public Window { +class ColorEdit : public FormGroup { public: - ColorEdit(Window * parent, const rect_t & rect, std::function getValue, std::function setValue = nullptr); + ColorEdit(FormGroup * parent, const rect_t & rect, std::function getValue, std::function setValue = nullptr); #if defined(DEBUG_WINDOWS) std::string getName() override From deef13e80c91dd4348f3caa1ad88762e5338929b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 19 Oct 2019 19:26:14 +0200 Subject: [PATCH 032/714] Window::~Window() is now in charge of calling deleteLater for all children --- radio/src/thirdparty/libopenui/src/coloredit.cpp | 5 ----- radio/src/thirdparty/libopenui/src/coloredit.h | 2 -- radio/src/thirdparty/libopenui/src/form.h | 5 ----- radio/src/thirdparty/libopenui/src/menu.h | 4 ++-- radio/src/thirdparty/libopenui/src/menutoolbar.h | 5 ----- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 6 files changed, 3 insertions(+), 20 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index 6ea5936c0..0de6d9386 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -78,8 +78,3 @@ ColorEdit::ColorEdit(FormGroup * parent, const rect_t & rect, std::function handler) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 81720d339..725bad6f9 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -77,11 +77,6 @@ class MenuToolbar: public FormGroup { { } - ~MenuToolbar() override - { - deleteChildren(); - } - void paint(BitmapBuffer * dc) override { dc->clear(DISABLE_COLOR); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 8c371e2ce..64360c71d 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -46,7 +46,7 @@ Window::~Window() focusWindow = nullptr; } - // TODO deleteChildren(); (remove from FormGroup) + deleteChildren(); } void Window::attach(Window * window) From 2214c54a05291f65eceeae8ec517c6cc9ab375d1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 21 Oct 2019 12:08:06 +0200 Subject: [PATCH 033/714] A form / form widget can avoid to have the focus --- radio/src/thirdparty/libopenui/src/form.cpp | 8 +++++--- radio/src/thirdparty/libopenui/src/form.h | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 1a4b3b1e7..72c36662d 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -24,9 +24,11 @@ FormField::FormField(Window *parent, const rect_t & rect, WindowFlags windowFlags) : Window(parent, rect, windowFlags) { - FormGroup * form = dynamic_cast(parent); - if (form) { - form->addField(this); + if (!(windowFlags & FORM_NO_FOCUS)) { + FormGroup * form = dynamic_cast(parent); + if (form) { + form->addField(this); + } } } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 73c57b804..30f5fe1e1 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -22,8 +22,9 @@ #include "window.h" -constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 1; -constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 2; +constexpr WindowFlags FORM_NO_FOCUS = WINDOW_FLAGS_LAST << 1; +constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 2; +constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 3; constexpr WindowFlags FORM_FLAGS_LAST = FORM_DETACHED; class FormField: public Window { From 847e1d9ddf23f39327828de69d157377f0dd1a02 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 21 Oct 2019 21:06:42 +0200 Subject: [PATCH 034/714] BitmapBuffer::load renamed to BitmapBuffer::loadBitmap --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 2 +- radio/src/thirdparty/libopenui/src/static.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 7f78b1702..de671a657 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -582,7 +582,7 @@ void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h // } //} -BitmapBuffer * BitmapBuffer::load(const char * filename) +BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename) { const char * ext = getFileExtension(filename); if (ext && !strcmp(ext, ".bmp")) @@ -593,7 +593,7 @@ BitmapBuffer * BitmapBuffer::load(const char * filename) BitmapBuffer * BitmapBuffer::loadMask(const char * filename) { - BitmapBuffer * bitmap = BitmapBuffer::load(filename); + BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(filename); if (bitmap) { pixel_t * p = bitmap->getPixelPtr(0, 0); for (int i = bitmap->getWidth() * bitmap->getHeight(); i > 0; i--) { diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 8677f7864..eb5664c6d 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -351,7 +351,7 @@ class BitmapBuffer: public BitmapBufferBase void drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle); - static BitmapBuffer * load(const char * filename); + static BitmapBuffer * loadBitmap(const char * filename); static BitmapBuffer * loadMask(const char * filename); diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 20c843d30..e99a304fe 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -69,7 +69,7 @@ class StaticBitmap: public Window { public: StaticBitmap(Window * parent, const rect_t & rect, const char * filename): Window(parent, rect), - bitmap(BitmapBuffer::load(filename)) + bitmap(BitmapBuffer::loadBitmap(filename)) { } From 1c99e1a67fc25ff12d43d78222651e47ae02385f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 22 Oct 2019 10:25:18 +0200 Subject: [PATCH 035/714] Fonts refactoring (no more font list in libopenui) --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 2 +- .../libopenui/src/libopenui_defines.h | 38 +------------------ .../libopenui/src/libopenui_globals.h | 4 +- .../thirdparty/libopenui/src/menutoolbar.cpp | 4 +- radio/src/thirdparty/libopenui/src/static.h | 2 +- 5 files changed, 8 insertions(+), 42 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index de671a657..ee99591b1 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -413,7 +413,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ return x; } - uint32_t fontindex = FONTINDEX(flags); + uint32_t fontindex = FONT_INDEX(flags); const unsigned char * font = fontsTable[fontindex]; const uint16_t * fontspecs = fontspecsTable[fontindex]; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index a1f3444c7..56db231b8 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -44,42 +44,8 @@ /* telemetry flags */ #define NO_UNIT 0x40 -enum FontSizeIndex -{ - STDSIZE_INDEX, - TINSIZE_INDEX, - SMLSIZE_INDEX, - MIDSIZE_INDEX, - DBLSIZE_INDEX, - XXLSIZE_INDEX, - SPARE6_INDEX, - SPARE7_INDEX, - STDSIZE_BOLD_INDEX, - SPARE9_INDEX, - SPAREa_INDEX, - SPAREb_INDEX, - SPAREc_INDEX, - SPAREd_INDEX, - SPAREe_INDEX, - SPAREf_INDEX, -}; - -#define STDSIZE (STDSIZE_INDEX << 8) -#define TINSIZE (TINSIZE_INDEX << 8) -#define SMLSIZE (SMLSIZE_INDEX << 8) -#define MIDSIZE (MIDSIZE_INDEX << 8) -#define DBLSIZE (DBLSIZE_INDEX << 8) -#define XXLSIZE (XXLSIZE_INDEX << 8) -#define BOLD (STDSIZE_BOLD_INDEX << 8) -#define FONTSIZE_MASK 0x0f00 - -#if !defined(BOOT) -#define FONTSIZE(flags) ((flags) & FONTSIZE_MASK) -#define FONTINDEX(flags) (FONTSIZE(flags) >> 8) -#else -#define FONTSIZE(flags) STDSIZE -#define FONTINDEX(flags) STDSIZE_INDEX -#endif +#define FONT_MASK 0x0F00 +#define FONT_INDEX(flags) (((flags) & FONT_MASK) >> 8) #define TIMEBLINK 0x1000 #define TIMEHOUR 0x2000 diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.h b/radio/src/thirdparty/libopenui/src/libopenui_globals.h index ebcc1d9ff..fe14e3e9d 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_globals.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.h @@ -24,8 +24,8 @@ #include "libopenui_config.h" extern uint16_t lcdColorTable[LCD_COLOR_COUNT]; -extern const uint16_t * const fontspecsTable[FONT_TABLE_SIZE]; -extern const uint8_t * fontsTable[FONT_TABLE_SIZE]; +extern const uint16_t * const fontspecsTable[FONTS_COUNT]; +extern const uint8_t * fontsTable[FONTS_COUNT]; inline void lcdSetColor(uint16_t color) { diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 37174cdf4..f6cceee29 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -24,9 +24,9 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) { if (checked()) { dc->drawSolidFilledRect(MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, FOCUS_BGCOLOR); - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(STDSIZE)) / 2 + 1, &picto, 1, CENTERED | FOCUS_COLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, &picto, 1, CENTERED | FOCUS_COLOR); } else { - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(STDSIZE)) / 2 + 1, &picto, 1, CENTERED | DEFAULT_COLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, &picto, 1, CENTERED | DEFAULT_COLOR); } } \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index e99a304fe..6e5e1a196 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -53,7 +53,7 @@ class StaticText : public Window { class Subtitle: public StaticText { public: Subtitle(Window * parent, const rect_t & rect, const char * text): - StaticText(parent, rect, text, 0, BOLD) + StaticText(parent, rect, text, 0, FONT(BOLD)) { } From 72f8420f9c4e4347a810d146385ef3b6905264f9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 22 Oct 2019 10:36:52 +0200 Subject: [PATCH 036/714] Fonts refactoring (no more font list in libopenui) --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 56db231b8..fdc939c9f 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -46,8 +46,8 @@ #define FONT_MASK 0x0F00 #define FONT_INDEX(flags) (((flags) & FONT_MASK) >> 8) +#define FONT(xx) (FONT_ ## xx ## _INDEX << 8) -#define TIMEBLINK 0x1000 #define TIMEHOUR 0x2000 #define EXPANDED 0x2000 #define VERTICAL 0x4000 From 5590c601740f7fabe227c43bea319f0b56e7eaa1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 22 Oct 2019 17:35:15 +0200 Subject: [PATCH 037/714] Fix for drawMask() when x < 0 --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index ee99591b1..bb952fd12 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -326,18 +326,24 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd width = xmax - x; } + if (x < 0) { + width += x; + offset -= x; + x = 0; + } + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { return; } pixel_t color = lcdColorTable[COLOR_IDX(flags)]; - for (coord_t row=0; row= ymax) continue; - pixel_t * p = getPixelPtr(x, y+row); + pixel_t * p = getPixelPtr(x, y + row); const pixel_t * q = mask->getPixelPtr(offset, row); - for (coord_t col=0; col Date: Tue, 22 Oct 2019 18:05:36 +0200 Subject: [PATCH 038/714] Horizontal scroll missing --- radio/src/thirdparty/libopenui/src/window.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 0eeb051ba..8abf7d2f3 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -232,6 +232,11 @@ class Window { } } + coord_t getScrollPositionX() const + { + return scrollPositionX; + } + coord_t getScrollPositionY() const { return scrollPositionY; From 303bb7e3814f66daad94c0a26042b2650d8ed6fe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 23 Oct 2019 14:38:37 +0200 Subject: [PATCH 039/714] Code hardening --- radio/src/thirdparty/libopenui/src/form.cpp | 31 +++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 72c36662d..c859a57e0 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -97,22 +97,35 @@ void FormGroup::addField(FormField * field) void FormGroup::setFocus(uint8_t flag) { + TRACE_WINDOWS("FormGroup::setFocus(%d)", flag); + if (windowFlags & FORM_FORWARD_FOCUS) { switch (flag) { case SET_FOCUS_FIRST: - first->setFocus(SET_FOCUS_FIRST); + if (first) + first->setFocus(SET_FOCUS_FIRST); break; case SET_FOCUS_BACKWARD: - if (focusWindow == first) - previous->setFocus(SET_FOCUS_BACKWARD); - else - last->setFocus(SET_FOCUS_BACKWARD); + if (focusWindow == first) { + if (previous == this) + last->setFocus(SET_FOCUS_BACKWARD); + else if (previous) + previous->setFocus(SET_FOCUS_BACKWARD); + } + else { + if (last) + last->setFocus(SET_FOCUS_BACKWARD); + } break; default: - if (focusWindow == previous) - first->setFocus(); - else - next->setFocus(); + if (focusWindow == previous) { + if (first) + first->setFocus(); + } + else { + if (next) + next->setFocus(); + } break; } } From 7296241db0964cdcb1b1d1a1caef2755d04dc3ec Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 23 Oct 2019 14:49:04 +0200 Subject: [PATCH 040/714] Merge with latest libopenui changes --- radio/src/thirdparty/libopenui/src/form.h | 10 ------- radio/src/thirdparty/libopenui/src/window.cpp | 29 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/window.h | 19 ++++++++---- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 30f5fe1e1..3403845ed 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -104,16 +104,6 @@ class FormGroup: public FormField { void addField(FormField * field); - inline FormField * getFirstField() - { - return first; - } - - inline FormField * getLastField() - { - return last; - } - protected: FormField * first = nullptr; FormField * last = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 64360c71d..c79289c72 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -18,6 +18,7 @@ */ #include "window.h" +#include "touch.h" Window * Window::focusWindow = nullptr; std::list Window::trash; @@ -154,13 +155,18 @@ void Window::setScrollPositionY(coord_t value) void Window::scrollTo(Window * child) { + coord_t offsetX = 0; coord_t offsetY = 0; + Window * parent = child->getParent(); while (parent && parent != this) { + offsetX += parent->left(); offsetY += parent->top(); parent = parent->getParent(); } + coord_t left = offsetX + child->left(); + coord_t right = offsetX + child->right(); coord_t top = offsetY + child->top(); coord_t bottom = offsetY + child->bottom(); @@ -170,6 +176,13 @@ void Window::scrollTo(Window * child) else if (bottom > scrollPositionY + height() - 5) { setScrollPositionY(bottom - height() + 5); } + + if (left < scrollPositionX) { + setScrollPositionX(pageWidth ? left - (left % pageWidth) : left - 5); + } + else if (right > scrollPositionX + width() - 5) { + setScrollPositionX(pageWidth ? left - (left % pageWidth) : right - width() + 5); + } } void Window::fullPaint(BitmapBuffer * dc) @@ -275,6 +288,22 @@ void Window::checkEvents() if (windowFlags & REFRESH_ALWAYS) { invalidate(); } + +#if defined(HARDWARE_TOUCH) + if (pageWidth && touchState.event != TE_SLIDE) { + coord_t relativeScrollPosition = scrollPositionX % pageWidth; + if (relativeScrollPosition) { + if (relativeScrollPosition > pageWidth / 2) { + setScrollPositionX(getScrollPositionX() - relativeScrollPosition + pageWidth); + invalidate(); + } + else { + setScrollPositionX(getScrollPositionX() - relativeScrollPosition); + invalidate(); + } + } + } +#endif } void Window::onEvent(event_t event) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 8abf7d2f3..14873ee55 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -164,11 +164,6 @@ class Window { invalidate(); } - coord_t getHeight() - { - return rect.h; - } - void setLeft(coord_t x) { rect.x = x; @@ -224,6 +219,19 @@ class Window { } } + void setPageWidth(coord_t w) + { + pageWidth = w; + } + + uint8_t getPageIndex() + { + if (pageWidth) + return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; + else + return 0; + } + void setInnerHeight(coord_t h) { innerHeight = h; @@ -312,6 +320,7 @@ class Window { rect_t rect; coord_t innerWidth; coord_t innerHeight; + coord_t pageWidth = 0; coord_t scrollPositionX = 0; coord_t scrollPositionY = 0; WindowFlags windowFlags; From 0fc7a6cfd4a76c03dee3faa10f96ca184d4808c7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 23 Oct 2019 17:17:10 +0200 Subject: [PATCH 041/714] Merge BUTTON_NOFOCUS and FORM_NOFOCUS into NO_FOCUS --- radio/src/thirdparty/libopenui/src/button.cpp | 2 +- radio/src/thirdparty/libopenui/src/button.h | 9 ++++----- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- radio/src/thirdparty/libopenui/src/form.h | 5 ++--- .../libopenui/src/keyboard_number.cpp | 14 +++++++------- radio/src/thirdparty/libopenui/src/window.h | 19 ++++++++----------- 6 files changed, 23 insertions(+), 28 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 83af6d83f..97ab5c371 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -50,7 +50,7 @@ bool Button::onTouchEnd(coord_t x, coord_t y) { if (enabled()) { onPress(); - if (!(windowFlags & BUTTON_NOFOCUS)) { + if (!(windowFlags & NO_FOCUS)) { setFocus(); } } diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 075eb2f59..04ca3a8c8 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -25,9 +25,8 @@ constexpr WindowFlags BUTTON_BACKGROUND = FORM_FLAGS_LAST << 1; constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2; -constexpr WindowFlags BUTTON_NOFOCUS = FORM_FLAGS_LAST << 3; -constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 4; -constexpr WindowFlags BUTTON_DISABLED = FORM_FLAGS_LAST << 5; +constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3; +constexpr WindowFlags BUTTON_DISABLED = FORM_FLAGS_LAST << 4; class Button : public FormField { public: @@ -44,7 +43,7 @@ class Button : public FormField { } #endif - void enable(bool enabled=true) + void enable(bool enabled = true) { if (!enabled != bool(windowFlags & BUTTON_DISABLED)) { windowFlags ^= BUTTON_DISABLED; @@ -57,7 +56,7 @@ class Button : public FormField { enable(false); } - void check(bool checked=true) + void check(bool checked = true) { if (checked != bool(windowFlags & BUTTON_CHECKED)) { windowFlags ^= BUTTON_CHECKED; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index c859a57e0..ffdd5b744 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -24,7 +24,7 @@ FormField::FormField(Window *parent, const rect_t & rect, WindowFlags windowFlags) : Window(parent, rect, windowFlags) { - if (!(windowFlags & FORM_NO_FOCUS)) { + if (!(windowFlags & NO_FOCUS)) { FormGroup * form = dynamic_cast(parent); if (form) { form->addField(this); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 3403845ed..ec4f8838d 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -22,9 +22,8 @@ #include "window.h" -constexpr WindowFlags FORM_NO_FOCUS = WINDOW_FLAGS_LAST << 1; -constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 2; -constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 3; +constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 1; +constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 2; constexpr WindowFlags FORM_FLAGS_LAST = FORM_DETACHED; class FormField: public Window { diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index 90767690a..df578ebe1 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -32,43 +32,43 @@ NumberKeyboard::NumberKeyboard() : [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_BACKWARD); return 0; - }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + }, BUTTON_BACKGROUND | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_MINUS); return 0; - }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + }, BUTTON_BACKGROUND | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_PLUS); return 0; - }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + }, BUTTON_BACKGROUND | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_FORWARD); return 0; - }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + }, BUTTON_BACKGROUND | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_MIN); return 0; - }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + }, BUTTON_BACKGROUND | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_MAX); return 0; - }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + }, BUTTON_BACKGROUND | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 55, 50, 110, 30}, "DEFAULT", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_DEFAULT); return 0; - }, BUTTON_BACKGROUND | BUTTON_NOFOCUS); + }, BUTTON_BACKGROUND | NO_FOCUS); } NumberKeyboard::~NumberKeyboard() diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 14873ee55..e86d3111c 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -33,17 +33,14 @@ typedef uint32_t WindowFlags; -// OPAQUE/TRANSPARENT defined in WinGDI -#ifndef OPAQUE - #define OPAQUE 1 - #define TRANSPARENT 2 -#endif - -constexpr WindowFlags NO_SCROLLBAR = 4; -constexpr WindowFlags FORWARD_SCROLL = 8; -constexpr WindowFlags REFRESH_ALWAYS = 16; -constexpr WindowFlags BORDER_FOCUS_ONLY = 32; -constexpr WindowFlags PAINT_CHILDREN_FIRST = 64; +constexpr WindowFlags OPAQUE = 1 << 0; +constexpr WindowFlags TRANSPARENT = 1 << 1; +constexpr WindowFlags NO_SCROLLBAR = 1 << 2; +constexpr WindowFlags NO_FOCUS = 1 << 3; +constexpr WindowFlags FORWARD_SCROLL = 1 << 4; +constexpr WindowFlags REFRESH_ALWAYS = 1 << 5; +constexpr WindowFlags BORDER_FOCUS_ONLY = 1 << 6; +constexpr WindowFlags PAINT_CHILDREN_FIRST = 1 << 7; constexpr WindowFlags WINDOW_FLAGS_LAST = PAINT_CHILDREN_FIRST; enum SetFocusFlag { From 7fc7e89b85501618a30bf77f0282639c0cb95b2e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 28 Oct 2019 11:14:57 +0100 Subject: [PATCH 042/714] Cosmetics --- radio/src/thirdparty/libopenui/src/mainwindow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 86d51e8c9..fe6cd90ba 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -53,7 +53,7 @@ class MainWindow: public Window { void run(bool trash=true); protected: - void emptyTrash(); + static void emptyTrash(); rect_t invalidatedRect; const char * shutdown = nullptr; }; From 5447a23af15404f11b70b8e8dece4d76d7226af8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 28 Oct 2019 15:05:58 +0100 Subject: [PATCH 043/714] TextEdit focus issues --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 18 ++--- .../thirdparty/libopenui/src/bitmapbuffer.h | 70 +++++++++---------- radio/src/thirdparty/libopenui/src/form.h | 6 ++ .../libopenui/src/libopenui_defines.h | 1 + .../src/thirdparty/libopenui/src/textedit.cpp | 23 +++++- radio/src/thirdparty/libopenui/src/textedit.h | 4 +- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 7 files changed, 74 insertions(+), 50 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index bb952fd12..896d2ad0d 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -45,11 +45,11 @@ void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t p { APPLY_OFFSET(); - if (y >= height || y >= ymax || y < ymin) + if (y >= _height || y >= ymax || y < ymin) return; - if (x+w > width) { - w = width - x; + if (x + w > _width) { + w = _width - x; } pixel_t * p = getPixelPtr(x, y); @@ -89,7 +89,7 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat } if (y < ymin) { - h += y-ymin; + h += y - ymin; y = ymin; } @@ -163,7 +163,7 @@ void BitmapBuffer::drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t if (!data || h<=0 || w<=0) return; - DMAFillRect(data, width, height, x, y, w, h, lcdColorTable[COLOR_IDX(flags)]); + DMAFillRect(data, _width, _height, x, y, w, h, lcdColorTable[COLOR_IDX(flags)]); } void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags att) @@ -315,8 +315,8 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd APPLY_OFFSET(); - coord_t w = mask->getWidth(); - coord_t height = mask->getHeight(); + coord_t w = mask->width(); + coord_t height = mask->height(); if (!width || width > w) { width = w; @@ -602,7 +602,7 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(filename); if (bitmap) { pixel_t * p = bitmap->getPixelPtr(0, 0); - for (int i = bitmap->getWidth() * bitmap->getHeight(); i > 0; i--) { + for (int i = bitmap->width() * bitmap->height(); i > 0; i--) { *((uint8_t *)p) = OPACITY_MAX - ((*p) >> 12); MOVE_TO_NEXT_RIGHT_PIXEL(p); } @@ -615,7 +615,7 @@ BitmapBuffer * BitmapBuffer::loadMaskOnBackground(const char * filename, LcdFlag BitmapBuffer * result = nullptr; BitmapBuffer * mask = BitmapBuffer::loadMask(filename); if (mask) { - result = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight()); + result = new BitmapBuffer(BMP_RGB565, mask->width(), mask->height()); if (result) { result->clear(background); result->drawMask(0, 0, mask, foreground); diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index eb5664c6d..a8bbb0fe8 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -58,8 +58,8 @@ class BitmapBufferBase public: BitmapBufferBase(uint8_t format, uint16_t width, uint16_t height, T * data): format(format), - width(width), - height(height), + _width(width), + _height(height), xmax(width), ymax(height), data(data), @@ -69,21 +69,21 @@ class BitmapBufferBase BitmapBufferBase(uint8_t format, T * data): format(format), - width(*((uint16_t*)data)), - height(*(((uint16_t*)data) + 1)), - xmax(width), - ymax(height), + _width(*((uint16_t*)data)), + _height(*(((uint16_t*)data) + 1)), + xmax(_width), + ymax(_height), data(data + 2), - data_end(data + 2 + (width * height)) + data_end(data + 2 + (_width * _height)) { } inline void clearClippingRect() { xmin = 0; - xmax = width; + xmax = _width; ymin = 0; - ymax = height; + ymax = _height; } inline void setClippingRect(coord_t xmin, coord_t xmax, coord_t ymin, coord_t ymax) @@ -123,14 +123,14 @@ class BitmapBufferBase return format; } - inline uint16_t getWidth() const + inline uint16_t width() const { - return width; + return _width; } - inline uint16_t getHeight() const + inline uint16_t height() const { - return height; + return _height; } inline T * getData() const @@ -140,7 +140,7 @@ class BitmapBufferBase uint32_t getDataSize() const { - return width * height * sizeof(T); + return _width * _height * sizeof(T); } inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const @@ -149,13 +149,13 @@ class BitmapBufferBase x = width - x - 1; y = height - y - 1; #endif - return &data[y*width + x]; + return &data[y * _width + x]; } protected: uint8_t format; - coord_t width; - coord_t height; + coord_t _width; + coord_t _height; coord_t xmin = 0; coord_t xmax; coord_t ymin = 0; @@ -175,9 +175,9 @@ class RLEBitmap: RLEBitmap(uint8_t format, const uint8_t* rle_data) : BitmapBufferBase(format, 0, 0, nullptr) { - width = *((uint16_t *)rle_data); - height = *(((uint16_t *)rle_data)+1); - uint32_t pixels = width * height; + _width = *((uint16_t *)rle_data); + _height = *(((uint16_t *)rle_data)+1); + uint32_t pixels = _width * _height; data = (uint16_t*)malloc(pixels * sizeof(uint16_t)); decode((uint8_t *)data, pixels * sizeof(uint16_t), rle_data+4); data_end = data + pixels; @@ -265,7 +265,7 @@ class BitmapBuffer: public BitmapBufferBase inline void clear(LcdFlags flags=0) { - drawSolidFilledRect(0, 0, width - offsetX, height - offsetY, flags); + drawSolidFilledRect(0, 0, _width - offsetX, _height - offsetY, flags); } inline void drawPixel(pixel_t * p, pixel_t value) @@ -287,7 +287,7 @@ class BitmapBuffer: public BitmapBufferBase x = width - x - 1; y = height - y - 1; #endif - return &data[y*width + x]; + return &data[y * _width + x]; } inline pixel_t * getPixelPtr(coord_t x, coord_t y) @@ -296,7 +296,7 @@ class BitmapBuffer: public BitmapBufferBase x = width - x - 1; y = height - y - 1; #endif - return &data[y*width + x]; + return &data[y * _width + x]; } inline void drawPixel(coord_t x, coord_t y, pixel_t value) @@ -387,8 +387,8 @@ class BitmapBuffer: public BitmapBufferBase if (!data || !bmp) return; - coord_t srcw = bmp->getWidth(); - coord_t srch = bmp->getHeight(); + coord_t srcw = bmp->width(); + coord_t srch = bmp->height(); if (w == 0) w = srcw; @@ -432,20 +432,20 @@ class BitmapBuffer: public BitmapBufferBase h = height - y; }*/ if (bmp->getFormat() == BMP_ARGB4444) { - DMACopyAlphaBitmap(data, width, height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); + DMACopyAlphaBitmap(data, _width, _height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); } else { - DMACopyBitmap(data, width, height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); + DMACopyBitmap(data, _width, _height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); } } else { int scaledw = w * scale; int scaledh = h * scale; - if (x + scaledw > width) - scaledw = width - x; - if (y + scaledh > height) - scaledh = height - y; + if (x + scaledw > _width) + scaledw = _width - x; + if (y + scaledh > _height) + scaledh = _height - y; for (int i = 0; i < scaledh; i++) { pixel_t * p = getPixelPtr(x, y + i); @@ -469,12 +469,12 @@ class BitmapBuffer: public BitmapBufferBase template void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h) { - float vscale = float(h) / bitmap->getHeight(); - float hscale = float(w) / bitmap->getWidth(); + float vscale = float(h) / bitmap->height(); + float hscale = float(w) / bitmap->width(); float scale = vscale < hscale ? vscale : hscale; - int xshift = (w - (bitmap->getWidth() * scale)) / 2; - int yshift = (h - (bitmap->getHeight() * scale)) / 2; + int xshift = (w - (bitmap->width() * scale)) / 2; + int yshift = (h - (bitmap->height() * scale)) / 2; drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index ec4f8838d..7afd60ba1 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -60,6 +60,12 @@ class FormField: public Window { editMode = newEditMode; } + void onFocusLost() override + { + editMode = false; + Window::onFocusLost(); + } + inline static void link(FormField * previous, FormField * next) { previous->setNextField(next); diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index fdc939c9f..c24cd90f6 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -95,6 +95,7 @@ #define DISABLE_COLOR COLOR(DISABLE_COLOR_INDEX) #define HIGHLIGHT_COLOR COLOR(HIGHLIGHT_COLOR_INDEX) #define CHECKBOX_COLOR COLOR(CHECKBOX_COLOR_INDEX) +#define SCROLLBAR_COLOR COLOR(SCROLLBAR_COLOR_INDEX) #define MENU_COLOR COLOR(MENU_COLOR_INDEX) #define MENU_BGCOLOR COLOR(MENU_BGCOLOR_INDEX) #define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 27006581b..638a6a337 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -62,6 +62,16 @@ void TextEdit::paint(BitmapBuffer * dc) } } +void TextEdit::trim() +{ + for (int i = length - 1; i >= 0; i--) { + if (value[i] == ' ' || value[i] == '\0') + value[i] = '\0'; + else + break; + } +} + void TextEdit::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -111,7 +121,7 @@ void TextEdit::onEvent(event_t event) break; case EVT_KEY_BREAK(KEY_RIGHT): - if (cursorPos < length - 1) { + if (cursorPos < length - 1 && value[cursorPos + 1] != '\0') { cursorPos++; invalidate(); } @@ -120,15 +130,19 @@ void TextEdit::onEvent(event_t event) case EVT_KEY_BREAK(KEY_ENTER): if (cursorPos < length - 1) { cursorPos++; + if (value[cursorPos] == '\0') + value[cursorPos] = ' '; invalidate(); } else { + trim(); FormField::onEvent(event); } break; case EVT_KEY_BREAK(KEY_EXIT): cursorPos = 0; + trim(); FormField::onEvent(event); break; @@ -191,12 +205,15 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) } #endif -#if defined(SOFTWARE_KEYBOARD) void TextEdit::onFocusLost() { +#if defined(SOFTWARE_KEYBOARD) TextKeyboard::hide(); +#endif + + trim(); + cursorPos = 0; // TODO storageDirty(...); FormField::onFocusLost(); } -#endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 8f7d3f05f..0ab93e928 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -58,15 +58,15 @@ class TextEdit : public FormField { bool onTouchEnd(coord_t x, coord_t y) override; #endif -#if defined(SOFTWARE_KEYBOARD) void onFocusLost() override; -#endif protected: char * value; uint8_t length; uint8_t cursorPos = 0; + void trim(); + static uint8_t getNextChar(uint8_t c) { if (c == ' ' || c == 0) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index c79289c72..56abd5977 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -418,6 +418,6 @@ void Window::drawVerticalScrollbar(BitmapBuffer * dc) yofs = innerHeight - y - yhgt; } } - dc->drawSolidFilledRect(x-1, y + yofs, 3, yhgt, CHECKBOX_COLOR); + dc->drawSolidFilledRect(x-1, y + yofs, 3, yhgt, SCROLLBAR_COLOR); } } From 02e0964000d86091ed6197113e891831187723ca Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 28 Oct 2019 15:41:57 +0100 Subject: [PATCH 044/714] Cosmetics --- radio/src/thirdparty/libopenui/src/message_dialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/message_dialog.cpp b/radio/src/thirdparty/libopenui/src/message_dialog.cpp index 71a301f65..004be79f8 100644 --- a/radio/src/thirdparty/libopenui/src/message_dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/message_dialog.cpp @@ -23,7 +23,7 @@ MessageDialog::MessageDialog(const char * title, const char * message): Dialog(title, {50, 73, LCD_W - 100, LCD_H - 146}) { - new StaticText(this, {0, coord_t(height() - PAGE_LINE_HEIGHT) / 2, width(), PAGE_LINE_HEIGHT}, message, CENTERED); + new StaticText(this, {0, coord_t(height() - PAGE_LINE_HEIGHT) / 2, width(), PAGE_LINE_HEIGHT}, message, 0, CENTERED); } #if defined(HARDWARE_KEYS) From d5c34453d31a1f5c3ba28e6e2dc06469bd3b2a07 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 28 Oct 2019 17:05:41 +0100 Subject: [PATCH 045/714] Issue with bitmaps scale --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 12 +++++++----- radio/src/thirdparty/libopenui/src/filechoice.cpp | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index a8bbb0fe8..808435001 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -411,12 +411,14 @@ class BitmapBuffer: public BitmapBufferBase y = ymin; } - if (x + w > xmax) { - w = xmax - x; - } + if (scale == 0) { + if (x + w > xmax) { + w = xmax - x; + } - if (y + h > ymax) { - h = ymax - y; + if (y + h > ymax) { + h = ymax - y; + } } if (w <= 0 || h <= 0) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index ddca3da82..780acdd63 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -68,7 +68,7 @@ bool FileChoice::openMenu() continue; // skip system files fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); - fnLen -= extLen; + // fnLen -= extLen; if (!fnLen || fnLen > maxlen) continue; // wrong size From 6341d0d530896a9699c2860da2e3c706cbe0c5f2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 28 Oct 2019 18:00:11 +0100 Subject: [PATCH 046/714] Model Select cosmetics --- radio/src/thirdparty/libopenui/src/textedit.cpp | 8 +++----- radio/src/thirdparty/libopenui/src/textedit.h | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 638a6a337..fd8886f44 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -135,14 +135,13 @@ void TextEdit::onEvent(event_t event) invalidate(); } else { - trim(); + changeEnd(); FormField::onEvent(event); } break; case EVT_KEY_BREAK(KEY_EXIT): - cursorPos = 0; - trim(); + changeEnd(); FormField::onEvent(event); break; @@ -211,8 +210,7 @@ void TextEdit::onFocusLost() TextKeyboard::hide(); #endif - trim(); - cursorPos = 0; + changeEnd(); // TODO storageDirty(...); FormField::onFocusLost(); diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 0ab93e928..90f316169 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -26,13 +26,14 @@ class TextEdit : public FormField { friend class TextKeyboard; public: - TextEdit(Window * parent, const rect_t &rect, char * value, uint8_t length, LcdFlags flags = 0) : + TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags flags = 0) : FormField(parent, rect), value(value), length(length) { } + #if defined(DEBUG_WINDOWS) std::string getName() override { @@ -40,6 +41,11 @@ class TextEdit : public FormField { } #endif + void setChangeHandler(std::function handler) + { + changeHandler = handler; + } + uint8_t getMaxLength() { return length; @@ -64,9 +70,18 @@ class TextEdit : public FormField { char * value; uint8_t length; uint8_t cursorPos = 0; + std::function changeHandler = nullptr; void trim(); + void changeEnd() + { + cursorPos = 0; + trim(); + if (changeHandler) + changeHandler(); + } + static uint8_t getNextChar(uint8_t c) { if (c == ' ' || c == 0) From d156bf8171bd5ccde63951e9bf81e43113acb694 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 29 Oct 2019 10:22:37 +0100 Subject: [PATCH 047/714] Choice and Menu fixes --- radio/src/thirdparty/libopenui/src/choice.cpp | 7 +++---- radio/src/thirdparty/libopenui/src/menu.cpp | 20 +++++++++---------- radio/src/thirdparty/libopenui/src/menu.h | 4 +--- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 9185653f7..34de099f9 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -44,8 +44,6 @@ void Choice::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { - editMode = true; - invalidate(); openMenu(); } else { @@ -88,6 +86,9 @@ void Choice::openMenu() editMode = false; setFocus(); }); + + setEditMode(true); + invalidate(); } #if defined(HARDWARE_TOUCH) @@ -95,8 +96,6 @@ bool Choice::onTouchEnd(coord_t, coord_t) { onKeyPress(); openMenu(); - setEditMode(true); - setFocus(); return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index c4150b65d..9a79f2358 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -29,7 +29,7 @@ void MenuWindow::select(int index) { selectedIndex = index; if (innerHeight > height()) { - setScrollPositionY(lineHeight * index - 3 * lineHeight); + setScrollPositionY(MENUS_LINE_HEIGHT * index - 3 * MENUS_LINE_HEIGHT); } invalidate(); } @@ -63,7 +63,7 @@ void MenuWindow::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool MenuWindow::onTouchEnd(coord_t x, coord_t y) { - int index = y / lineHeight; + int index = y / MENUS_LINE_HEIGHT; lines[index].onPress(); return false; // = close the menu (inverted so that click outside the menu closes it) } @@ -73,18 +73,18 @@ void MenuWindow::paint(BitmapBuffer * dc) { int width = (innerHeight > height() ? 195 : 200); dc->clear(MENU_BGCOLOR); - for (unsigned i = 0; i < lines.size(); i++) { + for (auto i = 0; i < lines.size(); i++) { auto & line = lines[i]; LcdFlags flags = (selectedIndex == (int) i ? HIGHLIGHT_COLOR : MENU_COLOR); if (line.drawLine) { - line.drawLine(dc, 0, i * lineHeight /*+ (lineHeight - 20) / 2*/, flags); + line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT /*+ (lineHeight - 20) / 2*/, flags); } else { - const char *text = line.text.data(); - dc->drawText(10, i * lineHeight + (lineHeight - 20) / 2, text[0] == '\0' ? "---" : text, flags); + const char * text = line.text.data(); + dc->drawText(10, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, text[0] == '\0' ? "---" : text, flags); } if (i > 0) { - dc->drawSolidHorizontalLine(0, i * lineHeight - 1, width, DISABLE_COLOR); + dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT - 1, width, DISABLE_COLOR); } } } @@ -93,12 +93,12 @@ void Menu::updatePosition() { if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen - int count = min(MenuWindow::maxLines, menuWindow.lines.size()); - coord_t h = count * MenuWindow::lineHeight - 1; + int count = min(MENUS_MAX_LINES, menuWindow.lines.size()); + coord_t h = count * MENUS_LINE_HEIGHT - 1; menuWindow.setTop((LCD_H - h) / 2 + 20); menuWindow.setHeight(h); } - menuWindow.setInnerHeight(menuWindow.lines.size() * MenuWindow::lineHeight - 1); + menuWindow.setInnerHeight(menuWindow.lines.size() * MENUS_LINE_HEIGHT - 1); } void Menu::addLine(const std::string & text, std::function onPress) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index c58ef6b6c..bcfb87d5d 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -109,8 +109,6 @@ class MenuWindow: public Window { int selectedIndex = 0; #endif std::function onCancel; - static constexpr uint8_t maxLines = 7; // TODO NV14 was 8 - static constexpr uint8_t lineHeight = 30; // TODO NV14 was 40 }; class Menu : public Window { @@ -186,7 +184,7 @@ class Menu : public Window { #endif protected: - Window * previousFocus; + Window * previousFocus = nullptr; MenuWindow menuWindow; Window * toolbar = nullptr; void updatePosition(); From afe02166719ede2e122982dd74909e27cc001877 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 31 Oct 2019 12:20:18 +0100 Subject: [PATCH 048/714] MSVC compilation fixes --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 9 ++++++--- radio/src/thirdparty/libopenui/src/libopenui_helpers.h | 7 ++++++- radio/src/thirdparty/libopenui/src/window.h | 5 +++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index c24cd90f6..d613b2bdc 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -75,9 +75,12 @@ #define GET_BLUE(color) \ (((color) & 0x001F) << 3) -// remove windows default definitions -#undef OPAQUE -#undef RGB +#ifndef __GNUC__ + #include + // remove windows default definitions + #undef OPAQUE + #undef RGB +#endif #define OPACITY_MAX 0x0F #define OPACITY(x) ((x)<<24) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index ed70ddd07..d93619683 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -22,6 +22,11 @@ #include +#if defined(min) + #undef min + #undef max +#endif + template inline T min(T a, T b) { @@ -51,7 +56,7 @@ inline int divRoundClosest(const int n, const int d) inline std::string TEXT_AT_INDEX(const char * val, uint8_t idx) { const char * start = val + 1 + idx * val[0]; - return std::string(start, min(val[0], strlen(start))); + return std::string(start, min(val[0], (uint8_t)strlen(start))); } #endif diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index e86d3111c..37a358a44 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -33,6 +33,11 @@ typedef uint32_t WindowFlags; +#if !defined(_GNUC_) + #undef OPAQUE + #undef TRANSPARENT +#endif + constexpr WindowFlags OPAQUE = 1 << 0; constexpr WindowFlags TRANSPARENT = 1 << 1; constexpr WindowFlags NO_SCROLLBAR = 1 << 2; From c744ee226a2485eab1c4da4ba6bf6128d11b4eb8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 4 Nov 2019 15:31:04 +0100 Subject: [PATCH 049/714] Sticky scroll in menus --- radio/src/thirdparty/libopenui/src/window.cpp | 23 ++++++++++--------- radio/src/thirdparty/libopenui/src/window.h | 8 +++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 56abd5977..9c151f722 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -171,10 +171,10 @@ void Window::scrollTo(Window * child) coord_t bottom = offsetY + child->bottom(); if (top < scrollPositionY) { - setScrollPositionY(top - 5); + setScrollPositionY(pageHeight ? top - (top % pageHeight) : top - 5); } else if (bottom > scrollPositionY + height() - 5) { - setScrollPositionY(bottom - height() + 5); + setScrollPositionY(pageHeight ? top - (top % pageHeight) : bottom - height() + 5); } if (left < scrollPositionX) { @@ -290,16 +290,17 @@ void Window::checkEvents() } #if defined(HARDWARE_TOUCH) - if (pageWidth && touchState.event != TE_SLIDE) { - coord_t relativeScrollPosition = scrollPositionX % pageWidth; - if (relativeScrollPosition) { - if (relativeScrollPosition > pageWidth / 2) { - setScrollPositionX(getScrollPositionX() - relativeScrollPosition + pageWidth); - invalidate(); + if (touchState.event != TE_SLIDE) { + if (pageWidth) { + coord_t relativeScrollPosition = scrollPositionX % pageWidth; + if (relativeScrollPosition) { + setScrollPositionX(getScrollPositionX() - relativeScrollPosition + (relativeScrollPosition > pageWidth / 2 ? pageWidth : 0)); } - else { - setScrollPositionX(getScrollPositionX() - relativeScrollPosition); - invalidate(); + } + if (pageHeight) { + coord_t relativeScrollPosition = scrollPositionY % pageHeight; + if (relativeScrollPosition) { + setScrollPositionY(getScrollPositionY() - relativeScrollPosition + (relativeScrollPosition > pageHeight / 2 ? pageHeight : 0)); } } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 37a358a44..9bf4b000b 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -226,10 +226,17 @@ class Window { pageWidth = w; } + void setPageHeight(coord_t h) + { + pageHeight = h; + } + uint8_t getPageIndex() { if (pageWidth) return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; + else if (pageHeight) + return (getScrollPositionX() + (pageHeight / 2)) / pageHeight; else return 0; } @@ -323,6 +330,7 @@ class Window { coord_t innerWidth; coord_t innerHeight; coord_t pageWidth = 0; + coord_t pageHeight = 0; coord_t scrollPositionX = 0; coord_t scrollPositionY = 0; WindowFlags windowFlags; From 3e3d8b95e77a0ba076c70fa5ce067dc6f5a7214f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 4 Nov 2019 18:31:50 +0100 Subject: [PATCH 050/714] Menus can now have a title and a Cancel button --- .../thirdparty/libopenui/src/bitmapbuffer.h | 23 ++++-- radio/src/thirdparty/libopenui/src/choice.cpp | 3 + radio/src/thirdparty/libopenui/src/choice.h | 6 ++ .../libopenui/src/libopenui_defines.h | 3 + radio/src/thirdparty/libopenui/src/menu.cpp | 69 ++++++++++++----- radio/src/thirdparty/libopenui/src/menu.h | 77 +++++++++++++------ 6 files changed, 134 insertions(+), 47 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 808435001..8945c6254 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -108,6 +108,17 @@ class BitmapBufferBase this->offsetY = offsetY; } + inline void clearOffset() + { + setOffset(0, 0); + } + + inline void reset() + { + clearOffset(); + clearClippingRect(); + } + coord_t getOffsetX() const { return offsetX; @@ -146,8 +157,8 @@ class BitmapBufferBase inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const { #if defined(PCBX10) && !defined(SIMU) - x = width - x - 1; - y = height - y - 1; + x = _width - x - 1; + y = _height - y - 1; #endif return &data[y * _width + x]; } @@ -284,8 +295,8 @@ class BitmapBuffer: public BitmapBufferBase inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const { #if defined(PCBX10) && !defined(SIMU) - x = width - x - 1; - y = height - y - 1; + x = _width - x - 1; + y = _height - y - 1; #endif return &data[y * _width + x]; } @@ -293,8 +304,8 @@ class BitmapBuffer: public BitmapBufferBase inline pixel_t * getPixelPtr(coord_t x, coord_t y) { #if defined(PCBX10) && !defined(SIMU) - x = width - x - 1; - y = height - y - 1; + x = _width - x - 1; + y = _height - y - 1; #endif return &data[y * _width + x]; } diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 34de099f9..57c084d8e 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -55,6 +55,9 @@ void Choice::onEvent(event_t event) void Choice::openMenu() { auto menu = new Menu(); + if (!menuTitle.empty()) + menu->setTitle(menuTitle); + auto value = getValue(); int count = 0; int current = -1; diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 6930f8192..1442bdcf2 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -80,10 +80,16 @@ class Choice : public ChoiceBase { textHandler = std::move(handler); } + void setMenuTitle(const std::string value) + { + menuTitle = std::move(value); + } + protected: const char * values = nullptr; int16_t vmin = 0; int16_t vmax = 0; + std::string menuTitle; std::function getValue; std::function setValue; std::function isValueAvailable; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index d613b2bdc..ab3a6e324 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -101,6 +101,9 @@ #define SCROLLBAR_COLOR COLOR(SCROLLBAR_COLOR_INDEX) #define MENU_COLOR COLOR(MENU_COLOR_INDEX) #define MENU_BGCOLOR COLOR(MENU_BGCOLOR_INDEX) +#define MENU_LINE_COLOR COLOR(MENU_LINE_COLOR_INDEX) +#define MENU_HIGHLIGHT_COLOR COLOR(MENU_HIGHLIGHT_COLOR_INDEX) +#define MENU_HIGHLIGHT_BGCOLOR COLOR(MENU_HIGHLIGHT_BGCOLOR_INDEX) #define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) #endif diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 9a79f2358..ceacfc7a5 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -18,14 +18,16 @@ */ #include "menu.h" +#include "font.h" MenuWindow::MenuWindow(Menu * parent): - Window(parent, {LCD_W / 2 - 100, LCD_H / 2, 200, 0}, OPAQUE) + Window(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}, OPAQUE), + body(this, {0, 0, width(), height()}, OPAQUE) { - setFocus(); + body.setFocus(); } -void MenuWindow::select(int index) +void MenuBody::select(int index) { selectedIndex = index; if (innerHeight > height()) { @@ -35,7 +37,7 @@ void MenuWindow::select(int index) } #if defined(HARDWARE_KEYS) -void MenuWindow::onEvent(event_t event) +void MenuBody::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -61,7 +63,7 @@ void MenuWindow::onEvent(event_t event) #endif #if defined(HARDWARE_TOUCH) -bool MenuWindow::onTouchEnd(coord_t x, coord_t y) +bool MenuBody::onTouchEnd(coord_t x, coord_t y) { int index = y / MENUS_LINE_HEIGHT; lines[index].onPress(); @@ -69,13 +71,18 @@ bool MenuWindow::onTouchEnd(coord_t x, coord_t y) } #endif -void MenuWindow::paint(BitmapBuffer * dc) +void MenuBody::paint(BitmapBuffer * dc) { - int width = (innerHeight > height() ? 195 : 200); dc->clear(MENU_BGCOLOR); - for (auto i = 0; i < lines.size(); i++) { + + for (auto i = 0; i < (int)lines.size(); i++) { auto & line = lines[i]; - LcdFlags flags = (selectedIndex == (int) i ? HIGHLIGHT_COLOR : MENU_COLOR); + LcdFlags flags = MENU_COLOR; + if (selectedIndex == i) { + flags = MENU_HIGHLIGHT_COLOR; + if (MENU_HIGHLIGHT_BGCOLOR != MENU_BGCOLOR) + dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), MENUS_LINE_HEIGHT, MENU_HIGHLIGHT_BGCOLOR); + } if (line.drawLine) { line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT /*+ (lineHeight - 20) / 2*/, flags); } @@ -84,38 +91,64 @@ void MenuWindow::paint(BitmapBuffer * dc) dc->drawText(10, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, text[0] == '\0' ? "---" : text, flags); } if (i > 0) { - dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT - 1, width, DISABLE_COLOR); + dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); } } } +void MenuWindow::paint(BitmapBuffer * dc) +{ + // the background + dc->clear(MENU_BGCOLOR); + + // the title + if (!title.empty()) { + dc->drawText(MENUS_WIDTH / 2, (MENUS_HEADER_HEIGHT - getFontHeight(FONT(BOLD))) / 2, title.c_str(), CENTERED | FONT(BOLD)); + dc->drawSolidHorizontalLine(0, MENUS_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); + } + + // the cancel button + if (MENUS_FOOTER_HEIGHT > 0) { + dc->drawText(MENUS_WIDTH / 2, height() - MENUS_FOOTER_HEIGHT + (MENUS_FOOTER_HEIGHT - getFontHeight(FONT(BOLD))) / 2, "Cancel", CENTERED); + dc->drawSolidHorizontalLine(0, height() - MENUS_FOOTER_HEIGHT, MENUS_WIDTH, MENU_LINE_COLOR); + } +} + void Menu::updatePosition() { if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen - int count = min(MENUS_MAX_LINES, menuWindow.lines.size()); - coord_t h = count * MENUS_LINE_HEIGHT - 1; - menuWindow.setTop((LCD_H - h) / 2 + 20); - menuWindow.setHeight(h); + auto headerHeight = menuWindow.title.empty() ? 0 : MENUS_HEADER_HEIGHT; + auto bodyHeight = limit(MENUS_MIN_HEIGHT, menuWindow.body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); + menuWindow.setTop((LCD_H - headerHeight - bodyHeight - MENUS_FOOTER_HEIGHT) / 2 + MENUS_OFFSET_TOP); + menuWindow.setHeight(headerHeight + bodyHeight + MENUS_FOOTER_HEIGHT); + menuWindow.body.setTop(headerHeight); + menuWindow.body.setHeight(bodyHeight); } - menuWindow.setInnerHeight(menuWindow.lines.size() * MENUS_LINE_HEIGHT - 1); + menuWindow.body.setInnerHeight(menuWindow.body.lines.size() * MENUS_LINE_HEIGHT - 1); +} + +void Menu::setTitle(const std::string text) +{ + menuWindow.setTitle(text); + updatePosition(); } void Menu::addLine(const std::string & text, std::function onPress) { - menuWindow.addLine(text, std::move(onPress)); + menuWindow.body.addLine(text, std::move(onPress)); updatePosition(); } void Menu::addCustomLine(std::function drawLine, std::function onPress) { - menuWindow.addCustomLine(drawLine, std::move(onPress)); + menuWindow.body.addCustomLine(drawLine, std::move(onPress)); updatePosition(); } void Menu::removeLines() { - menuWindow.removeLines(); + menuWindow.body.removeLines(); updatePosition(); } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index bcfb87d5d..fe0356a17 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -26,12 +26,14 @@ #include "mainwindow.h" class Menu; +class MenuWindow; -class MenuWindow: public Window { +class MenuBody: public Window { + friend class MenuWindow; friend class Menu; class MenuLine { - friend class MenuWindow; + friend class MenuBody; public: MenuLine(std::string text, std::function onPress): @@ -57,19 +59,21 @@ class MenuWindow: public Window { }; public: - explicit MenuWindow(Menu * parent); - -#if defined(DEBUG_WINDOWS) - std::string getName() override + MenuBody(Window * parent, const rect_t & rect, WindowFlags flags): + Window(parent, rect, flags) { - return "MenuWindow"; + setPageHeight(MENUS_LINE_HEIGHT); } + + void select(int index); + +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; #endif - void setCancelHandler(std::function handler) - { - onCancel = handler; - } +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif void addLine(const std::string & text, std::function onPress) { @@ -89,18 +93,13 @@ class MenuWindow: public Window { invalidate(); } - void select(int index); + void setCancelHandler(std::function handler) + { + onCancel = handler; + } void paint(BitmapBuffer * dc) override; -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif - protected: std::vector lines; #if defined(HARDWARE_TOUCH) @@ -111,6 +110,36 @@ class MenuWindow: public Window { std::function onCancel; }; +class MenuWindow: public Window { + friend class Menu; + + public: + explicit MenuWindow(Menu * parent); + + ~MenuWindow() override + { + body.detach(); + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "MenuWindow"; + } +#endif + + void setTitle(const std::string text) + { + title = std::move(text); + } + + void paint(BitmapBuffer * dc) override; + + protected: + MenuBody body; + std::string title; +}; + class Menu : public Window { public: Menu() : @@ -129,7 +158,7 @@ class Menu : public Window { void setCancelHandler(std::function handler) { - menuWindow.setCancelHandler(handler); + menuWindow.body.setCancelHandler(handler); } void deleteLater() @@ -157,15 +186,17 @@ class Menu : public Window { menuWindow.setHeight(toolbar->height()); } + void setTitle(const std::string text); + void addLine(const std::string & text, std::function onPress); void addCustomLine(std::function drawLine, std::function onPress); void removeLines(); - void select(int index) + inline void select(int index) { - menuWindow.select(index); + menuWindow.body.select(index); } #if defined(HARDWARE_KEYS) From b14a40a4d1d5215b817a65b4f45ebb725739ccf8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 4 Nov 2019 22:35:33 +0100 Subject: [PATCH 051/714] Menus can now have a title and a Cancel button --- radio/src/thirdparty/libopenui/src/menu.cpp | 32 ++++++++++++++------- radio/src/thirdparty/libopenui/src/menu.h | 30 ++++++------------- radio/src/thirdparty/libopenui/src/theme.h | 5 +++- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index ceacfc7a5..3c55faaa4 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -19,6 +19,7 @@ #include "menu.h" #include "font.h" +#include "theme.h" MenuWindow::MenuWindow(Menu * parent): Window(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}, OPAQUE), @@ -114,41 +115,50 @@ void MenuWindow::paint(BitmapBuffer * dc) } } +Menu::Menu(): + Window(&mainWindow, {0, 0, LCD_W, LCD_H}, TRANSPARENT), +#if !defined(HARDWARE_TOUCH) + previousFocus(focusWindow), +#endif + menuWindow(createMenuWindow(this)) +{ +} + void Menu::updatePosition() { if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen - auto headerHeight = menuWindow.title.empty() ? 0 : MENUS_HEADER_HEIGHT; - auto bodyHeight = limit(MENUS_MIN_HEIGHT, menuWindow.body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); - menuWindow.setTop((LCD_H - headerHeight - bodyHeight - MENUS_FOOTER_HEIGHT) / 2 + MENUS_OFFSET_TOP); - menuWindow.setHeight(headerHeight + bodyHeight + MENUS_FOOTER_HEIGHT); - menuWindow.body.setTop(headerHeight); - menuWindow.body.setHeight(bodyHeight); + auto headerHeight = menuWindow->title.empty() ? 0 : MENUS_HEADER_HEIGHT; + auto bodyHeight = limit(MENUS_MIN_HEIGHT, menuWindow->body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); + menuWindow->setTop((LCD_H - headerHeight - bodyHeight - MENUS_FOOTER_HEIGHT) / 2 + MENUS_OFFSET_TOP); + menuWindow->setHeight(headerHeight + bodyHeight + MENUS_FOOTER_HEIGHT); + menuWindow->body.setTop(headerHeight); + menuWindow->body.setHeight(bodyHeight); } - menuWindow.body.setInnerHeight(menuWindow.body.lines.size() * MENUS_LINE_HEIGHT - 1); + menuWindow->body.setInnerHeight(menuWindow->body.lines.size() * MENUS_LINE_HEIGHT - 1); } void Menu::setTitle(const std::string text) { - menuWindow.setTitle(text); + menuWindow->setTitle(text); updatePosition(); } void Menu::addLine(const std::string & text, std::function onPress) { - menuWindow.body.addLine(text, std::move(onPress)); + menuWindow->body.addLine(text, std::move(onPress)); updatePosition(); } void Menu::addCustomLine(std::function drawLine, std::function onPress) { - menuWindow.body.addCustomLine(drawLine, std::move(onPress)); + menuWindow->body.addCustomLine(drawLine, std::move(onPress)); updatePosition(); } void Menu::removeLines() { - menuWindow.body.removeLines(); + menuWindow->body.removeLines(); updatePosition(); } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index fe0356a17..d41c5981b 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -140,25 +140,13 @@ class MenuWindow: public Window { std::string title; }; -class Menu : public Window { +class Menu: public Window { public: - Menu() : - Window(&mainWindow, {0, 0, LCD_W, LCD_H}, TRANSPARENT), -#if !defined(HARDWARE_TOUCH) - previousFocus(focusWindow), -#endif - menuWindow(this) - { - } - - ~Menu() override - { - menuWindow.detach(); - } - + Menu(); + void setCancelHandler(std::function handler) { - menuWindow.body.setCancelHandler(handler); + menuWindow->body.setCancelHandler(handler); } void deleteLater() @@ -181,9 +169,9 @@ class Menu : public Window { void setToolbar(Window * window) { toolbar = window; - menuWindow.setLeft(toolbar->right()); - menuWindow.setTop(toolbar->top()); - menuWindow.setHeight(toolbar->height()); + menuWindow->setLeft(toolbar->right()); + menuWindow->setTop(toolbar->top()); + menuWindow->setHeight(toolbar->height()); } void setTitle(const std::string text); @@ -196,7 +184,7 @@ class Menu : public Window { inline void select(int index) { - menuWindow.body.select(index); + menuWindow->body.select(index); } #if defined(HARDWARE_KEYS) @@ -216,7 +204,7 @@ class Menu : public Window { protected: Window * previousFocus = nullptr; - MenuWindow menuWindow; + MenuWindow * menuWindow; Window * toolbar = nullptr; void updatePosition(); }; diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index 18241e250..c06d2b868 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -23,7 +23,8 @@ #include "bitmapbuffer.h" #include "checkbox.h" -class PageHeader; +class Menu; +class MenuWindow; class ChoiceBase; enum IconState { @@ -51,4 +52,6 @@ class Theme { extern Theme * theme; +MenuWindow * createMenuWindow(Menu * menu); + #endif // _THEME_H_ From 41fba5e4974f004f9ef6c7d92bf42856b41869ec Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 5 Nov 2019 18:26:28 +0100 Subject: [PATCH 052/714] MinGW compilation --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index ab3a6e324..f1a7a26ae 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -75,7 +75,7 @@ #define GET_BLUE(color) \ (((color) & 0x001F) << 3) -#ifndef __GNUC__ +#if defined(__MINGW32__) || !defined(__GNUC__) #include // remove windows default definitions #undef OPAQUE From 9f4cadf17fac6e5ee302afea4ed3d3c8b4734d26 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 8 Nov 2019 19:27:20 +0100 Subject: [PATCH 053/714] Cosmetics --- radio/src/thirdparty/libopenui/src/dialog.cpp | 2 +- radio/src/thirdparty/libopenui/src/dialog.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 26b540477..751f7b285 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -20,7 +20,7 @@ #include "dialog.h" #include "mainwindow.h" -Dialog::Dialog(std::string title, const rect_t rect): +Dialog::Dialog(std::string title, const rect_t & rect): FormGroup(&mainWindow, rect, OPAQUE), title(std::move(title)), previousFocus(focusWindow) diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 6807705a4..0827f21f5 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -24,7 +24,7 @@ class Dialog: public FormGroup { public: - Dialog(std::string title, const rect_t rect); + Dialog(std::string title, const rect_t & rect); void paint(BitmapBuffer * dc) override; From 8dcc23c2059ae537bc3143f98eda3eda8bb493a0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 10 Nov 2019 08:42:05 +0100 Subject: [PATCH 054/714] fixed compiler warnings (clang) --- radio/src/thirdparty/libopenui/src/button.h | 2 +- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 13 +++++++------ radio/src/thirdparty/libopenui/src/mainwindow.h | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 04ca3a8c8..a1bf1f0ee 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -88,7 +88,7 @@ class Button : public FormField { } #if defined(HARDWARE_KEYS) - void onEvent(event_t event); + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 48aec258b..69ce7d136 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -39,7 +39,7 @@ void MainWindow::emptyTrash() trash.clear(); } -void MainWindow::checkEvents(bool trash) +void MainWindow::checkEvents() { #if defined(HARDWARE_TOUCH) if (touchState.event == TE_DOWN) { @@ -60,10 +60,6 @@ void MainWindow::checkEvents(bool trash) #endif Window::checkEvents(); - - if (trash) { - emptyTrash(); - } } void MainWindow::invalidate(const rect_t & rect) @@ -106,7 +102,12 @@ bool MainWindow::refresh() void MainWindow::run(bool trash) { - checkEvents(trash); + checkEvents(); + + if (trash) { + emptyTrash(); + } + if (refresh()) { lcdRefresh(); } diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index fe6cd90ba..30b40ae49 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -39,7 +39,7 @@ class MainWindow: public Window { } #endif - void checkEvents(bool trash = true); + void checkEvents() override; void invalidate() { @@ -60,4 +60,4 @@ class MainWindow: public Window { extern MainWindow mainWindow; -#endif // _MAINWINDOW_H_ \ No newline at end of file +#endif // _MAINWINDOW_H_ From 6d2cbf71b65fcb9bb156d9204e0051c3277c15db Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 28 Nov 2019 17:03:56 +0100 Subject: [PATCH 055/714] New Table widget --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + .../libopenui/src/libopenui_defines.h | 5 +- .../libopenui/src/libopenui_file.cpp | 1 - radio/src/thirdparty/libopenui/src/static.h | 17 +- radio/src/thirdparty/libopenui/src/table.cpp | 50 ++++++ radio/src/thirdparty/libopenui/src/table.h | 157 ++++++++++++++++++ radio/src/thirdparty/libopenui/src/window.cpp | 20 +-- 7 files changed, 233 insertions(+), 18 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/table.cpp create mode 100644 radio/src/thirdparty/libopenui/src/table.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index e6c7c273f..f3b9ef290 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -29,6 +29,7 @@ set(LIBOPENUI_SRC textedit.cpp coloredit.cpp progress.cpp + table.cpp dialog.cpp message_dialog.cpp ) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index f1a7a26ae..e6b4aba39 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -83,7 +83,7 @@ #endif #define OPACITY_MAX 0x0F -#define OPACITY(x) ((x)<<24) +#define OPACITY(value) ((value) << 24) #define RGB(r, g, b) (uint16_t)((((r) & 0xF8) << 8) + (((g) & 0xFC) << 3) + (((b) & 0xF8) >> 3)) #define ARGB(a, r, g, b) (uint16_t)((((a) & 0xF0) << 8) + (((r) & 0xF0) << 4) + (((g) & 0xF0) << 0) + (((b) & 0xF0) >> 4)) @@ -101,9 +101,12 @@ #define SCROLLBAR_COLOR COLOR(SCROLLBAR_COLOR_INDEX) #define MENU_COLOR COLOR(MENU_COLOR_INDEX) #define MENU_BGCOLOR COLOR(MENU_BGCOLOR_INDEX) +#define MENU_TITLE_BGCOLOR COLOR(MENU_TITLE_BGCOLOR_INDEX) #define MENU_LINE_COLOR COLOR(MENU_LINE_COLOR_INDEX) #define MENU_HIGHLIGHT_COLOR COLOR(MENU_HIGHLIGHT_COLOR_INDEX) #define MENU_HIGHLIGHT_BGCOLOR COLOR(MENU_HIGHLIGHT_BGCOLOR_INDEX) +#define TABLE_BGCOLOR COLOR(TABLE_BGCOLOR_INDEX) +#define TABLE_HEADER_BGCOLOR COLOR(TABLE_HEADER_BGCOLOR_INDEX) #define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) #endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp index 83bfa1caa..1cdb76061 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp @@ -107,4 +107,3 @@ FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime) firstTime = false; return res; } - diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 6e5e1a196..46ae5e06d 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -67,6 +67,11 @@ class Subtitle: public StaticText { class StaticBitmap: public Window { public: + StaticBitmap(Window * parent, const rect_t & rect): + Window(parent, rect) + { + } + StaticBitmap(Window * parent, const rect_t & rect, const char * filename): Window(parent, rect), bitmap(BitmapBuffer::loadBitmap(filename)) @@ -79,6 +84,12 @@ class StaticBitmap: public Window { { } + void setBitmap(const char * filename) + { + bitmap = BitmapBuffer::loadBitmap(filename); + invalidate(); + } + #if defined(DEBUG_WINDOWS) std::string getName() override { @@ -88,11 +99,13 @@ class StaticBitmap: public Window { void paint(BitmapBuffer * dc) override { - dc->drawBitmap(0, 0, bitmap); + if (bitmap) { + dc->drawBitmap((width() - bitmap->width()) / 2, (height() - bitmap->height()) / 2, bitmap); + } } protected: - const BitmapBuffer * bitmap; + const BitmapBuffer * bitmap = nullptr; }; #endif diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp new file mode 100644 index 000000000..db8d5cf7d --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "table.h" +#include "font.h" + +void Table::Header::paint(BitmapBuffer * dc) +{ + coord_t x = 10; + if (!header.values.empty()) { + dc->clear(TABLE_HEADER_BGCOLOR); + for (auto &value: header.values) { + dc->drawText(x, (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2, value.c_str(), TABLE_HEADER_FONT); + x += 200; + } + } +} + +void Table::Body::paint(BitmapBuffer * dc) +{ + coord_t y = 0; + coord_t x; + int index = 0; + for (auto &line: lines) { + dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, index == selection ? FOCUS_BGCOLOR : TABLE_BGCOLOR); + x = 10; + for (auto &value: line.values) { + dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, value.c_str(), TABLE_BODY_FONT | (index == selection ? FOCUS_COLOR : DEFAULT_COLOR)); + x += 200; + } + y += TABLE_LINE_HEIGHT; + index += 1; + } +} diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h new file mode 100644 index 000000000..87c6c16a6 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _TABLE_H_ +#define _TABLE_H_ + +#include "window.h" +#include + +constexpr coord_t TABLE_LINE_HEIGHT = 50; +constexpr coord_t TABLE_HEADER_HEIGHT = 48; + +class Table: public Window { + + class Line { + public: + std::list values; + std::function onPress; + }; + + class Header: public Window { + public: + Header(Window * parent, const rect_t & rect): + Window(parent, rect, OPAQUE) + { + } + + void setLine(const Line & line) + { + header = line; + } + + void paint(BitmapBuffer * dc) override; + + protected: + Line header; + }; + + class Body: public Window { + friend class Table; + + public: + Body(Window * parent, const rect_t & rect): + Window(parent, rect, OPAQUE) + { + } + + void addLine(const Line & line) + { + lines.push_back(line); + setInnerHeight(lines.size() * TABLE_LINE_HEIGHT - 2); + } + + void clear() + { + lines.clear(); + } + + void paint(BitmapBuffer * dc) override; + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) + { + unsigned index = y / TABLE_LINE_HEIGHT; + auto onPress = lines[index].onPress; + if (onPress) + onPress(); + return true; + } +#endif + + protected: + std::vector lines; + int selection = -1; + }; + + public: + Table(Window * parent, const rect_t & rect, uint8_t columnsCount): + Window(parent, rect), + header(this, {0, 0, width(), TABLE_HEADER_HEIGHT}), + body(this, {0, TABLE_HEADER_HEIGHT, width(), height() - TABLE_HEADER_HEIGHT}) + { + setColumnsCount(columnsCount); + } + + ~Table() override + { + header.detach(); + body.detach(); + } + + void setColumnsCount(uint8_t value) + { + count = value; + } + + int getSelection() const + { + return body.selection; + } + + void setSelection(int index, bool scroll = false) + { + body.selection = index; + if (scroll) { + body.setScrollPositionY(index * TABLE_LINE_HEIGHT); + } + body.invalidate(); + } + + void setHeader(const char * values[]) + { + Line line; + for (uint8_t i = 0; i < count; i++) { + line.values.push_back(values[i]); + } + header.setLine(line); + } + + void addLine(const char * values[], std::function onPress = nullptr) + { + Line line; + for (uint8_t i = 0; i < count; i++) { + line.values.push_back(values[i]); + } + line.onPress = onPress; + body.addLine(line); + } + + void clear() + { + body.clear(); + } + + protected: + uint8_t count = 1; + Header header; + Body body; +}; + +#endif // _TABLE_H_ diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 9c151f722..1accc4896 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -405,20 +405,12 @@ void Window::invalidate(const rect_t & rect) void Window::drawVerticalScrollbar(BitmapBuffer * dc) { if (innerHeight > rect.h) { - coord_t x = rect.w - 3; - coord_t y = scrollPositionY + 3; - coord_t h = rect.h - 6; - // lcd->drawSolidFilledRect(x, y, 1, h, LINE_COLOR); - coord_t yofs = (h*scrollPositionY + innerHeight/2) / innerHeight; - coord_t yhgt = (h*rect.h + innerHeight/2) / innerHeight; - if (yhgt + yofs > h) - yhgt = h - yofs; - if (yhgt < 15) { + coord_t yofs = divRoundClosest(rect.h * scrollPositionY, innerHeight); + coord_t yhgt = divRoundClosest(rect.h * rect.h, innerHeight); + if (yhgt < 15) yhgt = 15; - if (y + yofs + yhgt > innerHeight) { - yofs = innerHeight - y - yhgt; - } - } - dc->drawSolidFilledRect(x-1, y + yofs, 3, yhgt, SCROLLBAR_COLOR); + if (yhgt + yofs > rect.h) + yhgt = rect.h - yofs; + dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, SCROLLBAR_COLOR); } } From e4202c6ef4fb7c20d048db75d75788536f4fba3d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 28 Nov 2019 17:20:47 +0100 Subject: [PATCH 056/714] Table updates --- radio/src/thirdparty/libopenui/src/table.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 87c6c16a6..8ba7452f8 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -22,12 +22,9 @@ #include "window.h" #include - -constexpr coord_t TABLE_LINE_HEIGHT = 50; -constexpr coord_t TABLE_HEADER_HEIGHT = 48; +#include "libopenui_config.h" class Table: public Window { - class Line { public: std::list values; From c96a31026a4ba94e29be403dd46833fb2b3719a4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 28 Nov 2019 17:24:21 +0100 Subject: [PATCH 057/714] Menu updates --- radio/src/thirdparty/libopenui/src/menu.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 3c55faaa4..a122c5119 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -107,12 +107,6 @@ void MenuWindow::paint(BitmapBuffer * dc) dc->drawText(MENUS_WIDTH / 2, (MENUS_HEADER_HEIGHT - getFontHeight(FONT(BOLD))) / 2, title.c_str(), CENTERED | FONT(BOLD)); dc->drawSolidHorizontalLine(0, MENUS_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); } - - // the cancel button - if (MENUS_FOOTER_HEIGHT > 0) { - dc->drawText(MENUS_WIDTH / 2, height() - MENUS_FOOTER_HEIGHT + (MENUS_FOOTER_HEIGHT - getFontHeight(FONT(BOLD))) / 2, "Cancel", CENTERED); - dc->drawSolidHorizontalLine(0, height() - MENUS_FOOTER_HEIGHT, MENUS_WIDTH, MENU_LINE_COLOR); - } } Menu::Menu(): @@ -130,8 +124,8 @@ void Menu::updatePosition() // there is no navigation bar at the left, we may center the window on screen auto headerHeight = menuWindow->title.empty() ? 0 : MENUS_HEADER_HEIGHT; auto bodyHeight = limit(MENUS_MIN_HEIGHT, menuWindow->body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); - menuWindow->setTop((LCD_H - headerHeight - bodyHeight - MENUS_FOOTER_HEIGHT) / 2 + MENUS_OFFSET_TOP); - menuWindow->setHeight(headerHeight + bodyHeight + MENUS_FOOTER_HEIGHT); + menuWindow->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); + menuWindow->setHeight(headerHeight + bodyHeight); menuWindow->body.setTop(headerHeight); menuWindow->body.setHeight(bodyHeight); } From 6f9a22e681cb950b880969bb451fb1b36784de12 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 29 Nov 2019 09:04:06 +0100 Subject: [PATCH 058/714] Menu overlay --- radio/src/thirdparty/libopenui/src/menu.cpp | 13 ++++++++----- radio/src/thirdparty/libopenui/src/menu.h | 6 ++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index a122c5119..9f3d426af 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -23,7 +23,7 @@ MenuWindow::MenuWindow(Menu * parent): Window(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}, OPAQUE), - body(this, {0, 0, width(), height()}, OPAQUE) + body(this, {0, 0, width(), height()}) { body.setFocus(); } @@ -74,8 +74,6 @@ bool MenuBody::onTouchEnd(coord_t x, coord_t y) void MenuBody::paint(BitmapBuffer * dc) { - dc->clear(MENU_BGCOLOR); - for (auto i = 0; i < (int)lines.size(); i++) { auto & line = lines[i]; LcdFlags flags = MENU_COLOR; @@ -104,13 +102,13 @@ void MenuWindow::paint(BitmapBuffer * dc) // the title if (!title.empty()) { - dc->drawText(MENUS_WIDTH / 2, (MENUS_HEADER_HEIGHT - getFontHeight(FONT(BOLD))) / 2, title.c_str(), CENTERED | FONT(BOLD)); + dc->drawText(MENUS_WIDTH / 2, (MENUS_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); dc->drawSolidHorizontalLine(0, MENUS_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); } } Menu::Menu(): - Window(&mainWindow, {0, 0, LCD_W, LCD_H}, TRANSPARENT), + Window(&mainWindow, {0, 0, LCD_W, LCD_H}), #if !defined(HARDWARE_TOUCH) previousFocus(focusWindow), #endif @@ -156,6 +154,11 @@ void Menu::removeLines() updatePosition(); } +void Menu::paint(BitmapBuffer * dc) +{ + dc->drawFilledRect(0, 0, width(), height(), SOLID, OVERLAY_COLOR | OPACITY(5)); +} + #if defined(HARDWARE_KEYS) void Menu::onEvent(event_t event) { diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index d41c5981b..0affe1363 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -59,8 +59,8 @@ class MenuBody: public Window { }; public: - MenuBody(Window * parent, const rect_t & rect, WindowFlags flags): - Window(parent, rect, flags) + MenuBody(Window * parent, const rect_t & rect): + Window(parent, rect) { setPageHeight(MENUS_LINE_HEIGHT); } @@ -187,6 +187,8 @@ class Menu: public Window { menuWindow->body.select(index); } + void paint(BitmapBuffer * dc) override; + #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; #endif From 96e6a932b681223c276fe6a2ebbc2332ea8a070c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 29 Nov 2019 09:09:29 +0100 Subject: [PATCH 059/714] Menu overlay --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index e6b4aba39..c3029b910 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -105,6 +105,7 @@ #define MENU_LINE_COLOR COLOR(MENU_LINE_COLOR_INDEX) #define MENU_HIGHLIGHT_COLOR COLOR(MENU_HIGHLIGHT_COLOR_INDEX) #define MENU_HIGHLIGHT_BGCOLOR COLOR(MENU_HIGHLIGHT_BGCOLOR_INDEX) +#define OVERLAY_COLOR COLOR(OVERLAY_COLOR_INDEX) #define TABLE_BGCOLOR COLOR(TABLE_BGCOLOR_INDEX) #define TABLE_HEADER_BGCOLOR COLOR(TABLE_HEADER_BGCOLOR_INDEX) #define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) From 1311885c3b00c169fd7870af037360b09ae4c880 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 29 Nov 2019 09:13:33 +0100 Subject: [PATCH 060/714] Menu different font for body and header --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 9f3d426af..ca8c91553 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -76,7 +76,7 @@ void MenuBody::paint(BitmapBuffer * dc) { for (auto i = 0; i < (int)lines.size(); i++) { auto & line = lines[i]; - LcdFlags flags = MENU_COLOR; + LcdFlags flags = MENU_COLOR | MENU_FONT; if (selectedIndex == i) { flags = MENU_HIGHLIGHT_COLOR; if (MENU_HIGHLIGHT_BGCOLOR != MENU_BGCOLOR) From 5de2d9c03e426f97505fd56e71ae5228e5ec7125 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 29 Nov 2019 14:18:29 +0100 Subject: [PATCH 061/714] ModalWindow layer added --- radio/src/thirdparty/libopenui/src/menu.cpp | 26 +----- radio/src/thirdparty/libopenui/src/menu.h | 28 +----- .../thirdparty/libopenui/src/modal_window.h | 86 +++++++++++++++++++ 3 files changed, 89 insertions(+), 51 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/modal_window.h diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index ca8c91553..a5629a1ed 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -108,10 +108,7 @@ void MenuWindow::paint(BitmapBuffer * dc) } Menu::Menu(): - Window(&mainWindow, {0, 0, LCD_W, LCD_H}), -#if !defined(HARDWARE_TOUCH) - previousFocus(focusWindow), -#endif + ModalWindow(), menuWindow(createMenuWindow(this)) { } @@ -154,11 +151,6 @@ void Menu::removeLines() updatePosition(); } -void Menu::paint(BitmapBuffer * dc) -{ - dc->drawFilledRect(0, 0, width(), height(), SOLID, OVERLAY_COLOR | OPACITY(5)); -} - #if defined(HARDWARE_KEYS) void Menu::onEvent(event_t event) { @@ -170,19 +162,3 @@ void Menu::onEvent(event_t event) } } #endif - -#if defined(HARDWARE_TOUCH) -bool Menu::onTouchEnd(coord_t x, coord_t y) -{ - if (!Window::onTouchEnd(x, y)) { - deleteLater(); - } - return true; -} - -bool Menu::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) -{ - Window::onTouchSlide(x, y, startX, startY, slideX, slideY); - return true; -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 0affe1363..278d0bb69 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -23,7 +23,7 @@ #include #include #include -#include "mainwindow.h" +#include "modal_window.h" class Menu; class MenuWindow; @@ -140,7 +140,7 @@ class MenuWindow: public Window { std::string title; }; -class Menu: public Window { +class Menu: public ModalWindow { public: Menu(); @@ -149,16 +149,6 @@ class Menu: public Window { menuWindow->body.setCancelHandler(handler); } - void deleteLater() - { -#if !defined(HARDWARE_TOUCH) - if (previousFocus) { - previousFocus->setFocus(); - } -#endif - Window::deleteLater(); - } - #if defined(DEBUG_WINDOWS) std::string getName() override { @@ -187,25 +177,11 @@ class Menu: public Window { menuWindow->body.select(index); } - void paint(BitmapBuffer * dc) override; - #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; #endif -#if defined(HARDWARE_TOUCH) - bool onTouchStart(coord_t x, coord_t y) override - { - return true; - } - - bool onTouchEnd(coord_t x, coord_t y) override; - - bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; -#endif - protected: - Window * previousFocus = nullptr; MenuWindow * menuWindow; Window * toolbar = nullptr; void updatePosition(); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h new file mode 100644 index 000000000..b04d12aaf --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MODAL_WINDOW_H_ +#define _MODAL_WINDOW_H_ + +#include +#include +#include +#include "mainwindow.h" + +class ModalWindow: public Window +{ + public: + ModalWindow(): + Window(&mainWindow, {0, 0, LCD_W, LCD_H}) +#if !defined(HARDWARE_TOUCH) + , previousFocus(focusWindow) +#endif + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ModalWindow"; + } +#endif + + void deleteLater() + { +#if !defined(HARDWARE_TOUCH) + if (previousFocus) { + previousFocus->setFocus(); + } +#endif + Window::deleteLater(); + } + + void paint(BitmapBuffer * dc) override + { + dc->drawFilledRect(0, 0, width(), height(), SOLID, OVERLAY_COLOR | OPACITY(5)); + } + +#if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override + { + return true; + } + + bool onTouchEnd(coord_t x, coord_t y) + { + if (!Window::onTouchEnd(x, y)) { + deleteLater(); + } + return true; + } + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) + { + Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + return true; + } +#endif + + protected: + Window * previousFocus = nullptr; +}; + +#endif // _MODAL_WINDOW_H_ From 72229f40c377d3cf047479ebdd3576623e639823 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 29 Nov 2019 17:17:28 +0100 Subject: [PATCH 062/714] ModalWindow layer used in Dialog --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + radio/src/thirdparty/libopenui/src/dialog.cpp | 26 ++------- radio/src/thirdparty/libopenui/src/dialog.h | 17 +++--- radio/src/thirdparty/libopenui/src/form.cpp | 6 +-- radio/src/thirdparty/libopenui/src/form.h | 4 +- .../src/thirdparty/libopenui/src/gridlayout.h | 2 +- radio/src/thirdparty/libopenui/src/menu.cpp | 40 +++++++------- radio/src/thirdparty/libopenui/src/menu.h | 30 +++++------ .../thirdparty/libopenui/src/modal_window.cpp | 53 +++++++++++++++++++ .../thirdparty/libopenui/src/modal_window.h | 45 ++++++++++------ radio/src/thirdparty/libopenui/src/theme.h | 4 +- radio/src/thirdparty/libopenui/src/window.h | 15 +++--- 12 files changed, 147 insertions(+), 96 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/modal_window.cpp diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index f3b9ef290..082eb2f3f 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -30,6 +30,7 @@ set(LIBOPENUI_SRC coloredit.cpp progress.cpp table.cpp + modal_window.cpp dialog.cpp message_dialog.cpp ) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 751f7b285..46b863579 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -21,27 +21,11 @@ #include "mainwindow.h" Dialog::Dialog(std::string title, const rect_t & rect): - FormGroup(&mainWindow, rect, OPAQUE), - title(std::move(title)), - previousFocus(focusWindow) + ModalWindow(), + content(this, rect), + form(&content, {0, 0, rect.w, rect.h}, FORM_NO_BORDER) { bringToTop(); - setFocus(); -} - -void Dialog::paint(BitmapBuffer * dc) -{ - dc->drawRect(0, 0, width() - 1, height() - 1, 1, SOLID, DEFAULT_COLOR); - dc->drawSolidFilledRect(1, 1, width() - 3, PAGE_LINE_HEIGHT, FOCUS_BGCOLOR); - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), FOCUS_COLOR); - dc->drawSolidFilledRect(1, PAGE_LINE_HEIGHT + 1, width() - 3, height() - PAGE_LINE_HEIGHT - 3, DEFAULT_BGCOLOR); -} - -void Dialog::deleteLater() -{ - if (previousFocus) { - previousFocus->setFocus(); - } - - Window::deleteLater(); + content.setTitle(title); + form.setFocus(); } diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 0827f21f5..7ab4f8a83 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -20,19 +20,22 @@ #ifndef _DIALOG_H_ #define _DIALOG_H_ +#include "modal_window.h" #include "form.h" -class Dialog: public FormGroup { +class Dialog: public ModalWindow { public: Dialog(std::string title, const rect_t & rect); - void paint(BitmapBuffer * dc) override; - - void deleteLater(); + ~Dialog() override + { + form.detach(); + content.detach(); + } protected: - std::string title; - Window * previousFocus; + ModalWindowContent content; + FormGroup form; }; -#endif // _CONFIRMATION_H_ +#endif // _DIALOG_H_ diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index ffdd5b744..91ccdf40b 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -69,7 +69,7 @@ void FormField::paint(BitmapBuffer * dc) else if (hasFocus()) { dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } - else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); } } @@ -155,11 +155,11 @@ void FormGroup::onEvent(event_t event) void FormGroup::paint(BitmapBuffer * dc) { - if (!(windowFlags & FORM_FORWARD_FOCUS)) { + if (!(windowFlags & (FORM_NO_BORDER | FORM_FORWARD_FOCUS))) { if (!editMode && hasFocus()) { dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } - else if (!(windowFlags & BORDER_FOCUS_ONLY)) { + else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); } } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 7afd60ba1..ec37a6d64 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -24,7 +24,9 @@ constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 1; constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 2; -constexpr WindowFlags FORM_FLAGS_LAST = FORM_DETACHED; +constexpr WindowFlags FORM_BORDER_FOCUS_ONLY = WINDOW_FLAGS_LAST << 3; +constexpr WindowFlags FORM_NO_BORDER = WINDOW_FLAGS_LAST << 4; +constexpr WindowFlags FORM_FLAGS_LAST = FORM_NO_BORDER; class FormField: public Window { public: diff --git a/radio/src/thirdparty/libopenui/src/gridlayout.h b/radio/src/thirdparty/libopenui/src/gridlayout.h index 9134b7c72..9d3b9111a 100644 --- a/radio/src/thirdparty/libopenui/src/gridlayout.h +++ b/radio/src/thirdparty/libopenui/src/gridlayout.h @@ -117,7 +117,7 @@ class FormGridLayout: public GridLayout { protected: coord_t labelWidth = PAGE_LABEL_WIDTH; coord_t lineMarginLeft = PAGE_PADDING; - coord_t lineMarginRight = 10; + coord_t lineMarginRight = PAGE_PADDING; }; #endif diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index a5629a1ed..84dc6412f 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,13 +21,6 @@ #include "font.h" #include "theme.h" -MenuWindow::MenuWindow(Menu * parent): - Window(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}, OPAQUE), - body(this, {0, 0, width(), height()}) -{ - body.setFocus(); -} - void MenuBody::select(int index) { selectedIndex = index; @@ -95,7 +88,14 @@ void MenuBody::paint(BitmapBuffer * dc) } } -void MenuWindow::paint(BitmapBuffer * dc) +MenuWindowContent::MenuWindowContent(Menu * parent): + ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), + body(this, {0, 0, width(), height()}) +{ + body.setFocus(); +} + +void MenuWindowContent::paint(BitmapBuffer * dc) { // the background dc->clear(MENU_BGCOLOR); @@ -109,7 +109,7 @@ void MenuWindow::paint(BitmapBuffer * dc) Menu::Menu(): ModalWindow(), - menuWindow(createMenuWindow(this)) + content(createMenuWindow(this)) { } @@ -117,37 +117,37 @@ void Menu::updatePosition() { if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen - auto headerHeight = menuWindow->title.empty() ? 0 : MENUS_HEADER_HEIGHT; - auto bodyHeight = limit(MENUS_MIN_HEIGHT, menuWindow->body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); - menuWindow->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); - menuWindow->setHeight(headerHeight + bodyHeight); - menuWindow->body.setTop(headerHeight); - menuWindow->body.setHeight(bodyHeight); + auto headerHeight = content->title.empty() ? 0 : MENUS_HEADER_HEIGHT; + auto bodyHeight = limit(MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); + content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); + content->setHeight(headerHeight + bodyHeight); + content->body.setTop(headerHeight); + content->body.setHeight(bodyHeight); } - menuWindow->body.setInnerHeight(menuWindow->body.lines.size() * MENUS_LINE_HEIGHT - 1); + content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT - 1); } void Menu::setTitle(const std::string text) { - menuWindow->setTitle(text); + content->setTitle(text); updatePosition(); } void Menu::addLine(const std::string & text, std::function onPress) { - menuWindow->body.addLine(text, std::move(onPress)); + content->body.addLine(text, std::move(onPress)); updatePosition(); } void Menu::addCustomLine(std::function drawLine, std::function onPress) { - menuWindow->body.addCustomLine(drawLine, std::move(onPress)); + content->body.addCustomLine(drawLine, std::move(onPress)); updatePosition(); } void Menu::removeLines() { - menuWindow->body.removeLines(); + content->body.removeLines(); updatePosition(); } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 278d0bb69..b7cdac108 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -26,10 +26,10 @@ #include "modal_window.h" class Menu; -class MenuWindow; +class MenuWindowContent; class MenuBody: public Window { - friend class MenuWindow; + friend class MenuWindowContent; friend class Menu; class MenuLine { @@ -110,13 +110,13 @@ class MenuBody: public Window { std::function onCancel; }; -class MenuWindow: public Window { +class MenuWindowContent: public ModalWindowContent { friend class Menu; public: - explicit MenuWindow(Menu * parent); + explicit MenuWindowContent(Menu * parent); - ~MenuWindow() override + ~MenuWindowContent() override { body.detach(); } @@ -124,20 +124,14 @@ class MenuWindow: public Window { #if defined(DEBUG_WINDOWS) std::string getName() override { - return "MenuWindow"; + return "MenuWindow::"; } #endif - void setTitle(const std::string text) - { - title = std::move(text); - } - void paint(BitmapBuffer * dc) override; protected: MenuBody body; - std::string title; }; class Menu: public ModalWindow { @@ -146,7 +140,7 @@ class Menu: public ModalWindow { void setCancelHandler(std::function handler) { - menuWindow->body.setCancelHandler(handler); + content->body.setCancelHandler(handler); } #if defined(DEBUG_WINDOWS) @@ -159,9 +153,9 @@ class Menu: public ModalWindow { void setToolbar(Window * window) { toolbar = window; - menuWindow->setLeft(toolbar->right()); - menuWindow->setTop(toolbar->top()); - menuWindow->setHeight(toolbar->height()); + content->setLeft(toolbar->right()); + content->setTop(toolbar->top()); + content->setHeight(toolbar->height()); } void setTitle(const std::string text); @@ -174,7 +168,7 @@ class Menu: public ModalWindow { inline void select(int index) { - menuWindow->body.select(index); + content->body.select(index); } #if defined(HARDWARE_KEYS) @@ -182,7 +176,7 @@ class Menu: public ModalWindow { #endif protected: - MenuWindow * menuWindow; + MenuWindowContent * content; Window * toolbar = nullptr; void updatePosition(); }; diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp new file mode 100644 index 000000000..09c54e210 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + + +#include "modal_window.h" +#include "mainwindow.h" +#include "font.h" + +ModalWindow::ModalWindow(): + Window(&mainWindow, {0, 0, LCD_W, LCD_H}) +#if !defined(HARDWARE_TOUCH) + , previousFocus(focusWindow) +#endif +{ +} + +void ModalWindow::paint(BitmapBuffer * dc) +{ + dc->drawFilledRect(0, 0, width(), height(), SOLID, OVERLAY_COLOR | OPACITY(5)); +} + +void ModalWindowContent::paint(BitmapBuffer * dc) +{ + dc->drawSolidFilledRect(0, 0, width(), PAGE_LINE_HEIGHT, FOCUS_BGCOLOR); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), FOCUS_COLOR); + dc->drawSolidFilledRect(0, PAGE_LINE_HEIGHT, width(), height() - PAGE_LINE_HEIGHT, DEFAULT_BGCOLOR); + +/* + // the background + dc->clear(MENU_BGCOLOR); + + // the title + if (!title.empty()) { + dc->drawText(MENUS_WIDTH / 2, (MENUS_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); + dc->drawSolidHorizontalLine(0, MENUS_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); + }*/ +} diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index b04d12aaf..9faa6143c 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -20,21 +20,12 @@ #ifndef _MODAL_WINDOW_H_ #define _MODAL_WINDOW_H_ -#include -#include -#include -#include "mainwindow.h" +#include "window.h" class ModalWindow: public Window { public: - ModalWindow(): - Window(&mainWindow, {0, 0, LCD_W, LCD_H}) -#if !defined(HARDWARE_TOUCH) - , previousFocus(focusWindow) -#endif - { - } + ModalWindow(); #if defined(DEBUG_WINDOWS) std::string getName() override @@ -53,10 +44,7 @@ class ModalWindow: public Window Window::deleteLater(); } - void paint(BitmapBuffer * dc) override - { - dc->drawFilledRect(0, 0, width(), height(), SOLID, OVERLAY_COLOR | OPACITY(5)); - } + void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_TOUCH) bool onTouchStart(coord_t x, coord_t y) override @@ -80,7 +68,34 @@ class ModalWindow: public Window #endif protected: +#if !defined(HARDWARE_TOUCH) Window * previousFocus = nullptr; +#endif +}; + +class ModalWindowContent: public Window { + public: + explicit ModalWindowContent(ModalWindow * parent, const rect_t & rect): + Window(parent, rect, OPAQUE) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ModalWindowContent"; + } +#endif + + void setTitle(std::string text) + { + title = std::move(text); + } + + void paint(BitmapBuffer * dc) override; + + protected: + std::string title; }; #endif // _MODAL_WINDOW_H_ diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index c06d2b868..4dafc524c 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -24,7 +24,7 @@ #include "checkbox.h" class Menu; -class MenuWindow; +class MenuWindowContent; class ChoiceBase; enum IconState { @@ -52,6 +52,6 @@ class Theme { extern Theme * theme; -MenuWindow * createMenuWindow(Menu * menu); +MenuWindowContent * createMenuWindow(Menu * menu); #endif // _THEME_H_ diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 9bf4b000b..b1b6e892f 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -38,14 +38,13 @@ typedef uint32_t WindowFlags; #undef TRANSPARENT #endif -constexpr WindowFlags OPAQUE = 1 << 0; -constexpr WindowFlags TRANSPARENT = 1 << 1; -constexpr WindowFlags NO_SCROLLBAR = 1 << 2; -constexpr WindowFlags NO_FOCUS = 1 << 3; -constexpr WindowFlags FORWARD_SCROLL = 1 << 4; -constexpr WindowFlags REFRESH_ALWAYS = 1 << 5; -constexpr WindowFlags BORDER_FOCUS_ONLY = 1 << 6; -constexpr WindowFlags PAINT_CHILDREN_FIRST = 1 << 7; +constexpr WindowFlags OPAQUE = 1u << 0; +constexpr WindowFlags TRANSPARENT = 1u << 1; +constexpr WindowFlags NO_SCROLLBAR = 1u << 2; +constexpr WindowFlags NO_FOCUS = 1u << 3; +constexpr WindowFlags FORWARD_SCROLL = 1u << 4; +constexpr WindowFlags REFRESH_ALWAYS = 1u << 5; +constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6; constexpr WindowFlags WINDOW_FLAGS_LAST = PAINT_CHILDREN_FIRST; enum SetFocusFlag { From ce725d09f45c7a2e39acd0e40304382f383f311e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 29 Nov 2019 17:58:32 +0100 Subject: [PATCH 063/714] Dialog now themeable --- radio/src/thirdparty/libopenui/src/dialog.cpp | 7 ++++--- radio/src/thirdparty/libopenui/src/dialog.h | 10 ++++++++-- radio/src/thirdparty/libopenui/src/menu.cpp | 6 +++--- radio/src/thirdparty/libopenui/src/menu.h | 2 +- radio/src/thirdparty/libopenui/src/modal_window.cpp | 10 +++++----- radio/src/thirdparty/libopenui/src/theme.h | 3 +++ 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 46b863579..3df9c439d 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -19,13 +19,14 @@ #include "dialog.h" #include "mainwindow.h" +#include "theme.h" Dialog::Dialog(std::string title, const rect_t & rect): ModalWindow(), - content(this, rect), - form(&content, {0, 0, rect.w, rect.h}, FORM_NO_BORDER) + content(createDialogWindow(this, rect)), + form(content, {0, POPUP_HEADER_HEIGHT, rect.w, coord_t(rect.h - POPUP_HEADER_HEIGHT)}, FORM_NO_BORDER) { bringToTop(); - content.setTitle(title); + content->setTitle(title); form.setFocus(); } diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 7ab4f8a83..9426e9bb6 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -30,11 +30,17 @@ class Dialog: public ModalWindow { ~Dialog() override { form.detach(); - content.detach(); } +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Dialog"; + } +#endif + protected: - ModalWindowContent content; + ModalWindowContent * content; FormGroup form; }; diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 84dc6412f..394fdd626 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -102,8 +102,8 @@ void MenuWindowContent::paint(BitmapBuffer * dc) // the title if (!title.empty()) { - dc->drawText(MENUS_WIDTH / 2, (MENUS_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); - dc->drawSolidHorizontalLine(0, MENUS_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); + dc->drawText(MENUS_WIDTH / 2, (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); + dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); } } @@ -117,7 +117,7 @@ void Menu::updatePosition() { if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen - auto headerHeight = content->title.empty() ? 0 : MENUS_HEADER_HEIGHT; + auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; auto bodyHeight = limit(MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); content->setHeight(headerHeight + bodyHeight); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index b7cdac108..dc60199ca 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -181,4 +181,4 @@ class Menu: public ModalWindow { void updatePosition(); }; -#endif +#endif // _MENU_H_ diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 09c54e210..a77c36a92 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -37,9 +37,9 @@ void ModalWindow::paint(BitmapBuffer * dc) void ModalWindowContent::paint(BitmapBuffer * dc) { - dc->drawSolidFilledRect(0, 0, width(), PAGE_LINE_HEIGHT, FOCUS_BGCOLOR); - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, title.c_str(), FOCUS_COLOR); - dc->drawSolidFilledRect(0, PAGE_LINE_HEIGHT, width(), height() - PAGE_LINE_HEIGHT, DEFAULT_BGCOLOR); + dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, FOCUS_BGCOLOR); + dc->drawText(FIELD_PADDING_LEFT, (POPUP_HEADER_HEIGHT - getFontHeight(FONT(STD))) / 2, title.c_str(), FOCUS_COLOR); + dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, width(), height() - POPUP_HEADER_HEIGHT, DEFAULT_BGCOLOR); /* // the background @@ -47,7 +47,7 @@ void ModalWindowContent::paint(BitmapBuffer * dc) // the title if (!title.empty()) { - dc->drawText(MENUS_WIDTH / 2, (MENUS_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); - dc->drawSolidHorizontalLine(0, MENUS_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); + dc->drawText(MENUS_WIDTH / 2, (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); + dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); }*/ } diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index 4dafc524c..c1834d4c7 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -25,6 +25,8 @@ class Menu; class MenuWindowContent; +class Dialog; +class ModalWindowContent; class ChoiceBase; enum IconState { @@ -53,5 +55,6 @@ class Theme { extern Theme * theme; MenuWindowContent * createMenuWindow(Menu * menu); +ModalWindowContent * createDialogWindow(Dialog * dialog, const rect_t & rect); #endif // _THEME_H_ From 4783327dbae506e167f339086c0f7e2b633f6bcd Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 29 Nov 2019 18:22:09 +0100 Subject: [PATCH 064/714] Dialog now themeable --- radio/src/thirdparty/libopenui/src/dialog.cpp | 12 ++++++-- radio/src/thirdparty/libopenui/src/dialog.h | 30 +++++++++++++++---- radio/src/thirdparty/libopenui/src/menu.h | 2 +- radio/src/thirdparty/libopenui/src/theme.h | 4 +-- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 3df9c439d..70b8d3440 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -21,12 +21,18 @@ #include "mainwindow.h" #include "theme.h" +DialogWindowContent::DialogWindowContent(Dialog * parent, const rect_t & rect): + ModalWindowContent(parent, rect), + form(this, {0, POPUP_HEADER_HEIGHT, rect.w, coord_t(rect.h - POPUP_HEADER_HEIGHT)}, FORM_NO_BORDER) +{ + form.setFocus(); +} + + Dialog::Dialog(std::string title, const rect_t & rect): ModalWindow(), - content(createDialogWindow(this, rect)), - form(content, {0, POPUP_HEADER_HEIGHT, rect.w, coord_t(rect.h - POPUP_HEADER_HEIGHT)}, FORM_NO_BORDER) + content(createDialogWindow(this, rect)) { bringToTop(); content->setTitle(title); - form.setFocus(); } diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 9426e9bb6..f4bb790c0 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -23,15 +23,34 @@ #include "modal_window.h" #include "form.h" -class Dialog: public ModalWindow { - public: - Dialog(std::string title, const rect_t & rect); +class Dialog; - ~Dialog() override +class DialogWindowContent: public ModalWindowContent { + friend class Dialog; + + public: + DialogWindowContent(Dialog * parent, const rect_t & rect); + + ~DialogWindowContent() override { form.detach(); } +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "DialogWindowContent"; + } +#endif + + public: + FormGroup form; +}; + +class Dialog: public ModalWindow { + public: + Dialog(std::string title, const rect_t & rect); + #if defined(DEBUG_WINDOWS) std::string getName() override { @@ -40,8 +59,7 @@ class Dialog: public ModalWindow { #endif protected: - ModalWindowContent * content; - FormGroup form; + DialogWindowContent * content; }; #endif // _DIALOG_H_ diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index dc60199ca..2775827ce 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -124,7 +124,7 @@ class MenuWindowContent: public ModalWindowContent { #if defined(DEBUG_WINDOWS) std::string getName() override { - return "MenuWindow::"; + return "MenuWindowContent"; } #endif diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index c1834d4c7..c4e439be9 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -26,7 +26,7 @@ class Menu; class MenuWindowContent; class Dialog; -class ModalWindowContent; +class DialogWindowContent; class ChoiceBase; enum IconState { @@ -55,6 +55,6 @@ class Theme { extern Theme * theme; MenuWindowContent * createMenuWindow(Menu * menu); -ModalWindowContent * createDialogWindow(Dialog * dialog, const rect_t & rect); +DialogWindowContent * createDialogWindow(Dialog * dialog, const rect_t & rect); #endif // _THEME_H_ From d34e749148a3d181ec1cb99d57e4e6aa60658f5b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 29 Nov 2019 18:42:03 +0100 Subject: [PATCH 065/714] ConfirmDialog moved to libopenui --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + .../libopenui/src/confirm_dialog.cpp | 44 +++++++++++++++++++ .../thirdparty/libopenui/src/confirm_dialog.h | 38 ++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/src/confirm_dialog.cpp create mode 100644 radio/src/thirdparty/libopenui/src/confirm_dialog.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 082eb2f3f..bf2756ea5 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -32,6 +32,7 @@ set(LIBOPENUI_SRC table.cpp modal_window.cpp dialog.cpp + confirm_dialog.cpp message_dialog.cpp ) diff --git a/radio/src/thirdparty/libopenui/src/confirm_dialog.cpp b/radio/src/thirdparty/libopenui/src/confirm_dialog.cpp new file mode 100644 index 000000000..87f2f69cd --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/confirm_dialog.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "confirm_dialog.h" +#include "static.h" + +ConfirmDialog::ConfirmDialog(const char * title, const char * message, std::function confirmHandler) : + Dialog(title, {50, 73, LCD_W - 100, LCD_H - 146}), + confirmHandler(std::move(confirmHandler)) +{ + new StaticText(this, {0, height() / 2, width(), PAGE_LINE_HEIGHT}, message, CENTERED); +} + +#if defined(HARDWARE_KEYS) +void ConfirmDialog::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + deleteLater(); // this restore the focus (it can be changed then in confirmHandler) + confirmHandler(); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + deleteLater(); + } +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/confirm_dialog.h b/radio/src/thirdparty/libopenui/src/confirm_dialog.h new file mode 100644 index 000000000..8d6c2b841 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/confirm_dialog.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CONFIRM_DIALOG_H_ +#define _CONFIRM_DIALOG_H_ + +#include "dialog.h" + +class ConfirmDialog: public Dialog { + public: + ConfirmDialog(const char * title, const char * message, std::function confirmHandler); + +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + + protected: + std::function confirmHandler; +}; + +#endif // _CONFIRM_DIALOG_H_ From 436f5d30566550a1ffdd9039e87c969cd6f5b503 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 2 Dec 2019 11:28:46 +0100 Subject: [PATCH 066/714] Revert "ConfirmDialog moved to libopenui" This reverts commit d34e7491 --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 - .../libopenui/src/confirm_dialog.cpp | 44 ------------------- .../thirdparty/libopenui/src/confirm_dialog.h | 38 ---------------- 3 files changed, 83 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/src/confirm_dialog.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/confirm_dialog.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index bf2756ea5..082eb2f3f 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -32,7 +32,6 @@ set(LIBOPENUI_SRC table.cpp modal_window.cpp dialog.cpp - confirm_dialog.cpp message_dialog.cpp ) diff --git a/radio/src/thirdparty/libopenui/src/confirm_dialog.cpp b/radio/src/thirdparty/libopenui/src/confirm_dialog.cpp deleted file mode 100644 index 87f2f69cd..000000000 --- a/radio/src/thirdparty/libopenui/src/confirm_dialog.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "confirm_dialog.h" -#include "static.h" - -ConfirmDialog::ConfirmDialog(const char * title, const char * message, std::function confirmHandler) : - Dialog(title, {50, 73, LCD_W - 100, LCD_H - 146}), - confirmHandler(std::move(confirmHandler)) -{ - new StaticText(this, {0, height() / 2, width(), PAGE_LINE_HEIGHT}, message, CENTERED); -} - -#if defined(HARDWARE_KEYS) -void ConfirmDialog::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - deleteLater(); // this restore the focus (it can be changed then in confirmHandler) - confirmHandler(); - } - else if (event == EVT_KEY_BREAK(KEY_EXIT)) { - deleteLater(); - } -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/confirm_dialog.h b/radio/src/thirdparty/libopenui/src/confirm_dialog.h deleted file mode 100644 index 8d6c2b841..000000000 --- a/radio/src/thirdparty/libopenui/src/confirm_dialog.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _CONFIRM_DIALOG_H_ -#define _CONFIRM_DIALOG_H_ - -#include "dialog.h" - -class ConfirmDialog: public Dialog { - public: - ConfirmDialog(const char * title, const char * message, std::function confirmHandler); - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - - protected: - std::function confirmHandler; -}; - -#endif // _CONFIRM_DIALOG_H_ From 177a4ddace33bc36b733cafd30d6a89dcef986e9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 2 Dec 2019 11:40:29 +0100 Subject: [PATCH 067/714] Revert (MessageDialog and ConfirmDialog) --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 - .../libopenui/src/message_dialog.cpp | 38 ------------------- .../thirdparty/libopenui/src/message_dialog.h | 34 ----------------- 3 files changed, 73 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/src/message_dialog.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/message_dialog.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 082eb2f3f..3fc35eb0e 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -32,7 +32,6 @@ set(LIBOPENUI_SRC table.cpp modal_window.cpp dialog.cpp - message_dialog.cpp ) if(SOFTWARE_KEYBOARD) diff --git a/radio/src/thirdparty/libopenui/src/message_dialog.cpp b/radio/src/thirdparty/libopenui/src/message_dialog.cpp deleted file mode 100644 index 004be79f8..000000000 --- a/radio/src/thirdparty/libopenui/src/message_dialog.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "message_dialog.h" -#include "static.h" - -MessageDialog::MessageDialog(const char * title, const char * message): - Dialog(title, {50, 73, LCD_W - 100, LCD_H - 146}) -{ - new StaticText(this, {0, coord_t(height() - PAGE_LINE_HEIGHT) / 2, width(), PAGE_LINE_HEIGHT}, message, 0, CENTERED); -} - -#if defined(HARDWARE_KEYS) -void MessageDialog::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { - deleteLater(); - } -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/message_dialog.h b/radio/src/thirdparty/libopenui/src/message_dialog.h deleted file mode 100644 index 755a0fe1f..000000000 --- a/radio/src/thirdparty/libopenui/src/message_dialog.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#ifndef _MESSAGE_DIALOG_H_ -#define _MESSAGE_DIALOG_H_ - -#include "dialog.h" - -class MessageDialog: public Dialog { - public: - MessageDialog(const char * title, const char * message); - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif -}; - -#endif // _MESSAGE_DIALOG_H_ From 366ac317073b81f8de7ca27bf0d7b62814cf7943 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 2 Dec 2019 15:59:29 +0100 Subject: [PATCH 068/714] Missing translations --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 780acdd63..250f8b3a9 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -23,6 +23,9 @@ #include "theme.h" #include "message_dialog.h" +extern const char STR_SDCARD[]; +extern const char STR_NO_FILES_ON_SD[]; + // comparison, not case sensitive. bool compare_nocase(const std::string &first, const std::string &second) { @@ -113,7 +116,7 @@ bool FileChoice::openMenu() } } - new MessageDialog("Files", "No files on SD"); + new MessageDialog(STR_SDCARD, STR_NO_FILES_ON_SD); return false; } From 82d5de34a53ea92bfb7995de9ed445f3ecd0bcb4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 2 Dec 2019 16:04:10 +0100 Subject: [PATCH 069/714] New helper Window::setWindowCentered() --- radio/src/thirdparty/libopenui/src/window.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index b1b6e892f..be6801acf 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -156,6 +156,12 @@ class Window { invalidate(); } + void setWindowCentered() + { + rect.x = (parent->width() - width()) / 2; + rect.y = (parent->height() - height()) / 2; + } + void setHeight(coord_t value) { rect.h = value; From 3f760166a5fa2464c7ef1f6cc5a2fc65e67eecbd Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 2 Dec 2019 21:57:42 +0100 Subject: [PATCH 070/714] Themeable TextButton --- radio/src/thirdparty/libopenui/src/theme.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index c4e439be9..d2eea9445 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -22,6 +22,7 @@ #include "bitmapbuffer.h" #include "checkbox.h" +#include "button.h" class Menu; class MenuWindowContent; @@ -50,6 +51,11 @@ class Theme { virtual void drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const = 0; virtual const BitmapBuffer * getIcon(uint8_t index, IconState state) const = 0; virtual const BitmapBuffer * getIconMask(uint8_t index) const = 0; + + virtual TextButton * createTextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND) const + { + return new TextButton(parent, rect, text, pressHandler, windowFlags); + } }; extern Theme * theme; From 4b0fc623168f1d0d2c7bce8f4d141d2e860747bc Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 5 Dec 2019 18:20:19 +0100 Subject: [PATCH 071/714] :lipstick: Cosmetics --- radio/src/thirdparty/libopenui/src/table.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index db8d5cf7d..c20ab298f 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -37,7 +37,7 @@ void Table::Body::paint(BitmapBuffer * dc) coord_t y = 0; coord_t x; int index = 0; - for (auto &line: lines) { + for (auto & line: lines) { dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, index == selection ? FOCUS_BGCOLOR : TABLE_BGCOLOR); x = 10; for (auto &value: line.values) { From 1ecd5499877405c6788b682e946e71353a2fb20b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 5 Dec 2019 18:58:02 +0100 Subject: [PATCH 072/714] Fonts optimization --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/button.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 896d2ad0d..fe10416ec 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -452,11 +452,11 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ c -= 1; c += CJK_FIRST_LETTER_INDEX; uint8_t width = drawChar(x, y, font, fontspecs, c, flags); - INCREMENT_POS(width); + INCREMENT_POS(width + 1); } else if (c > 0x20) { uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); - INCREMENT_POS(width); + INCREMENT_POS(width + 1); } else if (c == '\n') { pos = orig_pos; diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 97ab5c371..011c07d3f 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -102,7 +102,8 @@ void TextButton::paint(BitmapBuffer * dc) } } - dc->drawText(rect.w / 2, (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), CENTERED | textColor); + TRACE("ICI %d %d", rect.h, getFontHeight(textFlags)); + dc->drawText(rect.w / 2, (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), CENTERED | textFlags | textColor); } void IconButton::paint(BitmapBuffer * dc) From e54898733c58c4768e29393995a9e95f5ffadca8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 5 Dec 2019 19:44:05 +0100 Subject: [PATCH 073/714] Button focus issue --- radio/src/thirdparty/libopenui/src/button.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 011c07d3f..167a348ae 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -49,10 +49,10 @@ void Button::onEvent(event_t event) bool Button::onTouchEnd(coord_t x, coord_t y) { if (enabled()) { - onPress(); if (!(windowFlags & NO_FOCUS)) { setFocus(); } + onPress(); } return true; } @@ -102,7 +102,6 @@ void TextButton::paint(BitmapBuffer * dc) } } - TRACE("ICI %d %d", rect.h, getFontHeight(textFlags)); dc->drawText(rect.w / 2, (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), CENTERED | textFlags | textColor); } From b624fdaae66280ad2b8082ce0b1a7bd0dd46c030 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 6 Dec 2019 13:59:45 +0100 Subject: [PATCH 074/714] Fixes --- radio/src/thirdparty/libopenui/src/window.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index be6801acf..c8f18338c 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -236,12 +236,22 @@ class Window { pageHeight = h; } + uint8_t getPageCount() + { + if (pageWidth) + return innerWidth / pageWidth; + else if (pageHeight) + return innerHeight / pageHeight; + else + return 1; + } + uint8_t getPageIndex() { if (pageWidth) return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; else if (pageHeight) - return (getScrollPositionX() + (pageHeight / 2)) / pageHeight; + return (getScrollPositionY() + (pageHeight / 2)) / pageHeight; else return 0; } From 347183814e05182a23426b66ef5b6546fe103115 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 9 Dec 2019 16:16:56 +0100 Subject: [PATCH 075/714] Cosmetics --- radio/src/thirdparty/libopenui/src/static.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 5b38a12b1..5b2850333 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -23,9 +23,9 @@ void StaticText::paint(BitmapBuffer * dc) { if (windowFlags & BUTTON_BACKGROUND) - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); + dc->drawSolidFilledRect(0, 0 + FIELD_PADDING_TOP, rect.w, rect.h, DISABLE_COLOR); if (textFlags & CENTERED) dc->drawText(rect.w / 2, (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); else - dc->drawText(0, 0, text.c_str(), textFlags); + dc->drawText(0, 0 + FIELD_PADDING_TOP, text.c_str(), textFlags); } From 65e37af10cab7148afdb25ca0d8320f7531881d4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 9 Dec 2019 16:37:45 +0100 Subject: [PATCH 076/714] Cosmetics --- radio/src/thirdparty/libopenui/src/static.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 5b2850333..e4bb0685a 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -25,7 +25,7 @@ void StaticText::paint(BitmapBuffer * dc) if (windowFlags & BUTTON_BACKGROUND) dc->drawSolidFilledRect(0, 0 + FIELD_PADDING_TOP, rect.w, rect.h, DISABLE_COLOR); if (textFlags & CENTERED) - dc->drawText(rect.w / 2, (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); + dc->drawText(rect.w / 2, (1 + rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); else dc->drawText(0, 0 + FIELD_PADDING_TOP, text.c_str(), textFlags); } From 34f256839f7f593e185df30c29f8919c32803f8c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 9 Dec 2019 17:28:35 +0100 Subject: [PATCH 077/714] Cosmetics --- radio/src/thirdparty/libopenui/tools/encode-bitmap.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py index 1448ae737..ff0962c25 100755 --- a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py +++ b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py @@ -115,7 +115,8 @@ class ImageEncoder: for y in range(height): for x in range(width): value = self.get_pixel(image, x, y) - value = 0x0f - (value >> 4) + value = 0x0F - round(value / 16) + value = max(0x00, min(0x0F, value)) self.encode_byte(value) self.f.write("\n") self.encode_end() From f7a6076b76b6be188cea4e2ca504ba9c0f727e42 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 9 Dec 2019 17:37:36 +0100 Subject: [PATCH 078/714] Cosmetics --- radio/src/thirdparty/libopenui/src/static.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index e4bb0685a..609be95ba 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -23,7 +23,7 @@ void StaticText::paint(BitmapBuffer * dc) { if (windowFlags & BUTTON_BACKGROUND) - dc->drawSolidFilledRect(0, 0 + FIELD_PADDING_TOP, rect.w, rect.h, DISABLE_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); if (textFlags & CENTERED) dc->drawText(rect.w / 2, (1 + rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); else From 312eb9d79619375ad655efb9e8ab04c99fd088c8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 9 Dec 2019 18:11:33 +0100 Subject: [PATCH 079/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index fe10416ec..fd6a13668 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -442,9 +442,6 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ if (!c) { break; } - else if (c == 0x20) { - INCREMENT_POS(4); - } else if (c >= 0xFE) { s++; c = uint8_t(*s) + ((c & 0x01) << 8) - 1; @@ -454,7 +451,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ uint8_t width = drawChar(x, y, font, fontspecs, c, flags); INCREMENT_POS(width + 1); } - else if (c > 0x20) { + else if (c >= 0x20) { uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); INCREMENT_POS(width + 1); } From de744644819847f28f9b8d67d65fe7f1ce2b4c11 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 10 Dec 2019 18:37:29 +0100 Subject: [PATCH 080/714] Touch cannot traverse OPAQUE windows --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 1accc4896..5916b6f04 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -341,7 +341,7 @@ bool Window::onTouchEnd(coord_t x, coord_t y) } } - return false; + return windowFlags & OPAQUE; } bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) From 729324d8180233c1c33444b4538a65f38b669530 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 10 Dec 2019 18:45:28 +0100 Subject: [PATCH 081/714] Revert last commit --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 5916b6f04..1accc4896 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -341,7 +341,7 @@ bool Window::onTouchEnd(coord_t x, coord_t y) } } - return windowFlags & OPAQUE; + return false; } bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) From 57479af50a1eab2260901f87a078601b6f988aff Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 11 Dec 2019 08:31:41 +0100 Subject: [PATCH 082/714] Touch cannot traverse OPAQUE windows (2nd attempt) --- radio/src/thirdparty/libopenui/src/menu.cpp | 3 ++- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 394fdd626..dff52ef0c 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -61,7 +61,8 @@ bool MenuBody::onTouchEnd(coord_t x, coord_t y) { int index = y / MENUS_LINE_HEIGHT; lines[index].onPress(); - return false; // = close the menu (inverted so that click outside the menu closes it) + getParent()->getParent()->deleteLater(); + return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 1accc4896..5916b6f04 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -341,7 +341,7 @@ bool Window::onTouchEnd(coord_t x, coord_t y) } } - return false; + return windowFlags & OPAQUE; } bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index c8f18338c..b8c1285ef 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -297,7 +297,7 @@ class Window { void fullPaint(BitmapBuffer * dc); - bool pointInRect(coord_t x, coord_t y, rect_t & rect) + static bool pointInRect(coord_t x, coord_t y, rect_t & rect) { return (x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h); } From b7126ff7f8ab86f1795a28de0266328ccbb45080 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 11 Dec 2019 14:53:42 +0100 Subject: [PATCH 083/714] New classes for DynamicText and DynamicNumber --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 8 +-- radio/src/thirdparty/libopenui/src/button.h | 4 +- .../thirdparty/libopenui/src/modal_window.h | 4 +- radio/src/thirdparty/libopenui/src/slider.h | 2 +- radio/src/thirdparty/libopenui/src/static.h | 57 ++++++++++++++++++- radio/src/thirdparty/libopenui/src/window.h | 2 +- 6 files changed, 66 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index fd6a13668..7f3302dbb 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -474,7 +474,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ coord_t BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) { char str[48+1]; // max=16 for the prefix, 16 chars for the number, 16 chars for the suffix - char *s = str+32; + char *s = str + 32; *s = '\0'; int idx = 0; int mode = MODE(flags); @@ -487,13 +487,13 @@ coord_t BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags fla *--s = '0' + (val % 10); ++idx; val /= 10; - if (mode!=0 && idx==mode) { + if (mode != 0 && idx == mode) { mode = 0; *--s = '.'; - if (val==0) + if (val == 0) *--s = '0'; } - } while (val!=0 || mode>0 || (mode==MODE(LEADING0) && idx 0 || (mode == MODE(LEADING0) && idx < len)); if (neg) *--s = '-'; // TODO needs check on all string lengths ... diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index a1bf1f0ee..ce310310a 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -28,7 +28,7 @@ constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2; constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3; constexpr WindowFlags BUTTON_DISABLED = FORM_FLAGS_LAST << 4; -class Button : public FormField { +class Button: public FormField { public: Button(FormGroup * parent, const rect_t & rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0): FormField(parent, rect, windowFlags), @@ -104,7 +104,7 @@ class Button : public FormField { void onPress(); }; -class TextButton : public Button { +class TextButton: public Button { public: TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND): Button(parent, rect, pressHandler, windowFlags | OPAQUE), diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 9faa6143c..13322c1aa 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -52,7 +52,7 @@ class ModalWindow: public Window return true; } - bool onTouchEnd(coord_t x, coord_t y) + bool onTouchEnd(coord_t x, coord_t y) override { if (!Window::onTouchEnd(x, y)) { deleteLater(); @@ -60,7 +60,7 @@ class ModalWindow: public Window return true; } - bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override { Window::onTouchSlide(x, y, startX, startY, slideX, slideY); return true; diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index 7f00b9837..cf35fbfa8 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -22,7 +22,7 @@ #include "form.h" -class Slider : public FormField { +class Slider: public FormField { public: Slider(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue): FormField(parent, rect), diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 46ae5e06d..4b509aa8f 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -23,7 +23,7 @@ #include "window.h" #include "button.h" // TODO just for BUTTON_BACKGROUND -class StaticText : public Window { +class StaticText: public Window { public: StaticText(Window * parent, const rect_t & rect, std::string text = "", WindowFlags windowFlags = 0, LcdFlags textFlags = 0) : Window(parent, rect, windowFlags, textFlags), @@ -108,4 +108,59 @@ class StaticBitmap: public Window { const BitmapBuffer * bitmap = nullptr; }; +class DynamicText: public StaticText +{ + public: + DynamicText(Window * parent, const rect_t & rect, std::function textHandler): + StaticText(parent, rect), + textHandler(std::move(textHandler)) + { + } + + void checkEvents() override + { + std::string newText = textHandler(); + if (newText != text) { + text = newText; + invalidate(); + } + } + + protected: + std::function textHandler; +}; + +template +class DynamicNumber: public Window +{ + public: + DynamicNumber(Window * parent, const rect_t & rect, std::function numberHandler, LcdFlags textFlags = 0, const char * prefix = nullptr, const char * suffix = nullptr): + Window(parent, rect, 0, textFlags), + numberHandler(std::move(numberHandler)), + prefix(prefix), + suffix(suffix) + { + } + + void paint(BitmapBuffer * dc) override + { + dc->drawNumber(0, FIELD_PADDING_TOP, value, textFlags, 0, prefix, suffix); + } + + void checkEvents() override + { + T newValue = numberHandler(); + if (value != newValue) { + value = newValue; + invalidate(); + } + } + + protected: + T value = 0; + std::function numberHandler; + const char * prefix; + const char * suffix; +}; + #endif diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index b8c1285ef..946b2e9e5 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -357,7 +357,7 @@ class Window { std::function closeHandler; std::function focusHandler; - void attach(Window * window); + void attach(Window * window); void addChild(Window * window) { From 7eb6d028e307da1ca0f520003b3124efcd348b54 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 12 Dec 2019 17:12:59 +0100 Subject: [PATCH 084/714] Warnings removed --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index c3029b910..a66bdb431 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -82,14 +82,14 @@ #undef RGB #endif -#define OPACITY_MAX 0x0F -#define OPACITY(value) ((value) << 24) +#define OPACITY_MAX 0x0Fu +#define OPACITY(value) ((value) << 24u) #define RGB(r, g, b) (uint16_t)((((r) & 0xF8) << 8) + (((g) & 0xFC) << 3) + (((b) & 0xF8) >> 3)) #define ARGB(a, r, g, b) (uint16_t)((((a) & 0xF0) << 8) + (((r) & 0xF0) << 4) + (((g) & 0xF0) << 0) + (((b) & 0xF0) >> 4)) -#define COLOR(index) LcdFlags((index) << 16) -#define COLOR_IDX(att) uint8_t((att) >> 16) +#define COLOR(index) LcdFlags((index) << 16u) +#define COLOR_IDX(att) uint8_t((att) >> 16u) #define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) #define DEFAULT_BGCOLOR COLOR(DEFAULT_BGCOLOR_INDEX) From b41263a0fc1d617d514b67b83719c54b44d3bee8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 13 Dec 2019 20:53:53 +0100 Subject: [PATCH 085/714] Menu black overlay fix (need to give the pointer of the current window) --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 23 ++++----- .../thirdparty/libopenui/src/bitmapbuffer.h | 16 +++--- radio/src/thirdparty/libopenui/src/choice.cpp | 2 +- radio/src/thirdparty/libopenui/src/dialog.cpp | 4 +- radio/src/thirdparty/libopenui/src/dialog.h | 9 +--- .../thirdparty/libopenui/src/filechoice.cpp | 4 +- .../libopenui/src/libopenui_defines.h | 3 -- radio/src/thirdparty/libopenui/src/menu.cpp | 8 +-- radio/src/thirdparty/libopenui/src/menu.h | 11 +---- .../thirdparty/libopenui/src/menutoolbar.h | 4 +- .../thirdparty/libopenui/src/modal_window.cpp | 15 +----- .../thirdparty/libopenui/src/modal_window.h | 2 +- radio/src/thirdparty/libopenui/src/table.h | 4 +- .../src/thirdparty/libopenui/src/textedit.cpp | 6 +-- radio/src/thirdparty/libopenui/src/window.cpp | 34 +++++++------ radio/src/thirdparty/libopenui/src/window.h | 49 ++++++++++--------- 16 files changed, 86 insertions(+), 108 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 7f3302dbb..634a2a7c6 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -17,7 +17,6 @@ * Lesser General Public License for more details. */ -#include #include "bitmapbuffer.h" #include "libopenui_depends.h" #include "libopenui_globals.h" @@ -190,13 +189,11 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui if (x + w > xmax) w = xmax - x; - for (coord_t i=y; i= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) return; - } pixel_t color = lcdColorTable[COLOR_IDX(flags)]; diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 8945c6254..29e536945 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -390,12 +390,12 @@ class BitmapBuffer: public BitmapBufferBase template void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0) { + if (!data || !bmp) + return; + APPLY_OFFSET(); - if (x >= xmax) return; - if (y >= ymax) return; - - if (!data || !bmp) + if (x >= xmax || y >= ymax) return; coord_t srcw = bmp->width(); @@ -411,14 +411,14 @@ class BitmapBuffer: public BitmapBufferBase h = srch - srcy; if (x < xmin) { - w += x-xmin; - srcx -= x-xmin; + w += x - xmin; + srcx -= x - xmin; x = xmin; } if (y < ymin) { - h += y-ymin; - srcy -= y-ymin; + h += y - ymin; + srcy -= y - ymin; y = ymin; } diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 57c084d8e..5610157cc 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -54,7 +54,7 @@ void Choice::onEvent(event_t event) void Choice::openMenu() { - auto menu = new Menu(); + auto menu = new Menu(this); if (!menuTitle.empty()) menu->setTitle(menuTitle); diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 70b8d3440..50a8b88a9 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -29,8 +29,8 @@ DialogWindowContent::DialogWindowContent(Dialog * parent, const rect_t & rect): } -Dialog::Dialog(std::string title, const rect_t & rect): - ModalWindow(), +Dialog::Dialog(Window * parent, std::string title, const rect_t & rect): + ModalWindow(parent), content(createDialogWindow(this, rect)) { bringToTop(); diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index f4bb790c0..85b2b7737 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -49,14 +49,7 @@ class DialogWindowContent: public ModalWindowContent { class Dialog: public ModalWindow { public: - Dialog(std::string title, const rect_t & rect); - -#if defined(DEBUG_WINDOWS) - std::string getName() override - { - return "Dialog"; - } -#endif + Dialog(Window * parent, std::string title, const rect_t & rect); protected: DialogWindowContent * content; diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 250f8b3a9..861f56a3d 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -87,7 +87,7 @@ bool FileChoice::openMenu() files.push_front(""); - auto menu = new Menu(); + auto menu = new Menu(this); int count = 0; int current = -1; std::string value = getValue(); @@ -116,7 +116,7 @@ bool FileChoice::openMenu() } } - new MessageDialog(STR_SDCARD, STR_NO_FILES_ON_SD); + new MessageDialog(this, STR_SDCARD, STR_NO_FILES_ON_SD); return false; } diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index a66bdb431..5382a4080 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -38,9 +38,6 @@ #define PREC2 0x30 #define MODE(flags) ((((int8_t)(flags) & 0x30) - 0x10) >> 4) -/* rect, square flags */ -#define ROUND 0x04 - /* telemetry flags */ #define NO_UNIT 0x40 diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index dff52ef0c..a76991f28 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -108,8 +108,8 @@ void MenuWindowContent::paint(BitmapBuffer * dc) } } -Menu::Menu(): - ModalWindow(), +Menu::Menu(Window * parent): + ModalWindow(parent), content(createMenuWindow(this)) { } @@ -128,9 +128,9 @@ void Menu::updatePosition() content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT - 1); } -void Menu::setTitle(const std::string text) +void Menu::setTitle(std::string text) { - content->setTitle(text); + content->setTitle(std::move(text)); updatePosition(); } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 2775827ce..5e07fc809 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -136,20 +136,13 @@ class MenuWindowContent: public ModalWindowContent { class Menu: public ModalWindow { public: - Menu(); + explicit Menu(Window * parent); void setCancelHandler(std::function handler) { content->body.setCancelHandler(handler); } -#if defined(DEBUG_WINDOWS) - std::string getName() override - { - return "Menu"; - } -#endif - void setToolbar(Window * window) { toolbar = window; @@ -158,7 +151,7 @@ class Menu: public ModalWindow { content->setHeight(toolbar->height()); } - void setTitle(const std::string text); + void setTitle(std::string text); void addLine(const std::string & text, std::function onPress); diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 725bad6f9..ef1559bc7 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -138,7 +138,7 @@ class MenuToolbar: public FormGroup { std::list::iterator current = children.end(); T * choice; Menu * menu; - coord_t y = 0; // TODO NV14 was 5; + coord_t y = 0; void addButton(char picto, int16_t filtermin, int16_t filtermax) { @@ -164,7 +164,7 @@ class MenuToolbar: public FormGroup { return 1; }); - y += 30; + y += MENUS_TOOLBAR_BUTTON_WIDTH; setInnerHeight(y); } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index a77c36a92..f7c1de20a 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -19,11 +19,10 @@ #include "modal_window.h" -#include "mainwindow.h" #include "font.h" -ModalWindow::ModalWindow(): - Window(&mainWindow, {0, 0, LCD_W, LCD_H}) +ModalWindow::ModalWindow(Window * parent): + Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}) #if !defined(HARDWARE_TOUCH) , previousFocus(focusWindow) #endif @@ -40,14 +39,4 @@ void ModalWindowContent::paint(BitmapBuffer * dc) dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, FOCUS_BGCOLOR); dc->drawText(FIELD_PADDING_LEFT, (POPUP_HEADER_HEIGHT - getFontHeight(FONT(STD))) / 2, title.c_str(), FOCUS_COLOR); dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, width(), height() - POPUP_HEADER_HEIGHT, DEFAULT_BGCOLOR); - -/* - // the background - dc->clear(MENU_BGCOLOR); - - // the title - if (!title.empty()) { - dc->drawText(MENUS_WIDTH / 2, (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); - dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); - }*/ } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 13322c1aa..0f33abf71 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -25,7 +25,7 @@ class ModalWindow: public Window { public: - ModalWindow(); + explicit ModalWindow(Window * parent); #if defined(DEBUG_WINDOWS) std::string getName() override diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 8ba7452f8..bf3b3fb5b 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -121,7 +121,7 @@ class Table: public Window { body.invalidate(); } - void setHeader(const char * values[]) + void setHeader(const char * const values[]) { Line line; for (uint8_t i = 0; i < count; i++) { @@ -130,7 +130,7 @@ class Table: public Window { header.setLine(line); } - void addLine(const char * values[], std::function onPress = nullptr) + void addLine(const char * const values[], std::function onPress = nullptr) { Line line; for (uint8_t i = 0; i < count; i++) { diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index fd8886f44..45c109380 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -33,10 +33,10 @@ void TextEdit::paint(BitmapBuffer * dc) dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, FOCUS_COLOR); coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); #if defined(HARDWARE_TOUCH) - dc->drawSolidFilledRect(left + 2, FIELD_PADDING_TOP + 1, 2, 20, FOCUS_COLOR); + dc->drawSolidFilledRect(left + 2, 2, 2, height() - 4, FOCUS_COLOR); #else char s[] = { value[cursorPos], '\0' }; - dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP + 1, getTextWidth(s, 1) + 1, height() - 2, FOCUS_COLOR); + dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP - 1, getTextWidth(s, 1) + 1, height() - 2, FOCUS_COLOR); dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, DEFAULT_COLOR); #endif } @@ -193,7 +193,7 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) char c = value[cursorPos]; if (c == '\0') break; - uint8_t w = getCharWidth(c, fontspecsTable[0]); + uint8_t w = getCharWidth(c, fontspecsTable[0]) + 1; if (rest < w) break; rest -= w; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 5916b6f04..8ecb8e22c 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -187,26 +187,41 @@ void Window::scrollTo(Window * child) void Window::fullPaint(BitmapBuffer * dc) { - TRACE_WINDOWS("%s", getWindowDebugString().c_str()); + bool paintNeeded = true; + + auto firstChild = children.end(); + while (firstChild != children.begin()) { + auto child = *(--firstChild); + if ((child->windowFlags & OPAQUE) && isChildFullSize(child)) { + paintNeeded = false; + break; + } + } if (windowFlags & PAINT_CHILDREN_FIRST) { coord_t xmin, xmax, ymin, ymax; coord_t x = dc->getOffsetX(); coord_t y = dc->getOffsetY(); dc->getClippingRect(xmin, xmax, ymin, ymax); - paintChildren(dc); + paintChildren(dc, firstChild); dc->setOffset(x, y); dc->setClippingRect(xmin, xmax, ymin, ymax); } - paint(dc); + if (paintNeeded) { + TRACE_WINDOWS("%s", getWindowDebugString().c_str()); + paint(dc); + } + else { + TRACE_WINDOWS("%s (skipped)", getWindowDebugString().c_str()); + } if (!(windowFlags & NO_SCROLLBAR)) { drawVerticalScrollbar(dc); } if (!(windowFlags & PAINT_CHILDREN_FIRST)) { - paintChildren(dc); + paintChildren(dc, firstChild); } } @@ -229,22 +244,13 @@ bool Window::isChildVisible(Window * window) return false; } -void Window::paintChildren(BitmapBuffer * dc) +void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) { coord_t x = dc->getOffsetX(); coord_t y = dc->getOffsetY(); coord_t xmin, xmax, ymin, ymax; dc->getClippingRect(xmin, xmax, ymin, ymax); - auto it = children.end(); - - while(it != children.begin()) { - auto child = *(--it); - if ((child->windowFlags & OPAQUE) && isChildFullSize(child)) { - break; - } - } - for (; it != children.end(); it++) { auto child = *it; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 946b2e9e5..93a421259 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -97,6 +97,11 @@ class Window { return parent; } + Window * getFullScreenWindow() + { + return (width() == LCD_W && height() == LCD_H) ? this : parent->getFullScreenWindow(); + } + WindowFlags getWindowFlags() const { return windowFlags; @@ -287,37 +292,15 @@ class Window { return parent && parent->isChildVisible(this); } - virtual void paint(BitmapBuffer * dc) - { - } - void drawVerticalScrollbar(BitmapBuffer * dc); - void paintChildren(BitmapBuffer * dc); - - void fullPaint(BitmapBuffer * dc); - static bool pointInRect(coord_t x, coord_t y, rect_t & rect) { return (x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h); } - virtual void onFocusLost() - { - TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); - invalidate(); - }; - virtual void onEvent(event_t event); -#if defined(HARDWARE_TOUCH) - virtual bool onTouchStart(coord_t x, coord_t y); - - virtual bool onTouchEnd(coord_t x, coord_t y); - - virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); -#endif - void adjustInnerHeight(); coord_t adjustHeight(); @@ -371,6 +354,28 @@ class Window { } virtual void invalidate(const rect_t & rect); + + void paintChildren(BitmapBuffer * dc, std::list::iterator it); + + void fullPaint(BitmapBuffer * dc); + + virtual void paint(BitmapBuffer * dc) + { + } + + virtual void onFocusLost() + { + TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); + invalidate(); + } + +#if defined(HARDWARE_TOUCH) + virtual bool onTouchStart(coord_t x, coord_t y); + + virtual bool onTouchEnd(coord_t x, coord_t y); + + virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); +#endif }; #endif // _WINDOW_H_ From ed664773b4a7ea3f525e036b81c3b75373746eb1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 13 Dec 2019 22:11:05 +0100 Subject: [PATCH 086/714] Allow to call forwardTouchEnd from children classes --- radio/src/thirdparty/libopenui/src/window.cpp | 9 +++++++-- radio/src/thirdparty/libopenui/src/window.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 8ecb8e22c..328b0479a 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -336,7 +336,7 @@ bool Window::onTouchStart(coord_t x, coord_t y) return false; } -bool Window::onTouchEnd(coord_t x, coord_t y) +bool Window::forwardTouchEnd(coord_t x, coord_t y) { for (auto it = children.rbegin(); it != children.rend(); ++it) { auto child = *it; @@ -347,7 +347,12 @@ bool Window::onTouchEnd(coord_t x, coord_t y) } } - return windowFlags & OPAQUE; + return false; +} + +bool Window::onTouchEnd(coord_t x, coord_t y) +{ + return forwardTouchEnd(x, y) ? true : (windowFlags & OPAQUE); } bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 93a421259..3e1715a60 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -376,6 +376,8 @@ class Window { virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); #endif + + bool forwardTouchEnd(coord_t x, coord_t y); }; #endif // _WINDOW_H_ From 98162c57294fd663e5d5bb7296033a9d5d93012f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 18 Dec 2019 18:56:20 +0100 Subject: [PATCH 087/714] Form navigation issue --- radio/src/thirdparty/libopenui/src/form.cpp | 22 ++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 91ccdf40b..0159bc9ef 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -106,7 +106,8 @@ void FormGroup::setFocus(uint8_t flag) first->setFocus(SET_FOCUS_FIRST); break; case SET_FOCUS_BACKWARD: - if (focusWindow == first) { + // TODO this test should be recursive + if (focusWindow == first || focusWindow->getParent() == first) { if (previous == this) last->setFocus(SET_FOCUS_BACKWARD); else if (previous) @@ -117,6 +118,19 @@ void FormGroup::setFocus(uint8_t flag) last->setFocus(SET_FOCUS_BACKWARD); } break; + case SET_FOCUS_FORWARD: + // TODO this test should be recursive + if (focusWindow == last || focusWindow->getParent() == last) { + if (next == this) + first->setFocus(SET_FOCUS_FORWARD); + else if (next) + next->setFocus(SET_FOCUS_FORWARD); + } + else { + if (first) + first->setFocus(SET_FOCUS_FORWARD); + } + break; default: if (focusWindow == previous) { if (first) @@ -124,7 +138,7 @@ void FormGroup::setFocus(uint8_t flag) } else { if (next) - next->setFocus(); + next->setFocus(SET_FOCUS_FORWARD); } break; } @@ -140,11 +154,9 @@ void FormGroup::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { - editMode = true; first->setFocus(); } - else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { - editMode = false; + else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus()) { setFocus(SET_FOCUS_DEFAULT); } else { From b9dbc710c76f2d403564d6f378a4ba5412b1b082 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 19 Dec 2019 11:56:24 +0100 Subject: [PATCH 088/714] Table updates --- radio/src/thirdparty/libopenui/src/table.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index bf3b3fb5b..cbe98d386 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -72,12 +72,14 @@ class Table: public Window { void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) + bool onTouchEnd(coord_t x, coord_t y) override { unsigned index = y / TABLE_LINE_HEIGHT; - auto onPress = lines[index].onPress; - if (onPress) - onPress(); + if (index < lines.size()) { + auto onPress = lines[index].onPress; + if (onPress) + onPress(); + } return true; } #endif @@ -112,6 +114,12 @@ class Table: public Window { return body.selection; } + void clearSelection() + { + body.selection = -1; + body.invalidate(); + } + void setSelection(int index, bool scroll = false) { body.selection = index; @@ -143,6 +151,7 @@ class Table: public Window { void clear() { body.clear(); + clearSelection(); } protected: From e6850498f7b6d0774477214c5df12c034fd7d599 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 3 Jan 2020 17:28:36 +0100 Subject: [PATCH 089/714] drawText(..., RIGHT) will now return the text left position --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 +- radio/src/thirdparty/libopenui/src/table.h | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 634a2a7c6..027a21d31 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -463,7 +463,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ RESTORE_OFFSET(); - return pos - offsetX; + return ((flags & RIGHT) ? orig_pos : pos) - offsetX; } coord_t BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index cbe98d386..51f892137 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -92,8 +92,8 @@ class Table: public Window { public: Table(Window * parent, const rect_t & rect, uint8_t columnsCount): Window(parent, rect), - header(this, {0, 0, width(), TABLE_HEADER_HEIGHT}), - body(this, {0, TABLE_HEADER_HEIGHT, width(), height() - TABLE_HEADER_HEIGHT}) + header(this, {0, 0, width(), 0}), + body(this, {0, 0, width(), height()}) { setColumnsCount(columnsCount); } @@ -131,6 +131,9 @@ class Table: public Window { void setHeader(const char * const values[]) { + header.setHeight(TABLE_HEADER_HEIGHT); + body.setTop(TABLE_HEADER_HEIGHT); + body.setHeight(height() - TABLE_HEADER_HEIGHT); Line line; for (uint8_t i = 0; i < count; i++) { line.values.push_back(values[i]); From b1a52977a2312d2a41c7738a112976499128e129 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 7 Jan 2020 09:14:32 +0100 Subject: [PATCH 090/714] Compilation fix --- radio/src/thirdparty/libopenui/src/libopenui_helpers.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index d93619683..db5911410 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -21,6 +21,7 @@ #define _LIBOPEUI_HELPERS_H_ #include +#include #if defined(min) #undef min From 60d4ba17cd7ee3cff7d7c58ada959541494bb545 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 7 Jan 2020 10:23:39 +0100 Subject: [PATCH 091/714] Static now accepts right aligned text --- radio/src/thirdparty/libopenui/src/static.cpp | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 radio/src/thirdparty/libopenui/src/static.cpp diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp old mode 100644 new mode 100755 index 609be95ba..210031325 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -26,6 +26,8 @@ void StaticText::paint(BitmapBuffer * dc) dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); if (textFlags & CENTERED) dc->drawText(rect.w / 2, (1 + rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); + else if (textFlags & RIGHT) + dc->drawText(rect.w, FIELD_PADDING_TOP, text.c_str(), textFlags); else dc->drawText(0, 0 + FIELD_PADDING_TOP, text.c_str(), textFlags); } From b6e328ef40cc36e169cb5b4a54b28c15f0b763ba Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 8 Jan 2020 10:12:52 +0100 Subject: [PATCH 092/714] drawCircle and drawFilledCircle re-added --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 77 ++++++++++++------- .../thirdparty/libopenui/src/bitmapbuffer.h | 4 +- 2 files changed, 54 insertions(+), 27 deletions(-) mode change 100644 => 100755 radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp mode change 100644 => 100755 radio/src/thirdparty/libopenui/src/bitmapbuffer.h diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp old mode 100644 new mode 100755 index 027a21d31..19fe775b7 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -211,32 +211,57 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui // } //} // -//void BitmapBuffer::drawCircle(int x0, int y0, int radius) -//{ -// int x = radius; -// int y = 0; -// int decisionOver2 = 1 - x; -// -// while (y <= x) { -// drawPixel(x+x0, y+y0, WHITE); -// drawPixel(y+x0, x+y0, WHITE); -// drawPixel(-x+x0, y+y0, WHITE); -// drawPixel(-y+x0, x+y0, WHITE); -// drawPixel(-x+x0, -y+y0, WHITE); -// drawPixel(-y+x0, -x+y0, WHITE); -// drawPixel(x+x0, -y+y0, WHITE); -// drawPixel(y+x0, -x+y0, WHITE); -// y++; -// if (decisionOver2 <= 0) { -// decisionOver2 += 2*y + 1; -// } -// else { -// x--; -// decisionOver2 += 2 * (y-x) + 1; -// } -// } -//} -// + +void BitmapBuffer::drawCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags) +{ + int x1 = radius; + int y1 = 0; + int decisionOver2 = 1 - x1; + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + + APPLY_OFFSET(); + + while (y1 <= x1) { + drawPixel(x1 + x, y1 + y, color); + drawPixel(y1 + x, x1 + y, color); + drawPixel(-x1 + x, y1 + y, color); + drawPixel(-y1 + x, x1 + y, color); + drawPixel(-x1 + x, -y1 + y, color); + drawPixel(-y1 + x, -x1 + y, color); + drawPixel(x1 + x, -y1 + y, color); + drawPixel(y1 + x, -x1 + y, color); + y1++; + if (decisionOver2 <= 0) { + decisionOver2 += 2 * y1 + 1; + } + else { + x1--; + decisionOver2 += 2 * (y1 - x1) + 1; + } + } +} + +void BitmapBuffer::drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags) +{ + coord_t imax = ((coord_t)((coord_t)radius * 707)) / 1000 + 1; + coord_t sqmax = (coord_t)radius * (coord_t)radius + (coord_t)radius / 2; + coord_t x1 = radius; + drawSolidHorizontalLine(x - radius, y, radius * 2, flags); + for (coord_t i = 1; i <= imax; i++) { + if ((i * i + x1 * x1) > sqmax) { + // Draw lines from outside + if (x1 > imax) { + drawSolidHorizontalLine(x - i + 1, y + x1, (i - 1) * 2, flags); + drawSolidHorizontalLine(x - i + 1, y - x1, (i - 1) * 2, flags); + } + x1--; + } + // Draw lines from inside (center) + drawSolidHorizontalLine(x - x1, y + i, x1 * 2, flags); + drawSolidHorizontalLine(x - x1, y - i, x1 * 2, flags); + } +} + //bool evalSlopes(int * slopes, int startAngle, int endAngle) //{ // if (startAngle >= 360 || endAngle <= 0) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h old mode 100644 new mode 100755 index 29e536945..ebcca5c3d --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -354,7 +354,9 @@ class BitmapBuffer: public BitmapBufferBase void invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); - void drawCircle(int x0, int y0, int radius); + void drawCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags = 0); + + void drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags = 0); void drawPie(int x0, int y0, int radius, int startAngle, int endAngle); From 1def5f18bee430e39fdae77273885d113abbda4d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 10 Jan 2020 12:03:41 +0100 Subject: [PATCH 093/714] Some "explicit" missing --- radio/src/thirdparty/libopenui/src/gridlayout.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/gridlayout.h b/radio/src/thirdparty/libopenui/src/gridlayout.h index 9d3b9111a..535fc5050 100644 --- a/radio/src/thirdparty/libopenui/src/gridlayout.h +++ b/radio/src/thirdparty/libopenui/src/gridlayout.h @@ -24,12 +24,12 @@ class GridLayout { public: - GridLayout(coord_t width): + explicit GridLayout(coord_t width): width(width) { } - GridLayout(Window * window): + explicit GridLayout(Window * window): width(window->width()) { } @@ -69,7 +69,7 @@ class GridLayout { class FormGridLayout: public GridLayout { public: - FormGridLayout(coord_t width = LCD_W): + explicit FormGridLayout(coord_t width = LCD_W): GridLayout(width) { } From a0a75f869cb8cab7df38e02b25aecf661cf6d8b0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 13 Jan 2020 18:15:42 +0100 Subject: [PATCH 094/714] drawPie function is back (will need perhaps another name) --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 140 +++++++++--------- .../thirdparty/libopenui/src/bitmapbuffer.h | 2 +- 2 files changed, 75 insertions(+), 67 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 19fe775b7..6fed68877 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -17,6 +17,7 @@ * Lesser General Public License for more details. */ +#include #include "bitmapbuffer.h" #include "libopenui_depends.h" #include "libopenui_globals.h" @@ -262,72 +263,79 @@ void BitmapBuffer::drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFla } } -//bool evalSlopes(int * slopes, int startAngle, int endAngle) -//{ -// if (startAngle >= 360 || endAngle <= 0) -// return false; -// -// if (startAngle == 0) { -// slopes[1] = 100000; -// slopes[2] = -100000; -// } -// else { -// float angle1 = float(startAngle) * (M_PI / 180.0f); -// if (startAngle >= 180) { -// slopes[1] = -100000; -// slopes[2] = cosf(angle1) * 100 / sinf(angle1); -// } -// else { -// slopes[1] = cosf(angle1) * 100 / sinf(angle1); -// slopes[2] = -100000; -// } -// } -// -// if (endAngle == 360) { -// slopes[0] = -100000; -// slopes[3] = 100000; -// } -// else { -// float angle2 = float(endAngle) * (M_PI / 180.0f); -// if (endAngle >= 180) { -// slopes[0] = -100000; -// slopes[3] = -cosf(angle2) * 100 / sinf(angle2); -// } -// else { -// slopes[0] = cosf(angle2) * 100 / sinf(angle2); -// slopes[3] = -100000; -// } -// } -// -// return true; -//} -// -//void BitmapBuffer::drawPie(int x0, int y0, int radius, int startAngle, int endAngle) -//{ -// int slopes[4]; -// if (!evalSlopes(slopes, startAngle, endAngle)) -// return; -// -// for (int y=0; y<=radius; y++) { -// for (int x=0; x<=radius; x++) { -// if (x*x+y*y <= radius*radius) { -// int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x); -// if (slope >= slopes[0] && slope < slopes[1]) { -// drawPixel(x0+x, y0-y, WHITE); -// } -// if (-slope >= slopes[0] && -slope < slopes[1]) { -// drawPixel(x0+x, y0+y, WHITE); -// } -// if (slope >= slopes[2] && slope < slopes[3]) { -// drawPixel(x0-x, y0-y, WHITE); -// } -// if (-slope >= slopes[2] && -slope < slopes[3]) { -// drawPixel(x0-x, y0+y, WHITE); -// } -// } -// } -// } -//} +bool evalSlopes(int * slopes, int startAngle, int endAngle) +{ + if (startAngle >= 360 || endAngle <= 0) + return false; + + if (startAngle == 0) { + slopes[1] = 100000; + slopes[2] = -100000; + } + else { + float angle1 = float(startAngle) * (M_PI / 180.0f); + if (startAngle >= 180) { + slopes[1] = -100000; + slopes[2] = cosf(angle1) * 100 / sinf(angle1); + } + else { + slopes[1] = cosf(angle1) * 100 / sinf(angle1); + slopes[2] = -100000; + } + } + + if (endAngle == 360) { + slopes[0] = -100000; + slopes[3] = 100000; + } + else { + float angle2 = float(endAngle) * (M_PI / 180.0f); + if (endAngle >= 180) { + slopes[0] = -100000; + slopes[3] = -cosf(angle2) * 100 / sinf(angle2); + } + else { + slopes[0] = cosf(angle2) * 100 / sinf(angle2); + slopes[3] = -100000; + } + } + + return true; +} + +void BitmapBuffer::drawPie(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags) +{ + int slopes[4]; + if (!evalSlopes(slopes, startAngle, endAngle)) + return; + + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + APPLY_OFFSET(); + + coord_t internalDist = internalRadius * internalRadius; + coord_t externalDist = externalRadius * externalRadius; + + for (int y1 = 0; y1 <= externalRadius; y1++) { + for (int x1 = 0; x1 <= externalRadius; x1++) { + auto dist = x1 * x1 + y1 * y1; + if (dist >= internalDist && dist <= externalDist) { + int slope = (x1 == 0 ? (y1 < 0 ? -99000 : 99000) : y1 * 100 / x1); + if (slope >= slopes[0] && slope < slopes[1]) { + drawPixel(x + x1, y - y1, color); + } + if (-slope >= slopes[0] && -slope < slopes[1]) { + drawPixel(x + x1, y + y1, color); + } + if (slope >= slopes[2] && slope < slopes[3]) { + drawPixel(x - x1, y - y1, color); + } + if (-slope >= slopes[2] && -slope < slopes[3]) { + drawPixel(x - x1, y + y1, color); + } + } + } + } +} void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset, coord_t width) { diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index ebcca5c3d..0fcc445f7 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -358,7 +358,7 @@ class BitmapBuffer: public BitmapBufferBase void drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags = 0); - void drawPie(int x0, int y0, int radius, int startAngle, int endAngle); + void drawPie(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags = 0); void drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle); From 01e46700691d65701cc2401d5d4b2ed3074660b1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 14 Jan 2020 08:24:45 +0100 Subject: [PATCH 095/714] drawPie renamed to drawAnnulusSector --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 +- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 6fed68877..ff61ef544 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -303,7 +303,7 @@ bool evalSlopes(int * slopes, int startAngle, int endAngle) return true; } -void BitmapBuffer::drawPie(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags) +void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags) { int slopes[4]; if (!evalSlopes(slopes, startAngle, endAngle)) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 0fcc445f7..c4e6b6d53 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -358,7 +358,7 @@ class BitmapBuffer: public BitmapBufferBase void drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags = 0); - void drawPie(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags = 0); + void drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags = 0); void drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle); From f42ea4c1ef0209a6a9cb97e5acb79175ff1a4883 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 14 Jan 2020 09:49:05 +0100 Subject: [PATCH 096/714] drawAnnulusSector fixes --- .../src/thirdparty/libopenui/src/bitmapbuffer.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index ff61ef544..5a1b12786 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -265,9 +265,6 @@ void BitmapBuffer::drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFla bool evalSlopes(int * slopes, int startAngle, int endAngle) { - if (startAngle >= 360 || endAngle <= 0) - return false; - if (startAngle == 0) { slopes[1] = 100000; slopes[2] = -100000; @@ -278,6 +275,10 @@ bool evalSlopes(int * slopes, int startAngle, int endAngle) slopes[1] = -100000; slopes[2] = cosf(angle1) * 100 / sinf(angle1); } + else if (startAngle < 0 && endAngle > 0) { + slopes[1] = 100000; + slopes[2] = - cosf(angle1) * 100 / sinf(angle1); + } else { slopes[1] = cosf(angle1) * 100 / sinf(angle1); slopes[2] = -100000; @@ -294,6 +295,10 @@ bool evalSlopes(int * slopes, int startAngle, int endAngle) slopes[0] = -100000; slopes[3] = -cosf(angle2) * 100 / sinf(angle2); } + else if (startAngle < 0) { + slopes[0] = cosf(angle2) * 100 / sinf(angle2); + slopes[3] = 100000; + } else { slopes[0] = cosf(angle2) * 100 / sinf(angle2); slopes[3] = -100000; @@ -309,6 +314,8 @@ void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadiu if (!evalSlopes(slopes, startAngle, endAngle)) return; + // TRACE("slopes = %d %d %d %d", slopes[0], slopes[1], slopes[2], slopes[3]); + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; APPLY_OFFSET(); @@ -319,7 +326,7 @@ void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadiu for (int x1 = 0; x1 <= externalRadius; x1++) { auto dist = x1 * x1 + y1 * y1; if (dist >= internalDist && dist <= externalDist) { - int slope = (x1 == 0 ? (y1 < 0 ? -99000 : 99000) : y1 * 100 / x1); + int slope = (x1 == 0 ? 99000 : y1 * 100 / x1); if (slope >= slopes[0] && slope < slopes[1]) { drawPixel(x + x1, y - y1, color); } From d97adeff491974e9c914f6039fbbe8e011589516 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 14 Jan 2020 22:16:19 +0100 Subject: [PATCH 097/714] Better drawAnnulusSector --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 139 +++++++++++------- 1 file changed, 83 insertions(+), 56 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 5a1b12786..34baf7194 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -263,58 +263,89 @@ void BitmapBuffer::drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFla } } -bool evalSlopes(int * slopes, int startAngle, int endAngle) -{ - if (startAngle == 0) { - slopes[1] = 100000; - slopes[2] = -100000; - } - else { - float angle1 = float(startAngle) * (M_PI / 180.0f); - if (startAngle >= 180) { - slopes[1] = -100000; - slopes[2] = cosf(angle1) * 100 / sinf(angle1); - } - else if (startAngle < 0 && endAngle > 0) { - slopes[1] = 100000; - slopes[2] = - cosf(angle1) * 100 / sinf(angle1); - } - else { - slopes[1] = cosf(angle1) * 100 / sinf(angle1); - slopes[2] = -100000; - } - } +class Slope { + public: + explicit Slope(int angle) { + if (angle < 0) + angle += 360; + if (angle >= 360) + angle %= 360; + float radians = float(angle) * (M_PI / 180.0f); + if (angle == 0) { + left = false; + value = 100000; + } + else if (angle > 180) { + left = true; + value = -(cosf(radians) * 100 / sinf(radians)); + } + else { + left = false; + value = (cosf(radians) * 100 / sinf(radians)); + } - if (endAngle == 360) { - slopes[0] = -100000; - slopes[3] = 100000; - } - else { - float angle2 = float(endAngle) * (M_PI / 180.0f); - if (endAngle >= 180) { - slopes[0] = -100000; - slopes[3] = -cosf(angle2) * 100 / sinf(angle2); + // TRACE("angle=%d left=%d slope=%d", angle, left, value); } - else if (startAngle < 0) { - slopes[0] = cosf(angle2) * 100 / sinf(angle2); - slopes[3] = 100000; - } - else { - slopes[0] = cosf(angle2) * 100 / sinf(angle2); - slopes[3] = -100000; - } - } - return true; -} + Slope(bool left, int value): + left(left), + value(value) + { + } + + bool isBetween(const Slope & start, const Slope & end) const + { + if (left) { + if (start.left) { + if (end.left) + return end.value > start.value ? (value <= end.value && value >= start.value) : (value <= end.value || value >= start.value); + else + return value >= start.value; + } + else { + if (end.left) + return value <= end.value; + else + return end.value > start.value; + } + } + else { + if (start.left) { + if (end.left) + return start.value > end.value; + else + return value >= end.value; + } + else { + if (end.left) + return value <= start.value; + else + return end.value < start.value ? (value >= end.value && value <= start.value) : (value <= start.value || value >= end.value); + } + } + } + + Slope & invertVertical() + { + value = -value; + return *this; + } + + Slope & invertHorizontal() + { + left = !left; + return *this; + } + + protected: + bool left; + int value; +}; void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags) { - int slopes[4]; - if (!evalSlopes(slopes, startAngle, endAngle)) - return; - - // TRACE("slopes = %d %d %d %d", slopes[0], slopes[1], slopes[2], slopes[3]); + Slope startSlope(startAngle); + Slope endSlope(endAngle); pixel_t color = lcdColorTable[COLOR_IDX(flags)]; APPLY_OFFSET(); @@ -326,19 +357,15 @@ void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadiu for (int x1 = 0; x1 <= externalRadius; x1++) { auto dist = x1 * x1 + y1 * y1; if (dist >= internalDist && dist <= externalDist) { - int slope = (x1 == 0 ? 99000 : y1 * 100 / x1); - if (slope >= slopes[0] && slope < slopes[1]) { + Slope slope(false, x1 == 0 ? 99000 : y1 * 100 / x1); + if (slope.isBetween(startSlope, endSlope)) drawPixel(x + x1, y - y1, color); - } - if (-slope >= slopes[0] && -slope < slopes[1]) { + if (slope.invertVertical().isBetween(startSlope, endSlope)) drawPixel(x + x1, y + y1, color); - } - if (slope >= slopes[2] && slope < slopes[3]) { - drawPixel(x - x1, y - y1, color); - } - if (-slope >= slopes[2] && -slope < slopes[3]) { + if (slope.invertHorizontal().isBetween(startSlope, endSlope)) drawPixel(x - x1, y + y1, color); - } + if (slope.invertVertical().isBetween(startSlope, endSlope)) + drawPixel(x - x1, y - y1, color); } } } From 04d4363607dc28932cd0453ad25739d1f714233f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 15 Jan 2020 17:21:56 +0100 Subject: [PATCH 098/714] Add the definition LCD_VERTICAL_INVERT --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index c4e6b6d53..d49225f07 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -36,7 +36,7 @@ constexpr uint8_t STASHED = 0x33; #define APPLY_OFFSET() x += offsetX; y += offsetY #define RESTORE_OFFSET() this->offsetX = offsetX, this->offsetY = offsetY -#if defined(PCBX10) && !defined(SIMU) +#if defined(LCD_VERTICAL_INVERT) #define MOVE_PIXEL_RIGHT(p, count) p -= count #else #define MOVE_PIXEL_RIGHT(p, count) p += count @@ -156,7 +156,7 @@ class BitmapBufferBase inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const { -#if defined(PCBX10) && !defined(SIMU) +#if defined(LCD_VERTICAL_INVERT) x = _width - x - 1; y = _height - y - 1; #endif @@ -294,7 +294,7 @@ class BitmapBuffer: public BitmapBufferBase inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const { -#if defined(PCBX10) && !defined(SIMU) +#if defined(LCD_VERTICAL_INVERT) x = _width - x - 1; y = _height - y - 1; #endif @@ -303,7 +303,7 @@ class BitmapBuffer: public BitmapBufferBase inline pixel_t * getPixelPtr(coord_t x, coord_t y) { -#if defined(PCBX10) && !defined(SIMU) +#if defined(LCD_VERTICAL_INVERT) x = _width - x - 1; y = _height - y - 1; #endif From 02810fc6abc03ccc2ca034cba327a22744c76e26 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 10 Feb 2020 21:45:52 +0100 Subject: [PATCH 099/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 6 +++--- radio/src/thirdparty/libopenui/src/button.h | 1 - radio/src/thirdparty/libopenui/src/mainwindow.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.h | 5 +++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 34baf7194..6548b4abf 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -717,7 +717,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) uint32_t fsize = *((uint32_t *)&buf[2]); uint32_t hsize = *((uint32_t *)&buf[10]); /* header size */ - uint32_t len = limit((uint32_t)4, (uint32_t)(hsize-14), (uint32_t)32); + uint32_t len = limit(4, hsize - 14, 32); result = f_read(&imgFile, buf, len, &read); if (result != FR_OK || read != len) { f_close(&imgFile); @@ -800,7 +800,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) switch (depth) { case 32: - for (int i=h-1; i>=0; i--) { + for (int i = h-1; i >= 0; i--) { pixel_t * dst = bmp->getPixelPtr(0, i); for (unsigned int j=0; j constexpr WindowFlags BUTTON_BACKGROUND = FORM_FLAGS_LAST << 1; constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2; diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 69ce7d136..51097b405 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -81,7 +81,7 @@ bool MainWindow::refresh() if (invalidatedRect.w) { if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); - BitmapBuffer * previous = lcd; + const BitmapBuffer * previous = lcd; lcdNextLayer(); lcdCopy(lcd->getData(), previous->getData()); } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 3e1715a60..fb2723e2a 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -21,11 +21,12 @@ #define _WINDOW_H_ #include -#include +#include #include +#include +#include #include #include -#include #include "bitmapbuffer.h" #include "libopenui_defines.h" #include "libopenui_helpers.h" From 71b7b12e194bea79a682b833f62b993c55075d60 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 11 Feb 2020 08:04:26 +0100 Subject: [PATCH 100/714] memcpy doesn't work correctly if the buffers are not aligned --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 3 +- radio/src/thirdparty/libopenui/src/memcpy.h | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 radio/src/thirdparty/libopenui/src/memcpy.h diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 6548b4abf..303eb350b 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -901,6 +901,7 @@ void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) } #endif // #if defined(TRACE_STB_MALLOC) +#include "memcpy.h" #include "thirdparty/Stb/stb_image.h" // fill 'data' with 'size' bytes. return number of bytes actually read @@ -954,7 +955,7 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) // convert to RGB565 or ARGB4444 format BitmapBuffer * bmp = new BitmapBuffer(n == 4 ? BMP_ARGB4444 : BMP_RGB565, w, h); - if (bmp == NULL) { + if (bmp == nullptr) { TRACE("load_stb() malloc failed"); stbi_image_free(img); return nullptr; diff --git a/radio/src/thirdparty/libopenui/src/memcpy.h b/radio/src/thirdparty/libopenui/src/memcpy.h new file mode 100644 index 000000000..10ff167df --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/memcpy.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _MEMCPY_H_ +#define _MEMCPY_H_ + +#include + +#if !defined(SIMU) + +inline void safeMemcpy(void * dest, const void * src, unsigned int size) +{ + if (((uint32_t)dest & 0x03) || ((uint32_t)src & 0x03)) { + for (int i = 0; i < size; i++) { + ((uint8_t *)dest)[i] = ((uint8_t *)src)[i]; + } + } + else { + memcpy(dest, src, size); + } +} + +#define memcpy safeMemcpy + +#endif + +#endif // _HELPERS_H_ From 117b99991b2e95dc97573a63e7975d77fb9b6b29 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 11 Feb 2020 18:28:30 +0100 Subject: [PATCH 101/714] Cosmetics --- radio/src/thirdparty/libopenui/src/memcpy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/memcpy.h b/radio/src/thirdparty/libopenui/src/memcpy.h index 10ff167df..21aaf1417 100644 --- a/radio/src/thirdparty/libopenui/src/memcpy.h +++ b/radio/src/thirdparty/libopenui/src/memcpy.h @@ -27,7 +27,7 @@ inline void safeMemcpy(void * dest, const void * src, unsigned int size) { if (((uint32_t)dest & 0x03) || ((uint32_t)src & 0x03)) { - for (int i = 0; i < size; i++) { + for (unsigned i = 0; i < size; i++) { ((uint8_t *)dest)[i] = ((uint8_t *)src)[i]; } } From aca4f4f1605e566787f73301513e907a853ab5d0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 12 Feb 2020 15:53:47 +0100 Subject: [PATCH 102/714] No more memcpy needed when using arm compiler 6.3.1 --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 1 - .../thirdparty/libopenui/src/bitmapbuffer.h | 2 +- radio/src/thirdparty/libopenui/src/memcpy.h | 43 ------------------- 3 files changed, 1 insertion(+), 45 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/src/memcpy.h diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 303eb350b..20e42c70c 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -901,7 +901,6 @@ void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) } #endif // #if defined(TRACE_STB_MALLOC) -#include "memcpy.h" #include "thirdparty/Stb/stb_image.h" // fill 'data' with 'size' bytes. return number of bytes actually read diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index d49225f07..f238a312a 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -249,7 +249,7 @@ class BitmapBuffer: public BitmapBufferBase , leakReported(false) #endif { - data = (uint16_t *)malloc(width*height*sizeof(uint16_t)); + data = (uint16_t *)malloc(width * height * sizeof(uint16_t)); data_end = data + (width * height); } diff --git a/radio/src/thirdparty/libopenui/src/memcpy.h b/radio/src/thirdparty/libopenui/src/memcpy.h deleted file mode 100644 index 21aaf1417..000000000 --- a/radio/src/thirdparty/libopenui/src/memcpy.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#ifndef _MEMCPY_H_ -#define _MEMCPY_H_ - -#include - -#if !defined(SIMU) - -inline void safeMemcpy(void * dest, const void * src, unsigned int size) -{ - if (((uint32_t)dest & 0x03) || ((uint32_t)src & 0x03)) { - for (unsigned i = 0; i < size; i++) { - ((uint8_t *)dest)[i] = ((uint8_t *)src)[i]; - } - } - else { - memcpy(dest, src, size); - } -} - -#define memcpy safeMemcpy - -#endif - -#endif // _HELPERS_H_ From 8b674f864a3805edd2fafbff356dce1235406aa9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 19 Feb 2020 14:03:21 +0100 Subject: [PATCH 103/714] MSVC++ compilation fix --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 5382a4080..19e598080 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -74,9 +74,16 @@ #if defined(__MINGW32__) || !defined(__GNUC__) #include + #include + #define sleep(x) Sleep(x) + #define strcasecmp _stricmp + #define strncasecmp _strnicmp + #define chdir _chdir + #define getcwd _getcwd // remove windows default definitions #undef OPAQUE #undef RGB + #undef EXTERN_C #endif #define OPACITY_MAX 0x0Fu From a92db98b9f24e4d92dec7eb84ff585c9af2bf10e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 19 Feb 2020 14:11:01 +0100 Subject: [PATCH 104/714] MSVC++ compilation fix --- radio/src/thirdparty/libopenui/src/libopenui_file.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp index 1cdb76061..2d5360b6d 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.cpp +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.cpp @@ -20,6 +20,7 @@ #include #include #include "libopenui_file.h" +#include "libopenui_defines.h" const char * getFileExtension(const char * filename, uint8_t size, uint8_t extMaxLen, uint8_t * fnlen, uint8_t * extlen) { From d965541267cae4533ab1ad65e3a2a7370ed3d557 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 24 Feb 2020 17:25:37 +0100 Subject: [PATCH 105/714] Touch support reworked --- .../src/thirdparty/libopenui/src/mainwindow.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 51097b405..8d43c7acd 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -42,20 +42,21 @@ void MainWindow::emptyTrash() void MainWindow::checkEvents() { #if defined(HARDWARE_TOUCH) + if (touchPanelEventOccured()) { + touchPanelRead(); + } + if (touchState.event == TE_DOWN) { onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); - // touchState.Event = TE_NONE; } else if (touchState.event == TE_UP) { - touchState.event = TE_NONE; onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); + touchState.event = TE_NONE; } - else if (touchState.event == TE_SLIDE) { - coord_t x = touchState.x - touchState.lastX; - coord_t y = touchState.y - touchState.lastY; - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, x, y); - touchState.lastX = touchState.x; - touchState.lastY = touchState.y; + else if (touchState.event == TE_SLIDE && (touchState.deltaX || touchState.deltaY)) { + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); + touchState.deltaX = 0; + touchState.deltaY = 0; } #endif From 47cf1fbefe2a486ef5f1ab47f4bc800ff9309344 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 2 Mar 2020 10:03:41 +0100 Subject: [PATCH 106/714] Memory leak removed --- radio/src/thirdparty/libopenui/src/static.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 4b509aa8f..b8ecac3b9 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -86,6 +86,7 @@ class StaticBitmap: public Window { void setBitmap(const char * filename) { + delete bitmap; bitmap = BitmapBuffer::loadBitmap(filename); invalidate(); } From 7c3cbddbb346a488e73d806cf3ea47a4995d1c37 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 3 Mar 2020 10:32:56 +0100 Subject: [PATCH 107/714] Cosmetics --- .../libopenui/src/libopenui_defines.h | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 19e598080..2ff688979 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -23,31 +23,31 @@ #include "libopenui_types.h" /* lcd common flags */ -#define BLINK 0x01 +#define BLINK 0x01u /* drawText flags */ -#define INVERS 0x02 -#define LEFT 0x00 /* align left */ -#define CENTERED 0x04 /* align center */ -#define RIGHT 0x08 /* align right */ -#define SHADOWED 0x80 /* black copy at +1 +1 */ +#define INVERS 0x02u +#define LEFT 0x00u /* align left */ +#define CENTERED 0x04u /* align center */ +#define RIGHT 0x08u /* align right */ +#define SHADOWED 0x80u /* black copy at +1 +1 */ /* drawNumber flags */ -#define LEADING0 0x10 -#define PREC1 0x20 -#define PREC2 0x30 +#define LEADING0 0x10u +#define PREC1 0x20u +#define PREC2 0x30u #define MODE(flags) ((((int8_t)(flags) & 0x30) - 0x10) >> 4) /* telemetry flags */ -#define NO_UNIT 0x40 +#define NO_UNIT 0x40u -#define FONT_MASK 0x0F00 -#define FONT_INDEX(flags) (((flags) & FONT_MASK) >> 8) -#define FONT(xx) (FONT_ ## xx ## _INDEX << 8) +#define FONT_MASK 0x0F00u +#define FONT_INDEX(flags) (((flags) & FONT_MASK) >> 8u) +#define FONT(xx) (FONT_ ## xx ## _INDEX << 8u) -#define TIMEHOUR 0x2000 -#define EXPANDED 0x2000 -#define VERTICAL 0x4000 +#define TIMEHOUR 0x2000u +#define EXPANDED 0x2000u +#define VERTICAL 0x4000u #define ARGB_SPLIT(color, a, r, g, b) \ uint16_t a = ((color) & 0xF000) >> 12; \ From 5f8295eb406c7bf83c8e5f00d26f8d78ef85e325 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 3 Mar 2020 10:33:51 +0100 Subject: [PATCH 108/714] Table columns can now have a different width --- radio/src/thirdparty/libopenui/src/table.cpp | 12 ++--- radio/src/thirdparty/libopenui/src/table.h | 47 +++++++++++++------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index c20ab298f..d4eae2a7c 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -25,9 +25,9 @@ void Table::Header::paint(BitmapBuffer * dc) coord_t x = 10; if (!header.values.empty()) { dc->clear(TABLE_HEADER_BGCOLOR); - for (auto &value: header.values) { - dc->drawText(x, (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2, value.c_str(), TABLE_HEADER_FONT); - x += 200; + for (int i = 0; i < header.values.size(); i++) { + dc->drawText(x, (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2, header.values[i].c_str(), TABLE_HEADER_FONT); + x += columnsWidth[i]; } } } @@ -40,9 +40,9 @@ void Table::Body::paint(BitmapBuffer * dc) for (auto & line: lines) { dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, index == selection ? FOCUS_BGCOLOR : TABLE_BGCOLOR); x = 10; - for (auto &value: line.values) { - dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, value.c_str(), TABLE_BODY_FONT | (index == selection ? FOCUS_COLOR : DEFAULT_COLOR)); - x += 200; + for (int i = 0; i < line.values.size(); i++) { + dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), TABLE_BODY_FONT | (index == selection ? FOCUS_COLOR : DEFAULT_COLOR)); + x += columnsWidth[i]; } y += TABLE_LINE_HEIGHT; index += 1; diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 51f892137..aa62d3285 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -27,14 +27,20 @@ class Table: public Window { class Line { public: - std::list values; + explicit Line(uint8_t columnsCount): + values(columnsCount) + { + } + std::vector values; std::function onPress; }; class Header: public Window { public: - Header(Window * parent, const rect_t & rect): - Window(parent, rect, OPAQUE) + Header(Table * parent, const rect_t & rect, uint8_t columnsCount): + Window(parent, rect, OPAQUE), + header(columnsCount), + columnsWidth(parent->columnsWidth) { } @@ -47,14 +53,16 @@ class Table: public Window { protected: Line header; + std::vector & columnsWidth; }; class Body: public Window { friend class Table; public: - Body(Window * parent, const rect_t & rect): - Window(parent, rect, OPAQUE) + Body(Table * parent, const rect_t & rect): + Window(parent, rect, OPAQUE), + columnsWidth(parent->columnsWidth) { } @@ -85,6 +93,7 @@ class Table: public Window { #endif protected: + std::vector & columnsWidth; std::vector lines; int selection = -1; }; @@ -92,10 +101,11 @@ class Table: public Window { public: Table(Window * parent, const rect_t & rect, uint8_t columnsCount): Window(parent, rect), - header(this, {0, 0, width(), 0}), - body(this, {0, 0, width(), height()}) + header(this, {0, 0, width(), 0}, columnsCount), + body(this, {0, 0, width(), height()}), + columnsCount(columnsCount), + columnsWidth(columnsCount, width() / columnsCount) { - setColumnsCount(columnsCount); } ~Table() override @@ -104,9 +114,11 @@ class Table: public Window { body.detach(); } - void setColumnsCount(uint8_t value) + void setColumnsWidth(const coord_t width[]) { - count = value; + for (uint8_t i = 0; i < columnsCount; i++) { + columnsWidth[i] = width[i]; + } } int getSelection() const @@ -134,18 +146,18 @@ class Table: public Window { header.setHeight(TABLE_HEADER_HEIGHT); body.setTop(TABLE_HEADER_HEIGHT); body.setHeight(height() - TABLE_HEADER_HEIGHT); - Line line; - for (uint8_t i = 0; i < count; i++) { - line.values.push_back(values[i]); + Line line(columnsCount); + for (uint8_t i = 0; i < columnsCount; i++) { + line.values[i] = values[i]; } header.setLine(line); } void addLine(const char * const values[], std::function onPress = nullptr) { - Line line; - for (uint8_t i = 0; i < count; i++) { - line.values.push_back(values[i]); + Line line(columnsCount); + for (uint8_t i = 0; i < columnsCount; i++) { + line.values[i] = values[i]; } line.onPress = onPress; body.addLine(line); @@ -158,7 +170,8 @@ class Table: public Window { } protected: - uint8_t count = 1; + uint8_t columnsCount; + std::vector columnsWidth; Header header; Body body; }; From 561a98f0c4a2226d13c740a6fac52c83bca2a5cc Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 3 Mar 2020 11:29:40 +0100 Subject: [PATCH 109/714] Warning removed --- radio/src/thirdparty/libopenui/src/table.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index aa62d3285..b2a55a182 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -101,10 +101,10 @@ class Table: public Window { public: Table(Window * parent, const rect_t & rect, uint8_t columnsCount): Window(parent, rect), - header(this, {0, 0, width(), 0}, columnsCount), - body(this, {0, 0, width(), height()}), columnsCount(columnsCount), - columnsWidth(columnsCount, width() / columnsCount) + columnsWidth(columnsCount, width() / columnsCount), + header(this, {0, 0, width(), 0}, columnsCount), + body(this, {0, 0, width(), height()}) { } From 0704de36c4350015be4e911c7ccd8c66bf387cf9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 3 Mar 2020 11:30:39 +0100 Subject: [PATCH 110/714] Warning removed --- radio/src/thirdparty/libopenui/src/table.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index d4eae2a7c..8427e5d2c 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -25,7 +25,7 @@ void Table::Header::paint(BitmapBuffer * dc) coord_t x = 10; if (!header.values.empty()) { dc->clear(TABLE_HEADER_BGCOLOR); - for (int i = 0; i < header.values.size(); i++) { + for (unsigned i = 0; i < header.values.size(); i++) { dc->drawText(x, (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2, header.values[i].c_str(), TABLE_HEADER_FONT); x += columnsWidth[i]; } @@ -40,7 +40,7 @@ void Table::Body::paint(BitmapBuffer * dc) for (auto & line: lines) { dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, index == selection ? FOCUS_BGCOLOR : TABLE_BGCOLOR); x = 10; - for (int i = 0; i < line.values.size(); i++) { + for (unsigned i = 0; i < line.values.size(); i++) { dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), TABLE_BODY_FONT | (index == selection ? FOCUS_COLOR : DEFAULT_COLOR)); x += columnsWidth[i]; } From bc1b1ee3a97a15ce1f9edb5a5d1a84573fc058de Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 3 Mar 2020 17:15:42 +0100 Subject: [PATCH 111/714] Cosmetics --- radio/src/thirdparty/libopenui/src/form.cpp | 12 +++++++++--- radio/src/thirdparty/libopenui/src/modal_window.cpp | 6 ++---- radio/src/thirdparty/libopenui/src/modal_window.h | 4 ---- radio/src/thirdparty/libopenui/src/window.cpp | 3 +-- radio/src/thirdparty/libopenui/src/window.h | 4 ++-- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 0159bc9ef..25f9575c6 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -35,7 +35,7 @@ FormField::FormField(Window *parent, const rect_t & rect, WindowFlags windowFlag #if defined(HARDWARE_KEYS) void FormField::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", FormField::getWindowDebugString().c_str(), event); + TRACE_WINDOWS("%s received event 0x%X", FormField::getWindowDebugString("FormField").c_str(), event); if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { if (next) { @@ -151,7 +151,7 @@ void FormGroup::setFocus(uint8_t flag) #if defined(HARDWARE_KEYS) void FormGroup::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormGroup").c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { first->setFocus(); @@ -159,6 +159,12 @@ void FormGroup::onEvent(event_t event) else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus()) { setFocus(SET_FOCUS_DEFAULT); } + else if (event == EVT_ROTARY_RIGHT && !next) { + first->setFocus(); + } + else if (event == EVT_ROTARY_LEFT && !previous) { + last->setFocus(); + } else { FormField::onEvent(event); } @@ -180,7 +186,7 @@ void FormGroup::paint(BitmapBuffer * dc) #if defined(HARDWARE_KEYS) void FormWindow::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormWindow").c_str(), event); if (event == EVT_KEY_BREAK(KEY_EXIT) && first) { Window * currentFocus = getFocus(); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index f7c1de20a..303071027 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -22,10 +22,8 @@ #include "font.h" ModalWindow::ModalWindow(Window * parent): - Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}) -#if !defined(HARDWARE_TOUCH) - , previousFocus(focusWindow) -#endif + Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}), + previousFocus(focusWindow) { } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 0f33abf71..39bc6103a 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -36,11 +36,9 @@ class ModalWindow: public Window void deleteLater() { -#if !defined(HARDWARE_TOUCH) if (previousFocus) { previousFocus->setFocus(); } -#endif Window::deleteLater(); } @@ -68,9 +66,7 @@ class ModalWindow: public Window #endif protected: -#if !defined(HARDWARE_TOUCH) Window * previousFocus = nullptr; -#endif }; class ModalWindowContent: public Window { diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 328b0479a..99dbec8d8 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -286,7 +286,6 @@ void Window::checkEvents() if (this == focusWindow) { event_t event = getWindowEvent(); if (event) { - TRACE_WINDOWS("Event 0x%x received ...", event); this->onEvent(event); } } @@ -315,7 +314,7 @@ void Window::checkEvents() void Window::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString().c_str(), event); + TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString("Window").c_str(), event); if (parent) { parent->onEvent(event); } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index fb2723e2a..1f8ed5a53 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -87,9 +87,9 @@ class Window { return result; } - std::string getWindowDebugString() + std::string getWindowDebugString(const char * name = nullptr) { - return getIndentString() + getName() + " " + getRectString(); + return getIndentString() + getName() + (name ? std::string("/") + name : "") + " " + getRectString(); } #endif From 7bb48f92b0d6c60d0bc2e7f24b0e162753456b60 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Mar 2020 09:49:58 +0100 Subject: [PATCH 112/714] Form navigation issues + traces --- radio/src/thirdparty/libopenui/src/form.cpp | 12 ++++++------ radio/src/thirdparty/libopenui/src/window.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/window.h | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 25f9575c6..f4063f317 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -35,7 +35,7 @@ FormField::FormField(Window *parent, const rect_t & rect, WindowFlags windowFlag #if defined(HARDWARE_KEYS) void FormField::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", FormField::getWindowDebugString("FormField").c_str(), event); + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormField").c_str(), event); if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { if (next) { @@ -97,7 +97,7 @@ void FormGroup::addField(FormField * field) void FormGroup::setFocus(uint8_t flag) { - TRACE_WINDOWS("FormGroup::setFocus(%d)", flag); + TRACE_WINDOWS("%s setFocus(%d)", getWindowDebugString("FormGroup").c_str(), flag); if (windowFlags & FORM_FORWARD_FOCUS) { switch (flag) { @@ -154,16 +154,16 @@ void FormGroup::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormGroup").c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { - first->setFocus(); + first->setFocus(SET_FOCUS_FIRST); } - else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus()) { + else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus() && !(windowFlags & FORM_FORWARD_FOCUS)) { setFocus(SET_FOCUS_DEFAULT); } else if (event == EVT_ROTARY_RIGHT && !next) { - first->setFocus(); + first->setFocus(SET_FOCUS_FIRST); } else if (event == EVT_ROTARY_LEFT && !previous) { - last->setFocus(); + last->setFocus(SET_FOCUS_FIRST); } else { FormField::onEvent(event); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 99dbec8d8..6fbbfe257 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -209,11 +209,11 @@ void Window::fullPaint(BitmapBuffer * dc) } if (paintNeeded) { - TRACE_WINDOWS("%s", getWindowDebugString().c_str()); + TRACE_WINDOWS_INDENT("%s", getWindowDebugString().c_str()); paint(dc); } else { - TRACE_WINDOWS("%s (skipped)", getWindowDebugString().c_str()); + TRACE_WINDOWS_INDENT("%s (skipped)", getWindowDebugString().c_str()); } if (!(windowFlags & NO_SCROLLBAR)) { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 1f8ed5a53..009e7a061 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -87,9 +87,9 @@ class Window { return result; } - std::string getWindowDebugString(const char * name = nullptr) + std::string getWindowDebugString(const char * name = nullptr, bool indent = true) { - return getIndentString() + getName() + (name ? std::string("/") + name : "") + " " + getRectString(); + return getName() + (name ? std::string("[") + name + "]" : "") + " " + getRectString(); } #endif From a37ac5ebea0776422b33b31109171f39edbded6a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Mar 2020 09:58:21 +0100 Subject: [PATCH 113/714] Cosmetics --- radio/src/thirdparty/libopenui/src/button.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 167a348ae..48c44dd9a 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -34,7 +34,7 @@ void Button::onPress() #if defined(HARDWARE_KEYS) void Button::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("Button").c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { onPress(); From 3ea15c5586e778b8f6c33f45fbb95ce50b2d943c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Mar 2020 11:15:54 +0100 Subject: [PATCH 114/714] Crash when pressing [ENTER] in a menu when nothing is selected --- radio/src/thirdparty/libopenui/src/menu.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index a76991f28..31fd022ee 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -42,8 +42,13 @@ void MenuBody::onEvent(event_t event) select(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1); } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - Window::onEvent(event); // the window above will be closed on event - lines[selectedIndex].onPress(); + if (selectedIndex < 0) { + select(0); + } + else { + Window::onEvent(event); // the window above will be closed on event + lines[selectedIndex].onPress(); + } } else if (event == EVT_KEY_BREAK(KEY_EXIT)) { if (onCancel) From 9d2a29c2c763d29600ec2e4495fca3de7ce96637 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Mar 2020 11:22:38 +0100 Subject: [PATCH 115/714] Cosmetics --- radio/src/thirdparty/libopenui/src/window.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 009e7a061..a22874922 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -89,7 +89,7 @@ class Window { std::string getWindowDebugString(const char * name = nullptr, bool indent = true) { - return getName() + (name ? std::string("[") + name + "]" : "") + " " + getRectString(); + return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); } #endif From 65f08451d563539b0f4aa25cb8fe22225e242314 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Mar 2020 12:48:00 +0100 Subject: [PATCH 116/714] Checkbox refresh issue --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index dd6dd0f85..9bdcf08c5 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -28,12 +28,12 @@ void CheckBox::paint(BitmapBuffer * dc) #if defined(HARDWARE_KEYS) void CheckBox::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("CheckBox").c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { onKeyPress(); _setValue(!getValue()); - invalidate({0, 0, 20, 20}); + invalidate(); } else { FormField::onEvent(event); @@ -47,7 +47,7 @@ bool CheckBox::onTouchEnd(coord_t x, coord_t y) onKeyPress(); _setValue(!getValue()); setFocus(); - invalidate({0, 0, 20, 20}); + invalidate(); return true; } #endif From 2957e54a0402c8dbea059ec25c7f3e250b5abcbe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Mar 2020 18:06:53 +0100 Subject: [PATCH 117/714] New automatic scroll function --- radio/src/thirdparty/libopenui/src/window.cpp | 30 +++++++++++-------- radio/src/thirdparty/libopenui/src/window.h | 2 ++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 6fbbfe257..0c272be86 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -165,23 +165,29 @@ void Window::scrollTo(Window * child) parent = parent->getParent(); } - coord_t left = offsetX + child->left(); - coord_t right = offsetX + child->right(); - coord_t top = offsetY + child->top(); - coord_t bottom = offsetY + child->bottom(); + const rect_t rect = { + offsetX + child->left(), + offsetY + child->top(), + child->width(), + child->height() + }; + scrollTo(rect); +} - if (top < scrollPositionY) { - setScrollPositionY(pageHeight ? top - (top % pageHeight) : top - 5); +void Window::scrollTo(const rect_t & rect) +{ + if (rect.top() < scrollPositionY) { + setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.top() - 5); } - else if (bottom > scrollPositionY + height() - 5) { - setScrollPositionY(pageHeight ? top - (top % pageHeight) : bottom - height() + 5); + else if (rect.bottom() > scrollPositionY + height() - 5) { + setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.bottom() - height() + 5); } - if (left < scrollPositionX) { - setScrollPositionX(pageWidth ? left - (left % pageWidth) : left - 5); + if (rect.left() < scrollPositionX) { + setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.left() - 5); } - else if (right > scrollPositionX + width() - 5) { - setScrollPositionX(pageWidth ? left - (left % pageWidth) : right - width() + 5); + else if (rect.right() > scrollPositionX + width() - 5) { + setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.right() - width() + 5); } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index a22874922..63e01fe7d 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -144,6 +144,8 @@ class Window { return focusWindow; } + void scrollTo(const rect_t & rect); + void scrollTo(Window * child); static void clearFocus(); From 8002607b66fb843d1c32ad3ce121f2f5f0ac8806 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Mar 2020 18:07:15 +0100 Subject: [PATCH 118/714] Table events --- radio/src/thirdparty/libopenui/src/table.cpp | 47 ++++++++++++++ radio/src/thirdparty/libopenui/src/table.h | 68 +++++++++++++++----- 2 files changed, 98 insertions(+), 17 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 8427e5d2c..7966d4847 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -48,3 +48,50 @@ void Table::Body::paint(BitmapBuffer * dc) index += 1; } } + +#if defined(HARDWARE_TOUCH) +bool Table::Body::onTouchEnd(coord_t x, coord_t y) +{ + unsigned index = y / TABLE_LINE_HEIGHT; + if (index < lines.size()) { + auto onPress = lines[index].onPress; + if (onPress) + onPress(); + } + return true; +} +#endif + +#if defined(HARDWARE_KEYS) +void Table::Body::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + if (selection >= 0) { + auto onPress = lines[selection].onPress; + if (onPress) + onPress(); + } + } + if (event == EVT_ROTARY_RIGHT) { + int newSelection = (selection + 1) % lines.size(); + select(newSelection, true); + auto onSelect = lines[selection].onSelect; + if (onSelect) { + onSelect(); + } + } + else if (event == EVT_ROTARY_LEFT) { + int newSelection = selection == 0 ? lines.size() - 1 : selection - 1; + select(newSelection, true); + auto onSelect = lines[selection].onSelect; + if (onSelect) { + onSelect(); + } + } + else { + Window::onEvent(event); + } +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index b2a55a182..06f1cf992 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -33,6 +33,7 @@ class Table: public Window { } std::vector values; std::function onPress; + std::function onSelect; }; class Header: public Window { @@ -44,6 +45,13 @@ class Table: public Window { { } +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Table::Header"; + } +#endif + void setLine(const Line & line) { header = line; @@ -66,6 +74,13 @@ class Table: public Window { { } +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Table::Body"; + } +#endif + void addLine(const Line & line) { lines.push_back(line); @@ -77,19 +92,29 @@ class Table: public Window { lines.clear(); } + void select(int index, bool scroll) + { + selection = index; + if (scroll) { + const rect_t rect = { + 0, + index * TABLE_LINE_HEIGHT, + width(), + TABLE_LINE_HEIGHT + }; + scrollTo(rect); + } + invalidate(); + } + void paint(BitmapBuffer * dc) override; +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + #if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override - { - unsigned index = y / TABLE_LINE_HEIGHT; - if (index < lines.size()) { - auto onPress = lines[index].onPress; - if (onPress) - onPress(); - } - return true; - } + bool onTouchEnd(coord_t x, coord_t y) override; #endif protected: @@ -108,6 +133,13 @@ class Table: public Window { { } +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Table"; + } +#endif + ~Table() override { header.detach(); @@ -132,13 +164,14 @@ class Table: public Window { body.invalidate(); } - void setSelection(int index, bool scroll = false) + void setFocus(uint8_t flag) override { - body.selection = index; - if (scroll) { - body.setScrollPositionY(index * TABLE_LINE_HEIGHT); - } - body.invalidate(); + body.setFocus(flag); + } + + void select(int index, bool scroll = true) + { + body.select(index, scroll); } void setHeader(const char * const values[]) @@ -153,13 +186,14 @@ class Table: public Window { header.setLine(line); } - void addLine(const char * const values[], std::function onPress = nullptr) + void addLine(const char * const values[], std::function onPress = nullptr, std::function onSelect = nullptr) { Line line(columnsCount); for (uint8_t i = 0; i < columnsCount; i++) { line.values[i] = values[i]; } line.onPress = onPress; + line.onSelect = onSelect; body.addLine(line); } From e71dca77ea2beb7c998989fe7cd9de9f6c0bea02 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 5 Mar 2020 10:02:17 +0100 Subject: [PATCH 119/714] Cosmetics --- radio/src/thirdparty/libopenui/tools/encode-bitmap.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py index ff0962c25..a6768a3cf 100755 --- a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py +++ b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py @@ -1,6 +1,5 @@ #!/usr/bin/env python - import argparse from PIL import Image @@ -121,7 +120,7 @@ class ImageEncoder: self.f.write("\n") self.encode_end() - def encode_16bits(self, image): + def encode_5_6_5(self, image): width, height = image.size self.write_size(width, height) for y in range(height): @@ -132,7 +131,7 @@ class ImageEncoder: self.encode_byte(val >> 8) self.encode_end() - def encode_16bits_alpha(self, image): + def encode_4_4_4_4(self, image): width, height = image.size self.write_size(width, height) for y in range(height): @@ -181,9 +180,9 @@ def main(): elif args.format == "8bits": encoder.encode_8bits(image) elif args.format == "4/4/4/4": - encoder.encode_16bits_alpha(image) + encoder.encode_4_4_4_4(image) elif args.format == "5/6/5": - encoder.encode_16bits(image) + encoder.encode_5_6_5(image) if __name__ == "__main__": From e66a95b60ca747543a2671ad0da990555f13b72d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 5 Mar 2020 11:24:13 +0100 Subject: [PATCH 120/714] Warning fixed --- radio/src/thirdparty/libopenui/src/checkbox.h | 2 +- radio/src/thirdparty/libopenui/src/form.h | 2 +- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- radio/src/thirdparty/libopenui/src/modal_window.h | 2 +- radio/src/thirdparty/libopenui/src/textedit.h | 2 +- radio/src/thirdparty/libopenui/src/window.h | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index c0b2193d9..61e1ebce9 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -25,7 +25,7 @@ class CheckBox : public FormField { public: CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, - LcdFlags flags = 0) : + LcdFlags /*flags*/ = 0) : FormField(parent, rect), _getValue(getValue), _setValue(setValue) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index ec37a6d64..635e42860 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -137,7 +137,7 @@ class FormWindow: public FormGroup { #endif protected: - void paint(BitmapBuffer * dc) override + void paint(BitmapBuffer *) override { } diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 31fd022ee..998c86d3a 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -62,7 +62,7 @@ void MenuBody::onEvent(event_t event) #endif #if defined(HARDWARE_TOUCH) -bool MenuBody::onTouchEnd(coord_t x, coord_t y) +bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { int index = y / MENUS_LINE_HEIGHT; lines[index].onPress(); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 39bc6103a..75eda6700 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -45,7 +45,7 @@ class ModalWindow: public Window void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_TOUCH) - bool onTouchStart(coord_t x, coord_t y) override + bool onTouchStart(coord_t /*x*/, coord_t /*y*/) override { return true; } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 90f316169..292bbecb8 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -26,7 +26,7 @@ class TextEdit : public FormField { friend class TextKeyboard; public: - TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags flags = 0) : + TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags /*flags*/ = 0) : FormField(parent, rect), value(value), length(length) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 63e01fe7d..80f7655e9 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -87,7 +87,7 @@ class Window { return result; } - std::string getWindowDebugString(const char * name = nullptr, bool indent = true) + std::string getWindowDebugString(const char * name = nullptr) { return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); } @@ -362,7 +362,7 @@ class Window { void fullPaint(BitmapBuffer * dc); - virtual void paint(BitmapBuffer * dc) + virtual void paint(BitmapBuffer *) { } From 4e47630eec9cda41d5ba32993f13881314cfbec3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 5 Mar 2020 11:25:45 +0100 Subject: [PATCH 121/714] Protection --- radio/src/thirdparty/libopenui/src/form.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index f4063f317..592598e5d 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -154,16 +154,19 @@ void FormGroup::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormGroup").c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { - first->setFocus(SET_FOCUS_FIRST); + if (first) + first->setFocus(SET_FOCUS_FIRST); } else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus() && !(windowFlags & FORM_FORWARD_FOCUS)) { setFocus(SET_FOCUS_DEFAULT); } else if (event == EVT_ROTARY_RIGHT && !next) { - first->setFocus(SET_FOCUS_FIRST); + if (first) + first->setFocus(SET_FOCUS_FIRST); } else if (event == EVT_ROTARY_LEFT && !previous) { - last->setFocus(SET_FOCUS_FIRST); + if (last) + last->setFocus(SET_FOCUS_FIRST); } else { FormField::onEvent(event); From 94c6ab1ddd0878b7bb78648c1b76bbc3b94eb69a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 5 Mar 2020 23:07:17 +0100 Subject: [PATCH 122/714] New BufferedWindow class for avoiding redrawing a window which doesn't need --- .../thirdparty/libopenui/src/bitmapbuffer.h | 3 +- .../thirdparty/libopenui/src/bufferedwindow.h | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/bufferedwindow.h diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index f238a312a..0a9a4387a 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -241,7 +241,6 @@ class BitmapBuffer: public BitmapBufferBase #endif public: - BitmapBuffer(uint8_t format, uint16_t width, uint16_t height): BitmapBufferBase(format, width, height, nullptr), dataAllocated(true) @@ -390,7 +389,7 @@ class BitmapBuffer: public BitmapBufferBase coord_t drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); template - void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0) + void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx = 0, coord_t srcy = 0, coord_t w = 0, coord_t h = 0, float scale = 0) { if (!data || !bmp) return; diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h new file mode 100644 index 000000000..7a7826f8d --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _BUFFERED_WINDOW_H_ +#define _BUFFERED_WINDOW_H_ + +#include "window.h" + +class BufferedWindow: public Window +{ + public: + BufferedWindow(Window * parent, const rect_t & rect): + Window(parent, rect, OPAQUE) + { + } + + ~BufferedWindow() override + { + delete bitmap; + } + + void paint(BitmapBuffer * dc) override + { + if (!bitmap) { + paintInit(dc); + bitmap = new BitmapBuffer(BMP_RGB565, width(), height()); + bitmap->drawBitmap(0, 0, dc, dc->getOffsetX(), dc->getOffsetY()); + } + else { + dc->drawBitmap(0, 0, bitmap); + } + } + + protected: + BitmapBuffer * bitmap = nullptr; + virtual void paintInit(BitmapBuffer * dc) = 0; +}; + +#endif // _BUFFERED_WINDOW_H_ From 7d173106818e14685fc0e74293c3b55d70ea0a62 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 6 Mar 2020 21:34:43 +0100 Subject: [PATCH 123/714] Small refactoring --- radio/src/thirdparty/libopenui/src/libopenui_types.h | 10 ++++++++++ radio/src/thirdparty/libopenui/src/window.cpp | 6 +++--- radio/src/thirdparty/libopenui/src/window.h | 5 ----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_types.h b/radio/src/thirdparty/libopenui/src/libopenui_types.h index 1dbcc4086..683829f66 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_types.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_types.h @@ -28,6 +28,11 @@ struct point_t { coord_t x; coord_t y; + + bool operator != (const point_t & b) const + { + return x != b.x || y != b.y; + } }; struct rect_t @@ -53,6 +58,11 @@ struct rect_t { return y + h; } + + bool contains(coord_t x, coord_t y) const + { + return (x >= this->x && x < this->x + this->w && y >= this->y && y < this->y + this->h); + } }; typedef uint32_t LcdFlags; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0c272be86..86820cb49 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -331,7 +331,7 @@ bool Window::onTouchStart(coord_t x, coord_t y) { for (auto it = children.rbegin(); it != children.rend(); ++it) { auto child = *it; - if (pointInRect(x, y, child->rect)) { + if (child->rect.contains(x, y)) { if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { return true; } @@ -345,7 +345,7 @@ bool Window::forwardTouchEnd(coord_t x, coord_t y) { for (auto it = children.rbegin(); it != children.rend(); ++it) { auto child = *it; - if (pointInRect(x, y, child->rect)) { + if (child->rect.contains(x, y)) { if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { return true; } @@ -364,7 +364,7 @@ bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, { for (auto it = children.rbegin(); it != children.rend(); ++it) { auto child = *it; - if (pointInRect(startX, startY, child->rect)) { + if (child->rect.contains(startX, startY)) { if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { return true; } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 80f7655e9..ed4a86d1b 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -297,11 +297,6 @@ class Window { void drawVerticalScrollbar(BitmapBuffer * dc); - static bool pointInRect(coord_t x, coord_t y, rect_t & rect) - { - return (x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h); - } - virtual void onEvent(event_t event); void adjustInnerHeight(); From 55f8dabe127f5b9677e3e718f867dca5440b80c7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 9 Mar 2020 17:06:36 +0100 Subject: [PATCH 124/714] An annulus sector with equal start and end angles is drawn as a line --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 20e42c70c..ab9b8bb46 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -344,6 +344,10 @@ class Slope { void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags) { + if (endAngle == startAngle) { + endAngle += 1; + } + Slope startSlope(startAngle); Slope endSlope(endAngle); From db61c7c7b8f1e01c3bd451c1dea2467263a51b5d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 10 Mar 2020 17:28:25 +0100 Subject: [PATCH 125/714] Window::moveWindowsTop now invalidates the window if needed --- radio/src/thirdparty/libopenui/src/window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 86820cb49..1b0d9b0d0 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -406,6 +406,7 @@ void Window::moveWindowsTop(coord_t y, coord_t delta) for (auto child: children) { if (child->rect.y > y) { child->rect.y += delta; + invalidate(); } } innerHeight += delta; From 91d00309ed102c8b1ca13a52e53a97fc439e7a62 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 11 Mar 2020 11:50:24 +0100 Subject: [PATCH 126/714] Scroll issues --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 1 + radio/src/thirdparty/libopenui/src/mainwindow.cpp | 13 +++++++++---- radio/src/thirdparty/libopenui/src/window.cpp | 3 +++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index ab9b8bb46..dc182bb54 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -509,6 +509,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ break; } else if (c >= 0xFE) { + // CJK char s++; c = uint8_t(*s) + ((c & 0x01) << 8) - 1; if (c >= 0x101) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 8d43c7acd..c52910aa2 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -53,10 +53,15 @@ void MainWindow::checkEvents() onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); touchState.event = TE_NONE; } - else if (touchState.event == TE_SLIDE && (touchState.deltaX || touchState.deltaY)) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); - touchState.deltaX = 0; - touchState.deltaY = 0; + else if (touchState.event == TE_SLIDE) { + if (touchState.deltaX) { + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, 0); + touchState.deltaX = 0; + } + else if (touchState.deltaY) { + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, 0, touchState.deltaY); + touchState.deltaY = 0; + } } #endif diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 1b0d9b0d0..0cd244818 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -362,6 +362,9 @@ bool Window::onTouchEnd(coord_t x, coord_t y) bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) { + startX += getScrollPositionX(); + startY += getScrollPositionY(); + for (auto it = children.rbegin(); it != children.rend(); ++it) { auto child = *it; if (child->rect.contains(startX, startY)) { From 86e24f417a0e4d0497c8446069724f91ef679fb4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 11 Mar 2020 19:22:46 +0100 Subject: [PATCH 127/714] Possibility to PUSH_FRONT a field in a form --- .../thirdparty/libopenui/src/basenumberedit.h | 8 ++-- radio/src/thirdparty/libopenui/src/form.cpp | 38 ++++++++++++------- radio/src/thirdparty/libopenui/src/form.h | 4 +- .../thirdparty/libopenui/src/numberedit.cpp | 8 ++-- .../src/thirdparty/libopenui/src/numberedit.h | 2 +- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.h | 24 +++++++----- 7 files changed, 49 insertions(+), 37 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 7b8c9549f..e7d6daf0e 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -25,13 +25,12 @@ class BaseNumberEdit : public FormField { public: BaseNumberEdit(Window * parent, const rect_t &rect, int32_t vmin, int32_t vmax, - std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0): - FormField(parent, rect), + std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0): + FormField(parent, rect, windowFlags, textFlags), vmin(vmin), vmax(vmax), _getValue(std::move(getValue)), - _setValue(std::move(setValue)), - flags(flags) + _setValue(std::move(setValue)) { } @@ -98,7 +97,6 @@ class BaseNumberEdit : public FormField { int32_t step = 1; std::function _getValue; std::function _setValue; - LcdFlags flags; }; #endif // _BASENUMBEREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 592598e5d..c1dd1ce72 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -21,13 +21,13 @@ #include "bitmapbuffer.h" #include "libopenui_config.h" -FormField::FormField(Window *parent, const rect_t & rect, WindowFlags windowFlags) : - Window(parent, rect, windowFlags) +FormField::FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags) : + Window(parent, rect, windowFlags, textFlags) { if (!(windowFlags & NO_FOCUS)) { FormGroup * form = dynamic_cast(parent); if (form) { - form->addField(this); + form->addField(this, windowFlags & PUSH_FRONT); } } } @@ -74,25 +74,35 @@ void FormField::paint(BitmapBuffer * dc) } } -void FormGroup::addField(FormField * field) +void FormGroup::addField(FormField * field, bool front) { if (field->getWindowFlags() & FORM_DETACHED) return; if (!first) { first = field; - if (windowFlags & FORM_FORWARD_FOCUS) - link(previous, field); + last = field; } - if (last) - link(last, field); - last = field; - if (!(windowFlags & FORM_FORWARD_FOCUS)) - link(field, first); - else - field->setNextField(this); - if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) + if (front) { + if (first) + link(field, first); + first = field; + } + else { + if (last) + link(last, field); + last = field; + } + if (windowFlags & FORM_FORWARD_FOCUS) { + last->setNextField(this); + link(previous, first); + } + else { + link(last, first); + } + if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { field->setFocus(); + } } void FormGroup::setFocus(uint8_t flag) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 635e42860..ba7253bb6 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -30,7 +30,7 @@ constexpr WindowFlags FORM_FLAGS_LAST = FORM_NO_BORDER; class FormField: public Window { public: - FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0); + FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); inline void setNextField(FormField *field) { @@ -109,7 +109,7 @@ class FormGroup: public FormField { void setFocus(uint8_t flag = SET_FOCUS_DEFAULT) override; - void addField(FormField * field); + void addField(FormField * field, bool front = false); protected: FormField * first = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 4e2dd4d96..4d1f2d8b2 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -23,8 +23,8 @@ #include "keyboard_number.h" #endif -NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, LcdFlags flags): - BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), flags) +NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, WindowFlags windowFlags, LcdFlags textFlags): + BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags) { } @@ -47,9 +47,9 @@ void NumberEdit::paint(BitmapBuffer * dc) if (displayFunction) displayFunction(dc, textColor, value); else if (value == 0 && !zeroText.empty()) - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | flags); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | textFlags); else - dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | flags, 0, prefix.c_str(), suffix.c_str()); + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); } void NumberEdit::onEvent(event_t event) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 793df432e..ff18d61f7 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -26,7 +26,7 @@ class NumberEdit : public BaseNumberEdit { public: NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, - std::function getValue, std::function setValue = nullptr, LcdFlags flags = 0); + std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); #if defined(DEBUG_WINDOWS) std::string getName() override diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0cd244818..2259ef18f 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -32,7 +32,7 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc textFlags(textFlags) { if (parent) { - parent->addChild(this); + parent->addChild(this, windowFlags & PUSH_FRONT); if (!(windowFlags & TRANSPARENT)) { invalidate(); } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index ed4a86d1b..b8890ea1a 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -39,14 +39,15 @@ typedef uint32_t WindowFlags; #undef TRANSPARENT #endif -constexpr WindowFlags OPAQUE = 1u << 0; -constexpr WindowFlags TRANSPARENT = 1u << 1; -constexpr WindowFlags NO_SCROLLBAR = 1u << 2; -constexpr WindowFlags NO_FOCUS = 1u << 3; -constexpr WindowFlags FORWARD_SCROLL = 1u << 4; -constexpr WindowFlags REFRESH_ALWAYS = 1u << 5; -constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6; -constexpr WindowFlags WINDOW_FLAGS_LAST = PAINT_CHILDREN_FIRST; +constexpr WindowFlags OPAQUE = 1u << 0u; +constexpr WindowFlags TRANSPARENT = 1u << 1u; +constexpr WindowFlags NO_SCROLLBAR = 1u << 2u; +constexpr WindowFlags NO_FOCUS = 1u << 3u; +constexpr WindowFlags FORWARD_SCROLL = 1u << 4u; +constexpr WindowFlags REFRESH_ALWAYS = 1u << 5u; +constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6u; +constexpr WindowFlags PUSH_FRONT = 1u << 7u; +constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; enum SetFocusFlag { SET_FOCUS_DEFAULT, @@ -340,9 +341,12 @@ class Window { void attach(Window * window); - void addChild(Window * window) + void addChild(Window * window, bool front = false) { - children.push_back(window); + if (front) + children.push_front(window); + else + children.push_back(window); } void removeChild(Window * window) From b121895171535c54ae2765c69d79744e6f78c198 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 16 Mar 2020 08:27:09 +0100 Subject: [PATCH 128/714] Cosmetics --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- radio/src/thirdparty/libopenui/src/static.h | 7 ++++++- radio/src/thirdparty/libopenui/src/window.cpp | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index c1dd1ce72..3ada0ae14 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -130,7 +130,7 @@ void FormGroup::setFocus(uint8_t flag) break; case SET_FOCUS_FORWARD: // TODO this test should be recursive - if (focusWindow == last || focusWindow->getParent() == last) { + if (focusWindow == last || focusWindow->getParent() == last || focusWindow->getParent()->getParent() == last) { if (next == this) first->setFocus(SET_FOCUS_FORWARD); else if (next) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index b8ecac3b9..767bd521b 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -85,9 +85,14 @@ class StaticBitmap: public Window { } void setBitmap(const char * filename) + { + setBitmap(BitmapBuffer::loadBitmap(filename)); + } + + void setBitmap(const BitmapBuffer * newBitmap) { delete bitmap; - bitmap = BitmapBuffer::loadBitmap(filename); + bitmap = newBitmap; invalidate(); } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 2259ef18f..70c5cc24c 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -407,12 +407,12 @@ coord_t Window::adjustHeight() void Window::moveWindowsTop(coord_t y, coord_t delta) { for (auto child: children) { - if (child->rect.y > y) { + if (child->rect.y >= y) { child->rect.y += delta; invalidate(); } } - innerHeight += delta; + setInnerHeight(innerHeight + delta); } void Window::invalidate(const rect_t & rect) From 37b737fd7c7ec5f60e8c6a63ee49ce5ba7a43873 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 16 Mar 2020 12:34:48 +0100 Subject: [PATCH 129/714] Expansion Panel added --- .../libopenui/src/expansion_panel.h | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/src/expansion_panel.h diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h new file mode 100644 index 000000000..d44e798a5 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _EXPANSION_PANEL_H_ +#define _EXPANSION_PANEL_H_ + +#include "form.h" + +class ExpansionPanel; + +class ExpansionPanelHeader: virtual public FormGroup { + public: + ExpansionPanelHeader(ExpansionPanel * parent); + + void onEvent(event_t event) override; + + bool onTouchEnd(coord_t, coord_t) override; +}; + + +class ExpansionPanel: public FormGroup { + friend class ExpansionPanelHeader; + + public: + ExpansionPanel(Window * parent, const rect_t & rect): + FormGroup(parent, rect, FORM_NO_BORDER | FORM_FORWARD_FOCUS) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ExpansionPanel()"; + } +#endif + + void toggle() + { + open(!isOpen); + } + + void open(bool state = true) + { + isOpen = state; + coord_t newHeight = (state ? header->height() + body->height() : header->height()); + parent->moveWindowsTop(bottom(), newHeight - height()); + setHeight(newHeight); + } + + void setFocus(uint8_t flag) override + { + if (!isOpen) + header->setFocus(flag); + else + FormGroup::setFocus(flag); + } + + FormGroup * body = nullptr; + + protected: + Window * header = nullptr; + bool isOpen = false; +}; + +ExpansionPanelHeader::ExpansionPanelHeader(ExpansionPanel * parent): + FormGroup(parent, {0, 0, parent->width(), parent->height()}, FORWARD_SCROLL) +{ +} + +bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) +{ + static_cast(parent)->toggle(); + setFocus(); + return true; +} + +void ExpansionPanelHeader::onEvent(event_t event) +{ + ExpansionPanel * panel = static_cast(parent); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + panel->toggle(); + } + else if (event == EVT_ROTARY_RIGHT && !panel->isOpen) { + panel->getNextField()->setFocus(SET_FOCUS_FORWARD); + } + else { + FormGroup::onEvent(event); + } +} + +#endif // _EXPANSION_PANEL_H_ From 17d7a4593495d43d04891a6dfb5e56b046cb87db Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 17 Mar 2020 16:49:24 +0100 Subject: [PATCH 130/714] POPUP_WARNING(...) is back on colorlcd (without breaking X7) --- radio/src/thirdparty/libopenui/src/libopenui_helpers.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index db5911410..d188a92bd 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -54,6 +54,7 @@ inline int divRoundClosest(const int n, const int d) return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d); } +// TODO duplicated code inline std::string TEXT_AT_INDEX(const char * val, uint8_t idx) { const char * start = val + 1 + idx * val[0]; From 7ceb25a51966cca2c8f10d00c9fc9975aac67212 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 18 Mar 2020 10:47:36 +0100 Subject: [PATCH 131/714] WindowFlags can be given on Choice creation --- radio/src/thirdparty/libopenui/src/choice.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/choice.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 5610157cc..919f1941d 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -22,8 +22,8 @@ #include "theme.h" Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, - std::function getValue, std::function setValue) : - ChoiceBase(parent, rect), + std::function getValue, std::function setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), values(values), vmin(vmin), vmax(vmax), diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 1442bdcf2..8b0d1092b 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -29,8 +29,8 @@ enum ChoiceType { class ChoiceBase : public FormField { public: - ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN): - FormField(parent, rect), + ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0): + FormField(parent, rect, windowFlags), type(type) { } @@ -46,7 +46,7 @@ class ChoiceBase : public FormField { class Choice : public ChoiceBase { public: - Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr); + Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); #if defined(DEBUG_WINDOWS) std::string getName() override From 74fa59ebec0d6ac476543fb52e8b83860fb490e7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 18 Mar 2020 11:46:04 +0100 Subject: [PATCH 132/714] BufferedWindow splitted into OpaqueBufferedWindow and TransparentBufferedWindow --- .../thirdparty/libopenui/src/bufferedwindow.h | 69 +++++++++++++++---- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h index 7a7826f8d..74aa70e24 100644 --- a/radio/src/thirdparty/libopenui/src/bufferedwindow.h +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -22,34 +22,73 @@ #include "window.h" -class BufferedWindow: public Window +template +class BufferedWindow: public T { public: - BufferedWindow(Window * parent, const rect_t & rect): - Window(parent, rect, OPAQUE) - { - } + using T::T; ~BufferedWindow() override { delete bitmap; } - void paint(BitmapBuffer * dc) override + void forcePaint() { - if (!bitmap) { - paintInit(dc); - bitmap = new BitmapBuffer(BMP_RGB565, width(), height()); - bitmap->drawBitmap(0, 0, dc, dc->getOffsetX(), dc->getOffsetY()); - } - else { - dc->drawBitmap(0, 0, bitmap); - } + paintUpdateNeeded = true; + T::invalidate(); } protected: BitmapBuffer * bitmap = nullptr; - virtual void paintInit(BitmapBuffer * dc) = 0; + bool paintUpdateNeeded = false; + virtual void paintUpdate(BitmapBuffer * dc) = 0; +}; + +template +class OpaqueBufferedWindow: public BufferedWindow +{ + public: + using BufferedWindow::BufferedWindow; + + void paint(BitmapBuffer * dc) override + { + if (!this->bitmap) { + this->bitmap = new BitmapBuffer(BMP_RGB565, T::width(), T::height()); + this->paintUpdateNeeded = true; + } + + if (this->paintUpdateNeeded) { + this->paintUpdate(this->bitmap); + this->paintUpdateNeeded = false; + } + + dc->drawBitmap(0, 0, this->bitmap); + } +}; + +template +class TransparentBufferedWindow: public BufferedWindow +{ + public: + using BufferedWindow::BufferedWindow; + + void paint(BitmapBuffer * dc) override + { + if (!BufferedWindow::bitmap) { + this->bitmap = new BitmapBuffer(BMP_RGB565, T::width(), T::height()); + this->paintUpdateNeeded = true; + } + + if (this->paintUpdateNeeded) { + this->paintUpdate(dc); + this->bitmap->drawBitmap(0, 0, dc, dc->getOffsetX(), dc->getOffsetY()); + this->paintUpdateNeeded = false; + } + else { + dc->drawBitmap(0, 0, this->bitmap); + } + } }; #endif // _BUFFERED_WINDOW_H_ From c79b7c8ee633f6e86d7ca3bc808a611a7e3ba898 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 18 Mar 2020 15:18:44 +0100 Subject: [PATCH 133/714] New helper for testing if rect inside another rect --- radio/src/thirdparty/libopenui/src/libopenui_types.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_types.h b/radio/src/thirdparty/libopenui/src/libopenui_types.h index 683829f66..a643b2384 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_types.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_types.h @@ -63,6 +63,11 @@ struct rect_t { return (x >= this->x && x < this->x + this->w && y >= this->y && y < this->y + this->h); } + + bool contains(const rect_t & other) const + { + return left() <= other.left() && right() >= other.right() && top() <= other.top() && bottom() >= other.bottom(); + } }; typedef uint32_t LcdFlags; From b1e7e0985091635d9a80e44bdcf491f821cc65c3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 18 Mar 2020 15:19:18 +0100 Subject: [PATCH 134/714] Menu cosmetics --- radio/src/thirdparty/libopenui/src/menu.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 5e07fc809..03dbeb676 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -60,11 +60,18 @@ class MenuBody: public Window { public: MenuBody(Window * parent, const rect_t & rect): - Window(parent, rect) + Window(parent, rect, OPAQUE) { setPageHeight(MENUS_LINE_HEIGHT); } +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "MenuBody"; + } +#endif + void select(int index); #if defined(HARDWARE_KEYS) From f6022058d3dc8505d275d6ee60aa052529e670d3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 18 Mar 2020 17:05:02 +0100 Subject: [PATCH 135/714] Slide fixes --- .../thirdparty/libopenui/src/mainwindow.cpp | 19 +++++++++--- radio/src/thirdparty/libopenui/src/window.cpp | 31 +++++++++++++++++-- radio/src/thirdparty/libopenui/src/window.h | 3 ++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index c52910aa2..046c936d2 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -48,21 +48,23 @@ void MainWindow::checkEvents() if (touchState.event == TE_DOWN) { onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); + slidingWindow = nullptr; } else if (touchState.event == TE_UP) { onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); touchState.event = TE_NONE; + slidingWindow = nullptr; } else if (touchState.event == TE_SLIDE) { - if (touchState.deltaX) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, 0); + if (touchState.deltaX || touchState.deltaY) { + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); touchState.deltaX = 0; - } - else if (touchState.deltaY) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, 0, touchState.deltaY); touchState.deltaY = 0; } } + else { + slidingWindow = nullptr; + } #endif Window::checkEvents(); @@ -108,6 +110,8 @@ bool MainWindow::refresh() void MainWindow::run(bool trash) { + auto start = ticksNow(); + checkEvents(); if (trash) { @@ -117,4 +121,9 @@ void MainWindow::run(bool trash) if (refresh()) { lcdRefresh(); } + + auto delta = ticksNow() - start; + if (delta > 10 * SYSTEM_TICKS_1MS) { + TRACE_WINDOWS("MainWindow::run took %dms", (ticksNow() - start) / SYSTEM_TICKS_1MS); + } } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 70c5cc24c..d8ea137ed 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -21,6 +21,7 @@ #include "touch.h" Window * Window::focusWindow = nullptr; +Window * Window::slidingWindow = nullptr; std::list Window::trash; Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): @@ -191,24 +192,42 @@ void Window::scrollTo(const rect_t & rect) } } +bool Window::hasOpaqueRect(const rect_t & rect) +{ + if (!this->rect.contains(rect)) + return false; + + if (windowFlags & OPAQUE) { + return true; + } + + for (auto child: children) { + if (child->hasOpaqueRect(rect)) + return true; + } + + return false; +} + void Window::fullPaint(BitmapBuffer * dc) { bool paintNeeded = true; + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + auto firstChild = children.end(); while (firstChild != children.begin()) { auto child = *(--firstChild); - if ((child->windowFlags & OPAQUE) && isChildFullSize(child)) { + if (child->hasOpaqueRect({xmin, ymin, xmax - xmin, ymax - ymin})) { paintNeeded = false; break; } } if (windowFlags & PAINT_CHILDREN_FIRST) { - coord_t xmin, xmax, ymin, ymax; coord_t x = dc->getOffsetX(); coord_t y = dc->getOffsetY(); - dc->getClippingRect(xmin, xmax, ymin, ymax); paintChildren(dc, firstChild); dc->setOffset(x, y); dc->setClippingRect(xmin, xmax, ymin, ymax); @@ -374,13 +393,19 @@ bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, } } + if (slidingWindow && slidingWindow != this) { + return false; + } + if (slideY && innerHeight > rect.h) { setScrollPositionY(scrollPositionY - slideY); + slidingWindow = this; return true; } if (slideX && innerWidth > rect.w) { setScrollPositionX(scrollPositionX - slideX); + slidingWindow = this; return true; } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index b8890ea1a..3b1a72f98 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -334,6 +334,7 @@ class Window { LcdFlags textFlags; static Window * focusWindow; + static Window * slidingWindow; static std::list trash; std::function closeHandler; @@ -380,6 +381,8 @@ class Window { #endif bool forwardTouchEnd(coord_t x, coord_t y); + + bool hasOpaqueRect(const rect_t & rect); }; #endif // _WINDOW_H_ From 6f646c78e65cdc15f943ee0d39c35142b0147b01 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 19 Mar 2020 19:35:17 +0100 Subject: [PATCH 136/714] Cosmetics --- radio/src/thirdparty/libopenui/src/table.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 7966d4847..10eaa991e 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -38,10 +38,10 @@ void Table::Body::paint(BitmapBuffer * dc) coord_t x; int index = 0; for (auto & line: lines) { - dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, index == selection ? FOCUS_BGCOLOR : TABLE_BGCOLOR); + dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, index == selection ? MENU_HIGHLIGHT_BGCOLOR : TABLE_BGCOLOR); x = 10; for (unsigned i = 0; i < line.values.size(); i++) { - dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), TABLE_BODY_FONT | (index == selection ? FOCUS_COLOR : DEFAULT_COLOR)); + dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), TABLE_BODY_FONT | (index == selection ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); x += columnsWidth[i]; } y += TABLE_LINE_HEIGHT; From 4898a0429d8b34f5e27ea4101bbe23b11f5c33c6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 20 Mar 2020 11:39:58 +0100 Subject: [PATCH 137/714] TransparentBufferedWindow fix --- radio/src/thirdparty/libopenui/src/bufferedwindow.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h index 74aa70e24..6e6aa4a2c 100644 --- a/radio/src/thirdparty/libopenui/src/bufferedwindow.h +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -82,8 +82,13 @@ class TransparentBufferedWindow: public BufferedWindow if (this->paintUpdateNeeded) { this->paintUpdate(dc); - this->bitmap->drawBitmap(0, 0, dc, dc->getOffsetX(), dc->getOffsetY()); - this->paintUpdateNeeded = false; + // we can only store the bitmap if fully drawn + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + if (xmax - xmin >= this->width() && ymax - ymin >= this->height()) { + this->bitmap->drawBitmap(0, 0, dc, dc->getOffsetX(), dc->getOffsetY()); + this->paintUpdateNeeded = false; + } } else { dc->drawBitmap(0, 0, this->bitmap); From 3200b45ec2385b327813d383c8c026b409c21a77 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 20 Mar 2020 18:04:17 +0100 Subject: [PATCH 138/714] Slide energy taken into account --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 046c936d2..2a93d2534 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -53,17 +53,26 @@ void MainWindow::checkEvents() else if (touchState.event == TE_UP) { onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); touchState.event = TE_NONE; - slidingWindow = nullptr; } else if (touchState.event == TE_SLIDE) { if (touchState.deltaX || touchState.deltaY) { onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); + touchState.lastDeltaX = touchState.deltaX; + touchState.lastDeltaY = touchState.deltaY; touchState.deltaX = 0; touchState.deltaY = 0; } } - else { - slidingWindow = nullptr; + else if (touchState.event == TE_SLIDE_END && slidingWindow) { + if (abs(touchState.lastDeltaX) >= 5 || abs(touchState.lastDeltaY) >= 5) { + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); + touchState.lastDeltaX = (touchState.lastDeltaX * 3) >> 2; + touchState.lastDeltaY = (touchState.lastDeltaY * 3) >> 2; + } + else { + touchState.lastDeltaX = 0; + touchState.lastDeltaY = 0; + } } #endif From 4f6627e6db266acb0859717938297d24e3bcbb78 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 25 Mar 2020 09:33:49 +0100 Subject: [PATCH 139/714] Scroll issues with ExpansionPanel --- radio/src/thirdparty/libopenui/src/expansion_panel.h | 4 ++-- radio/src/thirdparty/libopenui/src/window.h | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index d44e798a5..b624996c7 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -26,7 +26,7 @@ class ExpansionPanel; class ExpansionPanelHeader: virtual public FormGroup { public: - ExpansionPanelHeader(ExpansionPanel * parent); + explicit ExpansionPanelHeader(ExpansionPanel * parent); void onEvent(event_t event) override; @@ -92,7 +92,7 @@ bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) void ExpansionPanelHeader::onEvent(event_t event) { - ExpansionPanel * panel = static_cast(parent); + auto panel = static_cast(parent); if (event == EVT_KEY_BREAK(KEY_ENTER)) { panel->toggle(); diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 3b1a72f98..f4e4d7202 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -269,7 +269,12 @@ class Window { { innerHeight = h; if (height() >= h) { - scrollPositionY = 0; + setScrollPositionY(0); + } + else { + coord_t maxScrollPosition = h - height(); + if (scrollPositionY > maxScrollPosition) + setScrollPositionY(maxScrollPosition); } } From 68fa08ae83e4157e0161f060a001f4e73298321a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 26 Mar 2020 17:52:21 +0100 Subject: [PATCH 140/714] New helper for transparent bitmap background --- .../thirdparty/libopenui/src/bufferedwindow.h | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h index 6e6aa4a2c..a7f693cc1 100644 --- a/radio/src/thirdparty/libopenui/src/bufferedwindow.h +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -96,4 +96,24 @@ class TransparentBufferedWindow: public BufferedWindow } }; +class TransparentBitmapBackground: public TransparentBufferedWindow +{ + public: + TransparentBitmapBackground(Window * parent, const rect_t & rect, const BitmapBuffer * bitmap): + TransparentBufferedWindow(parent, rect, OPAQUE), + background(bitmap) + { + } + + protected: + const BitmapBuffer * background; + + void paintUpdate(BitmapBuffer * dc) override + { + if (background) { + dc->drawBitmap((width() - background->width()) / 2, (height() - background->height()) / 2, background); + } + } +}; + #endif // _BUFFERED_WINDOW_H_ From 8e3a41d796830f8c38a860823a08774bf20173e4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 31 Mar 2020 15:52:12 +0200 Subject: [PATCH 141/714] Choice can now accept an array of const char * as values argument --- radio/src/thirdparty/libopenui/src/choice.cpp | 63 ++++++++++++++++--- radio/src/thirdparty/libopenui/src/choice.h | 9 ++- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 919f1941d..01fd41a80 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -21,10 +21,9 @@ #include "menu.h" #include "theme.h" -Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : +Choice::Choice(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), - values(values), vmin(vmin), vmax(vmax), getValue(std::move(getValue)), @@ -32,10 +31,56 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int { } +Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values[], int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) +{ + const char ** value = &values[0]; + for (int i = vmin; i <= vmax; i++) { + this->values.emplace_back(*value++); + } +} + +Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) +{ + if (values) { + uint8_t len = values[0]; + const char * value = &values[1]; + for (int i = vmin; i <= vmax; i++) { + this->values.emplace_back(std::string(value, min(len, strlen(value)))); + value += len; + } + } +} + +void Choice::addItem(const char * item) +{ + values.emplace_back(item); + vmax += 1; +} + +void Choice::addItems(const char * items[], uint8_t count) +{ + values.reserve(values.size() + count); + for (uint8_t i = 0; i < count; i++) + values.emplace_back(items[i]); + vmax += count; +} + void Choice::paint(BitmapBuffer * dc) { FormField::paint(dc); - theme->drawChoice(dc, this, textHandler ? textHandler(getValue()).c_str() : TEXT_AT_INDEX(values, getValue() - vmin).c_str()); + theme->drawChoice(dc, this, textHandler ? textHandler(getValue()).c_str() : values[getValue() - vmin].c_str()); } #if defined(HARDWARE_KEYS) @@ -67,12 +112,12 @@ void Choice::openMenu() continue; if (textHandler) { menu->addLine(textHandler(i), [=]() { - setValue(i); + setValue(i); }); } else { - menu->addLine(TEXT_AT_INDEX(values, i - vmin), [=]() { - setValue(i); + menu->addLine(values[i - vmin], [=]() { + setValue(i); }); } if (value == i) { @@ -86,8 +131,8 @@ void Choice::openMenu() } menu->setCloseHandler([=]() { - editMode = false; - setFocus(); + editMode = false; + setFocus(); }); setEditMode(true); diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 8b0d1092b..fdca595be 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -20,6 +20,7 @@ #ifndef _CHOICE_H_ #define _CHOICE_H_ +#include #include "form.h" enum ChoiceType { @@ -46,8 +47,14 @@ class ChoiceBase : public FormField { class Choice : public ChoiceBase { public: + Choice(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + Choice(FormGroup * parent, const rect_t & rect, const char * values[], int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + void addItem(const char * item); + + void addItems(const char * items[], uint8_t count); + #if defined(DEBUG_WINDOWS) std::string getName() override { @@ -86,7 +93,7 @@ class Choice : public ChoiceBase { } protected: - const char * values = nullptr; + std::vector values; int16_t vmin = 0; int16_t vmax = 0; std::string menuTitle; From d6313ab1e1d67d9dc7e29ed1a49608c77d5860a6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 1 Apr 2020 09:52:15 +0200 Subject: [PATCH 142/714] Expansion panel link issue --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + .../libopenui/src/expansion_panel.cpp | 47 +++++++++++++++++++ .../libopenui/src/expansion_panel.h | 27 ----------- 3 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/expansion_panel.cpp diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 3fc35eb0e..788521440 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -32,6 +32,7 @@ set(LIBOPENUI_SRC table.cpp modal_window.cpp dialog.cpp + expansion_panel.cpp ) if(SOFTWARE_KEYBOARD) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp new file mode 100644 index 000000000..a775a91f8 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "expansion_panel.h" + +ExpansionPanelHeader::ExpansionPanelHeader(ExpansionPanel * parent): + FormGroup(parent, {0, 0, parent->width(), parent->height()}, FORWARD_SCROLL) +{ +} + +bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) +{ + static_cast(parent)->toggle(); + setFocus(); + return true; +} + +void ExpansionPanelHeader::onEvent(event_t event) +{ + auto panel = static_cast(parent); + + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + panel->toggle(); + } + else if (event == EVT_ROTARY_RIGHT && !panel->isOpen) { + panel->getNextField()->setFocus(SET_FOCUS_FORWARD); + } + else { + FormGroup::onEvent(event); + } +} diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index b624996c7..08fbb9126 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -78,31 +78,4 @@ class ExpansionPanel: public FormGroup { bool isOpen = false; }; -ExpansionPanelHeader::ExpansionPanelHeader(ExpansionPanel * parent): - FormGroup(parent, {0, 0, parent->width(), parent->height()}, FORWARD_SCROLL) -{ -} - -bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) -{ - static_cast(parent)->toggle(); - setFocus(); - return true; -} - -void ExpansionPanelHeader::onEvent(event_t event) -{ - auto panel = static_cast(parent); - - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - panel->toggle(); - } - else if (event == EVT_ROTARY_RIGHT && !panel->isOpen) { - panel->getNextField()->setFocus(SET_FOCUS_FORWARD); - } - else { - FormGroup::onEvent(event); - } -} - #endif // _EXPANSION_PANEL_H_ From 89d5a87a89cdb48b874beb43e8765303ad59e224 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 1 Apr 2020 09:53:44 +0200 Subject: [PATCH 143/714] Expansion panel link issue --- .../thirdparty/libopenui/src/expansion_panel.cpp | 16 +++++++++------- .../thirdparty/libopenui/src/expansion_panel.h | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp index a775a91f8..11601f1a6 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -24,13 +24,6 @@ ExpansionPanelHeader::ExpansionPanelHeader(ExpansionPanel * parent): { } -bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) -{ - static_cast(parent)->toggle(); - setFocus(); - return true; -} - void ExpansionPanelHeader::onEvent(event_t event) { auto panel = static_cast(parent); @@ -45,3 +38,12 @@ void ExpansionPanelHeader::onEvent(event_t event) FormGroup::onEvent(event); } } + +#if defined(HARDWARE_TOUCH) +bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) +{ + static_cast(parent)->toggle(); + setFocus(); + return true; +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 08fbb9126..09841bb86 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -30,7 +30,9 @@ class ExpansionPanelHeader: virtual public FormGroup { void onEvent(event_t event) override; +#if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t, coord_t) override; +#endif }; From b03bd80ecc72e7528036020d9d7432ea70a83c66 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 2 Apr 2020 21:52:35 +0200 Subject: [PATCH 144/714] Refresh issue --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 2a93d2534..38840e464 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -82,10 +82,10 @@ void MainWindow::checkEvents() void MainWindow::invalidate(const rect_t & rect) { if (invalidatedRect.w) { - coord_t left = min(invalidatedRect.left(), rect.left()); - coord_t right = max(invalidatedRect.right(), rect.right()); - coord_t top = min(invalidatedRect.top(), rect.top()); - coord_t bottom = max(invalidatedRect.bottom(), rect.bottom()); + auto left = limit(0, rect.left(), invalidatedRect.left()); + auto right = limit(invalidatedRect.right(), rect.right(), LCD_W); + auto top = limit(0, rect.top(), invalidatedRect.top()); + auto bottom = limit(rect.bottom(), invalidatedRect.bottom(), LCD_H); invalidatedRect = {left, top, right - left, bottom - top}; } else { From c686a9406683dab77016d19b7b43ce598c6ba989 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 2 Apr 2020 21:53:06 +0200 Subject: [PATCH 145/714] Cosmetics --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.cpp | 6 +++--- radio/src/thirdparty/libopenui/src/window.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 998c86d3a..5827c11cc 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -77,7 +77,7 @@ void MenuBody::paint(BitmapBuffer * dc) auto & line = lines[i]; LcdFlags flags = MENU_COLOR | MENU_FONT; if (selectedIndex == i) { - flags = MENU_HIGHLIGHT_COLOR; + flags = MENU_HIGHLIGHT_COLOR | MENU_FONT; if (MENU_HIGHLIGHT_BGCOLOR != MENU_BGCOLOR) dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), MENUS_LINE_HEIGHT, MENU_HIGHLIGHT_BGCOLOR); } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index d8ea137ed..6cafc4f59 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -51,12 +51,12 @@ Window::~Window() deleteChildren(); } -void Window::attach(Window * window) +void Window::attach(Window * newParent) { if (parent) detach(); - parent = window; - parent->addChild(this); + parent = newParent; + newParent->addChild(this); } void Window::detach() diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index f4e4d7202..8a49b3ace 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -323,6 +323,8 @@ class Window { virtual void checkEvents(); + void attach(Window * window); + void detach(); protected: @@ -345,8 +347,6 @@ class Window { std::function closeHandler; std::function focusHandler; - void attach(Window * window); - void addChild(Window * window, bool front = false) { if (front) From bae0d4bcb6a9aca4e0ad1680347286061af3cf50 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 2 Apr 2020 22:09:03 +0200 Subject: [PATCH 146/714] Possibility to scale the bitmap in StaticBitmap widget --- radio/src/thirdparty/libopenui/src/static.h | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 767bd521b..acfd102d6 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -67,20 +67,23 @@ class Subtitle: public StaticText { class StaticBitmap: public Window { public: - StaticBitmap(Window * parent, const rect_t & rect): - Window(parent, rect) + StaticBitmap(Window * parent, const rect_t & rect, bool scale = false): + Window(parent, rect), + scale(scale) { } - StaticBitmap(Window * parent, const rect_t & rect, const char * filename): + StaticBitmap(Window * parent, const rect_t & rect, const char * filename, bool scale = false): Window(parent, rect), - bitmap(BitmapBuffer::loadBitmap(filename)) + bitmap(BitmapBuffer::loadBitmap(filename)), + scale(scale) { } - StaticBitmap(Window * parent, const rect_t & rect, const BitmapBuffer * bitmap): + StaticBitmap(Window * parent, const rect_t & rect, const BitmapBuffer * bitmap, bool scale = false): Window(parent, rect), - bitmap(bitmap) + bitmap(bitmap), + scale(scale) { } @@ -106,12 +109,16 @@ class StaticBitmap: public Window { void paint(BitmapBuffer * dc) override { if (bitmap) { - dc->drawBitmap((width() - bitmap->width()) / 2, (height() - bitmap->height()) / 2, bitmap); + if (scale) + dc->drawScaledBitmap(bitmap, 0, 0, width(), height()); + else + dc->drawBitmap((width() - bitmap->width()) / 2, (height() - bitmap->height()) / 2, bitmap); } } protected: const BitmapBuffer * bitmap = nullptr; + bool scale = false; }; class DynamicText: public StaticText From 2a0b3b30b63860e3c062397cf5a91e05f5e9b376 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 6 Apr 2020 09:16:40 +0200 Subject: [PATCH 147/714] Generic carousel added --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + .../src/thirdparty/libopenui/src/carousel.cpp | 58 +++++++++ radio/src/thirdparty/libopenui/src/carousel.h | 118 ++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/src/carousel.cpp create mode 100644 radio/src/thirdparty/libopenui/src/carousel.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 788521440..f6712eea4 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -19,6 +19,7 @@ set(LIBOPENUI_SRC button.cpp static.cpp checkbox.cpp + carousel.cpp slider.cpp mainwindow.cpp menu.cpp diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp new file mode 100644 index 000000000..108cf166d --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "carousel.h" + +void CarouselWindow::update() +{ + auto first = min(selection - 2, items.size() - 5); + first = max(0, first); + coord_t lastPosition = 0; + + for (auto & item: items) { + item.front->detach(); + item.back->detach(); + } + + for (uint8_t index = first; lastPosition < width() && index < items.size(); index++) { + auto & item = items[index]; + auto window = (index == selection ? item.front : item.back); + window->attach(this); + window->setLeft(lastPosition); + window->setTop((height() - window->height()) / 2); + lastPosition += window->width() + CAROUSEL_SPACING; + } +} + +void Carousel::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_ROTARY_RIGHT) { + if (body->selection < body->items.size() - 1) + select(body->selection + 1); + } + else if (event == EVT_ROTARY_LEFT) { + if (body->selection > 0) + select(body->selection - 1); + } + else { + Window::onEvent(event); + } +} diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h new file mode 100644 index 000000000..f81893430 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _CAROUSEL_H_ +#define _CAROUSEL_H_ + +#include +#include "button.h" + +constexpr coord_t CAROUSEL_SPACING = 20; + +class CarouselItem { + public: + CarouselItem(Window * front, Window * back): + front(front), + back(back) + { + } + + public: + Window * front; + Window * back; +}; + +class CarouselWindow: public Window { + friend class Carousel; + + public: + CarouselWindow(Window * parent, const rect_t & rect): + Window(parent, rect, NO_SCROLLBAR) + { + } + + void addItem(CarouselItem item) + { + items.emplace_back(item); + update(); + } + + void clear() + { + items.clear(); + Window::clear(); + } + + void select(int index, bool scroll = true) + { + selection = index; + update(); + } + + int selection = 0; + + protected: + std::vector items; + + void update(); +}; + +class Carousel: public Window { + public: + Carousel(Window * parent, const rect_t & rect): + Window(parent, rect, NO_SCROLLBAR) + { + } + +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "Carousel"; + } +#endif + + void addItem(CarouselItem item) + { + body->addItem(std::move(item)); + } + + void clear() + { + body->clear(); + } + + void select(int index) + { + body->select(index); + previousButton->enable(index > 0); + nextButton->enable(index < (int)body->items.size() - 1); + } + + +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + + protected: + Button * previousButton = nullptr; + Button * nextButton = nullptr; + CarouselWindow * body = nullptr; +}; + +#endif // _CAROUSEL_H_ From db58d78388589e4209baccf1775a6072ba1963f5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 6 Apr 2020 09:26:49 +0200 Subject: [PATCH 148/714] Add Carousel::getSelection() method --- .../src/thirdparty/libopenui/src/carousel.cpp | 5 +---- radio/src/thirdparty/libopenui/src/carousel.h | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp index 108cf166d..abcd1392d 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.cpp +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -25,10 +25,7 @@ void CarouselWindow::update() first = max(0, first); coord_t lastPosition = 0; - for (auto & item: items) { - item.front->detach(); - item.back->detach(); - } + detachItems(); for (uint8_t index = first; lastPosition < width() && index < items.size(); index++) { auto & item = items[index]; diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index f81893430..18abffdf6 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -47,6 +47,16 @@ class CarouselWindow: public Window { { } + ~CarouselWindow() override + { + detachItems(); + // TODO memory leak here +// for (auto & item: items) { +// item.front->deleteLater(); +// item.back->deleteLater(); +// } + } + void addItem(CarouselItem item) { items.emplace_back(item); @@ -70,6 +80,14 @@ class CarouselWindow: public Window { protected: std::vector items; + void detachItems() + { + for (auto & item: items) { + item.front->detach(); + item.back->detach(); + } + } + void update(); }; @@ -104,6 +122,10 @@ class Carousel: public Window { nextButton->enable(index < (int)body->items.size() - 1); } + int getSelection() + { + return body->selection; + } #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; From a99198b3b41fceae32b4c3bca0b29decf6a5608e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 6 Apr 2020 10:56:26 +0200 Subject: [PATCH 149/714] Remove BufferedWindow::forcePaint() as invalidate() should do the job --- radio/src/thirdparty/libopenui/src/bufferedwindow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h index a7f693cc1..8b0f86a19 100644 --- a/radio/src/thirdparty/libopenui/src/bufferedwindow.h +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -33,7 +33,7 @@ class BufferedWindow: public T delete bitmap; } - void forcePaint() + void invalidate(const rect_t & rect) override { paintUpdateNeeded = true; T::invalidate(); From 2b551260f3aea693751f877373a43d2799f5f0e5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 6 Apr 2020 11:19:57 +0200 Subject: [PATCH 150/714] Stupid infinite loop --- radio/src/thirdparty/libopenui/src/bufferedwindow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h index 8b0f86a19..8c2b23651 100644 --- a/radio/src/thirdparty/libopenui/src/bufferedwindow.h +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -36,7 +36,7 @@ class BufferedWindow: public T void invalidate(const rect_t & rect) override { paintUpdateNeeded = true; - T::invalidate(); + T::invalidate(rect); } protected: From 7c0d03305972dc6be6988e5c596aeaeb5e058a4a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 6 Apr 2020 16:51:22 +0200 Subject: [PATCH 151/714] Scaled bitmaps paint fix --- .../thirdparty/libopenui/src/bitmapbuffer.h | 82 +++++++++++-------- .../src/thirdparty/libopenui/src/carousel.cpp | 23 ++++-- radio/src/thirdparty/libopenui/src/carousel.h | 22 ++--- 3 files changed, 71 insertions(+), 56 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 0a9a4387a..f05ff9e37 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -389,7 +389,7 @@ class BitmapBuffer: public BitmapBufferBase coord_t drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); template - void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx = 0, coord_t srcy = 0, coord_t w = 0, coord_t h = 0, float scale = 0) + void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx = 0, coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0, float scale = 0) { if (!data || !bmp) return; @@ -399,42 +399,58 @@ class BitmapBuffer: public BitmapBufferBase if (x >= xmax || y >= ymax) return; - coord_t srcw = bmp->width(); - coord_t srch = bmp->height(); + coord_t bmpw = bmp->width(); + coord_t bmph = bmp->height(); - if (w == 0) - w = srcw; - if (h == 0) - h = srch; - if (srcx + w > srcw) - w = srcw - srcx; - if (srcy + h > srch) - h = srch - srcy; - - if (x < xmin) { - w += x - xmin; - srcx -= x - xmin; - x = xmin; - } - - if (y < ymin) { - h += y - ymin; - srcy -= y - ymin; - y = ymin; - } + if (srcw == 0) + srcw = bmpw; + if (srch == 0) + srch = bmph; + if (srcx + srcw > bmpw) + srcw = bmpw - srcx; + if (srcy + srch > bmph) + srch = bmph - srcy; if (scale == 0) { - if (x + w > xmax) { - w = xmax - x; + if (x < xmin) { + srcw += x - xmin; + srcx -= x - xmin; + x = xmin; } - - if (y + h > ymax) { - h = ymax - y; + if (y < ymin) { + srch += y - ymin; + srcy -= y - ymin; + y = ymin; + } + if (x + srcw > xmax) { + srcw = xmax - x; + } + if (y + srch > ymax) { + srch = ymax - y; + } + } + else { + if (x < xmin) { + srcw += (x - xmin) / scale; + srcx -= (x - xmin) / scale; + x = xmin; + } + if (y < ymin) { + srch += (y - ymin) / scale; + srcy -= (y - ymin) / scale; + y = ymin; + } + if (x + srcw * scale > xmax) { + srcw = (xmax - x) / scale; + } + if (y + srch * scale > ymax) { + srch = (ymax - y) / scale; } } - if (w <= 0 || h <= 0) + if (srcw <= 0 || srch <= 0) { return; + } // TRACE("x=%d xmin=%d xmax=%d w=%d", x, xmin, xmax, w); @@ -446,15 +462,15 @@ class BitmapBuffer: public BitmapBufferBase h = height - y; }*/ if (bmp->getFormat() == BMP_ARGB4444) { - DMACopyAlphaBitmap(data, _width, _height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); + DMACopyAlphaBitmap(data, _width, _height, x, y, bmp->getData(), bmpw, bmph, srcx, srcy, srcw, srch); } else { - DMACopyBitmap(data, _width, _height, x, y, bmp->getData(), srcw, srch, srcx, srcy, w, h); + DMACopyBitmap(data, _width, _height, x, y, bmp->getData(), bmpw, bmph, srcx, srcy, srcw, srch); } } else { - int scaledw = w * scale; - int scaledh = h * scale; + int scaledw = srcw * scale; + int scaledh = srch * scale; if (x + scaledw > _width) scaledw = _width - x; diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp index abcd1392d..8519c6c8b 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.cpp +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -25,16 +25,27 @@ void CarouselWindow::update() first = max(0, first); coord_t lastPosition = 0; - detachItems(); - - for (uint8_t index = first; lastPosition < width() && index < items.size(); index++) { - auto & item = items[index]; - auto window = (index == selection ? item.front : item.back); + int index = 0; + for (auto & item: items) { + Window * window; + if (index == selection) { + item.back->detach(); + window = item.front; + } + else { + item.front->detach(); + window = item.back; + } window->attach(this); window->setLeft(lastPosition); window->setTop((height() - window->height()) / 2); + if (index == first) + setScrollPositionX(lastPosition); lastPosition += window->width() + CAROUSEL_SPACING; + index += 1; } + + setInnerWidth(lastPosition); } void Carousel::onEvent(event_t event) @@ -42,7 +53,7 @@ void Carousel::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_ROTARY_RIGHT) { - if (body->selection < body->items.size() - 1) + if (body->selection < (int)body->items.size() - 1) select(body->selection + 1); } else if (event == EVT_ROTARY_LEFT) { diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index 18abffdf6..a80bf4633 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -49,12 +49,10 @@ class CarouselWindow: public Window { ~CarouselWindow() override { - detachItems(); - // TODO memory leak here -// for (auto & item: items) { -// item.front->deleteLater(); -// item.back->deleteLater(); -// } + for (auto & item: items) { + item.front->deleteLater(); + item.back->deleteLater(); + } } void addItem(CarouselItem item) @@ -75,19 +73,9 @@ class CarouselWindow: public Window { update(); } - int selection = 0; - protected: std::vector items; - - void detachItems() - { - for (auto & item: items) { - item.front->detach(); - item.back->detach(); - } - } - + int selection = 0; void update(); }; From e5647e9ea5f37b0efb0857359a58bb25f43a9f3c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 8 Apr 2020 11:36:48 +0200 Subject: [PATCH 152/714] - drawMask bugfix - WINDOWS_INSPECT_BORDER_COLOR option added - missing names on some widgets --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 4 +--- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 8 -------- radio/src/thirdparty/libopenui/src/bufferedwindow.h | 7 +++++++ radio/src/thirdparty/libopenui/src/window.cpp | 3 +++ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index dc182bb54..16f8bcce6 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -283,8 +283,6 @@ class Slope { left = false; value = (cosf(radians) * 100 / sinf(radians)); } - - // TRACE("angle=%d left=%d slope=%d", angle, left, value); } Slope(bool left, int value): @@ -395,7 +393,7 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd if (x < xmin) { width += x - xmin; - offset -= x + xmin; + offset -= x - xmin; x = xmin; } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index f05ff9e37..dcb63abe6 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -452,15 +452,7 @@ class BitmapBuffer: public BitmapBufferBase return; } - // TRACE("x=%d xmin=%d xmax=%d w=%d", x, xmin, xmax, w); - if (scale == 0) { - /*if (x + w > width) { - w = width - x; - } - if (y + h > height) { - h = height - y; - }*/ if (bmp->getFormat() == BMP_ARGB4444) { DMACopyAlphaBitmap(data, _width, _height, x, y, bmp->getData(), bmpw, bmph, srcx, srcy, srcw, srch); } diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h index 8c2b23651..674dcd235 100644 --- a/radio/src/thirdparty/libopenui/src/bufferedwindow.h +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -105,6 +105,13 @@ class TransparentBitmapBackground: public TransparentBufferedWindow { } +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "TransparentBitmapBackground"; + } +#endif + protected: const BitmapBuffer * background; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 6cafc4f59..52a0f948a 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -236,6 +236,9 @@ void Window::fullPaint(BitmapBuffer * dc) if (paintNeeded) { TRACE_WINDOWS_INDENT("%s", getWindowDebugString().c_str()); paint(dc); +#if defined(WINDOWS_INSPECT_BORDER_COLOR) + if dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR); +#endif } else { TRACE_WINDOWS_INDENT("%s (skipped)", getWindowDebugString().c_str()); From f60fc766286e3dd94643c6690a8c5adaf3e7fb69 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 8 Apr 2020 13:11:34 +0200 Subject: [PATCH 153/714] drawAnnulusSector fix (0-180) and (0-360) --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 16f8bcce6..03543a2cf 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -268,14 +268,18 @@ class Slope { explicit Slope(int angle) { if (angle < 0) angle += 360; - if (angle >= 360) + if (angle > 360) angle %= 360; float radians = float(angle) * (M_PI / 180.0f); if (angle == 0) { left = false; value = 100000; } - else if (angle > 180) { + else if (angle == 360) { + left = true; + value = 100000; + } + else if (angle >= 180) { left = true; value = -(cosf(radians) * 100 / sinf(radians)); } From 4e27a07a38966a5734fc01123daaffbed601cf18 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 8 Apr 2020 16:14:59 +0200 Subject: [PATCH 154/714] Add horizontalFlip() and verticalFlip() to BitmapBuffer --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 26 +++++++++++++++++++ .../thirdparty/libopenui/src/bitmapbuffer.h | 6 ++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 03543a2cf..6763cf060 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -676,6 +676,32 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) return bitmap; } +BitmapBuffer * BitmapBuffer::horizontalFlip() const +{ + BitmapBuffer * result = new BitmapBuffer(format, width(), height()); + pixel_t * srcData = data; + pixel_t * destData = result->data; + for (uint8_t y = 0; y < height(); y++) { + for (uint8_t x = 0; x < width(); x++) { + destData[x] = srcData[width() - 1 - x]; + } + srcData += width(); + destData += width(); + } + return result; +} + +BitmapBuffer * BitmapBuffer::verticalFlip() const +{ + BitmapBuffer * result = new BitmapBuffer(format, width(), height()); + for (uint8_t y = 0; y < height(); y++) { + for (uint8_t x = 0; x < width(); x++) { + result->data[y * width() + x] = data[(height() - 1 - y) * width() + x]; + } + } + return result; +} + BitmapBuffer * BitmapBuffer::loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background) { BitmapBuffer * result = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index dcb63abe6..c50db7c13 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -232,7 +232,7 @@ class RLEBitmap: } }; -class BitmapBuffer: public BitmapBufferBase +class BitmapBuffer: public BitmapBufferBase { private: bool dataAllocated; @@ -500,6 +500,10 @@ class BitmapBuffer: public BitmapBufferBase drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); } + BitmapBuffer * horizontalFlip() const; + + BitmapBuffer * verticalFlip() const; + protected: static BitmapBuffer * load_bmp(const char * filename); static BitmapBuffer * load_stb(const char * filename); From d48f8beb68c166ae6c32a8ab8a0dfa45c8a21dbd Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 9 Apr 2020 19:05:05 +0200 Subject: [PATCH 155/714] Warning fixes --- .../src/thirdparty/libopenui/src/carousel.cpp | 8 ++++---- radio/src/thirdparty/libopenui/src/carousel.h | 19 ++++++++++++------- .../libopenui/src/libopenui_defines.h | 4 ++-- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp index 8519c6c8b..c8915b6a1 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.cpp +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -29,12 +29,12 @@ void CarouselWindow::update() for (auto & item: items) { Window * window; if (index == selection) { - item.back->detach(); - window = item.front; + item->back->detach(); + window = item->front; } else { - item.front->detach(); - window = item.back; + item->front->detach(); + window = item->back; } window->attach(this); window->setLeft(lastPosition); diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index a80bf4633..dc75cf0af 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -50,19 +50,24 @@ class CarouselWindow: public Window { ~CarouselWindow() override { for (auto & item: items) { - item.front->deleteLater(); - item.back->deleteLater(); + item->front->deleteLater(); + item->back->deleteLater(); } } - void addItem(CarouselItem item) + void addItem(CarouselItem * item) { - items.emplace_back(item); + items.push_back(item); update(); } void clear() { + for (auto & item: items) { + item->front->deleteLater(); + item->back->deleteLater(); + delete item; + } items.clear(); Window::clear(); } @@ -74,7 +79,7 @@ class CarouselWindow: public Window { } protected: - std::vector items; + std::vector items; int selection = 0; void update(); }; @@ -93,9 +98,9 @@ class Carousel: public Window { } #endif - void addItem(CarouselItem item) + void addItem(CarouselItem * item) { - body->addItem(std::move(item)); + body->addItem(item); } void clear() diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 2ff688979..56e2e49df 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -43,7 +43,7 @@ #define FONT_MASK 0x0F00u #define FONT_INDEX(flags) (((flags) & FONT_MASK) >> 8u) -#define FONT(xx) (FONT_ ## xx ## _INDEX << 8u) +#define FONT(xx) (unsigned(FONT_ ## xx ## _INDEX) << 8u) #define TIMEHOUR 0x2000u #define EXPANDED 0x2000u @@ -92,7 +92,7 @@ #define RGB(r, g, b) (uint16_t)((((r) & 0xF8) << 8) + (((g) & 0xFC) << 3) + (((b) & 0xF8) >> 3)) #define ARGB(a, r, g, b) (uint16_t)((((a) & 0xF0) << 8) + (((r) & 0xF0) << 4) + (((g) & 0xF0) << 0) + (((b) & 0xF0) >> 4)) -#define COLOR(index) LcdFlags((index) << 16u) +#define COLOR(index) LcdFlags(unsigned(index) << 16u) #define COLOR_IDX(att) uint8_t((att) >> 16u) #define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) From a12865277b2cb4307846d66d4f46c9e5e6b1e3fd Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 10 Apr 2020 20:56:28 +0200 Subject: [PATCH 156/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 14 ++++++++------ radio/src/thirdparty/libopenui/src/carousel.h | 7 +++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index c50db7c13..18e70039d 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -491,13 +491,15 @@ class BitmapBuffer: public BitmapBufferBase template void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h) { - float vscale = float(h) / bitmap->height(); - float hscale = float(w) / bitmap->width(); - float scale = vscale < hscale ? vscale : hscale; + if (bitmap) { + float vscale = float(h) / bitmap->height(); + float hscale = float(w) / bitmap->width(); + float scale = vscale < hscale ? vscale : hscale; - int xshift = (w - (bitmap->width() * scale)) / 2; - int yshift = (h - (bitmap->height() * scale)) / 2; - drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); + int xshift = (w - (bitmap->width() * scale)) / 2; + int yshift = (h - (bitmap->height() * scale)) / 2; + drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); + } } BitmapBuffer * horizontalFlip() const; diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index dc75cf0af..342233c27 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -33,6 +33,8 @@ class CarouselItem { { } + virtual ~CarouselItem() = default; + public: Window * front; Window * back; @@ -49,10 +51,7 @@ class CarouselWindow: public Window { ~CarouselWindow() override { - for (auto & item: items) { - item->front->deleteLater(); - item->back->deleteLater(); - } + clear(); } void addItem(CarouselItem * item) From 83e6f94f8aa79d005d3d0820a893d9f072af0dd5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 15 Apr 2020 18:20:54 +0200 Subject: [PATCH 157/714] Cosmetics for easier reuse --- radio/src/thirdparty/libopenui/src/choice.cpp | 11 +++++++++++ radio/src/thirdparty/libopenui/src/choice.h | 1 + radio/src/thirdparty/libopenui/src/filechoice.cpp | 6 ------ radio/src/thirdparty/libopenui/src/libopenui_file.h | 7 +++++++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 01fd41a80..2024d0067 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -45,6 +45,17 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values[], i } } +Choice::Choice(FormGroup * parent, const rect_t & rect, std::vector values, int16_t vmin, int16_t vmax, + std::function getValue, std::function setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + values(std::move(values)), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) +{ +} + Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index fdca595be..ea72f2315 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -48,6 +48,7 @@ class ChoiceBase : public FormField { class Choice : public ChoiceBase { public: Choice(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + Choice(FormGroup * parent, const rect_t & rect, std::vector values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); Choice(FormGroup * parent, const rect_t & rect, const char * values[], int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 861f56a3d..721bac985 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -26,12 +26,6 @@ extern const char STR_SDCARD[]; extern const char STR_NO_FILES_ON_SD[]; -// comparison, not case sensitive. -bool compare_nocase(const std::string &first, const std::string &second) -{ - return strcasecmp(first.c_str(), second.c_str()) < 0; -} - FileChoice::FileChoice(FormGroup * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): ChoiceBase(parent, rect, CHOICE_TYPE_FOLDER), folder(std::move(folder)), diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index 0aca1a8cf..0eace86a0 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -20,6 +20,7 @@ #ifndef _LIBOPEUI_FILE_H_ #define _LIBOPEUI_FILE_H_ +#include #include #include "ff.h" @@ -29,4 +30,10 @@ const char * getFileExtension(const char * filename, uint8_t size = 0, uint8_t e bool isExtensionMatching(const char * extension, const char * pattern, char * match = nullptr); FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime); +// comparison, not case sensitive. +inline bool compare_nocase(const std::string &first, const std::string &second) +{ + return strcasecmp(first.c_str(), second.c_str()) < 0; +} + #endif From f44c88bc3a96aa3dacc553bc5f431f71832cd6d9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 15 Apr 2020 18:27:36 +0200 Subject: [PATCH 158/714] Missing include --- radio/src/thirdparty/libopenui/src/libopenui_file.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index 0eace86a0..ee4d49c32 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -21,6 +21,7 @@ #define _LIBOPEUI_FILE_H_ #include +#include #include #include "ff.h" From 1fbc4a617da4fb37a65b05728642a0daf9521cff Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 16 Apr 2020 10:09:50 +0200 Subject: [PATCH 159/714] Missing initialization --- radio/src/thirdparty/libopenui/src/textedit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 45c109380..58cddb992 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -172,6 +172,7 @@ void TextEdit::onEvent(event_t event) } } else { + cursorPos = 0; FormField::onEvent(event); } #endif From b4077e9b5b7589f8729710ad48eb0df0f8ae372b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 16 Apr 2020 12:39:08 +0200 Subject: [PATCH 160/714] Missing invalidate --- radio/src/thirdparty/libopenui/src/expansion_panel.h | 9 ++++++++- radio/src/thirdparty/libopenui/src/form.h | 5 +++++ radio/src/thirdparty/libopenui/src/window.h | 6 +++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 09841bb86..f26292f13 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -28,6 +28,13 @@ class ExpansionPanelHeader: virtual public FormGroup { public: explicit ExpansionPanelHeader(ExpansionPanel * parent); +#if defined(DEBUG_WINDOWS) + std::string getName() override + { + return "ExpansionPanelHeader"; + } +#endif + void onEvent(event_t event) override; #if defined(HARDWARE_TOUCH) @@ -48,7 +55,7 @@ class ExpansionPanel: public FormGroup { #if defined(DEBUG_WINDOWS) std::string getName() override { - return "ExpansionPanel()"; + return "ExpansionPanel"; } #endif diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index ba7253bb6..3a71b84cc 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -111,6 +111,11 @@ class FormGroup: public FormField { void addField(FormField * field, bool front = false); + void setLastField(FormField * field) + { + last = field; + } + protected: FormField * first = nullptr; FormField * last = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 8a49b3ace..bbf020cb2 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -273,8 +273,12 @@ class Window { } else { coord_t maxScrollPosition = h - height(); - if (scrollPositionY > maxScrollPosition) + if (scrollPositionY > maxScrollPosition) { setScrollPositionY(maxScrollPosition); + } + else { + invalidate(); + } } } From 3811f6f813158ffb6d0cde26718a91b088534919 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 16 Apr 2020 12:48:01 +0200 Subject: [PATCH 161/714] Another invalidate missing --- radio/src/thirdparty/libopenui/src/window.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index bbf020cb2..49157509a 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -276,10 +276,8 @@ class Window { if (scrollPositionY > maxScrollPosition) { setScrollPositionY(maxScrollPosition); } - else { - invalidate(); - } } + invalidate(); } coord_t getScrollPositionX() const From 98cf1f6c9268c6caf483ac4316e8b8d7b941038f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 16 Apr 2020 13:00:50 +0200 Subject: [PATCH 162/714] ExpansionPanel scroll issue --- radio/src/thirdparty/libopenui/src/expansion_panel.h | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index f26292f13..6092b293c 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -48,7 +48,7 @@ class ExpansionPanel: public FormGroup { public: ExpansionPanel(Window * parent, const rect_t & rect): - FormGroup(parent, rect, FORM_NO_BORDER | FORM_FORWARD_FOCUS) + FormGroup(parent, rect, FORM_NO_BORDER | FORM_FORWARD_FOCUS | FORWARD_SCROLL) { } diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 3ada0ae14..10fe016d2 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -115,6 +115,7 @@ void FormGroup::setFocus(uint8_t flag) if (first) first->setFocus(SET_FOCUS_FIRST); break; + case SET_FOCUS_BACKWARD: // TODO this test should be recursive if (focusWindow == first || focusWindow->getParent() == first) { @@ -128,6 +129,7 @@ void FormGroup::setFocus(uint8_t flag) last->setFocus(SET_FOCUS_BACKWARD); } break; + case SET_FOCUS_FORWARD: // TODO this test should be recursive if (focusWindow == last || focusWindow->getParent() == last || focusWindow->getParent()->getParent() == last) { @@ -141,6 +143,7 @@ void FormGroup::setFocus(uint8_t flag) first->setFocus(SET_FOCUS_FORWARD); } break; + default: if (focusWindow == previous) { if (first) From fe9f5d9a3a0e8466679904ef811910f8c2ff727e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 16 Apr 2020 16:12:16 +0200 Subject: [PATCH 163/714] TODO implemented --- radio/src/thirdparty/libopenui/src/form.cpp | 6 ++---- radio/src/thirdparty/libopenui/src/window.cpp | 1 + radio/src/thirdparty/libopenui/src/window.h | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 10fe016d2..e966bf506 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -117,8 +117,7 @@ void FormGroup::setFocus(uint8_t flag) break; case SET_FOCUS_BACKWARD: - // TODO this test should be recursive - if (focusWindow == first || focusWindow->getParent() == first) { + if (focusWindow->isChild(first)) { if (previous == this) last->setFocus(SET_FOCUS_BACKWARD); else if (previous) @@ -131,8 +130,7 @@ void FormGroup::setFocus(uint8_t flag) break; case SET_FOCUS_FORWARD: - // TODO this test should be recursive - if (focusWindow == last || focusWindow->getParent() == last || focusWindow->getParent()->getParent() == last) { + if (focusWindow->isChild(last)) { if (next == this) first->setFocus(SET_FOCUS_FORWARD); else if (next) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 52a0f948a..69d068cd6 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -422,6 +422,7 @@ void Window::adjustInnerHeight() for (auto child: children) { innerHeight = max(innerHeight, child->rect.y + child->rect.h); } + invalidate(); } coord_t Window::adjustHeight() diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 49157509a..eb15e0806 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -99,6 +99,11 @@ class Window { return parent; } + bool isChild(Window * window) const + { + return window == this || (getParent() && getParent()->isChild(window)); + } + Window * getFullScreenWindow() { return (width() == LCD_W && height() == LCD_H) ? this : parent->getFullScreenWindow(); @@ -174,7 +179,9 @@ class Window { void setHeight(coord_t value) { rect.h = value; - if (innerHeight <= value) { + if (windowFlags & FORWARD_SCROLL) + innerHeight = value; + else if (innerHeight <= value) { setScrollPositionY(0); } invalidate(); @@ -268,7 +275,11 @@ class Window { void setInnerHeight(coord_t h) { innerHeight = h; - if (height() >= h) { + if (windowFlags & FORWARD_SCROLL) { + rect.h = innerHeight; + parent->adjustInnerHeight(); + } + else if (height() >= h) { setScrollPositionY(0); } else { From 61f8834c42fa721ca25e4fac8ece82d355306fc0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 16 Apr 2020 16:28:33 +0200 Subject: [PATCH 164/714] Cosmetics --- radio/src/thirdparty/libopenui/src/expansion_panel.h | 8 +++++++- radio/src/thirdparty/libopenui/src/window.cpp | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 6092b293c..c3de410fc 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -67,7 +67,13 @@ class ExpansionPanel: public FormGroup { void open(bool state = true) { isOpen = state; - coord_t newHeight = (state ? header->height() + body->height() : header->height()); + updateHeight(); + } + + void updateHeight() + { + body->adjustInnerHeight(); + coord_t newHeight = (isOpen ? header->height() + body->height() : header->height()); parent->moveWindowsTop(bottom(), newHeight - height()); setHeight(newHeight); } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 69d068cd6..69219c532 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -418,11 +418,11 @@ bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, void Window::adjustInnerHeight() { - innerHeight = 0; + coord_t bottomMax = 0; for (auto child: children) { - innerHeight = max(innerHeight, child->rect.y + child->rect.h); + bottomMax = max(bottomMax, child->rect.y + child->rect.h); } - invalidate(); + setInnerHeight(bottomMax); } coord_t Window::adjustHeight() From 1967049bc15ddd93a56389e71cca864b3f25650f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 20 Apr 2020 10:44:39 +0200 Subject: [PATCH 165/714] Defence --- radio/src/thirdparty/libopenui/src/choice.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 2024d0067..2cb4d7a26 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -39,9 +39,11 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values[], i getValue(std::move(getValue)), setValue(std::move(setValue)) { - const char ** value = &values[0]; - for (int i = vmin; i <= vmax; i++) { - this->values.emplace_back(*value++); + if (values) { + const char ** value = &values[0]; + for (int i = vmin; i <= vmax; i++) { + this->values.emplace_back(*value++); + } } } From 8b30604545a72f4d8efcb16e357c3b6d7957055d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 20 Apr 2020 10:45:25 +0200 Subject: [PATCH 166/714] Missing helpers --- radio/src/thirdparty/libopenui/src/menu.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 03dbeb676..333648f8f 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -74,6 +74,16 @@ class MenuBody: public Window { void select(int index); + int selection() + { + return selectedIndex; + } + + int count() + { + return lines.size(); + } + #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; #endif From 0d532076bf7bf3df8c4d6b836607d8da8767b789 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 20 Apr 2020 10:45:58 +0200 Subject: [PATCH 167/714] Missing helpers --- radio/src/thirdparty/libopenui/src/modal_window.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 75eda6700..f143ac5d6 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -88,6 +88,11 @@ class ModalWindowContent: public Window { title = std::move(text); } + const std::string & getTitle() + { + return title; + } + void paint(BitmapBuffer * dc) override; protected: From 41718815f20d327df4768fb2eab23e460bd1969b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 20 Apr 2020 10:48:57 +0200 Subject: [PATCH 168/714] Typo --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 69219c532..a6aa4cc99 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -237,7 +237,7 @@ void Window::fullPaint(BitmapBuffer * dc) TRACE_WINDOWS_INDENT("%s", getWindowDebugString().c_str()); paint(dc); #if defined(WINDOWS_INSPECT_BORDER_COLOR) - if dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR); + dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR); #endif } else { From 71ccf023b1842fd3ff8d8fd5d507c7f183542e00 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 20 Apr 2020 18:49:02 +0200 Subject: [PATCH 169/714] Cosmetics --- .../thirdparty/libopenui/src/basenumberedit.h | 2 +- .../thirdparty/libopenui/src/bufferedwindow.h | 2 +- radio/src/thirdparty/libopenui/src/button.h | 8 +++---- .../src/thirdparty/libopenui/src/carousel.cpp | 2 ++ radio/src/thirdparty/libopenui/src/carousel.h | 4 ++-- radio/src/thirdparty/libopenui/src/checkbox.h | 2 +- radio/src/thirdparty/libopenui/src/choice.h | 2 +- .../thirdparty/libopenui/src/coloredit.cpp | 2 +- .../src/thirdparty/libopenui/src/coloredit.h | 2 +- radio/src/thirdparty/libopenui/src/dialog.h | 2 +- .../libopenui/src/expansion_panel.h | 4 ++-- .../src/thirdparty/libopenui/src/filechoice.h | 2 +- radio/src/thirdparty/libopenui/src/form.h | 4 ++-- .../libopenui/src/keyboard_number.h | 2 +- .../thirdparty/libopenui/src/keyboard_text.h | 2 +- .../src/thirdparty/libopenui/src/mainwindow.h | 2 +- radio/src/thirdparty/libopenui/src/menu.cpp | 5 +++-- radio/src/thirdparty/libopenui/src/menu.h | 10 ++++----- .../thirdparty/libopenui/src/modal_window.h | 4 ++-- .../src/thirdparty/libopenui/src/numberedit.h | 2 +- radio/src/thirdparty/libopenui/src/progress.h | 4 ++-- radio/src/thirdparty/libopenui/src/slider.h | 2 +- radio/src/thirdparty/libopenui/src/static.h | 6 ++--- radio/src/thirdparty/libopenui/src/table.h | 6 ++--- radio/src/thirdparty/libopenui/src/textedit.h | 6 ++--- radio/src/thirdparty/libopenui/src/window.cpp | 20 ++++++++++++----- radio/src/thirdparty/libopenui/src/window.h | 22 ++++++++++--------- 27 files changed, 72 insertions(+), 59 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index e7d6daf0e..c1314c97f 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -85,7 +85,7 @@ class BaseNumberEdit : public FormField { _setValue = handler; } - int32_t getValue() + int32_t getValue() const { return _getValue(); } diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h index 674dcd235..56d25447c 100644 --- a/radio/src/thirdparty/libopenui/src/bufferedwindow.h +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -106,7 +106,7 @@ class TransparentBitmapBackground: public TransparentBufferedWindow } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "TransparentBitmapBackground"; } diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 6bcd7682c..41a6b463f 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -31,12 +31,12 @@ class Button: public FormField { public: Button(FormGroup * parent, const rect_t & rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0): FormField(parent, rect, windowFlags), - pressHandler(pressHandler) + pressHandler(std::move(pressHandler)) { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Button"; } @@ -112,7 +112,7 @@ class TextButton: public Button { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "TextButton \"" + text + "\""; } @@ -141,7 +141,7 @@ class IconButton: public Button { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "IconButton(" + std::to_string(icon) + ")"; } diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp index c8915b6a1..44c069ead 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.cpp +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -48,6 +48,7 @@ void CarouselWindow::update() setInnerWidth(lastPosition); } +#if defined(HARDWARE_KEYS) void Carousel::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); @@ -64,3 +65,4 @@ void Carousel::onEvent(event_t event) Window::onEvent(event); } } +#endif diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index 342233c27..696a20cfd 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -91,7 +91,7 @@ class Carousel: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Carousel"; } @@ -114,7 +114,7 @@ class Carousel: public Window { nextButton->enable(index < (int)body->items.size() - 1); } - int getSelection() + int getSelection() const { return body->selection; } diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 61e1ebce9..3c0d17077 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -33,7 +33,7 @@ class CheckBox : public FormField { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "CheckBox"; } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index ea72f2315..6b3a03936 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -57,7 +57,7 @@ class Choice : public ChoiceBase { void addItems(const char * items[], uint8_t count); #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Choice"; } diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index 0de6d9386..708498433 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -33,7 +33,7 @@ class ColorBox: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "ColorBox"; } diff --git a/radio/src/thirdparty/libopenui/src/coloredit.h b/radio/src/thirdparty/libopenui/src/coloredit.h index 28f57ae4e..c12dedb55 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.h +++ b/radio/src/thirdparty/libopenui/src/coloredit.h @@ -27,7 +27,7 @@ class ColorEdit : public FormGroup { ColorEdit(FormGroup * parent, const rect_t & rect, std::function getValue, std::function setValue = nullptr); #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "ColorEdit"; } diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 85b2b7737..58d26233b 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -37,7 +37,7 @@ class DialogWindowContent: public ModalWindowContent { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "DialogWindowContent"; } diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index c3de410fc..0a9e7bb3c 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -29,7 +29,7 @@ class ExpansionPanelHeader: virtual public FormGroup { explicit ExpansionPanelHeader(ExpansionPanel * parent); #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "ExpansionPanelHeader"; } @@ -53,7 +53,7 @@ class ExpansionPanel: public FormGroup { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "ExpansionPanel"; } diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index ebed4d8d4..877f3f933 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -28,7 +28,7 @@ class FileChoice: public ChoiceBase { FileChoice(FormGroup * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "FileChoice"; } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 3a71b84cc..8dea0331a 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -94,7 +94,7 @@ class FormGroup: public FormField { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "FormGroup"; } @@ -135,7 +135,7 @@ class FormWindow: public FormGroup { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "FormWindow"; } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h index 52e195658..b82f68f1d 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -29,7 +29,7 @@ class NumberKeyboard : public Keyboard { ~NumberKeyboard() override; #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "NumberKeyboard"; } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index bc333904e..368d239ec 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -37,7 +37,7 @@ class TextKeyboard : public Keyboard { ~TextKeyboard() override; #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "TextKeyboard"; } diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 30b40ae49..e345c9de7 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -33,7 +33,7 @@ class MainWindow: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "MainWindow"; } diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 5827c11cc..06cfa8311 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -73,13 +73,14 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) void MenuBody::paint(BitmapBuffer * dc) { - for (auto i = 0; i < (int)lines.size(); i++) { + for (unsigned i = 0; i < lines.size(); i++) { auto & line = lines[i]; LcdFlags flags = MENU_COLOR | MENU_FONT; if (selectedIndex == i) { flags = MENU_HIGHLIGHT_COLOR | MENU_FONT; - if (MENU_HIGHLIGHT_BGCOLOR != MENU_BGCOLOR) + if (MENU_HIGHLIGHT_BGCOLOR != MENU_BGCOLOR) { dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), MENUS_LINE_HEIGHT, MENU_HIGHLIGHT_BGCOLOR); + } } if (line.drawLine) { line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT /*+ (lineHeight - 20) / 2*/, flags); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 333648f8f..d1737a8c1 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -66,7 +66,7 @@ class MenuBody: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "MenuBody"; } @@ -74,12 +74,12 @@ class MenuBody: public Window { void select(int index); - int selection() + int selection() const { return selectedIndex; } - int count() + int count() const { return lines.size(); } @@ -112,7 +112,7 @@ class MenuBody: public Window { void setCancelHandler(std::function handler) { - onCancel = handler; + onCancel = std::move(handler); } void paint(BitmapBuffer * dc) override; @@ -139,7 +139,7 @@ class MenuWindowContent: public ModalWindowContent { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "MenuWindowContent"; } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index f143ac5d6..f82658ac4 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -28,7 +28,7 @@ class ModalWindow: public Window explicit ModalWindow(Window * parent); #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "ModalWindow"; } @@ -77,7 +77,7 @@ class ModalWindowContent: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "ModalWindowContent"; } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index ff18d61f7..e978f75f4 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -29,7 +29,7 @@ class NumberEdit : public BaseNumberEdit { std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "NumberEdit(" + std::to_string(getValue()) + ")"; } diff --git a/radio/src/thirdparty/libopenui/src/progress.h b/radio/src/thirdparty/libopenui/src/progress.h index e135b3715..176b38873 100644 --- a/radio/src/thirdparty/libopenui/src/progress.h +++ b/radio/src/thirdparty/libopenui/src/progress.h @@ -30,7 +30,7 @@ class Progress : public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Progress"; } @@ -45,7 +45,7 @@ class Progress : public Window { void paint(BitmapBuffer * dc) override; protected: - int value; + int value = 0; }; #endif diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index cf35fbfa8..c883516cc 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -34,7 +34,7 @@ class Slider: public FormField { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Slider"; } diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index acfd102d6..bf5486aac 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -32,7 +32,7 @@ class StaticText: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "StaticText \"" + text + "\""; } @@ -58,7 +58,7 @@ class Subtitle: public StaticText { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Subtitle \"" + text + "\""; } @@ -100,7 +100,7 @@ class StaticBitmap: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "StaticBitmap"; } diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 06f1cf992..dc9a86a43 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -46,7 +46,7 @@ class Table: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Table::Header"; } @@ -75,7 +75,7 @@ class Table: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Table::Body"; } @@ -134,7 +134,7 @@ class Table: public Window { } #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "Table"; } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 292bbecb8..adcaa08bc 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -35,7 +35,7 @@ class TextEdit : public FormField { #if defined(DEBUG_WINDOWS) - std::string getName() override + std::string getName() const override { return "TextEdit"; } @@ -46,12 +46,12 @@ class TextEdit : public FormField { changeHandler = handler; } - uint8_t getMaxLength() + uint8_t getMaxLength() const { return length; } - char * getData() + char * getData() const { return value; } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index a6aa4cc99..c6df7c10c 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -138,7 +138,7 @@ void Window::setFocus(uint8_t flag) void Window::setScrollPositionX(coord_t value) { - coord_t newScrollPosition = max(0, min(innerWidth - width(), value)); + auto newScrollPosition = max(0, min(innerWidth - width(), value)); if (newScrollPosition != scrollPositionX) { scrollPositionX = newScrollPosition; invalidate(); @@ -147,7 +147,12 @@ void Window::setScrollPositionX(coord_t value) void Window::setScrollPositionY(coord_t value) { - coord_t newScrollPosition = max(0, min(innerHeight - height(), value)); + auto newScrollPosition = min(innerHeight - height(), value); + + if (newScrollPosition < 0 && innerHeight != INFINITE_HEIGHT) { + newScrollPosition = 0; + } + if (newScrollPosition != scrollPositionY) { scrollPositionY = newScrollPosition; invalidate(); @@ -192,7 +197,7 @@ void Window::scrollTo(const rect_t & rect) } } -bool Window::hasOpaqueRect(const rect_t & rect) +bool Window::hasOpaqueRect(const rect_t & rect) const { if (!this->rect.contains(rect)) return false; @@ -253,12 +258,12 @@ void Window::fullPaint(BitmapBuffer * dc) } } -bool Window::isChildFullSize(Window * child) +bool Window::isChildFullSize(const Window * child) const { return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width(); } -bool Window::isChildVisible(Window * window) +bool Window::isChildVisible(const Window * window) const { for (auto rit = children.rbegin(); rit != children.rend(); rit++) { auto child = *rit; @@ -333,7 +338,10 @@ void Window::checkEvents() if (pageHeight) { coord_t relativeScrollPosition = scrollPositionY % pageHeight; if (relativeScrollPosition) { - setScrollPositionY(getScrollPositionY() - relativeScrollPosition + (relativeScrollPosition > pageHeight / 2 ? pageHeight : 0)); + if (relativeScrollPosition > 0) + setScrollPositionY(getScrollPositionY() - relativeScrollPosition + (relativeScrollPosition > pageHeight / 2 ? pageHeight : 0)); + else + setScrollPositionY(getScrollPositionY() - relativeScrollPosition - (relativeScrollPosition > -pageHeight / 2 ? 0 : pageHeight)); } } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index eb15e0806..9661fb634 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -39,6 +39,8 @@ typedef uint32_t WindowFlags; #undef TRANSPARENT #endif +constexpr int INFINITE_HEIGHT = INT32_MAX; + constexpr WindowFlags OPAQUE = 1u << 0u; constexpr WindowFlags TRANSPARENT = 1u << 1u; constexpr WindowFlags NO_SCROLLBAR = 1u << 2u; @@ -65,19 +67,19 @@ class Window { virtual ~Window(); #if defined(DEBUG_WINDOWS) - virtual std::string getName() + virtual std::string getName() const { return "Window"; } - std::string getRectString() + std::string getRectString() const { char result[32]; sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); return result; } - std::string getIndentString() + std::string getIndentString() const { std::string result; auto tmp = parent; @@ -88,7 +90,7 @@ class Window { return result; } - std::string getWindowDebugString(const char * name = nullptr) + std::string getWindowDebugString(const char * name = nullptr) const { return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); } @@ -252,7 +254,7 @@ class Window { pageHeight = h; } - uint8_t getPageCount() + uint8_t getPageCount() const { if (pageWidth) return innerWidth / pageWidth; @@ -262,7 +264,7 @@ class Window { return 1; } - uint8_t getPageIndex() + uint8_t getPageIndex() const { if (pageWidth) return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; @@ -305,11 +307,11 @@ class Window { void setScrollPositionY(coord_t value); - bool isChildVisible(Window * window); + bool isChildVisible(const Window * window) const; - bool isChildFullSize(Window * window); + bool isChildFullSize(const Window * window) const; - bool isVisible() + bool isVisible() const { return parent && parent->isChildVisible(this); } @@ -400,7 +402,7 @@ class Window { bool forwardTouchEnd(coord_t x, coord_t y); - bool hasOpaqueRect(const rect_t & rect); + bool hasOpaqueRect(const rect_t & rect) const; }; #endif // _WINDOW_H_ From 03ac973d7bcffb70ee73a7143fd5dc39a9174aa7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 20 Apr 2020 19:25:26 +0200 Subject: [PATCH 170/714] Roller finished --- radio/src/thirdparty/libopenui/src/roller.h | 114 ++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/src/roller.h diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h new file mode 100644 index 000000000..c9d78f541 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef _ROLLER_H_ +#define _ROLLER_H_ + +#include +#include +#include +#include +#include "libopenui_globals.h" +#include "libopenui_config.h" + +constexpr WindowFlags ROLLER_SEPARATION_LINES = (FORM_FLAGS_LAST << 1u); + +class Roller: public Choice { + public: + Roller(FormGroup * parent, const rect_t & rect, const char * label, const char ** values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): + Choice(parent, rect, values, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR) + { + if (label) { + // the label is another window, could be changed, but is it needed? + new StaticText(parent, {rect.x, rect.y - ROLLER_LINE_HEIGHT, rect.w, ROLLER_LINE_HEIGHT}, label, 0, CENTERED); + } + + setHeight(ROLLER_LINE_HEIGHT * 3 - 1); + setPageHeight(ROLLER_LINE_HEIGHT); + setInnerHeight(INFINITE_HEIGHT); + setScrollPositionY(ROLLER_LINE_HEIGHT * (this->getValue() - vmin - 1)); + lastScrollPositionY = scrollPositionY; + } + + void paint(BitmapBuffer * dc) override + { + int32_t value = getValue(); + + int index = (scrollPositionY - ROLLER_LINE_HEIGHT + 1) / ROLLER_LINE_HEIGHT; + coord_t y = index * ROLLER_LINE_HEIGHT; + coord_t yMax = scrollPositionY + 3 * ROLLER_LINE_HEIGHT; + + while (y < yMax) { + auto displayedValue = vmin + index % (vmax - vmin + 1); + if (displayedValue < vmin) + displayedValue += vmax - vmin + 1; + + auto fgColor = DISABLE_COLOR; + + if (value == displayedValue) { + fgColor = FOCUS_COLOR | FONT(STD); + } + + if ((unsigned)displayedValue < values.size()) { + dc->drawText(width() / 2, y, values[displayedValue].c_str(), fgColor | CENTERED); + } + else { + dc->drawNumber(width() / 2, y, displayedValue, fgColor | CENTERED); + } + + if (windowFlags & ROLLER_SEPARATION_LINES) { + dc->drawSolidHorizontalLine(0, y - 10, width(), DISABLE_COLOR); + } + + index += 1; + y += ROLLER_LINE_HEIGHT; + } + } + + void checkEvents() override + { + Window::checkEvents(); + +#if defined(HARDWARE_TOUCH) + if (touchState.event != TE_SLIDE) { + if (scrollPositionY != lastScrollPositionY) { + updateScrollPosition(); + } + } +#endif + } + + + protected: + coord_t lastScrollPositionY = 0; + + void updateScrollPosition() + { + lastScrollPositionY = scrollPositionY; + + auto newValue = vmin + ((scrollPositionY / ROLLER_LINE_HEIGHT) + 1) % (vmax - vmin + 1); + if (newValue < vmin) + newValue += vmax - vmin + 1; + + setValue(newValue); + invalidate(); + } +}; + +#endif // _ROLLER_H_ From 8b4cf2baa2f32785505f6c046cd2ed961eef3793 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 21 Apr 2020 09:22:37 +0200 Subject: [PATCH 171/714] Choice without text values should be OK now --- radio/src/thirdparty/libopenui/src/choice.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 2cb4d7a26..6db9afe3f 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -128,11 +128,16 @@ void Choice::openMenu() setValue(i); }); } - else { + else if (unsigned(i - vmin) < values.size()) { menu->addLine(values[i - vmin], [=]() { setValue(i); }); } + else { + menu->addLine(std::to_string(i), [=]() { + setValue(i); + }); + } if (value == i) { current = count; } From 588c326cfc87dbe7cdc4b69fdeaed4a184ae0bbb Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 21 Apr 2020 09:37:45 +0200 Subject: [PATCH 172/714] Add setMin and setMax to choice to update its limits --- radio/src/thirdparty/libopenui/src/choice.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 6b3a03936..ad7bfba8e 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -93,6 +93,18 @@ class Choice : public ChoiceBase { menuTitle = std::move(value); } + void setMin(int16_t value) + { + vmin = value; + invalidate(); + } + + void setMax(int16_t value) + { + vmax = value; + invalidate(); + } + protected: std::vector values; int16_t vmin = 0; From 851193980101fa1f3bee10ae3ef641b4c7421279 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 21 Apr 2020 23:05:25 +0200 Subject: [PATCH 173/714] Add the possibility to draw a bitmap through a mask --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 65 ++++++++++++++++++- .../thirdparty/libopenui/src/bitmapbuffer.h | 8 ++- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- radio/src/thirdparty/libopenui/src/roller.h | 20 +++++- 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 6763cf060..99ecc572d 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -377,7 +377,7 @@ void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadiu } } -void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset, coord_t width) +void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offsetX, coord_t width) { if (!mask) return; @@ -397,7 +397,7 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd if (x < xmin) { width += x - xmin; - offset -= x - xmin; + offsetX -= x - xmin; x = xmin; } @@ -410,7 +410,7 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd if (y + row < ymin || y + row >= ymax) continue; pixel_t * p = getPixelPtr(x, y + row); - const pixel_t * q = mask->getPixelPtr(offset, row); + const pixel_t * q = mask->getPixelPtr(offsetX, row); for (coord_t col = 0; col < width; col++) { drawAlphaPixel(p, *((uint8_t *)q), color); MOVE_TO_NEXT_RIGHT_PIXEL(p); @@ -419,6 +419,50 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd } } +void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, const BitmapBuffer * srcBitmap, coord_t offsetX, coord_t offsetY, coord_t width, coord_t height) +{ + if (!mask || !srcBitmap) + return; + + APPLY_OFFSET(); + + coord_t maskWidth = mask->width(); + coord_t maskHeight = mask->height(); + + if (!width || width > maskWidth) { + width = maskWidth; + } + + if (!height || height > maskHeight) { + height = maskHeight; + } + + if (x + width > xmax) { + width = xmax - x; + } + + if (x < xmin) { + width += x - xmin; + offsetX -= x - xmin; + x = xmin; + } + + if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) + return; + + for (coord_t row = 0; row < height; row++) { + if (y + row < ymin || y + row >= ymax) + continue; + pixel_t * p = getPixelPtr(x, y + row); + const pixel_t * q = mask->getPixelPtr(offsetX, offsetY + row); + for (coord_t col = 0; col < width; col++) { + drawAlphaPixel(p, *((uint8_t *)q), *srcBitmap->getPixelPtr(row, col)); + MOVE_TO_NEXT_RIGHT_PIXEL(p); + MOVE_TO_NEXT_RIGHT_PIXEL(q); + } + } +} + void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset, coord_t width) { APPLY_OFFSET(); @@ -676,6 +720,21 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) return bitmap; } +BitmapBuffer * BitmapBuffer::invertMask() const +{ + BitmapBuffer * result = new BitmapBuffer(format, width(), height()); + pixel_t * srcData = data; + pixel_t * destData = result->data; + for (auto y = 0; y < height(); y++) { + for (auto x = 0; x < width(); x++) { + destData[x] = OPACITY_MAX - (uint8_t)srcData[x]; + } + srcData += width(); + destData += width(); + } + return result; +} + BitmapBuffer * BitmapBuffer::horizontalFlip() const { BitmapBuffer * result = new BitmapBuffer(format, width(), height()); diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 18e70039d..f875368dc 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -33,7 +33,7 @@ constexpr uint8_t DOTTED = 0x55; constexpr uint8_t STASHED = 0x33; #define MOVE_OFFSET() coord_t offsetX = this->offsetX; x += offsetX; this->offsetX = 0; coord_t offsetY = this->offsetY; y += offsetY; this->offsetY = 0 -#define APPLY_OFFSET() x += offsetX; y += offsetY +#define APPLY_OFFSET() x += this->offsetX; y += this->offsetY #define RESTORE_OFFSET() this->offsetX = offsetX, this->offsetY = offsetY #if defined(LCD_VERTICAL_INVERT) @@ -369,7 +369,9 @@ class BitmapBuffer: public BitmapBufferBase static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background); - void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offset=0, coord_t width=0); + void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offsetX = 0, coord_t width = 0); + + void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, const BitmapBuffer * srcBitmap, coord_t offsetX = 0, coord_t offsetY = 0, coord_t width = 0, coord_t height = 0); void drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset=0, coord_t width=0); @@ -506,6 +508,8 @@ class BitmapBuffer: public BitmapBufferBase BitmapBuffer * verticalFlip() const; + BitmapBuffer * invertMask() const; + protected: static BitmapBuffer * load_bmp(const char * filename); static BitmapBuffer * load_stb(const char * filename); diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 06cfa8311..e5af31b24 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -76,7 +76,7 @@ void MenuBody::paint(BitmapBuffer * dc) for (unsigned i = 0; i < lines.size(); i++) { auto & line = lines[i]; LcdFlags flags = MENU_COLOR | MENU_FONT; - if (selectedIndex == i) { + if (selectedIndex == (int)i) { flags = MENU_HIGHLIGHT_COLOR | MENU_FONT; if (MENU_HIGHLIGHT_BGCOLOR != MENU_BGCOLOR) { dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), MENUS_LINE_HEIGHT, MENU_HIGHLIGHT_BGCOLOR); diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index c9d78f541..4e0dcfbde 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -46,6 +46,14 @@ class Roller: public Choice { lastScrollPositionY = scrollPositionY; } + +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "Roller"; + } +#endif + void paint(BitmapBuffer * dc) override { int32_t value = getValue(); @@ -85,20 +93,26 @@ class Roller: public Choice { { Window::checkEvents(); + if (lastEditMode != editMode) { + setScrollPositionY(ROLLER_LINE_HEIGHT * (this->getValue() - vmin - 1)); + lastScrollPositionY = scrollPositionY; + lastEditMode = editMode; + } + #if defined(HARDWARE_TOUCH) if (touchState.event != TE_SLIDE) { if (scrollPositionY != lastScrollPositionY) { - updateScrollPosition(); + updateValueFromScrollPosition(); } } #endif } - protected: coord_t lastScrollPositionY = 0; + bool lastEditMode = false; - void updateScrollPosition() + void updateValueFromScrollPosition() { lastScrollPositionY = scrollPositionY; From b1666746044cdf978eea0aab2a6b6307fe7b2286 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 22 Apr 2020 09:55:11 +0200 Subject: [PATCH 174/714] Focus issue on disabled form fields --- radio/src/thirdparty/libopenui/src/button.cpp | 6 ++-- radio/src/thirdparty/libopenui/src/button.h | 31 ++++------------- .../src/thirdparty/libopenui/src/checkbox.cpp | 2 +- radio/src/thirdparty/libopenui/src/choice.cpp | 2 +- radio/src/thirdparty/libopenui/src/dialog.cpp | 4 +-- .../libopenui/src/expansion_panel.cpp | 2 +- .../thirdparty/libopenui/src/filechoice.cpp | 8 ++--- radio/src/thirdparty/libopenui/src/form.cpp | 33 +++++++++++++++---- radio/src/thirdparty/libopenui/src/form.h | 21 +++++++++++- radio/src/thirdparty/libopenui/src/menu.cpp | 8 ++--- .../thirdparty/libopenui/src/menutoolbar.h | 2 +- .../thirdparty/libopenui/src/modal_window.h | 2 +- .../thirdparty/libopenui/src/numberedit.cpp | 2 +- .../src/thirdparty/libopenui/src/numberedit.h | 6 ---- radio/src/thirdparty/libopenui/src/slider.cpp | 2 +- .../src/thirdparty/libopenui/src/textedit.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.h | 6 ++-- 17 files changed, 76 insertions(+), 63 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 48c44dd9a..ffb46b445 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -48,9 +48,9 @@ void Button::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool Button::onTouchEnd(coord_t x, coord_t y) { - if (enabled()) { + if (enabled) { if (!(windowFlags & NO_FOCUS)) { - setFocus(); + setFocus(SET_FOCUS_DEFAULT); } onPress(); } @@ -69,7 +69,7 @@ void TextButton::paint(BitmapBuffer * dc) { FormField::paint(dc); - LcdFlags textColor = DEFAULT_COLOR; + auto textColor = DEFAULT_COLOR; if (checked()) { if (hasFocus()) { diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 41a6b463f..252b24eaf 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -22,10 +22,9 @@ #include "form.h" -constexpr WindowFlags BUTTON_BACKGROUND = FORM_FLAGS_LAST << 1; -constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2; -constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3; -constexpr WindowFlags BUTTON_DISABLED = FORM_FLAGS_LAST << 4; +constexpr WindowFlags BUTTON_BACKGROUND = FORM_FLAGS_LAST << 1u; +constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2u; +constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3u; class Button: public FormField { public: @@ -42,19 +41,6 @@ class Button: public FormField { } #endif - void enable(bool enabled = true) - { - if (!enabled != bool(windowFlags & BUTTON_DISABLED)) { - windowFlags ^= BUTTON_DISABLED; - invalidate(); - } - } - - void disable() - { - enable(false); - } - void check(bool checked = true) { if (checked != bool(windowFlags & BUTTON_CHECKED)) { @@ -63,12 +49,7 @@ class Button: public FormField { } } - bool enabled() - { - return !(windowFlags & BUTTON_DISABLED); - } - - bool checked() + bool checked() const { if (windowFlags & BUTTON_CHECKED_ON_FOCUS) return hasFocus(); @@ -106,7 +87,7 @@ class Button: public FormField { class TextButton: public Button { public: TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND): - Button(parent, rect, pressHandler, windowFlags | OPAQUE), + Button(parent, rect, std::move(pressHandler), windowFlags | OPAQUE), text(std::move(text)) { } @@ -135,7 +116,7 @@ class TextButton: public Button { class IconButton: public Button { public: IconButton(FormGroup * parent, const rect_t & rect, uint8_t icon, std::function pressHandler, WindowFlags flags = 0): - Button(parent, rect, pressHandler, flags), + Button(parent, rect, std::move(pressHandler), flags), icon(icon) { } diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 9bdcf08c5..18f0cb458 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -46,7 +46,7 @@ bool CheckBox::onTouchEnd(coord_t x, coord_t y) { onKeyPress(); _setValue(!getValue()); - setFocus(); + setFocus(SET_FOCUS_DEFAULT); invalidate(); return true; } diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 6db9afe3f..fe546fc30 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -150,7 +150,7 @@ void Choice::openMenu() menu->setCloseHandler([=]() { editMode = false; - setFocus(); + setFocus(SET_FOCUS_DEFAULT); }); setEditMode(true); diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 50a8b88a9..cd991edce 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -25,7 +25,7 @@ DialogWindowContent::DialogWindowContent(Dialog * parent, const rect_t & rect): ModalWindowContent(parent, rect), form(this, {0, POPUP_HEADER_HEIGHT, rect.w, coord_t(rect.h - POPUP_HEADER_HEIGHT)}, FORM_NO_BORDER) { - form.setFocus(); + form.setFocus(SET_FOCUS_DEFAULT); } @@ -34,5 +34,5 @@ Dialog::Dialog(Window * parent, std::string title, const rect_t & rect): content(createDialogWindow(this, rect)) { bringToTop(); - content->setTitle(title); + content->setTitle(std::move(title)); } diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp index 11601f1a6..6f2a84689 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -43,7 +43,7 @@ void ExpansionPanelHeader::onEvent(event_t event) bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) { static_cast(parent)->toggle(); - setFocus(); + setFocus(SET_FOCUS_DEFAULT); return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 721bac985..7a2319596 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -75,7 +75,7 @@ bool FileChoice::openMenu() files.emplace_back(fno.fname, fnLen); } - if (files.size() > 0) { + if (!files.empty()) { // sort files files.sort(compare_nocase); @@ -102,8 +102,8 @@ bool FileChoice::openMenu() } menu->setCloseHandler([=]() { - editMode = false; - setFocus(); + editMode = false; + setFocus(SET_FOCUS_DEFAULT); }); return true; @@ -136,7 +136,7 @@ void FileChoice::onEvent(event_t event) bool FileChoice::onTouchEnd(coord_t, coord_t) { openMenu(); - setFocus(); + setFocus(SET_FOCUS_DEFAULT); setEditMode(true); return true; } diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index e966bf506..33346d33c 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -25,7 +25,7 @@ FormField::FormField(Window * parent, const rect_t & rect, WindowFlags windowFla Window(parent, rect, windowFlags, textFlags) { if (!(windowFlags & NO_FOCUS)) { - FormGroup * form = dynamic_cast(parent); + auto * form = dynamic_cast(parent); if (form) { form->addField(this, windowFlags & PUSH_FRONT); } @@ -61,6 +61,23 @@ void FormField::onEvent(event_t event) } #endif +void FormField::setFocus(uint8_t flag) +{ + if (!enabled) { + if (flag == SET_FOCUS_BACKWARD) { + if (previous) + previous->setFocus(flag); + } + else { + if (next) + next->setFocus(flag); + } + } + else { + Window::setFocus(flag); + } +} + void FormField::paint(BitmapBuffer * dc) { if (editMode) { @@ -101,7 +118,7 @@ void FormGroup::addField(FormField * field, bool front) link(last, first); } if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { - field->setFocus(); + field->setFocus(SET_FOCUS_DEFAULT); } } @@ -131,21 +148,23 @@ void FormGroup::setFocus(uint8_t flag) case SET_FOCUS_FORWARD: if (focusWindow->isChild(last)) { - if (next == this) + if (next == this) { first->setFocus(SET_FOCUS_FORWARD); + } else if (next) next->setFocus(SET_FOCUS_FORWARD); } else { - if (first) + if (first) { first->setFocus(SET_FOCUS_FORWARD); + } } break; default: if (focusWindow == previous) { if (first) - first->setFocus(); + first->setFocus(SET_FOCUS_DEFAULT); } else { if (next) @@ -155,7 +174,7 @@ void FormGroup::setFocus(uint8_t flag) } } else { - FormField::setFocus(); + FormField::setFocus(SET_FOCUS_DEFAULT); } } @@ -209,6 +228,6 @@ void FormWindow::onEvent(event_t event) return; } - Window::onEvent(event); + FormGroup::onEvent(event); } #endif diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 8dea0331a..d326d846e 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -62,6 +62,24 @@ class FormField: public Window { editMode = newEditMode; } + void enable(bool enabled) + { + this->enabled = enabled; + invalidate(); + } + + bool isEnabled() const + { + return enabled; + } + + void disable() + { + enable(false); + } + + void setFocus(uint8_t flag) override; + void onFocusLost() override { editMode = false; @@ -84,6 +102,7 @@ class FormField: public Window { FormField * next = nullptr; FormField * previous = nullptr; bool editMode = false; + bool enabled = true; }; class FormGroup: public FormField { @@ -107,7 +126,7 @@ class FormGroup: public FormField { last = nullptr; } - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT) override; + void setFocus(uint8_t flag) override; void addField(FormField * field, bool front = false); diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index e5af31b24..b292e7b10 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -36,10 +36,10 @@ void MenuBody::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_ROTARY_RIGHT) { - select((selectedIndex + 1) % lines.size()); + select(int((selectedIndex + 1) % lines.size())); } else if (event == EVT_ROTARY_LEFT) { - select(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1); + select(int(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1)); } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { if (selectedIndex < 0) { @@ -99,7 +99,7 @@ MenuWindowContent::MenuWindowContent(Menu * parent): ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), body(this, {0, 0, width(), height()}) { - body.setFocus(); + body.setFocus(SET_FOCUS_DEFAULT); } void MenuWindowContent::paint(BitmapBuffer * dc) @@ -148,7 +148,7 @@ void Menu::addLine(const std::string & text, std::function onPress) void Menu::addCustomLine(std::function drawLine, std::function onPress) { - content->body.addCustomLine(drawLine, std::move(onPress)); + content->body.addCustomLine(std::move(drawLine), std::move(onPress)); updatePosition(); } diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index ef1559bc7..bb0de621e 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -47,7 +47,7 @@ class MenuToolbarButton: public Button { clearFocus(); } else { - setFocus(); + setFocus(SET_FOCUS_DEFAULT); } onPress(); return true; diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index f82658ac4..65275f670 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -37,7 +37,7 @@ class ModalWindow: public Window void deleteLater() { if (previousFocus) { - previousFocus->setFocus(); + previousFocus->setFocus(SET_FOCUS_DEFAULT); } Window::deleteLater(); } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 4d1f2d8b2..ecd91e3e8 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -127,7 +127,7 @@ bool NumberEdit::onTouchEnd(coord_t, coord_t) } if (!hasFocus()) { - setFocus(); + setFocus(SET_FOCUS_DEFAULT); } NumberKeyboard::show(this); diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index e978f75f4..03e34850b 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -37,11 +37,6 @@ class NumberEdit : public BaseNumberEdit { void paint(BitmapBuffer * dc) override; - void enable(bool enabled) - { - this->enabled = enabled; - } - void setAvailableHandler(std::function handler) { isValueAvailable = std::move(handler); @@ -80,7 +75,6 @@ class NumberEdit : public BaseNumberEdit { std::string prefix; std::string suffix; std::string zeroText; - bool enabled = true; std::function isValueAvailable; }; diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index 3284e884a..ecc08243d 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -57,7 +57,7 @@ bool Slider::onTouchStart(coord_t x, coord_t y) { if (!hasFocus()) { onKeyPress(); - setFocus(); + setFocus(SET_FOCUS_DEFAULT); } sliding = (value(x) == getValue()); return true; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 58cddb992..a3f8a1ecd 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -182,7 +182,7 @@ void TextEdit::onEvent(event_t event) bool TextEdit::onTouchEnd(coord_t x, coord_t y) { if (!hasFocus()) { - setFocus(); + setFocus(SET_FOCUS_DEFAULT); } #if defined(SOFTWARE_KEYBOARD) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 9661fb634..f9cce8e8e 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -158,11 +158,11 @@ class Window { static void clearFocus(); - virtual void setFocus(uint8_t flag = SET_FOCUS_DEFAULT); + virtual void setFocus(uint8_t flag); - void setRect(rect_t rect) + void setRect(rect_t value) { - this->rect = rect; + rect = value; invalidate(); } From dd9a87aa5f0d052389856ac0eeaa51d4af65b689 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 28 Apr 2020 10:09:38 +0200 Subject: [PATCH 175/714] onFocusLost calls now focusHandler(false) when this handler is set --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.h | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index c6df7c10c..aa24e9457 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -121,7 +121,7 @@ void Window::setFocus(uint8_t flag) clearFocus(); focusWindow = this; if (focusHandler) { - focusHandler(); + focusHandler(true); } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index f9cce8e8e..a261677aa 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -131,7 +131,7 @@ class Window { closeHandler = std::move(handler); } - void setFocusHandler(std::function handler) + void setFocusHandler(std::function handler) { focusHandler = std::move(handler); } @@ -360,7 +360,7 @@ class Window { static std::list trash; std::function closeHandler; - std::function focusHandler; + std::function focusHandler; void addChild(Window * window, bool front = false) { @@ -389,6 +389,9 @@ class Window { virtual void onFocusLost() { TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); + if (focusHandler) { + focusHandler(false); + } invalidate(); } From 77900db31060562551c03d9aabc821e6dc6f1413 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 7 May 2020 09:51:28 +0200 Subject: [PATCH 176/714] Cosmetics --- radio/src/thirdparty/libopenui/src/choice.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/choice.h | 2 +- radio/src/thirdparty/libopenui/src/roller.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index fe546fc30..47d17072b 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -31,7 +31,7 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vm { } -Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values[], int16_t vmin, int16_t vmax, +Choice::Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int16_t vmin, int16_t vmax, std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), vmin(vmin), @@ -40,7 +40,7 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values[], i setValue(std::move(setValue)) { if (values) { - const char ** value = &values[0]; + auto value = &values[0]; for (int i = vmin; i <= vmax; i++) { this->values.emplace_back(*value++); } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index ad7bfba8e..06deaef13 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -49,7 +49,7 @@ class Choice : public ChoiceBase { public: Choice(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); Choice(FormGroup * parent, const rect_t & rect, std::vector values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup * parent, const rect_t & rect, const char * values[], int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); void addItem(const char * item); diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 4e0dcfbde..61dcf4c0c 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -31,7 +31,7 @@ constexpr WindowFlags ROLLER_SEPARATION_LINES = (FORM_FLAGS_LAST << 1u); class Roller: public Choice { public: - Roller(FormGroup * parent, const rect_t & rect, const char * label, const char ** values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): + Roller(FormGroup * parent, const rect_t & rect, const char * label, const char * const * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): Choice(parent, rect, values, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR) { if (label) { From 0e395e0e39cd0d25fc60a4786d99121edcd33468 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 7 May 2020 11:18:52 +0200 Subject: [PATCH 177/714] Choice cosmetics --- radio/src/thirdparty/libopenui/src/choice.cpp | 33 ++++++++++++------- radio/src/thirdparty/libopenui/src/choice.h | 8 +++-- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 47d17072b..5c1559164 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -39,12 +39,7 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, const char * const value getValue(std::move(getValue)), setValue(std::move(setValue)) { - if (values) { - auto value = &values[0]; - for (int i = vmin; i <= vmax; i++) { - this->values.emplace_back(*value++); - } - } + setValues(values); } Choice::Choice(FormGroup * parent, const rect_t & rect, std::vector values, int16_t vmin, int16_t vmax, @@ -76,20 +71,36 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int } } -void Choice::addItem(const char * item) +void Choice::addValue(const char * value) { - values.emplace_back(item); + values.emplace_back(value); vmax += 1; } -void Choice::addItems(const char * items[], uint8_t count) +void Choice::addValues(const char * const values[], uint8_t count) { - values.reserve(values.size() + count); + this->values.reserve(this->values.size() + count); for (uint8_t i = 0; i < count; i++) - values.emplace_back(items[i]); + this->values.emplace_back(values[i]); vmax += count; } +void Choice::setValues(std::vector values) +{ + this->values = std::move(values); +} + +void Choice::setValues(const char * const values[]) +{ + this->values.clear(); + if (values) { + auto value = &values[0]; + for (int i = vmin; i <= vmax; i++) { + this->values.emplace_back(*value++); + } + } +} + void Choice::paint(BitmapBuffer * dc) { FormField::paint(dc); diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 06deaef13..3141868a9 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -52,9 +52,13 @@ class Choice : public ChoiceBase { Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - void addItem(const char * item); + void addValue(const char * value); - void addItems(const char * items[], uint8_t count); + void addValues(const char * const values[], uint8_t count); + + void setValues(std::vector values); + + void setValues(const char * const values[]); #if defined(DEBUG_WINDOWS) std::string getName() const override From 27a5d9d2ad893bd6f37d9ced08aac13c93394e7b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 11 May 2020 22:00:26 +0200 Subject: [PATCH 178/714] Cosmetics --- radio/src/thirdparty/libopenui/src/table.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index dc9a86a43..bfe8ea47c 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -21,6 +21,7 @@ #define _TABLE_H_ #include "window.h" +#include #include #include "libopenui_config.h" @@ -192,8 +193,8 @@ class Table: public Window { for (uint8_t i = 0; i < columnsCount; i++) { line.values[i] = values[i]; } - line.onPress = onPress; - line.onSelect = onSelect; + line.onPress = std::move(onPress); + line.onSelect = std::move(onSelect); body.addLine(line); } From 39c2fe6f16ddc7e8f145026550df5a171ba5bbe5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 12 May 2020 15:53:15 +0200 Subject: [PATCH 179/714] Cosmetics --- radio/src/thirdparty/libopenui/src/table.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index bfe8ea47c..19d4a7140 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -204,6 +204,11 @@ class Table: public Window { clearSelection(); } + int size() + { + return body.lines.size(); + } + protected: uint8_t columnsCount; std::vector columnsWidth; From 2e786b475a78bc029cbca525c444e4211e000664 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 May 2020 09:26:15 +0200 Subject: [PATCH 180/714] Roller new constructor --- radio/src/thirdparty/libopenui/src/roller.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 61dcf4c0c..9d66a2f78 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -33,6 +33,17 @@ class Roller: public Choice { public: Roller(FormGroup * parent, const rect_t & rect, const char * label, const char * const * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): Choice(parent, rect, values, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR) + { + init(label); + } + + Roller(FormGroup * parent, const rect_t & rect, const char * label, std::vector values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): + Choice(parent, rect, std::move(values), vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR) + { + init(label); + } + + void init(const char * label) { if (label) { // the label is another window, could be changed, but is it needed? @@ -46,7 +57,6 @@ class Roller: public Choice { lastScrollPositionY = scrollPositionY; } - #if defined(DEBUG_WINDOWS) std::string getName() const override { From fbea91176a7c74c74e80a4c4fd118d8c44fdba8c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 May 2020 09:41:57 +0200 Subject: [PATCH 181/714] Compilation fix --- radio/src/thirdparty/libopenui/src/roller.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 9d66a2f78..45f810fe1 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -20,6 +20,7 @@ #ifndef _ROLLER_H_ #define _ROLLER_H_ +#include #include #include #include From ca4e8654270ab811fbc2548c5559c67012988e93 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 May 2020 09:43:05 +0200 Subject: [PATCH 182/714] Revert last change --- radio/src/thirdparty/libopenui/src/roller.h | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 45f810fe1..9d66a2f78 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -20,7 +20,6 @@ #ifndef _ROLLER_H_ #define _ROLLER_H_ -#include #include #include #include From 514f9664357506a9e628920790e18c4c00931040 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 13 May 2020 11:49:12 +0200 Subject: [PATCH 183/714] Choice and Roller updates --- radio/src/thirdparty/libopenui/src/choice.h | 36 +++++++++++++++++++++ radio/src/thirdparty/libopenui/src/roller.h | 23 +++++++------ 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 3141868a9..14da958d6 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -87,6 +87,42 @@ class Choice : public ChoiceBase { isValueAvailable = std::move(handler); } + unsigned getIndexFromValue(int value) + { + if (!isValueAvailable) { + return value - vmin; + } + + unsigned index = 0; + for (int i = vmin; i < value; i++) { + if (isValueAvailable(i)) { + index++; + } + } + return index; + } + + int getValueFromIndex(unsigned index) + { + if (!isValueAvailable) { + return vmin + index; + } + + int value = vmin; + while (index > 0) { + while (value < vmax && !isValueAvailable(value)) { + value++; + } + index--; + } + return value; + } + + int getValuesCount() + { + return getIndexFromValue(vmax + 1); + } + void setTextHandler(std::function handler) { textHandler = std::move(handler); diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 9d66a2f78..bd7199c2f 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -53,8 +53,7 @@ class Roller: public Choice { setHeight(ROLLER_LINE_HEIGHT * 3 - 1); setPageHeight(ROLLER_LINE_HEIGHT); setInnerHeight(INFINITE_HEIGHT); - setScrollPositionY(ROLLER_LINE_HEIGHT * (this->getValue() - vmin - 1)); - lastScrollPositionY = scrollPositionY; + updateScrollPositionFromValue(); } #if defined(DEBUG_WINDOWS) @@ -73,9 +72,9 @@ class Roller: public Choice { coord_t yMax = scrollPositionY + 3 * ROLLER_LINE_HEIGHT; while (y < yMax) { - auto displayedValue = vmin + index % (vmax - vmin + 1); + auto displayedValue = getValueFromIndex(index % getValuesCount()); if (displayedValue < vmin) - displayedValue += vmax - vmin + 1; + displayedValue += getValuesCount(); auto fgColor = DISABLE_COLOR; @@ -104,8 +103,7 @@ class Roller: public Choice { Window::checkEvents(); if (lastEditMode != editMode) { - setScrollPositionY(ROLLER_LINE_HEIGHT * (this->getValue() - vmin - 1)); - lastScrollPositionY = scrollPositionY; + updateScrollPositionFromValue(); lastEditMode = editMode; } @@ -122,13 +120,20 @@ class Roller: public Choice { coord_t lastScrollPositionY = 0; bool lastEditMode = false; + void updateScrollPositionFromValue() + { + setScrollPositionY(ROLLER_LINE_HEIGHT * (getIndexFromValue(this->getValue()) - 1)); + lastScrollPositionY = scrollPositionY; + } + void updateValueFromScrollPosition() { lastScrollPositionY = scrollPositionY; - auto newValue = vmin + ((scrollPositionY / ROLLER_LINE_HEIGHT) + 1) % (vmax - vmin + 1); - if (newValue < vmin) - newValue += vmax - vmin + 1; + auto newValue = getValueFromIndex(((scrollPositionY / ROLLER_LINE_HEIGHT) + 1) % getValuesCount()); + if (newValue < vmin) { + newValue += getValuesCount(); + } setValue(newValue); invalidate(); From 4ac45a55fb249e3312e53aa240c3b85f7a1c059d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 14 May 2020 09:17:41 +0200 Subject: [PATCH 184/714] Choice fixes --- radio/src/thirdparty/libopenui/src/choice.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 14da958d6..3d3db203a 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -113,12 +113,13 @@ class Choice : public ChoiceBase { while (value < vmax && !isValueAvailable(value)) { value++; } + value++; index--; } return value; } - int getValuesCount() + unsigned getValuesCount() { return getIndexFromValue(vmax + 1); } @@ -128,7 +129,7 @@ class Choice : public ChoiceBase { textHandler = std::move(handler); } - void setMenuTitle(const std::string value) + void setMenuTitle(std::string value) { menuTitle = std::move(value); } From c4164e8302c719bec267febaffa8abe774d6c061 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 14 May 2020 09:22:25 +0200 Subject: [PATCH 185/714] Roller fixes --- radio/src/thirdparty/libopenui/src/roller.h | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index bd7199c2f..b2f567be8 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -66,15 +66,14 @@ class Roller: public Choice { void paint(BitmapBuffer * dc) override { int32_t value = getValue(); + auto valuesCount = getValuesCount(); int index = (scrollPositionY - ROLLER_LINE_HEIGHT + 1) / ROLLER_LINE_HEIGHT; coord_t y = index * ROLLER_LINE_HEIGHT; coord_t yMax = scrollPositionY + 3 * ROLLER_LINE_HEIGHT; while (y < yMax) { - auto displayedValue = getValueFromIndex(index % getValuesCount()); - if (displayedValue < vmin) - displayedValue += getValuesCount(); + auto displayedValue = getValueFromIndex((index + valuesCount) % valuesCount); auto fgColor = DISABLE_COLOR; @@ -130,10 +129,8 @@ class Roller: public Choice { { lastScrollPositionY = scrollPositionY; - auto newValue = getValueFromIndex(((scrollPositionY / ROLLER_LINE_HEIGHT) + 1) % getValuesCount()); - if (newValue < vmin) { - newValue += getValuesCount(); - } + auto valuesCount = getValuesCount(); + auto newValue = getValueFromIndex((((scrollPositionY / ROLLER_LINE_HEIGHT) + 1) + valuesCount) % valuesCount); setValue(newValue); invalidate(); From c07b28d3cc8d2f163579d9bccb898652663e347a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 15 May 2020 12:46:57 +0200 Subject: [PATCH 186/714] Form navigation fix --- radio/src/thirdparty/libopenui/src/form.cpp | 36 +++++++++++++++------ radio/src/thirdparty/libopenui/src/form.h | 3 ++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 33346d33c..9c76b458c 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -129,20 +129,32 @@ void FormGroup::setFocus(uint8_t flag) if (windowFlags & FORM_FORWARD_FOCUS) { switch (flag) { case SET_FOCUS_FIRST: - if (first) + if (first) { first->setFocus(SET_FOCUS_FIRST); + } + else if (next) { + next->setFocus(SET_FOCUS_FIRST); + } break; case SET_FOCUS_BACKWARD: if (focusWindow->isChild(first)) { - if (previous == this) + if (previous == this) { last->setFocus(SET_FOCUS_BACKWARD); - else if (previous) + } + else if (previous) { previous->setFocus(SET_FOCUS_BACKWARD); + } } else { - if (last) + if (last) { last->setFocus(SET_FOCUS_BACKWARD); + } + else if (previous) { + clearFocus(); + focusWindow = this; + previous->setFocus(SET_FOCUS_BACKWARD); + } } break; @@ -151,24 +163,30 @@ void FormGroup::setFocus(uint8_t flag) if (next == this) { first->setFocus(SET_FOCUS_FORWARD); } - else if (next) + else if (next) { next->setFocus(SET_FOCUS_FORWARD); + } } else { if (first) { first->setFocus(SET_FOCUS_FORWARD); } + else if (next) { + clearFocus(); + focusWindow = this; + next->setFocus(SET_FOCUS_FORWARD); + } } break; default: if (focusWindow == previous) { - if (first) + if (first) { first->setFocus(SET_FOCUS_DEFAULT); + } } - else { - if (next) - next->setFocus(SET_FOCUS_FORWARD); + else if (next) { + next->setFocus(SET_FOCUS_FORWARD); } break; } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index d326d846e..3bb9b8af5 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -124,6 +124,9 @@ class FormGroup: public FormField { Window::clear(); first = nullptr; last = nullptr; + if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { + previous->setNextField(this); + } } void setFocus(uint8_t flag) override; From a16fe0a89e6b70e8555bad95472c4e638fca45f0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 18 May 2020 12:39:46 +0200 Subject: [PATCH 187/714] Possibility to have a different font style for all table lines --- radio/src/thirdparty/libopenui/src/table.cpp | 2 +- radio/src/thirdparty/libopenui/src/table.h | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 10eaa991e..af683af4e 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -41,7 +41,7 @@ void Table::Body::paint(BitmapBuffer * dc) dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, index == selection ? MENU_HIGHLIGHT_BGCOLOR : TABLE_BGCOLOR); x = 10; for (unsigned i = 0; i < line.values.size(); i++) { - dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), TABLE_BODY_FONT | (index == selection ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); + dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), line.flags | (index == selection ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); x += columnsWidth[i]; } y += TABLE_LINE_HEIGHT; diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 19d4a7140..ff7a3b56a 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -35,6 +35,7 @@ class Table: public Window { std::vector values; std::function onPress; std::function onSelect; + LcdFlags flags = TABLE_BODY_FONT; }; class Header: public Window { @@ -88,6 +89,11 @@ class Table: public Window { setInnerHeight(lines.size() * TABLE_LINE_HEIGHT - 2); } + void setLineFlags(uint8_t index, LcdFlags flags) + { + lines[index].flags = flags; + } + void clear() { lines.clear(); @@ -175,6 +181,11 @@ class Table: public Window { body.select(index, scroll); } + void setLineFlags(uint8_t index, LcdFlags flags) + { + body.setLineFlags(index, flags); + } + void setHeader(const char * const values[]) { header.setHeight(TABLE_HEADER_HEIGHT); @@ -204,7 +215,7 @@ class Table: public Window { clearSelection(); } - int size() + uint8_t size() { return body.lines.size(); } From d9c721f7d027d7cd018ea33b029fef9f2083314b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 18 May 2020 13:35:43 +0200 Subject: [PATCH 188/714] Missing table invalidate --- radio/src/thirdparty/libopenui/src/table.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index ff7a3b56a..02bbe35ee 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -91,7 +91,10 @@ class Table: public Window { void setLineFlags(uint8_t index, LcdFlags flags) { - lines[index].flags = flags; + if (lines[index].flags != flags) { + lines[index].flags = flags; + invalidate({0, index * TABLE_LINE_HEIGHT, width(), TABLE_LINE_HEIGHT}); + } } void clear() From 04faa8a4009b65283c59067a91fb0c86734c2d8f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 18 May 2020 21:52:37 +0200 Subject: [PATCH 189/714] Roller fixes --- radio/src/thirdparty/libopenui/src/roller.h | 34 ++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index b2f567be8..76a948720 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -24,10 +24,12 @@ #include #include #include +#include #include "libopenui_globals.h" #include "libopenui_config.h" constexpr WindowFlags ROLLER_SEPARATION_LINES = (FORM_FLAGS_LAST << 1u); +constexpr coord_t SCROLL_POSITION_INVALIDATED = INT_MIN; class Roller: public Choice { public: @@ -106,6 +108,10 @@ class Roller: public Choice { lastEditMode = editMode; } + if (lastScrollPositionY == SCROLL_POSITION_INVALIDATED) { + updateScrollPositionFromValue(); + } + #if defined(HARDWARE_TOUCH) if (touchState.event != TE_SLIDE) { if (scrollPositionY != lastScrollPositionY) { @@ -115,10 +121,36 @@ class Roller: public Choice { #endif } + // virtual not needed until now + void setMin(int16_t value) + { + Choice::setMin(value); + invalidateScrollPosition(); + } + + // virtual not needed until now + void setMax(int16_t value) + { + Choice::setMax(value); + invalidateScrollPosition(); + } + + // virtual not needed until now + void setAvailableHandler(std::function handler) + { + Choice::setAvailableHandler(std::move(handler)); + invalidateScrollPosition(); + } + protected: - coord_t lastScrollPositionY = 0; + coord_t lastScrollPositionY = SCROLL_POSITION_INVALIDATED; bool lastEditMode = false; + void invalidateScrollPosition() + { + lastScrollPositionY = SCROLL_POSITION_INVALIDATED; + } + void updateScrollPositionFromValue() { setScrollPositionY(ROLLER_LINE_HEIGHT * (getIndexFromValue(this->getValue()) - 1)); From 43886e1fdf1fbb0502e677a19a4dcf2ea2b4d224 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 20 May 2020 11:13:01 +0200 Subject: [PATCH 190/714] Expansion panel fix --- radio/src/thirdparty/libopenui/src/expansion_panel.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 0a9e7bb3c..26ce33af4 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -72,9 +72,11 @@ class ExpansionPanel: public FormGroup { void updateHeight() { + coord_t previousHeight = height(); + coord_t previousBottom = bottom(); body->adjustInnerHeight(); coord_t newHeight = (isOpen ? header->height() + body->height() : header->height()); - parent->moveWindowsTop(bottom(), newHeight - height()); + parent->moveWindowsTop(previousBottom, newHeight - previousHeight); setHeight(newHeight); } From 0d7bca1abfaba04d9f612bb00cc8b65eb71d2253 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 22 May 2020 12:07:18 +0200 Subject: [PATCH 191/714] Choice fix --- radio/src/thirdparty/libopenui/src/choice.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 3d3db203a..8a1ad7639 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -110,11 +110,11 @@ class Choice : public ChoiceBase { int value = vmin; while (index > 0) { + index--; + value++; while (value < vmax && !isValueAvailable(value)) { value++; } - value++; - index--; } return value; } From 93056a281b0fb1279a827def800ed9da96c04cfe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 25 May 2020 11:41:48 +0200 Subject: [PATCH 192/714] Added Menu::count --- radio/src/thirdparty/libopenui/src/menu.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index d1737a8c1..3cf211f3f 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -176,7 +176,12 @@ class Menu: public ModalWindow { void removeLines(); - inline void select(int index) + unsigned count() const + { + return content->body.count(); + } + + void select(int index) { content->body.select(index); } From e4b19a27622bc44e4ad711ae65539f93e3187e92 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 26 May 2020 10:22:53 +0200 Subject: [PATCH 193/714] Choice can be disabled --- radio/src/thirdparty/libopenui/src/choice.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 5c1559164..e5585fbc3 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -171,8 +171,10 @@ void Choice::openMenu() #if defined(HARDWARE_TOUCH) bool Choice::onTouchEnd(coord_t, coord_t) { - onKeyPress(); - openMenu(); + if (enabled) { + onKeyPress(); + openMenu(); + } return true; } #endif From 211bb1dc4a93dfc7564e792fafc107318173565a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 26 May 2020 12:28:20 +0200 Subject: [PATCH 194/714] Form navigation fix (when disabled field in form) --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 9c76b458c..d6f3ed1ec 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -159,7 +159,7 @@ void FormGroup::setFocus(uint8_t flag) break; case SET_FOCUS_FORWARD: - if (focusWindow->isChild(last)) { + if (focusWindow->isChild(this)) { if (next == this) { first->setFocus(SET_FOCUS_FORWARD); } From 41cace6e2e439db15253e36c116c225a3ba122f9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 26 May 2020 16:10:24 +0200 Subject: [PATCH 195/714] ExpansionPanel fix --- radio/src/thirdparty/libopenui/src/expansion_panel.cpp | 7 +++++++ radio/src/thirdparty/libopenui/src/expansion_panel.h | 10 +--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp index 6f2a84689..a73e098c9 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -19,6 +19,13 @@ #include "expansion_panel.h" +void ExpansionPanel::updateHeight() +{ + coord_t newHeight = (isOpen ? header->height() + body->height() : header->height()); + parent->moveWindowsTop(bottom(), newHeight - height()); + setHeight(newHeight); +} + ExpansionPanelHeader::ExpansionPanelHeader(ExpansionPanel * parent): FormGroup(parent, {0, 0, parent->width(), parent->height()}, FORWARD_SCROLL) { diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 26ce33af4..67286faee 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -70,15 +70,7 @@ class ExpansionPanel: public FormGroup { updateHeight(); } - void updateHeight() - { - coord_t previousHeight = height(); - coord_t previousBottom = bottom(); - body->adjustInnerHeight(); - coord_t newHeight = (isOpen ? header->height() + body->height() : header->height()); - parent->moveWindowsTop(previousBottom, newHeight - previousHeight); - setHeight(newHeight); - } + void updateHeight(); void setFocus(uint8_t flag) override { From 924627cd1db8c287ea9c9e5e9b13ea1993dccafb Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 30 May 2020 11:57:02 +0200 Subject: [PATCH 196/714] Merge with latest 2.3 --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/button.cpp | 4 +++- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- radio/src/thirdparty/libopenui/src/static.cpp | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 99ecc572d..e577be270 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -562,11 +562,11 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ c -= 1; c += CJK_FIRST_LETTER_INDEX; uint8_t width = drawChar(x, y, font, fontspecs, c, flags); - INCREMENT_POS(width + 1); + INCREMENT_POS(width + CHAR_SPACING); } else if (c >= 0x20) { uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); - INCREMENT_POS(width + 1); + INCREMENT_POS(width + CHAR_SPACING); } else if (c == '\n') { pos = orig_pos; diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index ffb46b445..71e2f2f24 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -102,7 +102,9 @@ void TextButton::paint(BitmapBuffer * dc) } } - dc->drawText(rect.w / 2, (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), CENTERED | textFlags | textColor); + TRACE("ICI text=%s rect.h=%d fontHeight=%d", text.c_str(), rect.h, getFontHeight(textFlags)); + + dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), CENTERED | textFlags | textColor); } void IconButton::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index d6f3ed1ec..c13cc3713 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -110,7 +110,7 @@ void FormGroup::addField(FormField * field, bool front) link(last, field); last = field; } - if (windowFlags & FORM_FORWARD_FOCUS) { + if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { last->setNextField(this); link(previous, first); } diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 210031325..25766d162 100755 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -25,7 +25,7 @@ void StaticText::paint(BitmapBuffer * dc) if (windowFlags & BUTTON_BACKGROUND) dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); if (textFlags & CENTERED) - dc->drawText(rect.w / 2, (1 + rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); + dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); else if (textFlags & RIGHT) dc->drawText(rect.w, FIELD_PADDING_TOP, text.c_str(), textFlags); else From 7993f303daa4e1313305512fdf47b6b040b8ff1e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 2 Jun 2020 22:47:24 +0200 Subject: [PATCH 197/714] Roller fix --- radio/src/thirdparty/libopenui/src/roller.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 76a948720..9f18cc3ed 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -83,8 +83,9 @@ class Roller: public Choice { fgColor = FOCUS_COLOR | FONT(STD); } - if ((unsigned)displayedValue < values.size()) { - dc->drawText(width() / 2, y, values[displayedValue].c_str(), fgColor | CENTERED); + unsigned valueIndex = displayedValue - vmin; + if (valueIndex < values.size()) { + dc->drawText(width() / 2, y, values[valueIndex].c_str(), fgColor | CENTERED); } else { dc->drawNumber(width() / 2, y, displayedValue, fgColor | CENTERED); From baf7b199a23e7e91471e6d61d0af3236060f45d9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 2 Jun 2020 23:38:59 +0200 Subject: [PATCH 198/714] Remaining trace --- radio/src/thirdparty/libopenui/src/button.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 71e2f2f24..6862a9b7e 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -102,8 +102,6 @@ void TextButton::paint(BitmapBuffer * dc) } } - TRACE("ICI text=%s rect.h=%d fontHeight=%d", text.c_str(), rect.h, getFontHeight(textFlags)); - dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), CENTERED | textFlags | textColor); } From 514612f27161179cf289e85ed7808025dd35ced8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 2 Jun 2020 23:39:31 +0200 Subject: [PATCH 199/714] Choice issue when the first item is not available --- radio/src/thirdparty/libopenui/src/choice.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 8a1ad7639..7fce5cfd4 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -102,14 +102,14 @@ class Choice : public ChoiceBase { return index; } - int getValueFromIndex(unsigned index) + int getValueFromIndex(int index) { if (!isValueAvailable) { return vmin + index; } - int value = vmin; - while (index > 0) { + int value = vmin - 1; + while (index >= 0) { index--; value++; while (value < vmax && !isValueAvailable(value)) { From 0310a9a5916a06a3e8ff49708a9033fa890203ed Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 8 Jun 2020 17:24:52 +0200 Subject: [PATCH 200/714] Cosmetics --- radio/src/thirdparty/libopenui/src/choice.cpp | 5 ++++- radio/src/thirdparty/libopenui/src/form.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index e5585fbc3..f7f55af44 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -161,7 +161,10 @@ void Choice::openMenu() menu->setCloseHandler([=]() { editMode = false; - setFocus(SET_FOCUS_DEFAULT); + auto focus = getFocus(); + if (focus && focus->isChild(menu)) { + setFocus(SET_FOCUS_DEFAULT); + } }); setEditMode(true); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 3bb9b8af5..da8927b26 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -62,7 +62,7 @@ class FormField: public Window { editMode = newEditMode; } - void enable(bool enabled) + void enable(bool enabled = true) { this->enabled = enabled; invalidate(); From b433e12a46f13a84cd7b0dffe7094917fbd15157 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 10 Jun 2020 15:22:50 +0200 Subject: [PATCH 201/714] Roller fixes --- radio/src/thirdparty/libopenui/src/roller.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 9f18cc3ed..77a897801 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -73,12 +73,13 @@ class Roller: public Choice { int index = (scrollPositionY - ROLLER_LINE_HEIGHT + 1) / ROLLER_LINE_HEIGHT; coord_t y = index * ROLLER_LINE_HEIGHT; coord_t yMax = scrollPositionY + 3 * ROLLER_LINE_HEIGHT; + while (index < 0) + index += valuesCount; while (y < yMax) { - auto displayedValue = getValueFromIndex((index + valuesCount) % valuesCount); + auto displayedValue = getValueFromIndex(index % valuesCount); auto fgColor = DISABLE_COLOR; - if (value == displayedValue) { fgColor = FOCUS_COLOR | FONT(STD); } From e4b6a87f281a809fc8a73517ffa2bc66be8aa5f9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 17 Jun 2020 09:15:58 +0200 Subject: [PATCH 202/714] Missing windowFlags in TextEdit --- radio/src/thirdparty/libopenui/src/textedit.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index adcaa08bc..19ae36826 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -26,8 +26,8 @@ class TextEdit : public FormField { friend class TextKeyboard; public: - TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags /*flags*/ = 0) : - FormField(parent, rect), + TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0) : + FormField(parent, rect, windowFlags), value(value), length(length) { From d653eb4cb6da175e6a78ca3396b0160c1eba4fd7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Jun 2020 12:15:23 +0200 Subject: [PATCH 203/714] INVERS flag removed and VCENTERED added --- .../thirdparty/libopenui/src/libopenui_defines.h | 2 +- radio/src/thirdparty/libopenui/src/static.cpp | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 56e2e49df..ecb80dbe1 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -26,8 +26,8 @@ #define BLINK 0x01u /* drawText flags */ -#define INVERS 0x02u #define LEFT 0x00u /* align left */ +#define VCENTERED 0x02u /* align center vertically */ #define CENTERED 0x04u /* align center */ #define RIGHT 0x08u /* align right */ #define SHADOWED 0x80u /* black copy at +1 +1 */ diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 25766d162..d5aeec609 100755 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -22,12 +22,19 @@ void StaticText::paint(BitmapBuffer * dc) { - if (windowFlags & BUTTON_BACKGROUND) + if (windowFlags & BUTTON_BACKGROUND) { dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); + } + + coord_t x; if (textFlags & CENTERED) - dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), textFlags); + x = rect.w / 2; else if (textFlags & RIGHT) - dc->drawText(rect.w, FIELD_PADDING_TOP, text.c_str(), textFlags); + x = rect.w; else - dc->drawText(0, 0 + FIELD_PADDING_TOP, text.c_str(), textFlags); + x = 0; + + coord_t y = (textFlags & VCENTERED) ? (rect.h - getFontHeight(textFlags)) / 2 : FIELD_PADDING_TOP; + + dc->drawText(x, y, text.c_str(), textFlags); } From 8debea53446721cf42c98187f0101381e9864922 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Jun 2020 12:16:11 +0200 Subject: [PATCH 204/714] Possibility to avoid a Modal Dialog from being closed by the user --- radio/src/thirdparty/libopenui/src/modal_window.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 65275f670..b82c0faf3 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -52,7 +52,7 @@ class ModalWindow: public Window bool onTouchEnd(coord_t x, coord_t y) override { - if (!Window::onTouchEnd(x, y)) { + if (!Window::onTouchEnd(x, y) && closeAllowed) { deleteLater(); } return true; @@ -65,8 +65,14 @@ class ModalWindow: public Window } #endif + void setCloseAllowed(bool value = true) + { + closeAllowed = value; + } + protected: Window * previousFocus = nullptr; + bool closeAllowed = true; }; class ModalWindowContent: public Window { From d395564127d838c0a7431cf1f90dcf082df57c1b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Jun 2020 12:16:27 +0200 Subject: [PATCH 205/714] Cosmetics --- radio/src/thirdparty/libopenui/src/textedit.h | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 19ae36826..8676441d9 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -33,7 +33,6 @@ class TextEdit : public FormField { { } - #if defined(DEBUG_WINDOWS) std::string getName() const override { From 095677b0a5a03a650e398413bebf2eb95d9bb62d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Jun 2020 12:17:05 +0200 Subject: [PATCH 206/714] Window dynamic height fix --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index aa24e9457..a79b3c1c4 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -435,8 +435,8 @@ void Window::adjustInnerHeight() coord_t Window::adjustHeight() { - adjustInnerHeight(); coord_t old = rect.h; + adjustInnerHeight(); rect.h = innerHeight; return rect.h - old; } From 4f065ef1cbe1e481ebed91815ddff6281f093137 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Jun 2020 11:26:08 +0200 Subject: [PATCH 207/714] VC++ compilation fix --- radio/src/thirdparty/libopenui/src/libopenui_file.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index ee4d49c32..966c4ae7f 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -24,6 +24,7 @@ #include #include #include "ff.h" +#include "libopenui_defines.h" constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the dot, excluding null term. From 5053b702e123bb304761c2f9abc4709a3db3894a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Jun 2020 12:39:21 +0200 Subject: [PATCH 208/714] Protection against empty menus --- radio/src/thirdparty/libopenui/src/menu.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index b292e7b10..f122af814 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -65,7 +65,9 @@ void MenuBody::onEvent(event_t event) bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { int index = y / MENUS_LINE_HEIGHT; - lines[index].onPress(); + if (index < lines.size()) { + lines[index].onPress(); + } getParent()->getParent()->deleteLater(); return true; } From a11e8799e52c939b03f59d70403d6b278bc69c2b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Jun 2020 12:41:48 +0200 Subject: [PATCH 209/714] Warning removed --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index f122af814..173ff80c9 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -65,7 +65,7 @@ void MenuBody::onEvent(event_t event) bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { int index = y / MENUS_LINE_HEIGHT; - if (index < lines.size()) { + if (index < (int)lines.size()) { lines[index].onPress(); } getParent()->getParent()->deleteLater(); From 88ba6045333136a31ce68c0471c21f292efb701a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Jun 2020 12:46:02 +0200 Subject: [PATCH 210/714] Protection against empty menus --- radio/src/thirdparty/libopenui/src/menu.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 173ff80c9..89253ae16 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -36,18 +36,22 @@ void MenuBody::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_ROTARY_RIGHT) { - select(int((selectedIndex + 1) % lines.size())); + if (lines.size() > 0) + select(int((selectedIndex + 1) % lines.size())); } else if (event == EVT_ROTARY_LEFT) { - select(int(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1)); + if (lines.size() > 0) + select(int(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1)); } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - if (selectedIndex < 0) { - select(0); - } - else { - Window::onEvent(event); // the window above will be closed on event - lines[selectedIndex].onPress(); + if (lines.size() > 0) { + if (selectedIndex < 0) { + select(0); + } + else { + Window::onEvent(event); // the window above will be closed on event + lines[selectedIndex].onPress(); + } } } else if (event == EVT_KEY_BREAK(KEY_EXIT)) { From bf3a4f6582bbccb0d390055ab61ec3b4ef578b4a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Jun 2020 12:52:16 +0200 Subject: [PATCH 211/714] Toggle the case in TextEdit --- radio/src/thirdparty/libopenui/src/textedit.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index a3f8a1ecd..18c65c649 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -155,11 +155,14 @@ void TextEdit::onEvent(event_t event) case EVT_KEY_LONG(KEY_LEFT): case EVT_KEY_LONG(KEY_RIGHT): - if (v >= -26 && v <= 26) { - v = -v; // toggle case - if (event == EVT_KEY_LONG(KEY_LEFT)) { - killEvents(KEY_LEFT); - } + if (v >= 'A' && v <= 'Z') { + v += 32; // tolower + } + else if (v >= 'a' && v <= 'z') { + v -= 32; // toupper + } + if (event == EVT_KEY_LONG(KEY_LEFT)) { + killEvents(KEY_LEFT); } break; } From a6a854320524bc527faf67004227c0910b1cb27b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Jun 2020 17:10:05 +0200 Subject: [PATCH 212/714] Avoid the crash if used in a simulator --- radio/src/thirdparty/libopenui/src/mainwindow.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index e345c9de7..da1cdef85 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -32,6 +32,11 @@ class MainWindow: public Window { { } + ~MainWindow() override + { + children.clear(); + } + #if defined(DEBUG_WINDOWS) std::string getName() const override { From aaf835ab2467a9f5cae7c2bb12c570473b5f528b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 23 Jun 2020 14:14:50 +0200 Subject: [PATCH 213/714] Fixes --- radio/src/thirdparty/libopenui/src/button.cpp | 4 +++- radio/src/thirdparty/libopenui/src/modal_window.h | 3 +++ radio/src/thirdparty/libopenui/src/window.cpp | 5 +++++ radio/src/thirdparty/libopenui/src/window.h | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 6862a9b7e..04efafa31 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -37,7 +37,9 @@ void Button::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("Button").c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { - onPress(); + if (enabled) { + onPress(); + } } else { FormField::onEvent(event); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index b82c0faf3..2ec3e3629 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -36,6 +36,9 @@ class ModalWindow: public Window void deleteLater() { + if (deleted) + return; + if (previousFocus) { previousFocus->setFocus(SET_FOCUS_DEFAULT); } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index a79b3c1c4..438efc141 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -69,6 +69,9 @@ void Window::detach() void Window::deleteLater(bool detach) { + if (deleted) + return; + TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); if (static_cast(focusWindow) == static_cast(this)) { @@ -85,6 +88,8 @@ void Window::deleteLater(bool detach) } trash.push_back(this); + + deleted = true; } void Window::clear() diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index a261677aa..18b131828 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -354,6 +354,7 @@ class Window { coord_t scrollPositionY = 0; WindowFlags windowFlags; LcdFlags textFlags; + bool deleted = false; static Window * focusWindow; static Window * slidingWindow; From 85e5d15c2d400b028827754a139eaa75424a2ea4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 24 Jun 2020 13:09:33 +0200 Subject: [PATCH 214/714] Cosmetics --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 438efc141..ea5ec763d 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -321,7 +321,7 @@ void Window::checkEvents() child->checkEvents(); } - if (this == focusWindow) { + if (this == Window::focusWindow) { event_t event = getWindowEvent(); if (event) { this->onEvent(event); From 5819dc0813868f1da5864742ab6645e5337db091 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 25 Jun 2020 11:48:09 +0200 Subject: [PATCH 215/714] Static can now have multilines --- radio/src/thirdparty/libopenui/src/static.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index d5aeec609..b511b6715 100755 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -36,5 +36,20 @@ void StaticText::paint(BitmapBuffer * dc) coord_t y = (textFlags & VCENTERED) ? (rect.h - getFontHeight(textFlags)) / 2 : FIELD_PADDING_TOP; - dc->drawText(x, y, text.c_str(), textFlags); + auto start = text.c_str(); + auto nextline = strchr(start, '\n'); + if (nextline) { + auto current = start; + do { + dc->drawText(x, y, text.substr(current - start, nextline - current).c_str(), textFlags); + current = nextline + 1; + nextline = strchr(current, '\n'); + y += getFontHeight(textFlags) + 2; + } while (nextline); + dc->drawText(x, y, current, textFlags); + } + else { + dc->drawText(x, y, start, textFlags); + } + } From 729f538d67c3e04991fe28503f02f8f5c56725aa Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 26 Jun 2020 16:02:18 +0200 Subject: [PATCH 216/714] Add a getter Window::deleted() --- radio/src/thirdparty/libopenui/src/modal_window.h | 2 +- radio/src/thirdparty/libopenui/src/window.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/window.h | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 2ec3e3629..9fed468bd 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -36,7 +36,7 @@ class ModalWindow: public Window void deleteLater() { - if (deleted) + if (_deleted) return; if (previousFocus) { diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index ea5ec763d..df605368d 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -69,7 +69,7 @@ void Window::detach() void Window::deleteLater(bool detach) { - if (deleted) + if (_deleted) return; TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); @@ -89,7 +89,7 @@ void Window::deleteLater(bool detach) trash.push_back(this); - deleted = true; + _deleted = true; } void Window::clear() diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 18b131828..0bc16533c 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -342,6 +342,11 @@ class Window { void detach(); + bool deleted() + { + return _deleted; + } + protected: Window * parent; std::list children; @@ -354,7 +359,7 @@ class Window { coord_t scrollPositionY = 0; WindowFlags windowFlags; LcdFlags textFlags; - bool deleted = false; + bool _deleted = false; static Window * focusWindow; static Window * slidingWindow; From dccda25da545e567b1915992036a7f16a91ab8ef Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 2 Jul 2020 12:30:16 +0200 Subject: [PATCH 217/714] Checkbox window flags lost on the way --- radio/src/thirdparty/libopenui/src/checkbox.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 3c0d17077..ed4420d89 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -24,9 +24,8 @@ class CheckBox : public FormField { public: - CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, - LcdFlags /*flags*/ = 0) : - FormField(parent, rect), + CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, WindowFlags flags = 0) : + FormField(parent, rect, flags), _getValue(getValue), _setValue(setValue) { From 349a02c841561132b69b7446aa802869a61a0acc Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 2 Jul 2020 17:26:45 +0200 Subject: [PATCH 218/714] Add an handler to CarouselItem select --- radio/src/thirdparty/libopenui/src/carousel.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index 696a20cfd..2fb4a7a82 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -33,11 +33,17 @@ class CarouselItem { { } + void setSelectHandler(std::function handler) + { + selectHandler = std::move(handler); + } + virtual ~CarouselItem() = default; public: Window * front; Window * back; + std::function selectHandler; }; class CarouselWindow: public Window { @@ -74,6 +80,10 @@ class CarouselWindow: public Window { void select(int index, bool scroll = true) { selection = index; + auto item = items[selection]; + if (item->selectHandler) { + item->selectHandler(); + } update(); } From 8235a1d97d11599d155f77aa454b28db46cfaf58 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 7 Jul 2020 12:57:32 +0200 Subject: [PATCH 219/714] Add a waitHandler to Menu --- radio/src/thirdparty/libopenui/src/menu.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 3cf211f3f..aac85d94f 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -160,6 +160,11 @@ class Menu: public ModalWindow { content->body.setCancelHandler(handler); } + void setWaitHandler(std::function handler) + { + waitHandler = std::move(handler); + } + void setToolbar(Window * window) { toolbar = window; @@ -190,9 +195,18 @@ class Menu: public ModalWindow { void onEvent(event_t event) override; #endif + void checkEvents() override + { + ModalWindow::checkEvents(); + if (waitHandler) { + waitHandler(); + } + } + protected: MenuWindowContent * content; Window * toolbar = nullptr; + std::function waitHandler; void updatePosition(); }; From 39430cc9376e1de7ad4a9ab6a66b9986760c159f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 31 Jul 2020 17:28:06 +0200 Subject: [PATCH 220/714] It was impossible to get back from MixOne screen --- radio/src/thirdparty/libopenui/src/form.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index c13cc3713..2802fc822 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -206,7 +206,11 @@ void FormGroup::onEvent(event_t event) first->setFocus(SET_FOCUS_FIRST); } else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus() && !(windowFlags & FORM_FORWARD_FOCUS)) { +#if defined(HARDWARE_TOUCH) setFocus(SET_FOCUS_DEFAULT); +#else + FormField::onEvent(event); +#endif } else if (event == EVT_ROTARY_RIGHT && !next) { if (first) From aede63f28d7a7687c1e79288adc290500ce7c5ab Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 4 Aug 2020 22:34:01 +0200 Subject: [PATCH 221/714] Possibility to override FormField::setEditMode --- radio/src/thirdparty/libopenui/src/basenumberedit.h | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/form.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index c1314c97f..f85f8754e 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -82,7 +82,7 @@ class BaseNumberEdit : public FormField { void setSetValueHandler(std::function handler) { - _setValue = handler; + _setValue = std::move(handler); } int32_t getValue() const diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 2802fc822..e36516d52 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -48,11 +48,11 @@ void FormField::onEvent(event_t event) } } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - editMode = !editMode; + setEditMode(!editMode); invalidate(); } else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { - editMode = false; + setEditMode(false); invalidate(); } else { diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index da8927b26..2f4a7a43b 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -57,7 +57,7 @@ class FormField: public Window { return editMode; } - inline void setEditMode(bool newEditMode) + virtual void setEditMode(bool newEditMode) { editMode = newEditMode; } @@ -82,7 +82,7 @@ class FormField: public Window { void onFocusLost() override { - editMode = false; + setEditMode(false); Window::onFocusLost(); } From 528ef68a1d1eb40262a5964a58d659d98d501b03 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 13 Aug 2020 10:32:28 +0200 Subject: [PATCH 222/714] Carousel update --- radio/src/thirdparty/libopenui/src/carousel.cpp | 7 +++++-- radio/src/thirdparty/libopenui/src/carousel.h | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp index 44c069ead..5495ba000 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.cpp +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -21,9 +21,12 @@ void CarouselWindow::update() { - auto first = min(selection - 2, items.size() - 5); + auto first = min(selection - 2, items.size() - count); first = max(0, first); coord_t lastPosition = 0; + coord_t spacing = 10; + if (count > 1) + spacing = (width() - items[0]->back->width() * (count - 1) - items[0]->front->width()) / (count - 1); int index = 0; for (auto & item: items) { @@ -41,7 +44,7 @@ void CarouselWindow::update() window->setTop((height() - window->height()) / 2); if (index == first) setScrollPositionX(lastPosition); - lastPosition += window->width() + CAROUSEL_SPACING; + lastPosition += window->width() + spacing; index += 1; } diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index 2fb4a7a82..23b4ec0ea 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -23,8 +23,6 @@ #include #include "button.h" -constexpr coord_t CAROUSEL_SPACING = 20; - class CarouselItem { public: CarouselItem(Window * front, Window * back): @@ -50,8 +48,9 @@ class CarouselWindow: public Window { friend class Carousel; public: - CarouselWindow(Window * parent, const rect_t & rect): - Window(parent, rect, NO_SCROLLBAR) + CarouselWindow(Window * parent, const rect_t & rect, uint8_t count): + Window(parent, rect, NO_SCROLLBAR), + count(count) { } @@ -90,6 +89,7 @@ class CarouselWindow: public Window { protected: std::vector items; int selection = 0; + uint8_t count; void update(); }; From 286bf5fdb2e0aa2ea01ff6b46261f2e10cfc0a82 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 17 Aug 2020 12:21:07 +0200 Subject: [PATCH 223/714] The Window which has the focus is now marked with "(*)" in traces --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index df605368d..e4e8745ae 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -244,7 +244,7 @@ void Window::fullPaint(BitmapBuffer * dc) } if (paintNeeded) { - TRACE_WINDOWS_INDENT("%s", getWindowDebugString().c_str()); + TRACE_WINDOWS_INDENT("%s%s", getWindowDebugString().c_str(), hasFocus() ? " (*)" : ""); paint(dc); #if defined(WINDOWS_INSPECT_BORDER_COLOR) dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR); From 08f8fd52a3d616e3f6d2519cedca37c852c12da4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 17 Aug 2020 13:10:10 +0200 Subject: [PATCH 224/714] Protection --- radio/src/thirdparty/libopenui/src/window.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index e4e8745ae..8f4b38425 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -120,6 +120,9 @@ void Window::clearFocus() void Window::setFocus(uint8_t flag) { + if (deleted()) + return; + TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); if (focusWindow != this) { From 1c9e39a69cbcf650f33c71ed958f1ad2ef635997 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 17 Aug 2020 14:43:30 +0200 Subject: [PATCH 225/714] Possibility to disable scrolling in a Table --- radio/src/thirdparty/libopenui/src/table.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 02bbe35ee..1791b3216 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -70,8 +70,8 @@ class Table: public Window { friend class Table; public: - Body(Table * parent, const rect_t & rect): - Window(parent, rect, OPAQUE), + Body(Table * parent, const rect_t & rect, WindowFlags windowFlags): + Window(parent, rect, windowFlags), columnsWidth(parent->columnsWidth) { } @@ -134,12 +134,12 @@ class Table: public Window { }; public: - Table(Window * parent, const rect_t & rect, uint8_t columnsCount): + Table(Window * parent, const rect_t & rect, uint8_t columnsCount, WindowFlags windowFlags = OPAQUE): Window(parent, rect), columnsCount(columnsCount), columnsWidth(columnsCount, width() / columnsCount), header(this, {0, 0, width(), 0}, columnsCount), - body(this, {0, 0, width(), height()}) + body(this, {0, 0, width(), height()}, windowFlags) { } From f6e730a94d56df7b512858860402ad8ebbf759c7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 21 Aug 2020 10:25:48 +0200 Subject: [PATCH 226/714] Cosmetics --- .../thirdparty/libopenui/src/bitmapbuffer.h | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index f875368dc..ea7a3c1f4 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -199,32 +199,33 @@ class RLEBitmap: free(data); } - static int decode(uint8_t * dest, unsigned int dest_size, const unsigned char* src) + static int decode(uint8_t * dest, unsigned int destSize, const uint8_t * src) { - //unsigned char count = 0; - unsigned char prev_byte = 0; - bool prev_byte_valid = false; + uint8_t prevByte = 0; + bool prevByteValid = false; - const unsigned char * dest_end = dest + dest_size; - unsigned char * d = dest; + const uint8_t * destEnd = dest + destSize; + uint8_t * d = dest; - while (d < dest_end) { - *d++ = *src; - if (prev_byte_valid && (*src == prev_byte)) { - src++; + while (d < destEnd) { + uint8_t byte = *src++; + *d++ = byte; - if (d + *src > dest + dest_size) { -// TRACE("rle_decode_8bit: destination overflow!\n"); + if (prevByteValid && byte == prevByte) { + uint8_t count = *src++; + + if (d + count > destEnd) { + TRACE("rle_decode_8bit: destination overflow!\n"); return -1; } - memset(d, prev_byte, *src); - d += *src++; - prev_byte_valid = false; + memset(d, byte, count); + d += count; + prevByteValid = false; } else { - prev_byte = *src++; - prev_byte_valid = true; + prevByte = byte; + prevByteValid = true; } } From 8632c038f8662c2959233ce330a595692199fde3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 25 Aug 2020 09:27:03 +0200 Subject: [PATCH 227/714] Multiple selection in Menu --- radio/src/thirdparty/libopenui/src/menu.cpp | 50 ++++++++++++++++----- radio/src/thirdparty/libopenui/src/menu.h | 39 ++++++++++------ 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 89253ae16..ddadf0cd8 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -36,21 +36,25 @@ void MenuBody::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_ROTARY_RIGHT) { - if (lines.size() > 0) + if (!lines.empty()) select(int((selectedIndex + 1) % lines.size())); } else if (event == EVT_ROTARY_LEFT) { - if (lines.size() > 0) + if (!lines.empty()) select(int(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1)); } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - if (lines.size() > 0) { + if (!lines.empty()) { if (selectedIndex < 0) { select(0); } else { Window::onEvent(event); // the window above will be closed on event lines[selectedIndex].onPress(); + Menu * menu = getParentMenu(); + if (menu->multiple) { + menu->invalidate(); + } } } } @@ -68,11 +72,23 @@ void MenuBody::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { + Menu * menu = getParentMenu(); int index = y / MENUS_LINE_HEIGHT; if (index < (int)lines.size()) { - lines[index].onPress(); + if (menu->multiple) { + if (selectedIndex == index) + lines[index].onPress(); + else + select(index); + menu->invalidate(); + } + else { + lines[index].onPress(); + } + } + if (!menu->multiple) { + menu->deleteLater(); } - getParent()->getParent()->deleteLater(); return true; } #endif @@ -95,6 +111,12 @@ void MenuBody::paint(BitmapBuffer * dc) const char * text = line.text.data(); dc->drawText(10, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, text[0] == '\0' ? "---" : text, flags); } + + Menu * menu = getParentMenu(); + if (menu->multiple && line.isChecked && line.isChecked()) { + theme->drawCheckBox(dc, 1, width() - 35, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, 0); + } + if (i > 0) { dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); } @@ -120,9 +142,10 @@ void MenuWindowContent::paint(BitmapBuffer * dc) } } -Menu::Menu(Window * parent): +Menu::Menu(Window * parent, bool multiple): ModalWindow(parent), - content(createMenuWindow(this)) + content(createMenuWindow(this)), + multiple(multiple) { } @@ -146,15 +169,15 @@ void Menu::setTitle(std::string text) updatePosition(); } -void Menu::addLine(const std::string & text, std::function onPress) +void Menu::addLine(const std::string & text, std::function onPress, std::function isChecked) { - content->body.addLine(text, std::move(onPress)); + content->body.addLine(text, std::move(onPress), std::move(isChecked)); updatePosition(); } -void Menu::addCustomLine(std::function drawLine, std::function onPress) +void Menu::addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) { - content->body.addCustomLine(std::move(drawLine), std::move(onPress)); + content->body.addCustomLine(std::move(drawLine), std::move(onPress), std::move(isChecked)); updatePosition(); } @@ -170,7 +193,10 @@ void Menu::onEvent(event_t event) if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN))) { toolbar->onEvent(event); } - else if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) { + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + deleteLater(); + } + else if (event == EVT_KEY_BREAK(KEY_ENTER) && !multiple) { deleteLater(); } } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index aac85d94f..e1398bef7 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -28,7 +28,7 @@ class Menu; class MenuWindowContent; -class MenuBody: public Window { +class MenuBody : public Window { friend class MenuWindowContent; friend class Menu; @@ -36,15 +36,17 @@ class MenuBody: public Window { friend class MenuBody; public: - MenuLine(std::string text, std::function onPress): + MenuLine(std::string text, std::function onPress, std::function isChecked): text(std::move(text)), - onPress(std::move(onPress)) + onPress(std::move(onPress)), + isChecked(std::move(isChecked)) { } - MenuLine(std::function drawLine, std::function onPress): + MenuLine(std::function drawLine, std::function onPress, std::function isChecked): drawLine(std::move(drawLine)), - onPress(std::move(onPress)) + onPress(std::move(onPress)), + isChecked(std::move(isChecked)) { } @@ -56,6 +58,7 @@ class MenuBody: public Window { std::string text; std::function drawLine; std::function onPress; + std::function isChecked; }; public: @@ -92,15 +95,15 @@ class MenuBody: public Window { bool onTouchEnd(coord_t x, coord_t y) override; #endif - void addLine(const std::string & text, std::function onPress) + void addLine(const std::string & text, std::function onPress, std::function isChecked) { - lines.emplace_back(text, onPress); + lines.emplace_back(text, onPress, isChecked); invalidate(); } - void addCustomLine(std::function drawLine, std::function onPress) + void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) { - lines.emplace_back(drawLine, onPress); + lines.emplace_back(drawLine, onPress, isChecked); invalidate(); } @@ -125,6 +128,8 @@ class MenuBody: public Window { int selectedIndex = 0; #endif std::function onCancel; + + inline Menu * getParentMenu(); }; class MenuWindowContent: public ModalWindowContent { @@ -152,12 +157,14 @@ class MenuWindowContent: public ModalWindowContent { }; class Menu: public ModalWindow { + friend class MenuBody; + public: - explicit Menu(Window * parent); + explicit Menu(Window * parent, bool multiple = false); void setCancelHandler(std::function handler) { - content->body.setCancelHandler(handler); + content->body.setCancelHandler(std::move(handler)); } void setWaitHandler(std::function handler) @@ -175,9 +182,9 @@ class Menu: public ModalWindow { void setTitle(std::string text); - void addLine(const std::string & text, std::function onPress); + void addLine(const std::string & text, std::function onPress, std::function isChecked = nullptr); - void addCustomLine(std::function drawLine, std::function onPress); + void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked = nullptr); void removeLines(); @@ -205,9 +212,15 @@ class Menu: public ModalWindow { protected: MenuWindowContent * content; + bool multiple; Window * toolbar = nullptr; std::function waitHandler; void updatePosition(); }; +Menu * MenuBody::getParentMenu() +{ + return static_cast(getParent()->getParent()); +} + #endif // _MENU_H_ From 3cbb18a521374ad2f2354db08728a7a543c1b705 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 25 Aug 2020 10:07:39 +0200 Subject: [PATCH 228/714] Multiple selection in Menu --- radio/src/thirdparty/libopenui/src/menu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index ddadf0cd8..8b3ccf6d9 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -113,8 +113,8 @@ void MenuBody::paint(BitmapBuffer * dc) } Menu * menu = getParentMenu(); - if (menu->multiple && line.isChecked && line.isChecked()) { - theme->drawCheckBox(dc, 1, width() - 35, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, 0); + if (menu->multiple && line.isChecked) { + theme->drawCheckBox(dc, line.isChecked(), width() - 35, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, 0); } if (i > 0) { From 61db77cfea4ce5074bb0bf83836b6f6589cf92ae Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 25 Aug 2020 11:47:07 +0200 Subject: [PATCH 229/714] Focus issue --- radio/src/thirdparty/libopenui/src/modal_window.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 9fed468bd..698a37747 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -39,9 +39,10 @@ class ModalWindow: public Window if (_deleted) return; - if (previousFocus) { + if (previousFocus && (!focusWindow || focusWindow->isChild(this))) { previousFocus->setFocus(SET_FOCUS_DEFAULT); } + Window::deleteLater(); } @@ -73,6 +74,11 @@ class ModalWindow: public Window closeAllowed = value; } + void forgetPreviousFocus() + { + previousFocus = nullptr; + } + protected: Window * previousFocus = nullptr; bool closeAllowed = true; From dcfc8c625efbb305652766758d3d9fcee7767d5e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 26 Aug 2020 09:37:52 +0200 Subject: [PATCH 230/714] Add Window::getWindowHeight() --- radio/src/thirdparty/libopenui/src/window.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 0bc16533c..2f6c0ba1e 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -274,6 +274,11 @@ class Window { return 0; } + coord_t getInnerHeight() const + { + return innerHeight; + } + void setInnerHeight(coord_t h) { innerHeight = h; From b17e7004a6ef491e89736d804285869d6126ac51 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 26 Aug 2020 10:35:36 +0200 Subject: [PATCH 231/714] Focus issue when a form is empty --- radio/src/thirdparty/libopenui/src/form.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index e36516d52..4a9dd43c8 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -188,6 +188,10 @@ void FormGroup::setFocus(uint8_t flag) else if (next) { next->setFocus(SET_FOCUS_FORWARD); } + else { + clearFocus(); + focusWindow = this; + } break; } } From 90ba459dc7545e8b9f84ce1234e0a46ce480887a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 31 Aug 2020 21:24:24 +0200 Subject: [PATCH 232/714] Segfault in Menu --- radio/src/thirdparty/libopenui/src/menu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 8b3ccf6d9..658397a63 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -49,12 +49,12 @@ void MenuBody::onEvent(event_t event) select(0); } else { - Window::onEvent(event); // the window above will be closed on event lines[selectedIndex].onPress(); Menu * menu = getParentMenu(); - if (menu->multiple) { + if (menu->multiple) menu->invalidate(); - } + else + menu->deleteLater(); } } } From c547371caa3d46ab3ca40647a64972a1c1fef198 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Sep 2020 19:40:57 +0200 Subject: [PATCH 233/714] [Table] SEGFAULT fixed --- radio/src/thirdparty/libopenui/src/table.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index af683af4e..3fc1b7e39 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -75,6 +75,9 @@ void Table::Body::onEvent(event_t event) } } if (event == EVT_ROTARY_RIGHT) { + if (lines.empty()) + return; + int newSelection = (selection + 1) % lines.size(); select(newSelection, true); auto onSelect = lines[selection].onSelect; @@ -83,7 +86,10 @@ void Table::Body::onEvent(event_t event) } } else if (event == EVT_ROTARY_LEFT) { - int newSelection = selection == 0 ? lines.size() - 1 : selection - 1; + if (lines.empty()) + return; + + int newSelection = selection <= 0 ? lines.size() - 1 : selection - 1; select(newSelection, true); auto onSelect = lines[selection].onSelect; if (onSelect) { From 417f40c755845f807fe77cfcfd08f7484b9c01d6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 4 Sep 2020 20:21:01 +0200 Subject: [PATCH 234/714] [Menu] Selection not updated when rotary encoder left as first event --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 658397a63..6b2519a33 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -41,7 +41,7 @@ void MenuBody::onEvent(event_t event) } else if (event == EVT_ROTARY_LEFT) { if (!lines.empty()) - select(int(selectedIndex == 0 ? lines.size() - 1 : selectedIndex - 1)); + select(int(selectedIndex <= 0 ? lines.size() - 1 : selectedIndex - 1)); } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { if (!lines.empty()) { From a54da932074cdcc7059e412b3a56861a8400156d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 7 Sep 2020 10:44:14 +0200 Subject: [PATCH 235/714] [Form] Focus issue when scrolling left in nested Forms --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 4a9dd43c8..a23713017 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -222,7 +222,7 @@ void FormGroup::onEvent(event_t event) } else if (event == EVT_ROTARY_LEFT && !previous) { if (last) - last->setFocus(SET_FOCUS_FIRST); + last->setFocus(SET_FOCUS_BACKWARD); } else { FormField::onEvent(event); From 8b6ce1e13cb630ea70f09b1a181dce59202c95a3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 11 Sep 2020 13:53:21 +0200 Subject: [PATCH 236/714] [Table] Focus / selection fixes --- radio/src/thirdparty/libopenui/src/table.cpp | 6 ++++-- radio/src/thirdparty/libopenui/src/table.h | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 3fc1b7e39..e0b1424fe 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -38,10 +38,11 @@ void Table::Body::paint(BitmapBuffer * dc) coord_t x; int index = 0; for (auto & line: lines) { - dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, index == selection ? MENU_HIGHLIGHT_BGCOLOR : TABLE_BGCOLOR); + bool highlight = hasFocus() && index == selection; + dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, highlight ? MENU_HIGHLIGHT_BGCOLOR : TABLE_BGCOLOR); x = 10; for (unsigned i = 0; i < line.values.size(); i++) { - dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), line.flags | (index == selection ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); + dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), line.flags | (highlight ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); x += columnsWidth[i]; } y += TABLE_LINE_HEIGHT; @@ -54,6 +55,7 @@ bool Table::Body::onTouchEnd(coord_t x, coord_t y) { unsigned index = y / TABLE_LINE_HEIGHT; if (index < lines.size()) { + setFocus(SET_FOCUS_DEFAULT); auto onPress = lines[index].onPress; if (onPress) onPress(); diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 1791b3216..bba715aed 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -20,12 +20,12 @@ #ifndef _TABLE_H_ #define _TABLE_H_ -#include "window.h" +#include "form.h" #include #include #include "libopenui_config.h" -class Table: public Window { +class Table: public FormField { class Line { public: explicit Line(uint8_t columnsCount): @@ -135,7 +135,7 @@ class Table: public Window { public: Table(Window * parent, const rect_t & rect, uint8_t columnsCount, WindowFlags windowFlags = OPAQUE): - Window(parent, rect), + FormField(parent, rect), columnsCount(columnsCount), columnsWidth(columnsCount, width() / columnsCount), header(this, {0, 0, width(), 0}, columnsCount), @@ -218,7 +218,7 @@ class Table: public Window { clearSelection(); } - uint8_t size() + uint8_t size() const { return body.lines.size(); } From e971714e3145229f6246392c34729f3d8bfb53b2 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Sep 2020 09:09:33 +0200 Subject: [PATCH 237/714] [FormField] Possible to avoid the border --- radio/src/thirdparty/libopenui/src/form.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index a23713017..c6526a573 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -83,11 +83,13 @@ void FormField::paint(BitmapBuffer * dc) if (editMode) { dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FOCUS_BGCOLOR); } - else if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); - } - else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); + else if (!(windowFlags & FORM_NO_BORDER)) { + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + } + else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); + } } } From 9a513b1fa386d78cf2df32eedbdfa6b7d91ccdd7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Sep 2020 09:10:57 +0200 Subject: [PATCH 238/714] [Table] Default = no border --- radio/src/thirdparty/libopenui/src/table.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index bba715aed..f8c00eb78 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -134,8 +134,8 @@ class Table: public FormField { }; public: - Table(Window * parent, const rect_t & rect, uint8_t columnsCount, WindowFlags windowFlags = OPAQUE): - FormField(parent, rect), + Table(Window * parent, const rect_t & rect, uint8_t columnsCount, WindowFlags windowFlags = OPAQUE | FORM_NO_BORDER): + FormField(parent, rect, windowFlags), columnsCount(columnsCount), columnsWidth(columnsCount, width() / columnsCount), header(this, {0, 0, width(), 0}, columnsCount), From 14b65cae3dee011a0ce350b28e0804c80ec2091a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Sep 2020 15:03:05 +0200 Subject: [PATCH 239/714] Add a revertPreviousFocus() helper --- radio/src/thirdparty/libopenui/src/modal_window.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 698a37747..ac7f5b8a3 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -79,6 +79,14 @@ class ModalWindow: public Window previousFocus = nullptr; } + void revertPreviousFocus() + { + if (previousFocus) { + previousFocus->setFocus(SET_FOCUS_DEFAULT); + previousFocus = nullptr; + } + } + protected: Window * previousFocus = nullptr; bool closeAllowed = true; From e0d3f2bb56bc62e1968a91d4f3d640f2b84dbab1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Sep 2020 16:44:09 +0200 Subject: [PATCH 240/714] [Table] Fixes --- radio/src/thirdparty/libopenui/src/table.cpp | 6 +++++- radio/src/thirdparty/libopenui/src/table.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index e0b1424fe..e188dd901 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -38,7 +38,7 @@ void Table::Body::paint(BitmapBuffer * dc) coord_t x; int index = 0; for (auto & line: lines) { - bool highlight = hasFocus() && index == selection; + bool highlight = (index == selection); dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, highlight ? MENU_HIGHLIGHT_BGCOLOR : TABLE_BGCOLOR); x = 10; for (unsigned i = 0; i < line.values.size(); i++) { @@ -98,6 +98,10 @@ void Table::Body::onEvent(event_t event) onSelect(); } } + else if (event == EVT_KEY_BREAK(KEY_EXIT) && selection >= 0) { + select(-1, true); + Window::onEvent(event); + } else { Window::onEvent(event); } diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index f8c00eb78..5658e7eb8 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -176,6 +176,10 @@ class Table: public FormField { void setFocus(uint8_t flag) override { + if (body.selection < 0) { + body.selection = 0; + body.invalidate(); + } body.setFocus(flag); } From 645bc868d0402996e1da21d40d4e127eea0c6d32 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Sep 2020 18:12:47 +0200 Subject: [PATCH 241/714] [Table] Factorize the onSelect call --- radio/src/thirdparty/libopenui/src/table.cpp | 22 ++++---------------- radio/src/thirdparty/libopenui/src/table.h | 9 ++++++-- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index e188dd901..3067b485b 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -77,26 +77,12 @@ void Table::Body::onEvent(event_t event) } } if (event == EVT_ROTARY_RIGHT) { - if (lines.empty()) - return; - - int newSelection = (selection + 1) % lines.size(); - select(newSelection, true); - auto onSelect = lines[selection].onSelect; - if (onSelect) { - onSelect(); - } + if (!lines.empty()) + select((selection + 1) % lines.size(), true); } else if (event == EVT_ROTARY_LEFT) { - if (lines.empty()) - return; - - int newSelection = selection <= 0 ? lines.size() - 1 : selection - 1; - select(newSelection, true); - auto onSelect = lines[selection].onSelect; - if (onSelect) { - onSelect(); - } + if (!lines.empty()) + select(selection <= 0 ? lines.size() - 1 : selection - 1, true); } else if (event == EVT_KEY_BREAK(KEY_EXIT) && selection >= 0) { select(-1, true); diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 5658e7eb8..1be678ac9 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -115,6 +115,12 @@ class Table: public FormField { scrollTo(rect); } invalidate(); + if (index >= 0) { + auto onSelect = lines[index].onSelect; + if (onSelect) { + onSelect(); + } + } } void paint(BitmapBuffer * dc) override; @@ -177,8 +183,7 @@ class Table: public FormField { void setFocus(uint8_t flag) override { if (body.selection < 0) { - body.selection = 0; - body.invalidate(); + select(0); } body.setFocus(flag); } From 1bdd16d3c8b1c894dc59f7a9cd501eeec4e42218 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 17 Sep 2020 14:53:41 +0200 Subject: [PATCH 242/714] Support for BMP565 when loading bitmaps from SD --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index e577be270..0e2e50de8 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -816,9 +816,9 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) return nullptr; } - uint32_t ihsize = *((uint32_t *)&buf[0]); /* more header size */ + uint32_t ihsize = *((uint32_t *)&buf[0]); /* extra header size */ - /* invalid header size */ + /* invalid extra header size */ if (ihsize + 14 > hsize) { f_close(&imgFile); return nullptr; @@ -836,7 +836,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) uint32_t w, h; - switch (ihsize){ + switch (ihsize) { case 40: // windib case 56: // windib v3 case 64: // OS/2 v2 @@ -866,11 +866,11 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) buf = &bmpBuf[0]; if (depth == 4) { - if (f_lseek(&imgFile, hsize-64) != FR_OK || f_read(&imgFile, buf, 64, &read) != FR_OK || read != 64) { + if (f_lseek(&imgFile, hsize - 64) != FR_OK || f_read(&imgFile, buf, 64, &read) != FR_OK || read != 64) { f_close(&imgFile); return nullptr; } - for (uint8_t i=0; i<16; i++) { + for (uint8_t i = 0; i < 16; i++) { palette[i] = buf[4*i]; } } @@ -891,10 +891,25 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) bool hasAlpha = false; switch (depth) { - case 32: - for (int i = h-1; i >= 0; i--) { + case 16: + for (int i = h - 1; i >= 0; i--) { pixel_t * dst = bmp->getPixelPtr(0, i); - for (unsigned int j=0; j= 0; i--) { + pixel_t * dst = bmp->getPixelPtr(0, i); + for (unsigned int j = 0; j < w; j++) { uint32_t pixel; result = f_read(&imgFile, (uint8_t *)&pixel, 4, &read); if (result != FR_OK || read != 4) { From 689a62d1e732c8b99271aabde6e205ca29f296a0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 18 Sep 2020 15:56:56 +0200 Subject: [PATCH 243/714] Missing beeps / vibrations when editing values --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index ecd91e3e8..6795121f5 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -64,10 +64,13 @@ void NumberEdit::onEvent(event_t event) do { value += rotencSpeed * step; } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); - if (value <= vmax) + if (value <= vmax) { setValue(value); - else + onKeyPress(); + } + else { onKeyError(); + } return; } @@ -76,10 +79,13 @@ void NumberEdit::onEvent(event_t event) do { value -= rotencSpeed * step; } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); - if (value >= vmin) + if (value >= vmin) { setValue(value); - else + onKeyPress(); + } + else { onKeyError(); + } return; } #endif From 63ac88d0a79b052e8b9a2d097e00be1cea8aa51c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 24 Sep 2020 11:58:25 +0200 Subject: [PATCH 244/714] Possibility to have a constant width for numbers --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 7 +++++-- .../libopenui/src/libopenui_defines.h | 20 ++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 0e2e50de8..956b9abd3 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -557,7 +557,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ else if (c >= 0xFE) { // CJK char s++; - c = uint8_t(*s) + ((c & 0x01) << 8) - 1; + c = uint8_t(*s) + ((c & 0x01u) << 8u) - 1; if (c >= 0x101) c -= 1; c += CJK_FIRST_LETTER_INDEX; @@ -566,7 +566,10 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ } else if (c >= 0x20) { uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); - INCREMENT_POS(width + CHAR_SPACING); + if ((flags & SPACING_NUMBERS_CONST) && c >= '0' && c <= '9') + INCREMENT_POS(getCharWidth('9', fontspecs) + CHAR_SPACING); + else + INCREMENT_POS(width + CHAR_SPACING); } else if (c == '\n') { pos = orig_pos; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index ecb80dbe1..67e6111df 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -22,8 +22,10 @@ #include "libopenui_types.h" -/* lcd common flags */ -#define BLINK 0x01u +/* obsolete flags */ +#define BLINK 0 +#define EXPANDED 0 +#define TIMEHOUR 0 /* drawText flags */ #define LEFT 0x00u /* align left */ @@ -31,6 +33,10 @@ #define CENTERED 0x04u /* align center */ #define RIGHT 0x08u /* align right */ #define SHADOWED 0x80u /* black copy at +1 +1 */ +// unused 0x1000u +#define SPACING_NUMBERS_CONST 0x2000u +#define VERTICAL 0x4000u +// unused 0x8000u /* drawNumber flags */ #define LEADING0 0x10u @@ -41,13 +47,9 @@ /* telemetry flags */ #define NO_UNIT 0x40u -#define FONT_MASK 0x0F00u -#define FONT_INDEX(flags) (((flags) & FONT_MASK) >> 8u) -#define FONT(xx) (unsigned(FONT_ ## xx ## _INDEX) << 8u) - -#define TIMEHOUR 0x2000u -#define EXPANDED 0x2000u -#define VERTICAL 0x4000u +#define FONT_MASK 0x0F00u +#define FONT_INDEX(flags) (((flags) & FONT_MASK) >> 8u) +#define FONT(xx) (unsigned(FONT_ ## xx ## _INDEX) << 8u) #define ARGB_SPLIT(color, a, r, g, b) \ uint16_t a = ((color) & 0xF000) >> 12; \ From df117ecdf95eda6820416f54aee0eb34ddc136eb Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 25 Sep 2020 12:16:35 +0200 Subject: [PATCH 245/714] Fixes --- radio/src/thirdparty/libopenui/src/form.cpp | 11 +++++------ radio/src/thirdparty/libopenui/src/menu.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/table.h | 10 ++++++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index c6526a573..88b8cd2bc 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -122,6 +122,9 @@ void FormGroup::addField(FormField * field, bool front) if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { field->setFocus(SET_FOCUS_DEFAULT); } + else if (focusWindow == this && (windowFlags & FORM_FORWARD_FOCUS)) { + field->setFocus(SET_FOCUS_DEFAULT); + } } void FormGroup::setFocus(uint8_t flag) @@ -212,11 +215,7 @@ void FormGroup::onEvent(event_t event) first->setFocus(SET_FOCUS_FIRST); } else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus() && !(windowFlags & FORM_FORWARD_FOCUS)) { -#if defined(HARDWARE_TOUCH) - setFocus(SET_FOCUS_DEFAULT); -#else - FormField::onEvent(event); -#endif + setFocus(SET_FOCUS_DEFAULT); // opentx - model - timers settings } else if (event == EVT_ROTARY_RIGHT && !next) { if (first) @@ -249,7 +248,7 @@ void FormWindow::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormWindow").c_str(), event); - if (event == EVT_KEY_BREAK(KEY_EXIT) && first) { + if (event == EVT_KEY_BREAK(KEY_EXIT) && (windowFlags & FORM_FORWARD_FOCUS) && first) { Window * currentFocus = getFocus(); first->setFocus(SET_FOCUS_FIRST); if (getFocus() != currentFocus) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 6b2519a33..e63dbc027 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -105,11 +105,11 @@ void MenuBody::paint(BitmapBuffer * dc) } } if (line.drawLine) { - line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT /*+ (lineHeight - 20) / 2*/, flags); + line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT, flags); } else { const char * text = line.text.data(); - dc->drawText(10, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, text[0] == '\0' ? "---" : text, flags); + dc->drawText(10, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, text[0] == '\0' ? "---" : text, flags); } Menu * menu = getParentMenu(); diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 1be678ac9..ddaa710ea 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -20,9 +20,9 @@ #ifndef _TABLE_H_ #define _TABLE_H_ -#include "form.h" #include #include +#include "form.h" #include "libopenui_config.h" class Table: public FormField { @@ -87,6 +87,9 @@ class Table: public FormField { { lines.push_back(line); setInnerHeight(lines.size() * TABLE_LINE_HEIGHT - 2); + if (hasFocus() && selection < 0) { + select(0, true); + } } void setLineFlags(uint8_t index, LcdFlags flags) @@ -147,6 +150,9 @@ class Table: public FormField { header(this, {0, 0, width(), 0}, columnsCount), body(this, {0, 0, width(), height()}, windowFlags) { + if (hasFocus()) { + setFocus(SET_FOCUS_DEFAULT); + } } #if defined(DEBUG_WINDOWS) @@ -182,7 +188,7 @@ class Table: public FormField { void setFocus(uint8_t flag) override { - if (body.selection < 0) { + if (body.selection < 0 && !body.lines.empty()) { select(0); } body.setFocus(flag); From 7c5aa513e524d94c86b4a7494679e2a222e3ea84 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 25 Sep 2020 12:52:23 +0200 Subject: [PATCH 246/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 956b9abd3..197c77208 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -7,7 +7,7 @@ * This file is a part of libopenui library. * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public + * modify it underresult the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * From ce74048a51e6d82cee28d6866dcb6f25fbbc630d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 25 Sep 2020 17:16:38 +0200 Subject: [PATCH 247/714] Optimization --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 ++ radio/src/thirdparty/libopenui/src/textedit.cpp | 9 ++++++--- radio/src/thirdparty/libopenui/src/textedit.h | 10 +++++++--- radio/src/thirdparty/libopenui/src/window.cpp | 14 ++++++++------ radio/src/thirdparty/libopenui/src/window.h | 2 +- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index e63dbc027..f4782c67d 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -95,6 +95,8 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) void MenuBody::paint(BitmapBuffer * dc) { + dc->clear(MENU_BGCOLOR); + for (unsigned i = 0; i < lines.size(); i++) { auto & line = lines[i]; LcdFlags flags = MENU_COLOR | MENU_FONT; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 18c65c649..097d534f4 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -85,12 +85,14 @@ void TextEdit::onEvent(event_t event) value[length - 1] = '\0'; --cursorPos; invalidate(); + changed = true; } } else if (cursorPos < length) { memmove(value + cursorPos + 1, value + cursorPos, length - cursorPos - 1); value[cursorPos++] = c; invalidate(); + changed = true; } } #endif @@ -130,8 +132,10 @@ void TextEdit::onEvent(event_t event) case EVT_KEY_BREAK(KEY_ENTER): if (cursorPos < length - 1) { cursorPos++; - if (value[cursorPos] == '\0') + if (value[cursorPos] == '\0') { value[cursorPos] = ' '; + changed = true; + } invalidate(); } else { @@ -171,7 +175,7 @@ void TextEdit::onEvent(event_t event) // TRACE("value[%d] = %d", cursorPos, v); value[cursorPos] = v; invalidate(); - // TODO storageDirty(...); + changed = true; } } else { @@ -216,6 +220,5 @@ void TextEdit::onFocusLost() changeEnd(); - // TODO storageDirty(...); FormField::onFocusLost(); } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 8676441d9..52a732cc1 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -67,6 +67,7 @@ class TextEdit : public FormField { protected: char * value; + bool changed = false; uint8_t length; uint8_t cursorPos = 0; std::function changeHandler = nullptr; @@ -76,9 +77,12 @@ class TextEdit : public FormField { void changeEnd() { cursorPos = 0; - trim(); - if (changeHandler) - changeHandler(); + if (changed) { + changed = false; + trim(); + if (changeHandler) + changeHandler(); + } } static uint8_t getNextChar(uint8_t c) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 8f4b38425..6842bd32d 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -205,17 +205,18 @@ void Window::scrollTo(const rect_t & rect) } } -bool Window::hasOpaqueRect(const rect_t & rect) const +bool Window::hasOpaqueRect(const rect_t & testRect) const { - if (!this->rect.contains(rect)) + if (!rect.contains(testRect)) return false; if (windowFlags & OPAQUE) { return true; } + rect_t relativeRect = {testRect.x - rect.x, testRect.y - rect.y, testRect.w, testRect.h}; for (auto child: children) { - if (child->hasOpaqueRect(rect)) + if (child->hasOpaqueRect(relativeRect)) return true; } @@ -228,19 +229,20 @@ void Window::fullPaint(BitmapBuffer * dc) coord_t xmin, xmax, ymin, ymax; dc->getClippingRect(xmin, xmax, ymin, ymax); + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); auto firstChild = children.end(); + rect_t relativeRect = {xmin - x, ymin - y, xmax - xmin, ymax - ymin}; while (firstChild != children.begin()) { auto child = *(--firstChild); - if (child->hasOpaqueRect({xmin, ymin, xmax - xmin, ymax - ymin})) { + if (child->hasOpaqueRect(relativeRect)) { paintNeeded = false; break; } } if (windowFlags & PAINT_CHILDREN_FIRST) { - coord_t x = dc->getOffsetX(); - coord_t y = dc->getOffsetY(); paintChildren(dc, firstChild); dc->setOffset(x, y); dc->setClippingRect(xmin, xmax, ymin, ymax); diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 2f6c0ba1e..2fa1273cb 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -416,7 +416,7 @@ class Window { bool forwardTouchEnd(coord_t x, coord_t y); - bool hasOpaqueRect(const rect_t & rect) const; + bool hasOpaqueRect(const rect_t & testRect) const; }; #endif // _WINDOW_H_ From e1243604842a179436ff4fbdd820f2d2ac2c1430 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 25 Sep 2020 21:29:16 +0200 Subject: [PATCH 248/714] Paint order fix --- radio/src/thirdparty/libopenui/src/window.cpp | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 6842bd32d..076645b5d 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -226,27 +226,29 @@ bool Window::hasOpaqueRect(const rect_t & testRect) const void Window::fullPaint(BitmapBuffer * dc) { bool paintNeeded = true; + std::list::iterator firstChild; coord_t xmin, xmax, ymin, ymax; dc->getClippingRect(xmin, xmax, ymin, ymax); coord_t x = dc->getOffsetX(); coord_t y = dc->getOffsetY(); - auto firstChild = children.end(); - rect_t relativeRect = {xmin - x, ymin - y, xmax - xmin, ymax - ymin}; - while (firstChild != children.begin()) { - auto child = *(--firstChild); - if (child->hasOpaqueRect(relativeRect)) { - paintNeeded = false; - break; - } - } - if (windowFlags & PAINT_CHILDREN_FIRST) { - paintChildren(dc, firstChild); + paintChildren(dc, children.begin()); dc->setOffset(x, y); dc->setClippingRect(xmin, xmax, ymin, ymax); } + else { + firstChild = children.end(); + rect_t relativeRect = {xmin - x, ymin - y, xmax - xmin, ymax - ymin}; + while (firstChild != children.begin()) { + auto child = *(--firstChild); + if (child->hasOpaqueRect(relativeRect)) { + paintNeeded = false; + break; + } + } + } if (paintNeeded) { TRACE_WINDOWS_INDENT("%s%s", getWindowDebugString().c_str(), hasFocus() ? " (*)" : ""); From c16eb8212233e6bfafb944fdd86c7a5405c93061 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 29 Sep 2020 19:12:47 +0200 Subject: [PATCH 249/714] Cosmetics --- radio/src/thirdparty/libopenui/src/roller.h | 11 ++++++----- radio/src/thirdparty/libopenui/src/window.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 77a897801..2a44db723 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -31,7 +31,8 @@ constexpr WindowFlags ROLLER_SEPARATION_LINES = (FORM_FLAGS_LAST << 1u); constexpr coord_t SCROLL_POSITION_INVALIDATED = INT_MIN; -class Roller: public Choice { +class Roller: public Choice +{ public: Roller(FormGroup * parent, const rect_t & rect, const char * label, const char * const * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): Choice(parent, rect, values, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR) @@ -144,15 +145,15 @@ class Roller: public Choice { invalidateScrollPosition(); } - protected: - coord_t lastScrollPositionY = SCROLL_POSITION_INVALIDATED; - bool lastEditMode = false; - void invalidateScrollPosition() { lastScrollPositionY = SCROLL_POSITION_INVALIDATED; } + protected: + coord_t lastScrollPositionY = SCROLL_POSITION_INVALIDATED; + bool lastEditMode = false; + void updateScrollPositionFromValue() { setScrollPositionY(ROLLER_LINE_HEIGHT * (getIndexFromValue(this->getValue()) - 1)); diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 2fa1273cb..2f6e9789f 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -347,7 +347,7 @@ class Window { void detach(); - bool deleted() + bool deleted() const { return _deleted; } From c4ac2082edb6e957afa741552ef6dc64357087f4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 5 Oct 2020 23:12:25 +0200 Subject: [PATCH 250/714] Table can now have custom widgets in cells --- radio/src/thirdparty/libopenui/src/table.cpp | 42 +++++-- radio/src/thirdparty/libopenui/src/table.h | 118 +++++++++++++++---- 2 files changed, 128 insertions(+), 32 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 3067b485b..3d2f021d1 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -23,26 +23,52 @@ void Table::Header::paint(BitmapBuffer * dc) { coord_t x = 10; - if (!header.values.empty()) { + if (!cells.empty()) { dc->clear(TABLE_HEADER_BGCOLOR); - for (unsigned i = 0; i < header.values.size(); i++) { - dc->drawText(x, (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2, header.values[i].c_str(), TABLE_HEADER_FONT); + for (unsigned i = 0; i < cells.size(); i++) { + auto cell = cells[i]; + if (cell) { + cell->paint(dc, x, 0, TABLE_HEADER_FONT); + } x += columnsWidth[i]; } } } +void Table::Body::checkEvents() +{ + Window::checkEvents(); + + coord_t y = 0; + coord_t x; + for (auto line: lines) { + x = 10; + for (unsigned i = 0; i < line->cells.size(); i++) { + auto cell = line->cells[i]; + auto width = columnsWidth[i]; + if (cell && cell->needsInvalidate()) { + invalidate({x, y, width, TABLE_LINE_HEIGHT - 2}); + } + x += width; + } + } +} + void Table::Body::paint(BitmapBuffer * dc) { coord_t y = 0; coord_t x; int index = 0; - for (auto & line: lines) { + dc->clear(DEFAULT_BGCOLOR); + for (auto line: lines) { bool highlight = (index == selection); dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, highlight ? MENU_HIGHLIGHT_BGCOLOR : TABLE_BGCOLOR); x = 10; - for (unsigned i = 0; i < line.values.size(); i++) { - dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, line.values[i].c_str(), line.flags | (highlight ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); + for (unsigned i = 0; i < line->cells.size(); i++) { + auto cell = line->cells[i]; + if (cell) { + cell->paint(dc, x, y, line->flags | (highlight ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); + } x += columnsWidth[i]; } y += TABLE_LINE_HEIGHT; @@ -56,7 +82,7 @@ bool Table::Body::onTouchEnd(coord_t x, coord_t y) unsigned index = y / TABLE_LINE_HEIGHT; if (index < lines.size()) { setFocus(SET_FOCUS_DEFAULT); - auto onPress = lines[index].onPress; + auto onPress = lines[index]->onPress; if (onPress) onPress(); } @@ -71,7 +97,7 @@ void Table::Body::onEvent(event_t event) if (event == EVT_KEY_BREAK(KEY_ENTER)) { if (selection >= 0) { - auto onPress = lines[selection].onPress; + auto onPress = lines[selection]->onPress; if (onPress) onPress(); } diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index ddaa710ea..6c2a21f74 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -24,25 +24,88 @@ #include #include "form.h" #include "libopenui_config.h" +#include "font.h" class Table: public FormField { + public: + class Cell { + public: + virtual ~Cell() = default; + + virtual void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) = 0; + + virtual bool needsInvalidate() = 0; + }; + + class StringCell : public Cell { + public: + StringCell() = default; + + explicit StringCell(std::string value): + value(std::move(value)) + { + } + + void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override + { + dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, value.c_str(), flags); + } + + bool needsInvalidate() override + { + return false; + } + + protected: + std::string value; + }; + + class DynamicStringCell : public Cell { + public: + explicit DynamicStringCell(std::function getText): + getText(std::move(getText)) + { + } + + void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override + { + auto text = getText(); + dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, text.c_str(), SPACING_NUMBERS_CONST | flags); + } + + bool needsInvalidate() override + { + return true; // TODO optimize this + } + + protected: + std::function getText; + }; + class Line { public: explicit Line(uint8_t columnsCount): - values(columnsCount) + cells(columnsCount, nullptr) { } - std::vector values; + + ~Line() + { + for (auto cell: cells) { + delete cell; + } + } + std::vector cells; std::function onPress; std::function onSelect; LcdFlags flags = TABLE_BODY_FONT; }; - class Header: public Window { + class Header: public Window, public Line { public: Header(Table * parent, const rect_t & rect, uint8_t columnsCount): Window(parent, rect, OPAQUE), - header(columnsCount), + Line(columnsCount), columnsWidth(parent->columnsWidth) { } @@ -54,15 +117,9 @@ class Table: public FormField { } #endif - void setLine(const Line & line) - { - header = line; - } - void paint(BitmapBuffer * dc) override; protected: - Line header; std::vector & columnsWidth; }; @@ -76,6 +133,11 @@ class Table: public FormField { { } + ~Body() override + { + clear(); + } + #if defined(DEBUG_WINDOWS) std::string getName() const override { @@ -83,7 +145,7 @@ class Table: public FormField { } #endif - void addLine(const Line & line) + void addLine(Line * line) { lines.push_back(line); setInnerHeight(lines.size() * TABLE_LINE_HEIGHT - 2); @@ -94,14 +156,17 @@ class Table: public FormField { void setLineFlags(uint8_t index, LcdFlags flags) { - if (lines[index].flags != flags) { - lines[index].flags = flags; + if (lines[index]->flags != flags) { + lines[index]->flags = flags; invalidate({0, index * TABLE_LINE_HEIGHT, width(), TABLE_LINE_HEIGHT}); } } void clear() { + for (auto line: lines) { + delete line; + } lines.clear(); } @@ -119,7 +184,7 @@ class Table: public FormField { } invalidate(); if (index >= 0) { - auto onSelect = lines[index].onSelect; + auto onSelect = lines[index]->onSelect; if (onSelect) { onSelect(); } @@ -136,9 +201,11 @@ class Table: public FormField { bool onTouchEnd(coord_t x, coord_t y) override; #endif + void checkEvents() override; + protected: std::vector & columnsWidth; - std::vector lines; + std::vector lines; int selection = -1; }; @@ -209,28 +276,31 @@ class Table: public FormField { header.setHeight(TABLE_HEADER_HEIGHT); body.setTop(TABLE_HEADER_HEIGHT); body.setHeight(height() - TABLE_HEADER_HEIGHT); - Line line(columnsCount); for (uint8_t i = 0; i < columnsCount; i++) { - line.values[i] = values[i]; + header.cells[i] = new StringCell(values[i]); } - header.setLine(line); + } + + void addLine(Line * line, std::function onPress = nullptr, std::function onSelect = nullptr) + { + line->onPress = std::move(onPress); + line->onSelect = std::move(onSelect); + body.addLine(line); } void addLine(const char * const values[], std::function onPress = nullptr, std::function onSelect = nullptr) { - Line line(columnsCount); + Line * line = new Line(columnsCount); for (uint8_t i = 0; i < columnsCount; i++) { - line.values[i] = values[i]; + line->cells[i] = new StringCell(values[i]); } - line.onPress = std::move(onPress); - line.onSelect = std::move(onSelect); - body.addLine(line); + addLine(line, std::move(onPress), std::move(onSelect)); } void clear() { - body.clear(); clearSelection(); + body.clear(); } uint8_t size() const From 31657fe534bf0de64f75960954a0620877fa7320 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 5 Oct 2020 23:16:34 +0200 Subject: [PATCH 251/714] Defense --- radio/src/thirdparty/libopenui/src/roller.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 2a44db723..608eab033 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -70,6 +70,9 @@ class Roller: public Choice { int32_t value = getValue(); auto valuesCount = getValuesCount(); + if (valuesCount == 0) { + return; + } int index = (scrollPositionY - ROLLER_LINE_HEIGHT + 1) / ROLLER_LINE_HEIGHT; coord_t y = index * ROLLER_LINE_HEIGHT; From 99599743229a77a66fc8acbaa16f1d961b746e15 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 7 Oct 2020 19:51:10 +0200 Subject: [PATCH 252/714] [Table] Cosmetics --- radio/src/thirdparty/libopenui/src/libopenui_file.h | 2 +- radio/src/thirdparty/libopenui/src/table.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index 966c4ae7f..bbae7f362 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -24,7 +24,7 @@ #include #include #include "ff.h" -#include "libopenui_defines.h" +// #include "libopenui_defines.h" constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the dot, excluding null term. diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 6c2a21f74..695f9d161 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -48,7 +48,8 @@ class Table: public FormField { void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override { - dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, value.c_str(), flags); + + dc->drawText(x, y - 2 + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2 + 3, value.c_str(), flags); } bool needsInvalidate() override @@ -70,7 +71,7 @@ class Table: public FormField { void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override { auto text = getText(); - dc->drawText(x, y + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, text.c_str(), SPACING_NUMBERS_CONST | flags); + dc->drawText(x, y - 2 + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, text.c_str(), SPACING_NUMBERS_CONST | flags); } bool needsInvalidate() override From cc76efd38be8c245572adeb83489dca9c4887f8a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 8 Oct 2020 10:57:29 +0200 Subject: [PATCH 253/714] [Table] Custom cells not refreshed --- radio/src/thirdparty/libopenui/src/table.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 3d2f021d1..aa8e5b6cb 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -40,9 +40,8 @@ void Table::Body::checkEvents() Window::checkEvents(); coord_t y = 0; - coord_t x; for (auto line: lines) { - x = 10; + coord_t x = 10; for (unsigned i = 0; i < line->cells.size(); i++) { auto cell = line->cells[i]; auto width = columnsWidth[i]; @@ -51,19 +50,19 @@ void Table::Body::checkEvents() } x += width; } + y += TABLE_LINE_HEIGHT; } } void Table::Body::paint(BitmapBuffer * dc) { coord_t y = 0; - coord_t x; int index = 0; dc->clear(DEFAULT_BGCOLOR); for (auto line: lines) { bool highlight = (index == selection); dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, highlight ? MENU_HIGHLIGHT_BGCOLOR : TABLE_BGCOLOR); - x = 10; + coord_t x = 10; for (unsigned i = 0; i < line->cells.size(); i++) { auto cell = line->cells[i]; if (cell) { From 16ebf50f40140eea635f2bcba363057aa1677ca8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 8 Oct 2020 17:59:40 +0200 Subject: [PATCH 254/714] Fixes --- radio/src/thirdparty/libopenui/src/table.h | 38 +++++++++++++++++-- .../src/thirdparty/libopenui/src/textedit.cpp | 4 ++ radio/src/thirdparty/libopenui/src/window.h | 2 +- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 695f9d161..f217bf2ee 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -48,7 +48,6 @@ class Table: public FormField { void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override { - dc->drawText(x, y - 2 + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2 + 3, value.c_str(), flags); } @@ -83,6 +82,27 @@ class Table: public FormField { std::function getText; }; + class CustomCell : public Cell { + public: + explicit CustomCell(std::function paintFunction): + paintFunction(std::move(paintFunction)) + { + } + + void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override + { + paintFunction(dc, x, y, flags); + } + + bool needsInvalidate() override + { + return true; + } + + protected: + std::function paintFunction; + }; + class Line { public: explicit Line(uint8_t columnsCount): @@ -236,10 +256,22 @@ class Table: public FormField { body.detach(); } - void setColumnsWidth(const coord_t width[]) + void setColumnsWidth(const coord_t values[]) { + int8_t restColumn = -1; + coord_t restWidth = width(); for (uint8_t i = 0; i < columnsCount; i++) { - columnsWidth[i] = width[i]; + auto columnWidth = values[i]; + columnsWidth[i] = columnWidth; + if (columnWidth == 0) { + restColumn = i; + } + else { + restWidth -= columnWidth; + } + } + if (restColumn) { + columnsWidth[restColumn] = restWidth; } } diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 097d534f4..479db80d7 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -188,6 +188,10 @@ void TextEdit::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool TextEdit::onTouchEnd(coord_t x, coord_t y) { + if (!isEnabled()) { + return true; + } + if (!hasFocus()) { setFocus(SET_FOCUS_DEFAULT); } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 2f6e9789f..93f53815b 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -103,7 +103,7 @@ class Window { bool isChild(Window * window) const { - return window == this || (getParent() && getParent()->isChild(window)); + return window == this || (parent && parent->isChild(window)); } Window * getFullScreenWindow() From 1565a7a8e48eb8291b112ca8c95d4321852b6c41 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 8 Oct 2020 19:10:15 +0200 Subject: [PATCH 255/714] [Table] Fixes --- radio/src/thirdparty/libopenui/src/table.cpp | 6 +++--- radio/src/thirdparty/libopenui/src/table.h | 12 +++--------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index aa8e5b6cb..b64a31c0b 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -30,7 +30,7 @@ void Table::Header::paint(BitmapBuffer * dc) if (cell) { cell->paint(dc, x, 0, TABLE_HEADER_FONT); } - x += columnsWidth[i]; + x += static_cast(parent)->columnsWidth[i]; } } } @@ -44,7 +44,7 @@ void Table::Body::checkEvents() coord_t x = 10; for (unsigned i = 0; i < line->cells.size(); i++) { auto cell = line->cells[i]; - auto width = columnsWidth[i]; + auto width = static_cast
(parent)->columnsWidth[i]; if (cell && cell->needsInvalidate()) { invalidate({x, y, width, TABLE_LINE_HEIGHT - 2}); } @@ -68,7 +68,7 @@ void Table::Body::paint(BitmapBuffer * dc) if (cell) { cell->paint(dc, x, y, line->flags | (highlight ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); } - x += columnsWidth[i]; + x += static_cast
(parent)->columnsWidth[i]; } y += TABLE_LINE_HEIGHT; index += 1; diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index f217bf2ee..4cbcff089 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -126,8 +126,7 @@ class Table: public FormField { public: Header(Table * parent, const rect_t & rect, uint8_t columnsCount): Window(parent, rect, OPAQUE), - Line(columnsCount), - columnsWidth(parent->columnsWidth) + Line(columnsCount) { } @@ -139,9 +138,6 @@ class Table: public FormField { #endif void paint(BitmapBuffer * dc) override; - - protected: - std::vector & columnsWidth; }; class Body: public Window { @@ -149,8 +145,7 @@ class Table: public FormField { public: Body(Table * parent, const rect_t & rect, WindowFlags windowFlags): - Window(parent, rect, windowFlags), - columnsWidth(parent->columnsWidth) + Window(parent, rect, windowFlags) { } @@ -225,7 +220,6 @@ class Table: public FormField { void checkEvents() override; protected: - std::vector & columnsWidth; std::vector lines; int selection = -1; }; @@ -270,7 +264,7 @@ class Table: public FormField { restWidth -= columnWidth; } } - if (restColumn) { + if (restColumn >= 0) { columnsWidth[restColumn] = restWidth; } } From 8d27a510a4702aefda756dea639a84613f3c679d Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 9 Oct 2020 17:03:30 +0200 Subject: [PATCH 256/714] Focus fixes --- .../libopenui/src/expansion_panel.h | 6 +-- radio/src/thirdparty/libopenui/src/form.cpp | 39 +++++++++---------- radio/src/thirdparty/libopenui/src/form.h | 4 +- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 67286faee..4be4126d5 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -74,10 +74,10 @@ class ExpansionPanel: public FormGroup { void setFocus(uint8_t flag) override { - if (!isOpen) - header->setFocus(flag); - else + if (isOpen) FormGroup::setFocus(flag); + else + header->setFocus(flag); } FormGroup * body = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 88b8cd2bc..47d0e7e46 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -63,18 +63,22 @@ void FormField::onEvent(event_t event) void FormField::setFocus(uint8_t flag) { - if (!enabled) { - if (flag == SET_FOCUS_BACKWARD) { - if (previous) - previous->setFocus(flag); - } - else { - if (next) - next->setFocus(flag); - } + if (enabled) { + Window::setFocus(flag); } else { - Window::setFocus(flag); + clearFocus(); + focusWindow = this; + if (flag == SET_FOCUS_BACKWARD) { + if (previous) { + previous->setFocus(flag); + } + } + else { + if (next) { + next->setFocus(flag); + } + } } } @@ -133,15 +137,6 @@ void FormGroup::setFocus(uint8_t flag) if (windowFlags & FORM_FORWARD_FOCUS) { switch (flag) { - case SET_FOCUS_FIRST: - if (first) { - first->setFocus(SET_FOCUS_FIRST); - } - else if (next) { - next->setFocus(SET_FOCUS_FIRST); - } - break; - case SET_FOCUS_BACKWARD: if (focusWindow->isChild(first)) { if (previous == this) { @@ -163,8 +158,12 @@ void FormGroup::setFocus(uint8_t flag) } break; + case SET_FOCUS_FIRST: + clearFocus(); + // no break; + case SET_FOCUS_FORWARD: - if (focusWindow->isChild(this)) { + if (focusWindow && focusWindow->isChild(this)) { if (next == this) { first->setFocus(SET_FOCUS_FORWARD); } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 2f4a7a43b..9f3eff28b 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -62,9 +62,9 @@ class FormField: public Window { editMode = newEditMode; } - void enable(bool enabled = true) + void enable(bool value = true) { - this->enabled = enabled; + enabled = value; invalidate(); } From c8a798a66c26676fef3569814a956a04d6b635ed Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 9 Oct 2020 19:12:21 +0200 Subject: [PATCH 257/714] [StaticText] Possibility to have a background color --- radio/src/thirdparty/libopenui/src/static.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/static.h | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index b511b6715..8d1ef9c40 100755 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -22,8 +22,8 @@ void StaticText::paint(BitmapBuffer * dc) { - if (windowFlags & BUTTON_BACKGROUND) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); + if (bgColor) { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); } coord_t x; diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index bf5486aac..159e71ad9 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -29,6 +29,9 @@ class StaticText: public Window { Window(parent, rect, windowFlags, textFlags), text(std::move(text)) { + if (windowFlags & BUTTON_BACKGROUND) { + setBackgroundColor(DISABLE_COLOR); + } } #if defined(DEBUG_WINDOWS) @@ -46,8 +49,14 @@ class StaticText: public Window { invalidate(); } + void setBackgroundColor(LcdFlags color) + { + bgColor = color; + } + protected: std::string text; + LcdFlags bgColor = 0; }; class Subtitle: public StaticText { @@ -124,14 +133,15 @@ class StaticBitmap: public Window { class DynamicText: public StaticText { public: - DynamicText(Window * parent, const rect_t & rect, std::function textHandler): - StaticText(parent, rect), + DynamicText(Window * parent, const rect_t & rect, std::function textHandler, LcdFlags textFlags = 0): + StaticText(parent, rect, "", 0, textFlags), textHandler(std::move(textHandler)) { } void checkEvents() override { + StaticText::checkEvents(); std::string newText = textHandler(); if (newText != text) { text = newText; From 91b220bac2f3637292d7ee4a81a1b253773da38f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 12 Oct 2020 14:55:04 +0200 Subject: [PATCH 258/714] [Table] Focus issue --- radio/src/thirdparty/libopenui/src/choice.cpp | 3 ++ .../libopenui/src/expansion_panel.cpp | 9 +++- radio/src/thirdparty/libopenui/src/table.cpp | 42 +++++++++++++++++-- radio/src/thirdparty/libopenui/src/table.h | 2 +- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index f7f55af44..75ba22579 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -175,6 +175,9 @@ void Choice::openMenu() bool Choice::onTouchEnd(coord_t, coord_t) { if (enabled) { + if (!hasFocus()) { + setFocus(SET_FOCUS_DEFAULT); + } onKeyPress(); openMenu(); } diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp index a73e098c9..53ef22fe4 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -39,7 +39,14 @@ void ExpansionPanelHeader::onEvent(event_t event) panel->toggle(); } else if (event == EVT_ROTARY_RIGHT && !panel->isOpen) { - panel->getNextField()->setFocus(SET_FOCUS_FORWARD); + auto next = panel->getNextField(); + if (next) + next->setFocus(SET_FOCUS_FORWARD); + } + else if (event == EVT_ROTARY_LEFT) { + auto previous = panel->getPreviousField(); + if (previous) + previous->setFocus(SET_FOCUS_BACKWARD); } else { FormGroup::onEvent(event); diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index b64a31c0b..0a628fbea 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -102,12 +102,46 @@ void Table::Body::onEvent(event_t event) } } if (event == EVT_ROTARY_RIGHT) { - if (!lines.empty()) - select((selection + 1) % lines.size(), true); + auto table = static_cast
(parent); + if (table->windowFlags & FORWARD_SCROLL) { + auto index = selection + 1; + if (index < int(lines.size())) { + select(index, true); + } + else { + auto next = table->getNextField(); + if (next) { + select(-1, false); + next->setFocus(SET_FOCUS_FORWARD); + } + } + } + else { + if (!lines.empty()) { + select((selection + 1) % lines.size(), true); + } + } } else if (event == EVT_ROTARY_LEFT) { - if (!lines.empty()) - select(selection <= 0 ? lines.size() - 1 : selection - 1, true); + auto table = static_cast
(parent); + if (table->windowFlags & FORWARD_SCROLL) { + auto index = selection - 1; + if (index >= 0) { + select(index, true); + } + else { + auto previous = table->getPreviousField(); + if (previous) { + select(-1, false); + previous->setFocus(SET_FOCUS_BACKWARD); + } + } + } + else { + if (!lines.empty()) { + select(selection <= 0 ? lines.size() - 1 : selection - 1, true); + } + } } else if (event == EVT_KEY_BREAK(KEY_EXIT) && selection >= 0) { select(-1, true); diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 4cbcff089..75f5f3bd3 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -283,7 +283,7 @@ class Table: public FormField { void setFocus(uint8_t flag) override { if (body.selection < 0 && !body.lines.empty()) { - select(0); + select(flag == SET_FOCUS_BACKWARD ? body.lines.size() - 1 : 0); } body.setFocus(flag); } From 66253e1f7d975b0c5dc3e850499054534bb0b95b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 13 Oct 2020 09:25:46 +0200 Subject: [PATCH 259/714] Use #pragma once everywhere (thanks @guardonce project) --- radio/src/thirdparty/libopenui/src/basenumberedit.h | 4 +--- radio/src/thirdparty/libopenui/src/bitfield.h | 4 +--- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 4 +--- radio/src/thirdparty/libopenui/src/bufferedwindow.h | 4 +--- radio/src/thirdparty/libopenui/src/button.h | 4 +--- radio/src/thirdparty/libopenui/src/carousel.h | 4 +--- radio/src/thirdparty/libopenui/src/checkbox.h | 4 +--- radio/src/thirdparty/libopenui/src/choice.h | 4 +--- radio/src/thirdparty/libopenui/src/coloredit.h | 4 +--- radio/src/thirdparty/libopenui/src/dialog.h | 4 +--- radio/src/thirdparty/libopenui/src/expansion_panel.h | 4 +--- radio/src/thirdparty/libopenui/src/filechoice.h | 4 +--- radio/src/thirdparty/libopenui/src/font.h | 4 +--- radio/src/thirdparty/libopenui/src/form.h | 4 +--- radio/src/thirdparty/libopenui/src/gridlayout.h | 4 +--- radio/src/thirdparty/libopenui/src/keyboard_base.h | 4 +--- radio/src/thirdparty/libopenui/src/keyboard_number.h | 4 +--- radio/src/thirdparty/libopenui/src/keyboard_text.h | 4 +--- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 4 +--- radio/src/thirdparty/libopenui/src/libopenui_depends.h | 4 +--- radio/src/thirdparty/libopenui/src/libopenui_file.h | 4 +--- radio/src/thirdparty/libopenui/src/libopenui_globals.h | 4 +--- radio/src/thirdparty/libopenui/src/libopenui_helpers.h | 4 +--- radio/src/thirdparty/libopenui/src/libopenui_types.h | 4 +--- radio/src/thirdparty/libopenui/src/mainwindow.h | 4 +--- radio/src/thirdparty/libopenui/src/menu.h | 4 +--- radio/src/thirdparty/libopenui/src/menutoolbar.h | 4 +--- radio/src/thirdparty/libopenui/src/modal_window.h | 4 +--- radio/src/thirdparty/libopenui/src/numberedit.h | 4 +--- radio/src/thirdparty/libopenui/src/progress.h | 4 +--- radio/src/thirdparty/libopenui/src/roller.h | 4 +--- radio/src/thirdparty/libopenui/src/slider.h | 4 +--- radio/src/thirdparty/libopenui/src/static.h | 4 +--- radio/src/thirdparty/libopenui/src/table.h | 4 +--- radio/src/thirdparty/libopenui/src/textedit.h | 4 +--- radio/src/thirdparty/libopenui/src/theme.h | 4 +--- radio/src/thirdparty/libopenui/src/window.h | 4 +--- 37 files changed, 37 insertions(+), 111 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index f85f8754e..12e5694a2 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _BASENUMBEREDIT_H_ -#define _BASENUMBEREDIT_H_ +#pragma once #include "form.h" @@ -99,4 +98,3 @@ class BaseNumberEdit : public FormField { std::function _setValue; }; -#endif // _BASENUMBEREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/bitfield.h b/radio/src/thirdparty/libopenui/src/bitfield.h index abb309ab1..093119613 100644 --- a/radio/src/thirdparty/libopenui/src/bitfield.h +++ b/radio/src/thirdparty/libopenui/src/bitfield.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _BITFIELD_H -#define _BITFIELD_H +#pragma once #include @@ -82,4 +81,3 @@ inline T bfSingleBitGet(T y, uint8_t i) return bfBitGet(y, bfBit(i)); } -#endif //BITFIELD_H diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index ea7a3c1f4..adfd435f4 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _BITMAP_BUFFER_H_ -#define _BITMAP_BUFFER_H_ +#pragma once #include #include @@ -519,4 +518,3 @@ class BitmapBuffer: public BitmapBufferBase extern BitmapBuffer * lcd; -#endif // _BITMAP_BUFFER_H_ diff --git a/radio/src/thirdparty/libopenui/src/bufferedwindow.h b/radio/src/thirdparty/libopenui/src/bufferedwindow.h index 56d25447c..9f9dd89fb 100644 --- a/radio/src/thirdparty/libopenui/src/bufferedwindow.h +++ b/radio/src/thirdparty/libopenui/src/bufferedwindow.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _BUFFERED_WINDOW_H_ -#define _BUFFERED_WINDOW_H_ +#pragma once #include "window.h" @@ -123,4 +122,3 @@ class TransparentBitmapBackground: public TransparentBufferedWindow } }; -#endif // _BUFFERED_WINDOW_H_ diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 252b24eaf..1eb14fc27 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _BUTTON_H_ -#define _BUTTON_H_ +#pragma once #include "form.h" @@ -134,4 +133,3 @@ class IconButton: public Button { uint8_t icon; }; -#endif diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index 23b4ec0ea..1bcbeeaef 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _CAROUSEL_H_ -#define _CAROUSEL_H_ +#pragma once #include #include "button.h" @@ -139,4 +138,3 @@ class Carousel: public Window { CarouselWindow * body = nullptr; }; -#endif // _CAROUSEL_H_ diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index ed4420d89..411f588b6 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _CHECKBOX_H_ -#define _CHECKBOX_H_ +#pragma once #include "form.h" @@ -69,4 +68,3 @@ class CheckBox : public FormField { std::function _setValue; }; -#endif // _CHECKBOX_H_ diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 7fce5cfd4..2344923a4 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _CHOICE_H_ -#define _CHOICE_H_ +#pragma once #include #include "form.h" @@ -159,4 +158,3 @@ class Choice : public ChoiceBase { virtual void openMenu(); }; -#endif // _CHOICE_H_ diff --git a/radio/src/thirdparty/libopenui/src/coloredit.h b/radio/src/thirdparty/libopenui/src/coloredit.h index c12dedb55..0d862905f 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.h +++ b/radio/src/thirdparty/libopenui/src/coloredit.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _COLOREDIT_H_ -#define _COLOREDIT_H_ +#pragma once #include "form.h" @@ -34,4 +33,3 @@ class ColorEdit : public FormGroup { #endif }; -#endif // _COLOREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 58d26233b..3e8816aae 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _DIALOG_H_ -#define _DIALOG_H_ +#pragma once #include "modal_window.h" #include "form.h" @@ -55,4 +54,3 @@ class Dialog: public ModalWindow { DialogWindowContent * content; }; -#endif // _DIALOG_H_ diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 4be4126d5..5aa7380c4 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _EXPANSION_PANEL_H_ -#define _EXPANSION_PANEL_H_ +#pragma once #include "form.h" @@ -87,4 +86,3 @@ class ExpansionPanel: public FormGroup { bool isOpen = false; }; -#endif // _EXPANSION_PANEL_H_ diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index 877f3f933..0157ff68d 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _FILECHOICE_H_ -#define _FILECHOICE_H_ +#pragma once #include "choice.h" #include @@ -54,4 +53,3 @@ class FileChoice: public ChoiceBase { bool openMenu(); }; -#endif // _FILECHOICE_H_ diff --git a/radio/src/thirdparty/libopenui/src/font.h b/radio/src/thirdparty/libopenui/src/font.h index 6503e3c78..b45bf8150 100644 --- a/radio/src/thirdparty/libopenui/src/font.h +++ b/radio/src/thirdparty/libopenui/src/font.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _FONT_H_ -#define _FONT_H_ +#pragma once #include "libopenui_types.h" @@ -27,4 +26,3 @@ uint8_t getFontHeight(LcdFlags flags); int getCharWidth(uint8_t c, const uint16_t * spec); int getTextWidth(const char * s, int len = 0, LcdFlags flags = 0); -#endif // _FONT_H_ diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 9f3eff28b..d2b989472 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _FORM_H_ -#define _FORM_H_ +#pragma once #include "window.h" @@ -173,4 +172,3 @@ class FormWindow: public FormGroup { #endif }; -#endif diff --git a/radio/src/thirdparty/libopenui/src/gridlayout.h b/radio/src/thirdparty/libopenui/src/gridlayout.h index 535fc5050..a606f5796 100644 --- a/radio/src/thirdparty/libopenui/src/gridlayout.h +++ b/radio/src/thirdparty/libopenui/src/gridlayout.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _GRIDLAYOUT_H_ -#define _GRIDLAYOUT_H_ +#pragma once #include "window.h" @@ -120,4 +119,3 @@ class FormGridLayout: public GridLayout { coord_t lineMarginRight = PAGE_PADDING; }; -#endif diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 540c228cc..072a6a961 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _KEYBOARD_BASE_H_ -#define _KEYBOARD_BASE_H_ +#pragma once #include "mainwindow.h" #include "form.h" @@ -86,4 +85,3 @@ class Keyboard: public FormWindow { } }; -#endif // _KEYBOARD_BASE_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h index b82f68f1d..4f8fca93c 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _KEYBOARD_NUMBER_H_ -#define _KEYBOARD_NUMBER_H_ +#pragma once #include "keyboard_base.h" @@ -48,4 +47,3 @@ class NumberKeyboard : public Keyboard { static NumberKeyboard * _instance; }; -#endif // _KEYBOARD_NUMBER_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index 368d239ec..afc8cfef2 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _KEYBOARD_TEXT_H_ -#define _KEYBOARD_TEXT_H_ +#pragma once #include "keyboard_base.h" @@ -61,4 +60,3 @@ class TextKeyboard : public Keyboard { const char * const * layout; }; -#endif // _KEYBOARD_TEXT_H_ \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 67e6111df..86ad01e18 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _LIBOPEUI_DEFINES_H_ -#define _LIBOPEUI_DEFINES_H_ +#pragma once #include "libopenui_types.h" @@ -116,4 +115,3 @@ #define TABLE_HEADER_BGCOLOR COLOR(TABLE_HEADER_BGCOLOR_INDEX) #define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) -#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index 6da9fa0d8..df319d028 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _LIBOPEUI_DEPENDS_H_ -#define _LIBOPEUI_DEPENDS_H_ +#pragma once #include #include "libopenui_config.h" @@ -31,4 +30,3 @@ void onKeyError(); void killEvents(event_t event); event_t getWindowEvent(); -#endif // _LIBOPEUI_DEPENDS_H_ diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index bbae7f362..e75cda9ee 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _LIBOPEUI_FILE_H_ -#define _LIBOPEUI_FILE_H_ +#pragma once #include #include @@ -38,4 +37,3 @@ inline bool compare_nocase(const std::string &first, const std::string &second) return strcasecmp(first.c_str(), second.c_str()) < 0; } -#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.h b/radio/src/thirdparty/libopenui/src/libopenui_globals.h index fe14e3e9d..27d5b92a9 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_globals.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _LIBOPEUI_GLOBALS_H_ -#define _LIBOPEUI_GLOBALS_H_ +#pragma once #include #include "libopenui_config.h" @@ -32,4 +31,3 @@ inline void lcdSetColor(uint16_t color) lcdColorTable[CUSTOM_COLOR_INDEX] = color; } -#endif // _LIBOPEUI_GLOBALS_H_ diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index d188a92bd..640f05046 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _LIBOPEUI_HELPERS_H_ -#define _LIBOPEUI_HELPERS_H_ +#pragma once #include #include @@ -61,4 +60,3 @@ inline std::string TEXT_AT_INDEX(const char * val, uint8_t idx) return std::string(start, min(val[0], (uint8_t)strlen(start))); } -#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_types.h b/radio/src/thirdparty/libopenui/src/libopenui_types.h index a643b2384..d9b3177c3 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_types.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_types.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _LIBOPEUI_TYPES_H_ -#define _LIBOPEUI_TYPES_H_ +#pragma once #include @@ -73,4 +72,3 @@ struct rect_t typedef uint32_t LcdFlags; typedef uint16_t event_t; -#endif // _LIBOPEUI_TYPES_H_ diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index da1cdef85..f51a0d56f 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _MAINWINDOW_H_ -#define _MAINWINDOW_H_ +#pragma once #include #include "window.h" @@ -65,4 +64,3 @@ class MainWindow: public Window { extern MainWindow mainWindow; -#endif // _MAINWINDOW_H_ diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index e1398bef7..e21c30497 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _MENU_H_ -#define _MENU_H_ +#pragma once #include #include @@ -223,4 +222,3 @@ Menu * MenuBody::getParentMenu() return static_cast(getParent()->getParent()); } -#endif // _MENU_H_ diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index bb0de621e..aae69d1a6 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _MENUTOOLBAR_H_ -#define _MENUTOOLBAR_H_ +#pragma once #include "button.h" #include "menu.h" @@ -170,5 +169,4 @@ class MenuToolbar: public FormGroup { } }; -#endif // _MENUTOOLBAR_H_ diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index ac7f5b8a3..4240fe43e 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _MODAL_WINDOW_H_ -#define _MODAL_WINDOW_H_ +#pragma once #include "window.h" @@ -122,4 +121,3 @@ class ModalWindowContent: public Window { std::string title; }; -#endif // _MODAL_WINDOW_H_ diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 03e34850b..55ef21c27 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _NUMBEREDIT_H_ -#define _NUMBEREDIT_H_ +#pragma once #include "basenumberedit.h" #include @@ -78,4 +77,3 @@ class NumberEdit : public BaseNumberEdit { std::function isValueAvailable; }; -#endif // _NUMBEREDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/progress.h b/radio/src/thirdparty/libopenui/src/progress.h index 176b38873..7653382bb 100644 --- a/radio/src/thirdparty/libopenui/src/progress.h +++ b/radio/src/thirdparty/libopenui/src/progress.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _PROGRESS_H_ -#define _PROGRESS_H_ +#pragma once #include "window.h" @@ -48,4 +47,3 @@ class Progress : public Window { int value = 0; }; -#endif diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 608eab033..c70184224 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _ROLLER_H_ -#define _ROLLER_H_ +#pragma once #include #include @@ -175,4 +174,3 @@ class Roller: public Choice } }; -#endif // _ROLLER_H_ diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index c883516cc..4b35c1fd0 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _SLIDER_H_ -#define _SLIDER_H_ +#pragma once #include "form.h" @@ -69,4 +68,3 @@ class Slider: public FormField { std::function _setValue; }; -#endif diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 159e71ad9..ea8015f2e 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _STATIC_H_ -#define _STATIC_H_ +#pragma once #include "window.h" #include "button.h" // TODO just for BUTTON_BACKGROUND @@ -186,4 +185,3 @@ class DynamicNumber: public Window const char * suffix; }; -#endif diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 75f5f3bd3..a00a1812c 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _TABLE_H_ -#define _TABLE_H_ +#pragma once #include #include @@ -342,4 +341,3 @@ class Table: public FormField { Body body; }; -#endif // _TABLE_H_ diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 52a732cc1..3688292b1 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _TEXTEDIT_H_ -#define _TEXTEDIT_H_ +#pragma once #include "form.h" @@ -115,4 +114,3 @@ class TextEdit : public FormField { }; -#endif // _TEXTEDIT_H_ diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index d2eea9445..5b5a36fc4 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _THEME_H_ -#define _THEME_H_ +#pragma once #include "bitmapbuffer.h" #include "checkbox.h" @@ -63,4 +62,3 @@ extern Theme * theme; MenuWindowContent * createMenuWindow(Menu * menu); DialogWindowContent * createDialogWindow(Dialog * dialog, const rect_t & rect); -#endif // _THEME_H_ diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 93f53815b..efd6b5122 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -17,8 +17,7 @@ * Lesser General Public License for more details. */ -#ifndef _WINDOW_H_ -#define _WINDOW_H_ +#pragma once #include #include @@ -419,4 +418,3 @@ class Window { bool hasOpaqueRect(const rect_t & testRect) const; }; -#endif // _WINDOW_H_ From dc747da1fafe3ced46cc43fca1bd0ffac2b6dac3 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 15 Oct 2020 15:47:00 +0200 Subject: [PATCH 260/714] Add feature --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 242 +++++++++--------- 1 file changed, 123 insertions(+), 119 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 197c77208..859653694 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -195,23 +195,23 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui } } -// -//void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags att) -//{ -// pixel_t color = lcdColorTable[COLOR_IDX(att)]; -// RGB_SPLIT(color, red, green, blue); -// -// for (int i=y; i 360) - angle %= 360; - float radians = float(angle) * (M_PI / 180.0f); - if (angle == 0) { - left = false; - value = 100000; - } - else if (angle == 360) { - left = true; - value = 100000; - } - else if (angle >= 180) { - left = true; - value = -(cosf(radians) * 100 / sinf(radians)); - } - else { - left = false; - value = (cosf(radians) * 100 / sinf(radians)); - } +public: + explicit Slope(int angle) { + if (angle < 0) + angle += 360; + if (angle > 360) + angle %= 360; + float radians = float(angle) * (M_PI / 180.0f); + if (angle == 0) { + left = false; + value = 100000; } + else if (angle == 360) { + left = true; + value = 100000; + } + else if (angle >= 180) { + left = true; + value = -(cosf(radians) * 100 / sinf(radians)); + } + else { + left = false; + value = (cosf(radians) * 100 / sinf(radians)); + } + } - Slope(bool left, int value): + Slope(bool left, int value): left(left), value(value) - { - } + { + } - bool isBetween(const Slope & start, const Slope & end) const - { - if (left) { - if (start.left) { - if (end.left) - return end.value > start.value ? (value <= end.value && value >= start.value) : (value <= end.value || value >= start.value); - else - return value >= start.value; - } - else { - if (end.left) - return value <= end.value; - else - return end.value > start.value; - } + bool isBetween(const Slope & start, const Slope & end) const + { + if (left) { + if (start.left) { + if (end.left) + return end.value > start.value ? (value <= end.value && value >= start.value) : (value <= end.value || value >= start.value); + else + return value >= start.value; } else { - if (start.left) { - if (end.left) - return start.value > end.value; - else - return value >= end.value; - } - else { - if (end.left) - return value <= start.value; - else - return end.value < start.value ? (value >= end.value && value <= start.value) : (value <= start.value || value >= end.value); - } + if (end.left) + return value <= end.value; + else + return end.value > start.value; } } - - Slope & invertVertical() - { - value = -value; - return *this; + else { + if (start.left) { + if (end.left) + return start.value > end.value; + else + return value >= end.value; + } + else { + if (end.left) + return value <= start.value; + else + return end.value < start.value ? (value >= end.value && value <= start.value) : (value <= start.value || value >= end.value); + } } + } - Slope & invertHorizontal() - { - left = !left; - return *this; - } + Slope & invertVertical() + { + value = -value; + return *this; + } - protected: - bool left; - int value; + Slope & invertHorizontal() + { + left = !left; + return *this; + } + +protected: + bool left; + int value; }; void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags) @@ -667,39 +667,43 @@ void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h // } //} // -//void BitmapBuffer::drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle) -//{ -// coord_t width = *((uint16_t *)img); -// coord_t height = *(((uint16_t *)img)+1); -// const uint8_t * q = img+4; -// -// int slopes[4]; -// if (!evalSlopes(slopes, startAngle, endAngle)) -// return; -// -// pixel_t color = lcdColorTable[COLOR_IDX(flags)]; -// -// int w2 = width/2; -// int h2 = height/2; -// -// for (int y=h2-1; y>=0; y--) { -// for (int x=w2-1; x>=0; x--) { -// int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x); -// if (slope >= slopes[0] && slope < slopes[1]) { -// drawAlphaPixel(x0+w2+x, y0+h2-y, q[(h2-y)*width + w2+x], color); -// } -// if (-slope >= slopes[0] && -slope < slopes[1]) { -// drawAlphaPixel(x0+w2+x, y0+h2+y, q[(h2+y)*width + w2+x], color); -// } -// if (slope >= slopes[2] && slope < slopes[3]) { -// drawAlphaPixel(x0+w2-x, y0+h2-y, q[(h2-y)*width + w2-x], color); -// } -// if (-slope >= slopes[2] && -slope < slopes[3]) { -// drawAlphaPixel(x0+w2-x, y0+h2+y, q[(h2+y)*width + w2-x], color); -// } -// } -// } -//} +void BitmapBuffer::drawBitmapPatternPie(coord_t x, coord_t y, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle) +{ + if (endAngle == startAngle) { + endAngle += 1; + } + + Slope startSlope(startAngle); + Slope endSlope(endAngle); + + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + APPLY_OFFSET(); + + coord_t width = *((uint16_t *)img); + coord_t height = *(((uint16_t *)img) + 1); + const uint8_t * q = img + 4; + + int w2 = width / 2; + int h2 = height / 2; + + for (int y1 = h2 - 1; y1 >= 0; y1--) { + for (int x1 = w2 - 1; x1 >= 0; x1--) { + Slope slope(false, x1 == 0 ? 99000 : y1 * 100 / x1); + if (slope.isBetween(startSlope, endSlope)) { + drawAlphaPixel(x + w2 + x1, y + h2 - y1, q[(h2 - y1) * width + w2 + x1], color); + } + if (slope.invertVertical().isBetween(startSlope, endSlope)) { + drawAlphaPixel(x + w2 + x1, y + h2 + y1, q[(h2 + y1) * width + w2 + x1], color); + } + if (slope.invertHorizontal().isBetween(startSlope, endSlope)) { + drawAlphaPixel(x + w2 - x1, y + h2 + y1, q[(h2 + y1) * width + w2 - x1], color); + } + if (slope.invertVertical().isBetween(startSlope, endSlope)) { + drawAlphaPixel(x + w2 - x1, y + h2 - y1, q[(h2 - y1) * width + w2 - x1], color); + } + } + } +} BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename) { @@ -1042,9 +1046,9 @@ int stbc_eof(void *user) // callbacks for stb-image const stbi_io_callbacks stbCallbacks = { - stbc_read, - stbc_skip, - stbc_eof + stbc_read, + stbc_skip, + stbc_eof }; BitmapBuffer * BitmapBuffer::load_stb(const char * filename) From 9d28df4aec2c326acb5a712f85a848ecf08b2077 Mon Sep 17 00:00:00 2001 From: 3djc Date: Thu, 15 Oct 2020 16:00:15 +0200 Subject: [PATCH 261/714] Cosmetics --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 213 +++++++++--------- 1 file changed, 107 insertions(+), 106 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 859653694..3c74f87fb 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -264,86 +264,124 @@ void BitmapBuffer::drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFla } class Slope { -public: - explicit Slope(int angle) { - if (angle < 0) - angle += 360; - if (angle > 360) - angle %= 360; - float radians = float(angle) * (M_PI / 180.0f); - if (angle == 0) { - left = false; - value = 100000; + public: + explicit Slope(int angle) { + if (angle < 0) + angle += 360; + if (angle > 360) + angle %= 360; + float radians = float(angle) * (M_PI / 180.0f); + if (angle == 0) { + left = false; + value = 100000; + } + else if (angle == 360) { + left = true; + value = 100000; + } + else if (angle >= 180) { + left = true; + value = -(cosf(radians) * 100 / sinf(radians)); + } + else { + left = false; + value = (cosf(radians) * 100 / sinf(radians)); + } } - else if (angle == 360) { - left = true; - value = 100000; - } - else if (angle >= 180) { - left = true; - value = -(cosf(radians) * 100 / sinf(radians)); - } - else { - left = false; - value = (cosf(radians) * 100 / sinf(radians)); - } - } - Slope(bool left, int value): + Slope(bool left, int value): left(left), value(value) - { - } + { + } - bool isBetween(const Slope & start, const Slope & end) const - { - if (left) { - if (start.left) { - if (end.left) - return end.value > start.value ? (value <= end.value && value >= start.value) : (value <= end.value || value >= start.value); - else - return value >= start.value; + bool isBetween(const Slope & start, const Slope & end) const + { + if (left) { + if (start.left) { + if (end.left) + return end.value > start.value ? (value <= end.value && value >= start.value) : (value <= end.value || value >= start.value); + else + return value >= start.value; + } + else { + if (end.left) + return value <= end.value; + else + return end.value > start.value; + } } else { - if (end.left) - return value <= end.value; - else - return end.value > start.value; + if (start.left) { + if (end.left) + return start.value > end.value; + else + return value >= end.value; + } + else { + if (end.left) + return value <= start.value; + else + return end.value < start.value ? (value >= end.value && value <= start.value) : (value <= start.value || value >= end.value); + } } } - else { - if (start.left) { - if (end.left) - return start.value > end.value; - else - return value >= end.value; - } - else { - if (end.left) - return value <= start.value; - else - return end.value < start.value ? (value >= end.value && value <= start.value) : (value <= start.value || value >= end.value); - } + + Slope & invertVertical() + { + value = -value; + return *this; } - } - Slope & invertVertical() - { - value = -value; - return *this; - } + Slope & invertHorizontal() + { + left = !left; + return *this; + } - Slope & invertHorizontal() - { - left = !left; - return *this; - } - -protected: - bool left; - int value; + protected: + bool left; + int value; }; +void BitmapBuffer::drawBitmapPatternPie(coord_t x, coord_t y, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle) +{ + if (endAngle == startAngle) { + endAngle += 1; + } + + Slope startSlope(startAngle); + Slope endSlope(endAngle); + + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + APPLY_OFFSET(); + + coord_t width = *((uint16_t *)img); + coord_t height = *(((uint16_t *)img) + 1); + const uint8_t * q = img + 4; + + int w2 = width / 2; + int h2 = height / 2; + + for (int y1 = h2 - 1; y1 >= 0; y1--) { + for (int x1 = w2 - 1; x1 >= 0; x1--) { + Slope slope(false, x1 == 0 ? 99000 : y1 * 100 / x1); + if (slope.isBetween(startSlope, endSlope)) { + drawAlphaPixel(x + w2 + x1, y + h2 - y1, q[(h2 - y1) * width + w2 + x1], color); + } + if (slope.invertVertical().isBetween(startSlope, endSlope)) { + drawAlphaPixel(x + w2 + x1, y + h2 + y1, q[(h2 + y1) * width + w2 + x1], color); + } + if (slope.invertHorizontal().isBetween(startSlope, endSlope)) { + drawAlphaPixel(x + w2 - x1, y + h2 + y1, q[(h2 + y1) * width + w2 - x1], color); + } + if (slope.invertVertical().isBetween(startSlope, endSlope)) { + drawAlphaPixel(x + w2 - x1, y + h2 - y1, q[(h2 - y1) * width + w2 - x1], color); + } + } + } +} + void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadius, coord_t externalRadius, int startAngle, int endAngle, LcdFlags flags) { if (endAngle == startAngle) { @@ -667,43 +705,6 @@ void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h // } //} // -void BitmapBuffer::drawBitmapPatternPie(coord_t x, coord_t y, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle) -{ - if (endAngle == startAngle) { - endAngle += 1; - } - - Slope startSlope(startAngle); - Slope endSlope(endAngle); - - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; - APPLY_OFFSET(); - - coord_t width = *((uint16_t *)img); - coord_t height = *(((uint16_t *)img) + 1); - const uint8_t * q = img + 4; - - int w2 = width / 2; - int h2 = height / 2; - - for (int y1 = h2 - 1; y1 >= 0; y1--) { - for (int x1 = w2 - 1; x1 >= 0; x1--) { - Slope slope(false, x1 == 0 ? 99000 : y1 * 100 / x1); - if (slope.isBetween(startSlope, endSlope)) { - drawAlphaPixel(x + w2 + x1, y + h2 - y1, q[(h2 - y1) * width + w2 + x1], color); - } - if (slope.invertVertical().isBetween(startSlope, endSlope)) { - drawAlphaPixel(x + w2 + x1, y + h2 + y1, q[(h2 + y1) * width + w2 + x1], color); - } - if (slope.invertHorizontal().isBetween(startSlope, endSlope)) { - drawAlphaPixel(x + w2 - x1, y + h2 + y1, q[(h2 + y1) * width + w2 - x1], color); - } - if (slope.invertVertical().isBetween(startSlope, endSlope)) { - drawAlphaPixel(x + w2 - x1, y + h2 - y1, q[(h2 - y1) * width + w2 - x1], color); - } - } - } -} BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename) { @@ -1046,9 +1047,9 @@ int stbc_eof(void *user) // callbacks for stb-image const stbi_io_callbacks stbCallbacks = { - stbc_read, - stbc_skip, - stbc_eof + stbc_read, + stbc_skip, + stbc_eof }; BitmapBuffer * BitmapBuffer::load_stb(const char * filename) From 4842d230bd6f0fc22c8409c808df71b88492b0c6 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 17 Oct 2020 09:38:24 +0200 Subject: [PATCH 262/714] Add drawLine function --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 53 +++++++++++++++++++ .../thirdparty/libopenui/src/bitmapbuffer.h | 2 + .../libopenui/src/libopenui_helpers.h | 6 +++ 3 files changed, 61 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 197c77208..7dceba807 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -126,6 +126,57 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat } } +void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att) +{ + // Offsets + x1 += this->offsetX; + y1 += this->offsetY; + x2 += this->offsetX; + y2 += this->offsetY; + + pixel_t color = lcdColorTable[COLOR_IDX(att)]; + + int dx = x2 - x1; /* the horizontal distance of the line */ + int dy = y2 - y1; /* the vertical distance of the line */ + int dxabs = abs(dx); + int dyabs = abs(dy); + int sdx = sgn(dx); + int sdy = sgn(dy); + int x = dyabs >> 1; + int y = dxabs >> 1; + int px = x1; + int py = y1; + + if (dxabs >= dyabs) { + /* the line is more horizontal than vertical */ + for (int i = 0; i <= dxabs; i++) { + if ((1 << (px % 8)) & pat) { + drawPixel(px, py, color); + } + y += dyabs; + if (y >= dxabs) { + y -= dxabs; + py += sdy; + } + px += sdx; + } + } else { + /* the line is more vertical than horizontal */ + for (int i = 0; i <= dyabs; i++) { + if ((1 << (py % 8)) & pat) { + drawPixel(px, py, color); + } + x += dxabs; + if (x >= dyabs) { + x -= dyabs; + px += sdx; + } + py += sdy; + } + } +} + + void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags att) { for (int i=0; idrawSolidFilledRect(x, y, thickness, h, att); diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index adfd435f4..e0243e56d 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -327,6 +327,8 @@ class BitmapBuffer: public BitmapBufferBase void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0); + void drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att); + inline void drawSolidHorizontalLine(coord_t x, coord_t y, coord_t w, LcdFlags flags) { drawSolidFilledRect(x, y, w, 1, flags); diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index 640f05046..508dd1548 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -53,6 +53,12 @@ inline int divRoundClosest(const int n, const int d) return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d); } +template +inline T sgn(T a) +{ + return a > 0 ? 1 : (a < 0 ? -1 : 0); +} + // TODO duplicated code inline std::string TEXT_AT_INDEX(const char * val, uint8_t idx) { From b13c383a4ded17d60610974e4d7233572191eab9 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 17 Oct 2020 09:39:54 +0200 Subject: [PATCH 263/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 7dceba807..09a0af32b 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -678,8 +678,6 @@ coord_t BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags fla return drawText(x, y, s, flags); } - - void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, LcdFlags att) { dc->drawSolidFilledRect(x, y, thickness, h, att); From 2c0e4df2eb1467b1f73704c64c607508800737cd Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 17 Oct 2020 10:13:39 +0200 Subject: [PATCH 264/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 09a0af32b..dd86e51c8 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -126,13 +126,12 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat } } -void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att) -{ +void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att) { // Offsets - x1 += this->offsetX; - y1 += this->offsetY; - x2 += this->offsetX; - y2 += this->offsetY; + x1 += offsetX; + y1 += offsetY; + x2 += offsetX; + y2 += offsetY; pixel_t color = lcdColorTable[COLOR_IDX(att)]; From 7f1ec0206630fdd45ba375c1325978e94289481b Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 17 Oct 2020 10:23:54 +0200 Subject: [PATCH 265/714] Cosmetics --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 88 ++++++++++--------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index dd86e51c8..f66cc0ee9 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -126,53 +126,55 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat } } -void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att) { - // Offsets - x1 += offsetX; - y1 += offsetY; - x2 += offsetX; - y2 += offsetY; +void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att) +{ + // Offsets + x1 += offsetX; + y1 += offsetY; + x2 += offsetX; + y2 += offsetY; - pixel_t color = lcdColorTable[COLOR_IDX(att)]; + pixel_t color = lcdColorTable[COLOR_IDX(att)]; - int dx = x2 - x1; /* the horizontal distance of the line */ - int dy = y2 - y1; /* the vertical distance of the line */ - int dxabs = abs(dx); - int dyabs = abs(dy); - int sdx = sgn(dx); - int sdy = sgn(dy); - int x = dyabs >> 1; - int y = dxabs >> 1; - int px = x1; - int py = y1; + int dx = x2 - x1; /* the horizontal distance of the line */ + int dy = y2 - y1; /* the vertical distance of the line */ + int dxabs = abs(dx); + int dyabs = abs(dy); + int sdx = sgn(dx); + int sdy = sgn(dy); + int x = dyabs >> 1; + int y = dxabs >> 1; + int px = x1; + int py = y1; - if (dxabs >= dyabs) { - /* the line is more horizontal than vertical */ - for (int i = 0; i <= dxabs; i++) { - if ((1 << (px % 8)) & pat) { - drawPixel(px, py, color); - } - y += dyabs; - if (y >= dxabs) { - y -= dxabs; - py += sdy; - } - px += sdx; - } - } else { - /* the line is more vertical than horizontal */ - for (int i = 0; i <= dyabs; i++) { - if ((1 << (py % 8)) & pat) { - drawPixel(px, py, color); - } - x += dxabs; - if (x >= dyabs) { - x -= dyabs; - px += sdx; - } - py += sdy; - } + if (dxabs >= dyabs) { + /* the line is more horizontal than vertical */ + for (int i = 0; i <= dxabs; i++) { + if ((1 << (px % 8)) & pat) { + drawPixel(px, py, color); + } + y += dyabs; + if (y >= dxabs) { + y -= dxabs; + py += sdy; + } + px += sdx; } + } + else { + /* the line is more vertical than horizontal */ + for (int i = 0; i <= dyabs; i++) { + if ((1 << (py % 8)) & pat) { + drawPixel(px, py, color); + } + x += dxabs; + if (x >= dyabs) { + x -= dyabs; + px += sdx; + } + py += sdy; + } + } } From 53afe96cb5fee93d66ab0245626ea855b475c747 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 17 Oct 2020 10:24:27 +0200 Subject: [PATCH 266/714] Add clion codestyle --- .../libopenui/.idea/codeStyles/Project.xml | 32 +++++++++++++++++++ .../.idea/codeStyles/codeStyleConfig.xml | 5 +++ 2 files changed, 37 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/.idea/codeStyles/Project.xml create mode 100644 radio/src/thirdparty/libopenui/.idea/codeStyles/codeStyleConfig.xml diff --git a/radio/src/thirdparty/libopenui/.idea/codeStyles/Project.xml b/radio/src/thirdparty/libopenui/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..efad3ce27 --- /dev/null +++ b/radio/src/thirdparty/libopenui/.idea/codeStyles/Project.xml @@ -0,0 +1,32 @@ + + + + \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/.idea/codeStyles/codeStyleConfig.xml b/radio/src/thirdparty/libopenui/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..79ee123c2 --- /dev/null +++ b/radio/src/thirdparty/libopenui/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file From cda1c440ab4d473cb35ebab549b42ba3928ef04e Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 17 Oct 2020 10:26:57 +0200 Subject: [PATCH 267/714] cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index f66cc0ee9..3f53642a2 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -126,7 +126,7 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat } } -void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att) +void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags flags) { // Offsets x1 += offsetX; @@ -134,7 +134,7 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint x2 += offsetX; y2 += offsetY; - pixel_t color = lcdColorTable[COLOR_IDX(att)]; + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; int dx = x2 - x1; /* the horizontal distance of the line */ int dy = y2 - y1; /* the vertical distance of the line */ From de8f4f37f2312ef021013646ab2dbc0a378a8cf0 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 17 Oct 2020 10:27:47 +0200 Subject: [PATCH 268/714] cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 3f53642a2..dcd492a21 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -177,7 +177,6 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint } } - void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags att) { for (int i=0; i Date: Sat, 17 Oct 2020 10:37:10 +0200 Subject: [PATCH 269/714] cosmetics --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 078ebdf7b..501377e7e 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -41,7 +41,7 @@ void BitmapBuffer::drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color) } } -void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags att) +void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags) { APPLY_OFFSET(); @@ -53,8 +53,8 @@ void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t p } pixel_t * p = getPixelPtr(x, y); - pixel_t color = lcdColorTable[COLOR_IDX(att)]; - uint8_t opacity = 0x0F - (att >> 24); + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + uint8_t opacity = 0x0F - (flags >> 24); if (pat == SOLID) { while (w--) { @@ -76,7 +76,7 @@ void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t p } } -void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags att) +void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags flags) { APPLY_OFFSET(); @@ -100,8 +100,8 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat if (h <= 0) return; - pixel_t color = lcdColorTable[COLOR_IDX(att)]; - uint8_t opacity = 0x0F - (att >> 24); + pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + uint8_t opacity = 0x0F - (flags >> 24); if (pat == SOLID) { while (h--) { @@ -177,13 +177,13 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint } } -void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags att) +void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags flags) { for (int i=0; idrawSolidFilledRect(x, y, thickness, h, att); - dc->drawSolidFilledRect(x+w-thickness, y, thickness, h, att); - dc->drawSolidFilledRect(x, y, w, thickness, att); - dc->drawSolidFilledRect(x, y+h-thickness, w, thickness, att); + dc->drawSolidFilledRect(x, y, thickness, h, flags); + dc->drawSolidFilledRect(x+w-thickness, y, thickness, h, flags); + dc->drawSolidFilledRect(x, y, w, thickness, flags); + dc->drawSolidFilledRect(x, y+h-thickness, w, thickness, flags); } //void BitmapBuffer::drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle) From a3f4390a25c4275c6c85955b7a6070695581fa0a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 26 Oct 2020 16:12:51 +0100 Subject: [PATCH 270/714] Allow translations (static or dynamic) --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 9 +++++++-- radio/src/thirdparty/libopenui/src/libopenui_file.h | 4 +--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 7a2319596..162c2bb1b 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -23,8 +23,13 @@ #include "theme.h" #include "message_dialog.h" -extern const char STR_SDCARD[]; -extern const char STR_NO_FILES_ON_SD[]; +#if !defined(STR_SDCARD) + #define STR_SDCARD "SD" +#endif + +#if !defined(STR_NO_FILES_ON_SD) + #define STR_NO_FILES_ON_SD "No files on SD Card!" +#endif FileChoice::FileChoice(FormGroup * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): ChoiceBase(parent, rect, CHOICE_TYPE_FOLDER), diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index e75cda9ee..8bccd2ff6 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -23,7 +23,6 @@ #include #include #include "ff.h" -// #include "libopenui_defines.h" constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the dot, excluding null term. @@ -32,8 +31,7 @@ bool isExtensionMatching(const char * extension, const char * pattern, char * ma FRESULT sdReadDir(DIR * dir, FILINFO * fno, bool & firstTime); // comparison, not case sensitive. -inline bool compare_nocase(const std::string &first, const std::string &second) +inline bool compare_nocase(const std::string & first, const std::string & second) { return strcasecmp(first.c_str(), second.c_str()) < 0; } - From 7456f99b247c2d016826d538fcb7a20e31dd4acc Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 27 Oct 2020 15:19:08 +0100 Subject: [PATCH 271/714] [TextEdit] Fix --- radio/src/thirdparty/libopenui/src/form.h | 1 + radio/src/thirdparty/libopenui/src/numberedit.cpp | 9 +++++++-- radio/src/thirdparty/libopenui/src/numberedit.h | 2 +- radio/src/thirdparty/libopenui/src/textedit.h | 3 +-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index d2b989472..4105a9832 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -59,6 +59,7 @@ class FormField: public Window { virtual void setEditMode(bool newEditMode) { editMode = newEditMode; + invalidate(); } void enable(bool value = true) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 6795121f5..b1d77e217 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -125,18 +125,23 @@ void NumberEdit::onEvent(event_t event) FormField::onEvent(event); } -#if defined(SOFTWARE_KEYBOARD) +#if defined(HARDWARE_TOUCH) bool NumberEdit::onTouchEnd(coord_t, coord_t) { if (!enabled) { return true; } - if (!hasFocus()) { + if (hasFocus()) { + setEditMode(true); + } + else { setFocus(SET_FOCUS_DEFAULT); } +#if defined(SOFTWARE_KEYBOARD) NumberKeyboard::show(this); +#endif return true; } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 55ef21c27..299b3073e 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -63,7 +63,7 @@ class NumberEdit : public BaseNumberEdit { void onEvent(event_t event) override; -#if defined(SOFTWARE_KEYBOARD) +#if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t x, coord_t y) override; #endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 3688292b1..4bbc022ba 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -106,11 +106,10 @@ class TextEdit : public FormField { return 'Z'; else if (c == '0') return 'z'; - else if (c == ' ') + else if (c == ' ' || c == 0) return '>'; else return c - 1; } - }; From dc93f3a75a2aa71a5b99b680d19501f7cdd89d83 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 27 Oct 2020 15:23:39 +0100 Subject: [PATCH 272/714] Cosmetics --- radio/src/thirdparty/libopenui/src/textedit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 479db80d7..1cd860f96 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -32,7 +32,7 @@ void TextEdit::paint(BitmapBuffer * dc) if (editMode) { dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, FOCUS_COLOR); coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); -#if defined(HARDWARE_TOUCH) +#if defined(SOFTWARE_KEYBOARD) dc->drawSolidFilledRect(left + 2, 2, 2, height() - 4, FOCUS_COLOR); #else char s[] = { value[cursorPos], '\0' }; From 008c01185a462be3391239b5468280f7804b4adb Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 28 Oct 2020 17:08:42 +0100 Subject: [PATCH 273/714] Warning fix --- radio/src/thirdparty/libopenui/src/table.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 0a628fbea..3c9de1c2e 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -124,7 +124,7 @@ void Table::Body::onEvent(event_t event) } else if (event == EVT_ROTARY_LEFT) { auto table = static_cast
(parent); - if (table->windowFlags & FORWARD_SCROLL) { + if (table->getWindowFlags() & FORWARD_SCROLL) { auto index = selection - 1; if (index >= 0) { select(index, true); From b2ab26489512df796dab6810fe05f3a7e7918532 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 28 Oct 2020 17:15:17 +0100 Subject: [PATCH 274/714] VC++ fixes --- radio/src/thirdparty/libopenui/src/libopenui_file.h | 5 +++-- radio/src/thirdparty/libopenui/src/libopenui_types.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index 8bccd2ff6..e250a7a1b 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -19,9 +19,10 @@ #pragma once +#include "libopenui_defines.h" // for strcasecmp / VC++ #include -#include -#include +#include +#include #include "ff.h" constexpr uint8_t LEN_FILE_EXTENSION_MAX = 5; // longest used, including the dot, excluding null term. diff --git a/radio/src/thirdparty/libopenui/src/libopenui_types.h b/radio/src/thirdparty/libopenui/src/libopenui_types.h index d9b3177c3..4be8f4390 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_types.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_types.h @@ -19,7 +19,7 @@ #pragma once -#include +#include typedef int coord_t; From 94039e60fcda6a19756c9d081e6e370dc2b065f7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 28 Oct 2020 17:19:04 +0100 Subject: [PATCH 275/714] VC++ fixes --- radio/src/thirdparty/libopenui/src/table.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 3c9de1c2e..4df8b9090 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -103,7 +103,7 @@ void Table::Body::onEvent(event_t event) } if (event == EVT_ROTARY_RIGHT) { auto table = static_cast
(parent); - if (table->windowFlags & FORWARD_SCROLL) { + if (table->getWindowFlags() & FORWARD_SCROLL) { auto index = selection + 1; if (index < int(lines.size())) { select(index, true); From 8c6550f8f17d7a6de2affb26aa494c4cd465328c Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 29 Oct 2020 11:49:55 +0100 Subject: [PATCH 276/714] Cosmetics --- radio/src/thirdparty/libopenui/src/basenumberedit.h | 5 +++-- radio/src/thirdparty/libopenui/src/choice.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 12e5694a2..93aa67ad5 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -21,7 +21,8 @@ #include "form.h" -class BaseNumberEdit : public FormField { +class BaseNumberEdit : public FormField +{ public: BaseNumberEdit(Window * parent, const rect_t &rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0): @@ -79,7 +80,7 @@ class BaseNumberEdit : public FormField { invalidate(); } - void setSetValueHandler(std::function handler) + void setSetValueHandler(std::function handler) { _setValue = std::move(handler); } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 2344923a4..f8d67b95c 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -76,7 +76,7 @@ class Choice : public ChoiceBase { bool onTouchEnd(coord_t x, coord_t y) override; #endif - void setSetValueHandler(std::function handler) + void setSetValueHandler(std::function handler) { setValue = std::move(handler); } From 6c489a81d175f28f4c5307cde9efcfd3961e23d5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 29 Oct 2020 11:59:45 +0100 Subject: [PATCH 277/714] Remove more int16_t --- .../thirdparty/libopenui/src/basenumberedit.h | 26 +++++++++---------- radio/src/thirdparty/libopenui/src/choice.cpp | 16 ++++++------ radio/src/thirdparty/libopenui/src/choice.h | 25 +++++++++--------- .../src/thirdparty/libopenui/src/filechoice.h | 3 ++- .../thirdparty/libopenui/src/numberedit.cpp | 2 +- .../src/thirdparty/libopenui/src/numberedit.h | 10 +++---- radio/src/thirdparty/libopenui/src/roller.h | 8 +++--- radio/src/thirdparty/libopenui/src/slider.h | 8 +++--- radio/src/thirdparty/libopenui/src/table.h | 3 ++- 9 files changed, 52 insertions(+), 49 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 93aa67ad5..97061798c 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -24,8 +24,8 @@ class BaseNumberEdit : public FormField { public: - BaseNumberEdit(Window * parent, const rect_t &rect, int32_t vmin, int32_t vmax, - std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0): + BaseNumberEdit(Window * parent, const rect_t &rect, int vmin, int vmax, + std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0): FormField(parent, rect, windowFlags, textFlags), vmin(vmin), vmax(vmax), @@ -34,17 +34,17 @@ class BaseNumberEdit : public FormField { } - void setMin(int32_t value) + void setMin(int value) { vmin = value; } - void setMax(int32_t value) + void setMax(int value) { vmax = value; } - void setDefault(int32_t value) + void setDefault(int value) { vdefault = value; } @@ -64,7 +64,7 @@ class BaseNumberEdit : public FormField return vdefault; } - void setStep(int32_t value) + void setStep(int value) { step = value; } @@ -74,7 +74,7 @@ class BaseNumberEdit : public FormField return step; } - void setValue(int32_t value) + void setValue(int value) { _setValue(limit(vmin, value, vmax)); invalidate(); @@ -91,11 +91,11 @@ class BaseNumberEdit : public FormField } protected: - int32_t vdefault = 0; - int32_t vmin; - int32_t vmax; - int32_t step = 1; - std::function _getValue; - std::function _setValue; + int vdefault = 0; + int vmin; + int vmax; + int step = 1; + std::function _getValue; + std::function _setValue; }; diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 75ba22579..7a01bc4c5 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -21,8 +21,8 @@ #include "menu.h" #include "theme.h" -Choice::Choice(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : +Choice::Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, + std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), vmin(vmin), vmax(vmax), @@ -31,8 +31,8 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vm { } -Choice::Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int16_t vmin, int16_t vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : +Choice::Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int vmin, int vmax, + std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), vmin(vmin), vmax(vmax), @@ -42,8 +42,8 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, const char * const value setValues(values); } -Choice::Choice(FormGroup * parent, const rect_t & rect, std::vector values, int16_t vmin, int16_t vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : +Choice::Choice(FormGroup * parent, const rect_t & rect, std::vector values, int vmin, int vmax, + std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), values(std::move(values)), vmin(vmin), @@ -53,8 +53,8 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, std::vector { } -Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : +Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int vmin, int vmax, + std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), vmin(vmin), vmax(vmax), diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index f8d67b95c..0a146f62a 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -27,7 +27,8 @@ enum ChoiceType { CHOICE_TYPE_FOLDER, }; -class ChoiceBase : public FormField { +class ChoiceBase : public FormField +{ public: ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0): FormField(parent, rect, windowFlags), @@ -46,10 +47,10 @@ class ChoiceBase : public FormField { class Choice : public ChoiceBase { public: - Choice(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup * parent, const rect_t & rect, std::vector values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup * parent, const rect_t & rect, const char * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + Choice(FormGroup * parent, const rect_t & rect, std::vector values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + Choice(FormGroup * parent, const rect_t & rect, const char * values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); void addValue(const char * value); @@ -133,13 +134,13 @@ class Choice : public ChoiceBase { menuTitle = std::move(value); } - void setMin(int16_t value) + void setMin(int value) { vmin = value; invalidate(); } - void setMax(int16_t value) + void setMax(int value) { vmax = value; invalidate(); @@ -147,13 +148,13 @@ class Choice : public ChoiceBase { protected: std::vector values; - int16_t vmin = 0; - int16_t vmax = 0; + int vmin = 0; + int vmax = 0; std::string menuTitle; - std::function getValue; - std::function setValue; + std::function getValue; + std::function setValue; std::function isValueAvailable; - std::function textHandler; + std::function textHandler; virtual void openMenu(); }; diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index 0157ff68d..b1c0ee514 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -22,7 +22,8 @@ #include "choice.h" #include -class FileChoice: public ChoiceBase { +class FileChoice: public ChoiceBase +{ public: FileChoice(FormGroup * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index b1d77e217..1a0e9e417 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -23,7 +23,7 @@ #include "keyboard_number.h" #endif -NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, WindowFlags windowFlags, LcdFlags textFlags): +NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags, LcdFlags textFlags): BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags) { } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 299b3073e..67ff5da90 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -22,10 +22,10 @@ #include "basenumberedit.h" #include -class NumberEdit : public BaseNumberEdit { +class NumberEdit : public BaseNumberEdit +{ public: - NumberEdit(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, - std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); + NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -56,7 +56,7 @@ class NumberEdit : public BaseNumberEdit { zeroText = std::move(value); } - void setDisplayHandler(std::function function) + void setDisplayHandler(std::function function) { displayFunction = std::move(function); } @@ -70,7 +70,7 @@ class NumberEdit : public BaseNumberEdit { void onFocusLost() override; protected: - std::function displayFunction; + std::function displayFunction; std::string prefix; std::string suffix; std::string zeroText; diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index c70184224..b4bf13fc9 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -33,13 +33,13 @@ constexpr coord_t SCROLL_POSITION_INVALIDATED = INT_MIN; class Roller: public Choice { public: - Roller(FormGroup * parent, const rect_t & rect, const char * label, const char * const * values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): + Roller(FormGroup * parent, const rect_t & rect, const char * label, const char * const * values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): Choice(parent, rect, values, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR) { init(label); } - Roller(FormGroup * parent, const rect_t & rect, const char * label, std::vector values, int16_t vmin, int16_t vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): + Roller(FormGroup * parent, const rect_t & rect, const char * label, std::vector values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = ROLLER_SEPARATION_LINES): Choice(parent, rect, std::move(values), vmin, vmax, std::move(getValue), std::move(setValue), windowFlags | NO_SCROLLBAR) { init(label); @@ -127,14 +127,14 @@ class Roller: public Choice } // virtual not needed until now - void setMin(int16_t value) + void setMin(int value) { Choice::setMin(value); invalidateScrollPosition(); } // virtual not needed until now - void setMax(int16_t value) + void setMax(int value) { Choice::setMax(value); invalidateScrollPosition(); diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index 4b35c1fd0..eb14b3e5b 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -23,7 +23,7 @@ class Slider: public FormField { public: - Slider(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue): + Slider(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue): FormField(parent, rect), vmin(vmin), vmax(vmax), @@ -39,7 +39,7 @@ class Slider: public FormField { } #endif - void setValue(int32_t value) + void setValue(int value) { _setValue(limit(vmin, value, vmax)); invalidate(); @@ -64,7 +64,7 @@ class Slider: public FormField { int32_t vmin; int32_t vmax; bool sliding = false; - std::function getValue; - std::function _setValue; + std::function getValue; + std::function _setValue; }; diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index a00a1812c..0e3c248cf 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -25,7 +25,8 @@ #include "libopenui_config.h" #include "font.h" -class Table: public FormField { +class Table: public FormField +{ public: class Cell { public: From 3f8fb008911132f2f44ea1f1c3f9e82a4b0caec0 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 29 Oct 2020 12:10:14 +0100 Subject: [PATCH 278/714] VC++ warning fix --- radio/src/thirdparty/libopenui/src/libopenui_helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index 508dd1548..325d3c0df 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -42,7 +42,7 @@ inline T max(T a, T b) template inline T limit(T vmin, T x, T vmax) { - return min(max(vmin, x), vmax); + return ::min(::max(vmin, x), vmax); } inline int divRoundClosest(const int n, const int d) From 4d1e2be9274198fb184c02a5783361f657c84930 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 29 Oct 2020 14:55:09 +0100 Subject: [PATCH 279/714] Another int32_t removed --- radio/src/thirdparty/libopenui/src/choice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 0a146f62a..4932c20e5 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -124,7 +124,7 @@ class Choice : public ChoiceBase { return getIndexFromValue(vmax + 1); } - void setTextHandler(std::function handler) + void setTextHandler(std::function handler) { textHandler = std::move(handler); } From cf3c2c3000a2a5f559bb4c1ce393dce1a551175f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 31 Oct 2020 18:03:26 +0100 Subject: [PATCH 280/714] Another int32_t removed --- radio/src/thirdparty/libopenui/src/slider.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index eb14b3e5b..c144ec4bc 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -61,8 +61,8 @@ class Slider: public FormField { protected: int value(coord_t x) const; - int32_t vmin; - int32_t vmax; + int vmin; + int vmax; bool sliding = false; std::function getValue; std::function _setValue; From 66ee2a26b6b94744b8d3afc3ff05dd69a7115790 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 2 Nov 2020 09:55:43 +0100 Subject: [PATCH 281/714] [Menu] Focus is reverted before the menu is destroyed when pressing a line --- radio/src/thirdparty/libopenui/src/menu.cpp | 1 + radio/src/thirdparty/libopenui/src/menu.h | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index f4782c67d..4bd56205e 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -83,6 +83,7 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) menu->invalidate(); } else { + menu->revertPreviousFocus(); lines[index].onPress(); } } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index e21c30497..20960c31e 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -27,7 +27,8 @@ class Menu; class MenuWindowContent; -class MenuBody : public Window { +class MenuBody : public Window +{ friend class MenuWindowContent; friend class Menu; @@ -42,7 +43,7 @@ class MenuBody : public Window { { } - MenuLine(std::function drawLine, std::function onPress, std::function isChecked): + MenuLine(std::function drawLine, std::function onPress, std::function isChecked): drawLine(std::move(drawLine)), onPress(std::move(onPress)), isChecked(std::move(isChecked)) @@ -96,13 +97,13 @@ class MenuBody : public Window { void addLine(const std::string & text, std::function onPress, std::function isChecked) { - lines.emplace_back(text, onPress, isChecked); + lines.emplace_back(text, std::move(onPress), std::move(isChecked)); invalidate(); } - void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) + void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) { - lines.emplace_back(drawLine, onPress, isChecked); + lines.emplace_back(std::move(drawLine), std::move(onPress), std::move(isChecked)); invalidate(); } @@ -131,7 +132,8 @@ class MenuBody : public Window { inline Menu * getParentMenu(); }; -class MenuWindowContent: public ModalWindowContent { +class MenuWindowContent: public ModalWindowContent +{ friend class Menu; public: @@ -155,7 +157,8 @@ class MenuWindowContent: public ModalWindowContent { MenuBody body; }; -class Menu: public ModalWindow { +class Menu: public ModalWindow +{ friend class MenuBody; public: From 8aeae1d72f23ad73f69317ae2e2e191b8d81f75f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Nov 2020 16:16:06 +0100 Subject: [PATCH 282/714] [Menu] Missing revert previous focus --- radio/src/thirdparty/libopenui/src/menu.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 4bd56205e..965ad4708 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -49,12 +49,16 @@ void MenuBody::onEvent(event_t event) select(0); } else { - lines[selectedIndex].onPress(); Menu * menu = getParentMenu(); - if (menu->multiple) + if (menu->multiple) { + lines[selectedIndex].onPress(); menu->invalidate(); - else + } + else { + menu->revertPreviousFocus(); + lines[selectedIndex].onPress(); menu->deleteLater(); + } } } } From 25a3a20642cdc177de0c3770e6681a6579e1efa7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 4 Nov 2020 16:48:53 +0100 Subject: [PATCH 283/714] [Window] deleteLater made virtual --- radio/src/thirdparty/libopenui/src/modal_window.h | 9 +++++---- radio/src/thirdparty/libopenui/src/window.h | 8 +++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 4240fe43e..5219818af 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -33,7 +33,7 @@ class ModalWindow: public Window } #endif - void deleteLater() + void deleteLater(bool detach=true) override { if (_deleted) return; @@ -42,7 +42,7 @@ class ModalWindow: public Window previousFocus->setFocus(SET_FOCUS_DEFAULT); } - Window::deleteLater(); + Window::deleteLater(detach); } void paint(BitmapBuffer * dc) override; @@ -87,11 +87,12 @@ class ModalWindow: public Window } protected: - Window * previousFocus = nullptr; + Window * previousFocus; bool closeAllowed = true; }; -class ModalWindowContent: public Window { +class ModalWindowContent: public Window +{ public: explicit ModalWindowContent(ModalWindow * parent, const rect_t & rect): Window(parent, rect, OPAQUE) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index efd6b5122..8af450f38 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -50,14 +50,16 @@ constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6u; constexpr WindowFlags PUSH_FRONT = 1u << 7u; constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; -enum SetFocusFlag { +enum SetFocusFlag +{ SET_FOCUS_DEFAULT, SET_FOCUS_FORWARD, SET_FOCUS_BACKWARD, SET_FOCUS_FIRST }; -class Window { +class Window +{ friend class GridLayout; public: @@ -135,7 +137,7 @@ class Window { focusHandler = std::move(handler); } - void deleteLater(bool detach=true); + virtual void deleteLater(bool detach=true); void clear(); From 8ff85a33b62bf90233e2d1200af745a4b0f16681 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 5 Nov 2020 15:36:00 +0100 Subject: [PATCH 284/714] Remove adherence from libopenui_file to libopenui_defines --- .../libopenui/src/libopenui_compat.h | 34 +++++++++++++++++++ .../libopenui/src/libopenui_defines.h | 15 +------- .../thirdparty/libopenui/src/libopenui_file.h | 2 +- 3 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/libopenui_compat.h diff --git a/radio/src/thirdparty/libopenui/src/libopenui_compat.h b/radio/src/thirdparty/libopenui/src/libopenui_compat.h new file mode 100644 index 000000000..ac94b9670 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/libopenui_compat.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#pragma once + +#if defined(__MINGW32__) || !defined(__GNUC__) + #include + #include + #define sleep(x) Sleep(x) + #define strcasecmp _stricmp + #define strncasecmp _strnicmp + #define chdir _chdir + #define getcwd _getcwd + // remove windows default definitions + #undef OPAQUE + #undef RGB + #undef EXTERN_C +#endif diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 86ad01e18..3606bd4b1 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -20,6 +20,7 @@ #pragma once #include "libopenui_types.h" +#include "libopenui_compat.h" /* obsolete flags */ #define BLINK 0 @@ -73,20 +74,6 @@ #define GET_BLUE(color) \ (((color) & 0x001F) << 3) -#if defined(__MINGW32__) || !defined(__GNUC__) - #include - #include - #define sleep(x) Sleep(x) - #define strcasecmp _stricmp - #define strncasecmp _strnicmp - #define chdir _chdir - #define getcwd _getcwd - // remove windows default definitions - #undef OPAQUE - #undef RGB - #undef EXTERN_C -#endif - #define OPACITY_MAX 0x0Fu #define OPACITY(value) ((value) << 24u) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_file.h b/radio/src/thirdparty/libopenui/src/libopenui_file.h index e250a7a1b..62f36bfa8 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_file.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_file.h @@ -19,7 +19,7 @@ #pragma once -#include "libopenui_defines.h" // for strcasecmp / VC++ +#include "libopenui_compat.h" // for strcasecmp / VC++ #include #include #include From d4dbfd4d1699227819f208a1c7bb3e3229dcfcaa Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 12 Nov 2020 12:57:14 +0100 Subject: [PATCH 285/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 501377e7e..e3ef9ed3f 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -179,11 +179,11 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags flags) { - for (int i=0; i Date: Thu, 12 Nov 2020 17:13:23 +0100 Subject: [PATCH 286/714] Cosmetics --- .../thirdparty/libopenui/src/numberedit.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 1a0e9e417..ab2b0c0af 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -44,12 +44,22 @@ void NumberEdit::paint(BitmapBuffer * dc) else textColor = DISABLE_COLOR; - if (displayFunction) + if (displayFunction) { displayFunction(dc, textColor, value); - else if (value == 0 && !zeroText.empty()) + } + else if (value == 0 && !zeroText.empty()) { dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | textFlags); - else - dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); + if (textFlags & RIGHT) + dc->drawText(rect.w - FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | textFlags); + else + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | textFlags); + } + else { + if (textFlags & RIGHT) + dc->drawNumber(rect.w - FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); + else + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); + } } void NumberEdit::onEvent(event_t event) From e5fa0eb078e5d729717f9604a25d774e82bf8f3b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 12 Nov 2020 17:24:55 +0100 Subject: [PATCH 287/714] Missing getInnerWidth() function --- radio/src/thirdparty/libopenui/src/window.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 8af450f38..20607c14a 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -237,6 +237,11 @@ class Window return rect; } + coord_t getInnerWidth() const + { + return innerWidth; + } + void setInnerWidth(coord_t w) { innerWidth = w; From 590e926b956694329dee609d1ef16489f76cb12b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 12 Nov 2020 17:40:17 +0100 Subject: [PATCH 288/714] Horizontal scrollbars --- radio/src/thirdparty/libopenui/src/window.cpp | 13 +++++++++++++ radio/src/thirdparty/libopenui/src/window.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 076645b5d..1d8933311 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -483,3 +483,16 @@ void Window::drawVerticalScrollbar(BitmapBuffer * dc) dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, SCROLLBAR_COLOR); } } + +void Window::drawHorizontalScrollbar(BitmapBuffer * dc) +{ + if (innerWidth > rect.w) { + coord_t xofs = divRoundClosest(rect.w * scrollPositionX, innerWidth); + coord_t xwdth = divRoundClosest(rect.w * rect.w, innerWidth); + if (xwdth < 15) + xwdth = 15; + if (xwdth + xofs > rect.w) + xwdth = rect.w - xofs; + dc->drawSolidFilledRect(scrollPositionX + xofs, rect.h - SCROLLBAR_WIDTH, xwdth, SCROLLBAR_WIDTH, SCROLLBAR_COLOR); + } +} diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 20607c14a..65332561d 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -329,6 +329,8 @@ class Window void drawVerticalScrollbar(BitmapBuffer * dc); + void drawHorizontalScrollbar(BitmapBuffer * dc); + virtual void onEvent(event_t event); void adjustInnerHeight(); From f4ccc332674f82a7772c3df296032033caf5e023 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 18 Nov 2020 09:56:50 +0100 Subject: [PATCH 289/714] Added a method MainWindow::needsRefresh --- radio/src/thirdparty/libopenui/src/mainwindow.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index f51a0d56f..4091beb65 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -52,6 +52,11 @@ class MainWindow: public Window { void invalidate(const rect_t & rect) override; + bool needsRefresh() const + { + return invalidatedRect.w > 0; + } + bool refresh(); void run(bool trash=true); From c04826f9ba939e645ac541ec3cc1dab3f7bbd658 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 24 Nov 2020 17:48:42 +0100 Subject: [PATCH 290/714] Added Layer function (get rid of previous focus functions) --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + radio/src/thirdparty/libopenui/src/layer.cpp | 22 +++++++ radio/src/thirdparty/libopenui/src/layer.h | 65 +++++++++++++++++++ .../src/thirdparty/libopenui/src/mainwindow.h | 6 +- radio/src/thirdparty/libopenui/src/menu.cpp | 2 - .../thirdparty/libopenui/src/modal_window.cpp | 15 ++++- .../thirdparty/libopenui/src/modal_window.h | 26 +------- 7 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/layer.cpp create mode 100644 radio/src/thirdparty/libopenui/src/layer.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index f6712eea4..98bcf5f78 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -15,6 +15,7 @@ set(LIBOPENUI_SRC libopenui_file.cpp bitmapbuffer.cpp window.cpp + layer.cpp form.cpp button.cpp static.cpp diff --git a/radio/src/thirdparty/libopenui/src/layer.cpp b/radio/src/thirdparty/libopenui/src/layer.cpp new file mode 100644 index 000000000..b40c72bad --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/layer.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "layer.h" + +std::list Layer::stack; diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h new file mode 100644 index 000000000..d0c8692c5 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#pragma once + +#include "window.h" + +class Layer +{ + public: + explicit Layer(Window * main): + main(main) + { + } + + Window * main; + Window * focus = nullptr; + + static std::list stack; + + static void push(Window * window) + { + if (!stack.empty()) { + Layer &parentLayer = stack.back(); + parentLayer.focus = Window::getFocus(); + } + stack.emplace_back(Layer(window)); + } + + static void pop(Window * window) + { + if (stack.back().main == window) { + stack.pop_back(); + const auto & back = stack.back(); + if (back.focus) { + back.focus->setFocus(SET_FOCUS_DEFAULT); + } + } + else { + // TODO it would be better to do it in reverse order + for (auto it = stack.begin(); it != stack.end(); it++) { + if (it->main == window) { + stack.erase(it); + break; + } + } + } + } +}; diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 4091beb65..375ec052e 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -20,15 +20,17 @@ #pragma once #include -#include "window.h" +#include "layer.h" #include "bitmapbuffer.h" -class MainWindow: public Window { +class MainWindow: public Window +{ public: MainWindow(): Window(nullptr, {0, 0, LCD_W, LCD_H}), invalidatedRect(rect) { + Layer::push(this); } ~MainWindow() override diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 965ad4708..f09629896 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -55,7 +55,6 @@ void MenuBody::onEvent(event_t event) menu->invalidate(); } else { - menu->revertPreviousFocus(); lines[selectedIndex].onPress(); menu->deleteLater(); } @@ -87,7 +86,6 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) menu->invalidate(); } else { - menu->revertPreviousFocus(); lines[index].onPress(); } } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 303071027..6d5b73d44 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -20,11 +20,22 @@ #include "modal_window.h" #include "font.h" +#include "layer.h" ModalWindow::ModalWindow(Window * parent): - Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}), - previousFocus(focusWindow) + Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}) { + Layer::push(this); +} + +void ModalWindow::deleteLater(bool detach) +{ + if (_deleted) + return; + + Layer::pop(this); + + Window::deleteLater(detach); } void ModalWindow::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 5219818af..3fb396fb4 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -33,17 +33,7 @@ class ModalWindow: public Window } #endif - void deleteLater(bool detach=true) override - { - if (_deleted) - return; - - if (previousFocus && (!focusWindow || focusWindow->isChild(this))) { - previousFocus->setFocus(SET_FOCUS_DEFAULT); - } - - Window::deleteLater(detach); - } + void deleteLater(bool detach=true) override; void paint(BitmapBuffer * dc) override; @@ -73,21 +63,7 @@ class ModalWindow: public Window closeAllowed = value; } - void forgetPreviousFocus() - { - previousFocus = nullptr; - } - - void revertPreviousFocus() - { - if (previousFocus) { - previousFocus->setFocus(SET_FOCUS_DEFAULT); - previousFocus = nullptr; - } - } - protected: - Window * previousFocus; bool closeAllowed = true; }; From f90e5b6d71fc8b7f9f8855daf835cd6fa40373e8 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 25 Nov 2020 10:01:37 +0100 Subject: [PATCH 291/714] More debug added --- radio/src/thirdparty/libopenui/src/layer.h | 9 +++++++++ radio/src/thirdparty/libopenui/src/menu.h | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index d0c8692c5..1422dc64e 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -41,6 +41,10 @@ class Layer parentLayer.focus = Window::getFocus(); } stack.emplace_back(Layer(window)); + TRACE_WINDOWS("New layer added for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); + for (auto layer: stack) { + TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); + } } static void pop(Window * window) @@ -61,5 +65,10 @@ class Layer } } } + + TRACE_WINDOWS("Layer removed for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); + for (auto layer: stack) { + TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); + } } }; diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 20960c31e..2af7ca085 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -163,6 +163,13 @@ class Menu: public ModalWindow public: explicit Menu(Window * parent, bool multiple = false); + +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "Menu"; + } +#endif void setCancelHandler(std::function handler) { From 7a9c3405aa1db90c4fa5684e22b41846f7ca8052 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 25 Nov 2020 10:04:59 +0100 Subject: [PATCH 292/714] Missing ifdef --- radio/src/thirdparty/libopenui/src/layer.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index 1422dc64e..379a8a267 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -41,10 +41,13 @@ class Layer parentLayer.focus = Window::getFocus(); } stack.emplace_back(Layer(window)); + +#if defined(DEBUG_WINDOWS) TRACE_WINDOWS("New layer added for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); for (auto layer: stack) { TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); } +#endif } static void pop(Window * window) @@ -66,9 +69,11 @@ class Layer } } +#if defined(DEBUG_WINDOWS) TRACE_WINDOWS("Layer removed for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); for (auto layer: stack) { TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); } +#endif } }; From 322055b34a9609d1701eea1ea5f678a315c9b5b7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 25 Nov 2020 23:48:45 +0100 Subject: [PATCH 293/714] Windows destruction refactoring --- radio/src/thirdparty/libopenui/src/carousel.h | 19 ++++++++++++++----- radio/src/thirdparty/libopenui/src/dialog.h | 8 +++++--- radio/src/thirdparty/libopenui/src/menu.h | 5 +++-- .../thirdparty/libopenui/src/modal_window.cpp | 4 ++-- .../thirdparty/libopenui/src/modal_window.h | 2 +- radio/src/thirdparty/libopenui/src/table.cpp | 3 +++ radio/src/thirdparty/libopenui/src/table.h | 14 ++++++++++---- radio/src/thirdparty/libopenui/src/window.cpp | 16 +++++++++++----- radio/src/thirdparty/libopenui/src/window.h | 2 +- 9 files changed, 50 insertions(+), 23 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index 1bcbeeaef..661fa58b5 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -22,7 +22,8 @@ #include #include "button.h" -class CarouselItem { +class CarouselItem +{ public: CarouselItem(Window * front, Window * back): front(front), @@ -43,7 +44,8 @@ class CarouselItem { std::function selectHandler; }; -class CarouselWindow: public Window { +class CarouselWindow: public Window +{ friend class Carousel; public: @@ -53,9 +55,13 @@ class CarouselWindow: public Window { { } - ~CarouselWindow() override + void deleteLater(bool detach = true, bool trash = true) override { + if (_deleted) + return; + clear(); + Window::deleteLater(detach, trash); } void addItem(CarouselItem * item) @@ -66,13 +72,15 @@ class CarouselWindow: public Window { void clear() { + // children will be deleted later (front and back) + children.clear(); + for (auto & item: items) { item->front->deleteLater(); item->back->deleteLater(); delete item; } items.clear(); - Window::clear(); } void select(int index, bool scroll = true) @@ -92,7 +100,8 @@ class CarouselWindow: public Window { void update(); }; -class Carousel: public Window { +class Carousel: public Window +{ public: Carousel(Window * parent, const rect_t & rect): Window(parent, rect, NO_SCROLLBAR) diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 3e8816aae..c0282b365 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -24,15 +24,17 @@ class Dialog; -class DialogWindowContent: public ModalWindowContent { +class DialogWindowContent: public ModalWindowContent +{ friend class Dialog; public: DialogWindowContent(Dialog * parent, const rect_t & rect); - ~DialogWindowContent() override + void deleteLater(bool detach = true, bool trash = true) override { - form.detach(); + form.deleteLater(true, false); + ModalWindowContent::deleteLater(detach, trash); } #if defined(DEBUG_WINDOWS) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 2af7ca085..ff9a7d88d 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -139,9 +139,10 @@ class MenuWindowContent: public ModalWindowContent public: explicit MenuWindowContent(Menu * parent); - ~MenuWindowContent() override + void deleteLater(bool detach = true, bool trash = true) override { - body.detach(); + body.deleteLater(true, false); + ModalWindowContent::deleteLater(detach, trash); } #if defined(DEBUG_WINDOWS) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 6d5b73d44..5ddf49cc9 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -28,14 +28,14 @@ ModalWindow::ModalWindow(Window * parent): Layer::push(this); } -void ModalWindow::deleteLater(bool detach) +void ModalWindow::deleteLater(bool detach, bool trash) { if (_deleted) return; Layer::pop(this); - Window::deleteLater(detach); + Window::deleteLater(detach, trash); } void ModalWindow::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 3fb396fb4..bdf6ba125 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -33,7 +33,7 @@ class ModalWindow: public Window } #endif - void deleteLater(bool detach=true) override; + void deleteLater(bool detach = true, bool trash = true) override; void paint(BitmapBuffer * dc) override; diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 4df8b9090..06764889b 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -39,6 +39,9 @@ void Table::Body::checkEvents() { Window::checkEvents(); + if (_deleted) + return; + coord_t y = 0; for (auto line: lines) { coord_t x = 10; diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 0e3c248cf..136622ef1 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -140,7 +140,8 @@ class Table: public FormField void paint(BitmapBuffer * dc) override; }; - class Body: public Window { + class Body: public Window + { friend class Table; public: @@ -244,10 +245,15 @@ class Table: public FormField } #endif - ~Table() override + void deleteLater(bool detach = true, bool trash = true) override { - header.detach(); - body.detach(); + if (_deleted) + return; + + header.deleteLater(true, false); + body.deleteLater(true, false); + + FormField::deleteLater(detach, trash); } void setColumnsWidth(const coord_t values[]) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 1d8933311..548bbac32 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -67,13 +67,15 @@ void Window::detach() } } -void Window::deleteLater(bool detach) +void Window::deleteLater(bool detach, bool trash) { if (_deleted) return; TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); + _deleted = true; + if (static_cast(focusWindow) == static_cast(this)) { focusWindow = nullptr; } @@ -83,13 +85,15 @@ void Window::deleteLater(bool detach) else parent = nullptr; + deleteChildren(); + if (closeHandler) { closeHandler(); } - trash.push_back(this); - - _deleted = true; + if (trash) { + Window::trash.push_back(this); + } } void Window::clear() @@ -325,7 +329,9 @@ void Window::checkEvents() { auto copy = children; for (auto child: copy) { - child->checkEvents(); + if (!child->deleted()) { + child->checkEvents(); + } } if (this == Window::focusWindow) { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 65332561d..d30cc2d62 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -137,7 +137,7 @@ class Window focusHandler = std::move(handler); } - virtual void deleteLater(bool detach=true); + virtual void deleteLater(bool detach = true, bool trash = true); void clear(); From 7da78ab5c4f561abae5e07f125619cbcd5c15110 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 26 Nov 2020 09:56:58 +0100 Subject: [PATCH 294/714] Update to latest libopenui --- radio/src/thirdparty/libopenui/src/dialog.h | 3 +++ radio/src/thirdparty/libopenui/src/menu.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index c0282b365..36140ec08 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -33,6 +33,9 @@ class DialogWindowContent: public ModalWindowContent void deleteLater(bool detach = true, bool trash = true) override { + if (_deleted) + return; + form.deleteLater(true, false); ModalWindowContent::deleteLater(detach, trash); } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index ff9a7d88d..de9c65a36 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -141,6 +141,9 @@ class MenuWindowContent: public ModalWindowContent void deleteLater(bool detach = true, bool trash = true) override { + if (_deleted) + return; + body.deleteLater(true, false); ModalWindowContent::deleteLater(detach, trash); } From ac5f0505abef0785e9214357a89c6c5a70b842fa Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 26 Nov 2020 10:26:46 +0100 Subject: [PATCH 295/714] Update to latest libopenui --- radio/src/thirdparty/libopenui/src/button.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 1eb14fc27..aa04227b6 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -25,7 +25,8 @@ constexpr WindowFlags BUTTON_BACKGROUND = FORM_FLAGS_LAST << 1u; constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2u; constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3u; -class Button: public FormField { +class Button: public FormField +{ public: Button(FormGroup * parent, const rect_t & rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0): FormField(parent, rect, windowFlags), @@ -83,7 +84,8 @@ class Button: public FormField { void onPress(); }; -class TextButton: public Button { +class TextButton: public Button +{ public: TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND): Button(parent, rect, std::move(pressHandler), windowFlags | OPAQUE), @@ -112,7 +114,8 @@ class TextButton: public Button { std::string text; }; -class IconButton: public Button { +class IconButton: public Button +{ public: IconButton(FormGroup * parent, const rect_t & rect, uint8_t icon, std::function pressHandler, WindowFlags flags = 0): Button(parent, rect, std::move(pressHandler), flags), @@ -132,4 +135,3 @@ class IconButton: public Button { protected: uint8_t icon; }; - From 38641c7cfc737e4b4ac08a48bc8df343c9dfdfbf Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 26 Nov 2020 12:42:58 +0100 Subject: [PATCH 296/714] New focus helpers --- radio/src/thirdparty/libopenui/src/form.cpp | 9 ++--- radio/src/thirdparty/libopenui/src/form.h | 42 +++++++++++++++++++-- radio/src/thirdparty/libopenui/src/window.h | 5 +++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 47d0e7e46..708ed35cb 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -210,19 +210,16 @@ void FormGroup::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormGroup").c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { - if (first) - first->setFocus(SET_FOCUS_FIRST); + setFocusOnFirstVisibleField(SET_FOCUS_FIRST); } else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus() && !(windowFlags & FORM_FORWARD_FOCUS)) { setFocus(SET_FOCUS_DEFAULT); // opentx - model - timers settings } else if (event == EVT_ROTARY_RIGHT && !next) { - if (first) - first->setFocus(SET_FOCUS_FIRST); + setFocusOnFirstVisibleField(SET_FOCUS_FIRST); } else if (event == EVT_ROTARY_LEFT && !previous) { - if (last) - last->setFocus(SET_FOCUS_BACKWARD); + setFocusOnLastVisibleField(SET_FOCUS_BACKWARD); } else { FormField::onEvent(event); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 4105a9832..d2ccdd437 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -27,7 +27,8 @@ constexpr WindowFlags FORM_BORDER_FOCUS_ONLY = WINDOW_FLAGS_LAST << 3; constexpr WindowFlags FORM_NO_BORDER = WINDOW_FLAGS_LAST << 4; constexpr WindowFlags FORM_FLAGS_LAST = FORM_NO_BORDER; -class FormField: public Window { +class FormField: public Window +{ public: FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); @@ -105,7 +106,8 @@ class FormField: public Window { bool enabled = true; }; -class FormGroup: public FormField { +class FormGroup: public FormField +{ public: FormGroup(Window * parent, const rect_t & rect, WindowFlags windowflags = 0) : FormField(parent, rect, windowflags) @@ -138,6 +140,38 @@ class FormGroup: public FormField { last = field; } + FormField * getFirstField() const + { + return first; + } + + FormField * getLastField() const + { + return last; + } + + void setFocusOnFirstVisibleField(uint8_t flag) const + { + auto field = getFirstField(); + while (field && !field->isInsideParentScrollingArea()) { + field = field->getNextField(); + } + if (field) { + field->setFocus(flag); + } + } + + void setFocusOnLastVisibleField(uint8_t flag) const + { + auto field = getLastField(); + while (field && !field->isInsideParentScrollingArea()) { + field = field->getPreviousField(); + } + if (field) { + field->setFocus(flag); + } + } + protected: FormField * first = nullptr; FormField * last = nullptr; @@ -149,7 +183,8 @@ class FormGroup: public FormField { void paint(BitmapBuffer * dc) override; }; -class FormWindow: public FormGroup { +class FormWindow: public FormGroup +{ public: FormWindow(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0) : FormGroup(parent, rect, windowFlags) @@ -172,4 +207,3 @@ class FormWindow: public FormGroup { void onEvent(event_t event) override; #endif }; - diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index d30cc2d62..08e013840 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -327,6 +327,11 @@ class Window return parent && parent->isChildVisible(this); } + bool isInsideParentScrollingArea() const + { + return parent && right() >= parent->getScrollPositionX() && left() <= parent->getScrollPositionX() + parent->width(); + } + void drawVerticalScrollbar(BitmapBuffer * dc); void drawHorizontalScrollbar(BitmapBuffer * dc); From 77a4379bb269efb30a2ad857c87fafdca5be7555 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 27 Nov 2020 14:56:19 +0100 Subject: [PATCH 297/714] Choice / Menu simplified by using the layers --- radio/src/thirdparty/libopenui/src/choice.cpp | 6 +----- radio/src/thirdparty/libopenui/src/menu.cpp | 6 ++---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 7a01bc4c5..366d0d03f 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -160,11 +160,7 @@ void Choice::openMenu() } menu->setCloseHandler([=]() { - editMode = false; - auto focus = getFocus(); - if (focus && focus->isChild(menu)) { - setFocus(SET_FOCUS_DEFAULT); - } + setEditMode(false); }); setEditMode(true); diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index f09629896..4ff454f63 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -55,8 +55,8 @@ void MenuBody::onEvent(event_t event) menu->invalidate(); } else { - lines[selectedIndex].onPress(); menu->deleteLater(); + lines[selectedIndex].onPress(); } } } @@ -86,12 +86,10 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) menu->invalidate(); } else { + menu->deleteLater(); lines[index].onPress(); } } - if (!menu->multiple) { - menu->deleteLater(); - } return true; } #endif From d7097f833b3af74d6850be105ee2c31667261e50 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 27 Nov 2020 16:49:25 +0100 Subject: [PATCH 298/714] Macro for rotary encoder speed --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/slider.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/textedit.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index ab2b0c0af..cdbda1dad 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -72,7 +72,7 @@ void NumberEdit::onEvent(event_t event) case EVT_ROTARY_RIGHT: { int value = getValue(); do { - value += rotencSpeed * step; + value += ROTARY_ENCODER_SPEED() * step; } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); if (value <= vmax) { setValue(value); @@ -87,7 +87,7 @@ void NumberEdit::onEvent(event_t event) case EVT_ROTARY_LEFT: { int value = getValue(); do { - value -= rotencSpeed * step; + value -= ROTARY_ENCODER_SPEED() * step; } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); if (value >= vmin) { setValue(value); diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index ecc08243d..fd53fb488 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -37,12 +37,12 @@ void Slider::onEvent(event_t event) if (editMode) { if (event == EVT_ROTARY_RIGHT) { - setValue(getValue() + rotencSpeed); + setValue(getValue() + ROTARY_ENCODER_SPEED()); onKeyPress(); return; } else if (event == EVT_ROTARY_LEFT) { - setValue(getValue() - rotencSpeed); + setValue(getValue() - ROTARY_ENCODER_SPEED()); onKeyPress(); return; } diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 1cd860f96..79eb2654a 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -104,13 +104,13 @@ void TextEdit::onEvent(event_t event) switch (event) { case EVT_ROTARY_RIGHT: - for (int i = 0; i < rotencSpeed; i++) { + for (int i = 0; i < ROTARY_ENCODER_SPEED(); i++) { v = getNextChar(v); } break; case EVT_ROTARY_LEFT: - for (int i = 0; i < rotencSpeed; i++) { + for (int i = 0; i < ROTARY_ENCODER_SPEED(); i++) { v = getPreviousChar(v); } break; From 7b2ada1607ef838666622e4c03db73219682754a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 2 Dec 2020 09:55:41 +0100 Subject: [PATCH 299/714] Use MainWindow::instance() instead of mainWindow global var --- radio/src/thirdparty/libopenui/src/keyboard_base.h | 5 +++-- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 2 +- radio/src/thirdparty/libopenui/src/mainwindow.h | 10 +++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 072a6a961..bd7376ee6 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -22,7 +22,8 @@ #include "mainwindow.h" #include "form.h" -class Keyboard: public FormWindow { +class Keyboard: public FormWindow +{ public: explicit Keyboard(coord_t height): FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) @@ -50,7 +51,7 @@ class Keyboard: public FormWindow { activeKeyboard->clearField(); } activeKeyboard = this; - attach(&mainWindow); + attach(MainWindow::instance()); fieldContainer = getFieldContainer(newField); fieldContainer->setHeight(LCD_H - height() - fieldContainer->top()); fieldContainer->scrollTo(newField); diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 38840e464..259c3306b 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -24,7 +24,7 @@ #include "touch.h" #endif -MainWindow mainWindow; +MainWindow * MainWindow::_instance = nullptr; #if defined(HARDWARE_TOUCH) TouchState touchState; diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 375ec052e..2b4368917 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -31,6 +31,7 @@ class MainWindow: public Window invalidatedRect(rect) { Layer::push(this); + _instance = this; } ~MainWindow() override @@ -38,6 +39,11 @@ class MainWindow: public Window children.clear(); } + static MainWindow * instance() + { + return _instance; + } + #if defined(DEBUG_WINDOWS) std::string getName() const override { @@ -64,10 +70,8 @@ class MainWindow: public Window void run(bool trash=true); protected: + static MainWindow * _instance; static void emptyTrash(); rect_t invalidatedRect; const char * shutdown = nullptr; }; - -extern MainWindow mainWindow; - From 4a828eae075348d5a02a0fb151bd984326dd7c99 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 9 Dec 2020 15:46:03 +0100 Subject: [PATCH 300/714] Cosmetics --- radio/src/thirdparty/libopenui/src/expansion_panel.cpp | 3 ++- radio/src/thirdparty/libopenui/src/expansion_panel.h | 8 +++++--- radio/src/thirdparty/libopenui/src/form.h | 5 +++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp index 53ef22fe4..a561454e2 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -45,8 +45,9 @@ void ExpansionPanelHeader::onEvent(event_t event) } else if (event == EVT_ROTARY_LEFT) { auto previous = panel->getPreviousField(); - if (previous) + if (previous) { previous->setFocus(SET_FOCUS_BACKWARD); + } } else { FormGroup::onEvent(event); diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 5aa7380c4..77b2eaeb1 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -23,7 +23,8 @@ class ExpansionPanel; -class ExpansionPanelHeader: virtual public FormGroup { +class ExpansionPanelHeader: virtual public FormGroup +{ public: explicit ExpansionPanelHeader(ExpansionPanel * parent); @@ -42,7 +43,8 @@ class ExpansionPanelHeader: virtual public FormGroup { }; -class ExpansionPanel: public FormGroup { +class ExpansionPanel: public FormGroup +{ friend class ExpansionPanelHeader; public: @@ -82,7 +84,7 @@ class ExpansionPanel: public FormGroup { FormGroup * body = nullptr; protected: - Window * header = nullptr; + ExpansionPanelHeader * header = nullptr; bool isOpen = false; }; diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index d2ccdd437..eab6cfde7 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -135,6 +135,11 @@ class FormGroup: public FormField void addField(FormField * field, bool front = false); + void setFirstField(FormField * field) + { + first = field; + } + void setLastField(FormField * field) { last = field; From 8fb42369364cae7595ee70b5102cb6d265555d88 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 9 Dec 2020 17:18:03 +0100 Subject: [PATCH 301/714] Cosmetics --- .../thirdparty/libopenui/src/expansion_panel.cpp | 14 ++++++++++++-- .../src/thirdparty/libopenui/src/expansion_panel.h | 8 +------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp index a561454e2..10b67f07c 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -26,6 +26,14 @@ void ExpansionPanel::updateHeight() setHeight(newHeight); } +void ExpansionPanel::setFocus(uint8_t flag) +{ + if (!enabled || isOpen) + FormGroup::setFocus(flag); + else + header->setFocus(flag); +} + ExpansionPanelHeader::ExpansionPanelHeader(ExpansionPanel * parent): FormGroup(parent, {0, 0, parent->width(), parent->height()}, FORWARD_SCROLL) { @@ -57,8 +65,10 @@ void ExpansionPanelHeader::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) { - static_cast(parent)->toggle(); - setFocus(SET_FOCUS_DEFAULT); + if (enabled) { + static_cast(parent)->toggle(); + setFocus(SET_FOCUS_DEFAULT); + } return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 77b2eaeb1..6d860504a 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -73,13 +73,7 @@ class ExpansionPanel: public FormGroup void updateHeight(); - void setFocus(uint8_t flag) override - { - if (isOpen) - FormGroup::setFocus(flag); - else - header->setFocus(flag); - } + void setFocus(uint8_t flag) override; FormGroup * body = nullptr; From 8c6025692bb44af3c1e2a0d61382efd51102d975 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 9 Dec 2020 17:32:02 +0100 Subject: [PATCH 302/714] Not sure, will need a comment in case it's wrong --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 708ed35cb..10e9ea624 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -200,7 +200,7 @@ void FormGroup::setFocus(uint8_t flag) } } else { - FormField::setFocus(SET_FOCUS_DEFAULT); + FormField::setFocus(flag); } } From d5444f9467b494ba848a1b7d5235c6623269d4af Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 10 Dec 2020 11:38:25 +0100 Subject: [PATCH 303/714] Expansion panel cosmetics --- .../src/thirdparty/libopenui/src/expansion_panel.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 6d860504a..179b61bc2 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -42,7 +42,6 @@ class ExpansionPanelHeader: virtual public FormGroup #endif }; - class ExpansionPanel: public FormGroup { friend class ExpansionPanelHeader; @@ -71,6 +70,18 @@ class ExpansionPanel: public FormGroup updateHeight(); } + void enable(bool value = true) + { + FormGroup::enable(value); + header->enable(value); + } + + void disable() + { + open(false); + enable(false); + } + void updateHeight(); void setFocus(uint8_t flag) override; @@ -81,4 +92,3 @@ class ExpansionPanel: public FormGroup ExpansionPanelHeader * header = nullptr; bool isOpen = false; }; - From d6612cd61ee5f09b678da8ee1e617a24c0677c75 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 14 Dec 2020 21:15:35 +0100 Subject: [PATCH 304/714] Table fixes (when using big tables) --- radio/src/thirdparty/libopenui/src/table.h | 12 ++++-- radio/src/thirdparty/libopenui/src/window.cpp | 42 +++++++++---------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 136622ef1..200ea9141 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -191,13 +191,19 @@ class Table: public FormField { selection = index; if (scroll) { + coord_t y = index * TABLE_LINE_HEIGHT; + Window * window = this; + while (window->getWindowFlags() & FORWARD_SCROLL) { + y += window->top(); + window = window->getParent(); + } const rect_t rect = { 0, - index * TABLE_LINE_HEIGHT, + y, width(), TABLE_LINE_HEIGHT }; - scrollTo(rect); + window->scrollTo(rect); } invalidate(); if (index >= 0) { @@ -288,10 +294,10 @@ class Table: public FormField void setFocus(uint8_t flag) override { + body.setFocus(flag); if (body.selection < 0 && !body.lines.empty()) { select(flag == SET_FOCUS_BACKWARD ? body.lines.size() - 1 : 0); } - body.setFocus(flag); } void select(int index, bool scroll = true) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 548bbac32..0c76aa00e 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -130,22 +130,21 @@ void Window::setFocus(uint8_t flag) TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); if (focusWindow != this) { + // scroll before calling focusHandler so that the window can adjust the scroll position if needed + Window * parent = this->parent; + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + if (parent) { + parent->scrollTo(this); + invalidate(); + } clearFocus(); focusWindow = this; if (focusHandler) { focusHandler(true); } } - - Window * parent = this->parent; - while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { - parent = parent->parent; - } - - if (parent) { - parent->scrollTo(this); - invalidate(); - } } void Window::setScrollPositionX(coord_t value) @@ -176,20 +175,21 @@ void Window::scrollTo(Window * child) coord_t offsetX = 0; coord_t offsetY = 0; - Window * parent = child->getParent(); - while (parent && parent != this) { - offsetX += parent->left(); - offsetY += parent->top(); - parent = parent->getParent(); + Window * parentWindow = child->getParent(); + while (parentWindow && parentWindow != this) { + offsetX += parentWindow->left(); + offsetY += parentWindow->top(); + parentWindow = parentWindow->getParent(); } - const rect_t rect = { - offsetX + child->left(), - offsetY + child->top(), - child->width(), - child->height() + const rect_t scrollRect = { + offsetX + child->left(), + offsetY + child->top(), + min(child->width(), width()), + min(child->height(), height()) }; - scrollTo(rect); + + scrollTo(scrollRect); } void Window::scrollTo(const rect_t & rect) From 445fd941cc77485eaed53d14fc8a76ad40ca8d90 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 17 Dec 2020 10:10:58 +0100 Subject: [PATCH 305/714] [Button] Add textFlags to constructor --- radio/src/thirdparty/libopenui/src/button.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index aa04227b6..5c9602ea3 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -28,8 +28,8 @@ constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3u; class Button: public FormField { public: - Button(FormGroup * parent, const rect_t & rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0): - FormField(parent, rect, windowFlags), + Button(FormGroup * parent, const rect_t & rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0): + FormField(parent, rect, windowFlags, textFlags), pressHandler(std::move(pressHandler)) { } @@ -87,8 +87,8 @@ class Button: public FormField class TextButton: public Button { public: - TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND): - Button(parent, rect, std::move(pressHandler), windowFlags | OPAQUE), + TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND, LcdFlags textFlags = 0): + Button(parent, rect, std::move(pressHandler), windowFlags | OPAQUE, textFlags), text(std::move(text)) { } From 57efa0d6bb4447408a57fdd4d4f4bf2a1eca79aa Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 17 Dec 2020 11:38:12 +0100 Subject: [PATCH 306/714] Cosmetics --- radio/src/thirdparty/libopenui/src/choice.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 4932c20e5..9b606fb78 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -87,7 +87,7 @@ class Choice : public ChoiceBase { isValueAvailable = std::move(handler); } - unsigned getIndexFromValue(int value) + unsigned getIndexFromValue(int value) const { if (!isValueAvailable) { return value - vmin; @@ -102,7 +102,7 @@ class Choice : public ChoiceBase { return index; } - int getValueFromIndex(int index) + int getValueFromIndex(int index) const { if (!isValueAvailable) { return vmin + index; @@ -119,7 +119,7 @@ class Choice : public ChoiceBase { return value; } - unsigned getValuesCount() + unsigned getValuesCount() const { return getIndexFromValue(vmax + 1); } @@ -158,4 +158,3 @@ class Choice : public ChoiceBase { virtual void openMenu(); }; - From c2a22158b2af0b22f257e5d16baf4b58743a0687 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 21 Dec 2020 11:57:50 +0100 Subject: [PATCH 307/714] Optimization --- radio/src/thirdparty/libopenui/src/textedit.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 4bbc022ba..584a64bc4 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -41,7 +41,7 @@ class TextEdit : public FormField { void setChangeHandler(std::function handler) { - changeHandler = handler; + changeHandler = std::move(handler); } uint8_t getMaxLength() const @@ -79,8 +79,9 @@ class TextEdit : public FormField { if (changed) { changed = false; trim(); - if (changeHandler) + if (changeHandler) { changeHandler(); + } } } From 83df13024586ca3fb7d32d8bb42981b4efc385e9 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 5 Jan 2021 14:14:49 +0100 Subject: [PATCH 308/714] TextEdit has a new menu (Clear / Copy / Paste) --- .../src/thirdparty/libopenui/src/textedit.cpp | 43 ++++++++++++++++--- radio/src/thirdparty/libopenui/src/textedit.h | 4 +- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 79eb2654a..d44f52593 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -20,11 +20,28 @@ #include "textedit.h" #include "font.h" #include "libopenui_globals.h" +#include "libopenui_config.h" + +#if !defined(STR_EDIT) +#define STR_EDIT "Edit" +#endif + +#if !defined(STR_CLEAR) +#define STR_CLEAR "Clear" +#endif #if defined(HARDWARE_TOUCH) #include "keyboard_text.h" #endif +#if defined(HARDWARE_KEYS) +#include "menu.h" +#endif + +#if defined(CLIPBOARD) +#include "clipboard.h" +#endif + void TextEdit::paint(BitmapBuffer * dc) { FormField::paint(dc); @@ -150,12 +167,27 @@ void TextEdit::onEvent(event_t event) break; case EVT_KEY_LONG(KEY_ENTER): - if (v == 0) { - killEvents(event); - FormField::onEvent(EVT_KEY_BREAK(KEY_ENTER)); - break; + { + killEvents(event); + auto menu = new Menu(this); + menu->setTitle(STR_EDIT); +#if defined(CLIPBOARD) + menu->addLine(STR_COPY, [=] { + clipboard.write((uint8_t *)value, length, Clipboard::CONTENT_TEXT); + }); + if (clipboard.contentType == Clipboard::CONTENT_TEXT) { + menu->addLine(STR_PASTE, [=] { + clipboard.read((uint8_t *)value, length); + changeEnd(true); + }); } - // no break +#endif + menu->addLine(STR_CLEAR, [=] { + memset(value, 0, length); + changeEnd(true); + }); + break; + } case EVT_KEY_LONG(KEY_LEFT): case EVT_KEY_LONG(KEY_RIGHT): @@ -223,6 +255,5 @@ void TextEdit::onFocusLost() #endif changeEnd(); - FormField::onFocusLost(); } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 584a64bc4..5906a5c7d 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -73,10 +73,10 @@ class TextEdit : public FormField { void trim(); - void changeEnd() + void changeEnd(bool forceChanged = false) { cursorPos = 0; - if (changed) { + if (changed || forceChanged) { changed = false; trim(); if (changeHandler) { From e00a9773aab436ba1db1092701354081e4b131b4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 5 Jan 2021 16:22:31 +0100 Subject: [PATCH 309/714] StaticBitmap can now display a mask with a color --- radio/src/thirdparty/libopenui/src/static.h | 25 +++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index ea8015f2e..e66315ce5 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -22,7 +22,8 @@ #include "window.h" #include "button.h" // TODO just for BUTTON_BACKGROUND -class StaticText: public Window { +class StaticText: public Window +{ public: StaticText(Window * parent, const rect_t & rect, std::string text = "", WindowFlags windowFlags = 0, LcdFlags textFlags = 0) : Window(parent, rect, windowFlags, textFlags), @@ -73,7 +74,8 @@ class Subtitle: public StaticText { #endif }; -class StaticBitmap: public Window { +class StaticBitmap: public Window +{ public: StaticBitmap(Window * parent, const rect_t & rect, bool scale = false): Window(parent, rect), @@ -95,11 +97,24 @@ class StaticBitmap: public Window { { } + StaticBitmap(Window * parent, const rect_t & rect, const BitmapBuffer * mask, LcdFlags color, bool scale = false): + Window(parent, rect), + bitmap(mask), + color(color), + scale(scale) + { + } + void setBitmap(const char * filename) { setBitmap(BitmapBuffer::loadBitmap(filename)); } + void setMaskColor(LcdFlags value) + { + color = value; + } + void setBitmap(const BitmapBuffer * newBitmap) { delete bitmap; @@ -117,7 +132,9 @@ class StaticBitmap: public Window { void paint(BitmapBuffer * dc) override { if (bitmap) { - if (scale) + if (color != 0xFFFFFFFF) + dc->drawMask(0, 0, bitmap, color); + else if (scale) dc->drawScaledBitmap(bitmap, 0, 0, width(), height()); else dc->drawBitmap((width() - bitmap->width()) / 2, (height() - bitmap->height()) / 2, bitmap); @@ -126,6 +143,7 @@ class StaticBitmap: public Window { protected: const BitmapBuffer * bitmap = nullptr; + LcdFlags color = 0xFFFFFFFF; bool scale = false; }; @@ -184,4 +202,3 @@ class DynamicNumber: public Window const char * prefix; const char * suffix; }; - From 5742a67cb93fd7d1358c8fc29e28cfc8c557b318 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 6 Jan 2021 08:54:45 +0100 Subject: [PATCH 310/714] [TextEdit] KEY_UP for toggle case --- radio/src/thirdparty/libopenui/src/textedit.cpp | 11 +++++------ radio/src/thirdparty/libopenui/src/textedit.h | 10 ++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index d44f52593..a7aa90e33 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -189,14 +189,13 @@ void TextEdit::onEvent(event_t event) break; } + case EVT_KEY_BREAK(KEY_UP): + v = toggleCase(v); + break; + case EVT_KEY_LONG(KEY_LEFT): case EVT_KEY_LONG(KEY_RIGHT): - if (v >= 'A' && v <= 'Z') { - v += 32; // tolower - } - else if (v >= 'a' && v <= 'z') { - v -= 32; // toupper - } + v = toggleCase(v); if (event == EVT_KEY_LONG(KEY_LEFT)) { killEvents(KEY_LEFT); } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 5906a5c7d..013bbc202 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -112,5 +112,15 @@ class TextEdit : public FormField { else return c - 1; } + + static uint8_t toggleCase(uint8_t c) + { + if (c >= 'A' && c <= 'Z') + return c + 32; // tolower + else if (c >= 'a' && c <= 'z') + return c - 32; // toupper + else + return c; + } }; From be9595c16146051c37752d9cdef298bed402e872 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 7 Jan 2021 20:14:02 +0100 Subject: [PATCH 311/714] [ExpansionPanel] Fixes --- radio/src/thirdparty/libopenui/src/expansion_panel.cpp | 6 ++++-- radio/src/thirdparty/libopenui/src/expansion_panel.h | 4 ++-- radio/src/thirdparty/libopenui/src/window.cpp | 4 ++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp index 10b67f07c..624931dd5 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -19,10 +19,12 @@ #include "expansion_panel.h" -void ExpansionPanel::updateHeight() +void ExpansionPanel::updateHeight(bool move) { coord_t newHeight = (isOpen ? header->height() + body->height() : header->height()); - parent->moveWindowsTop(bottom(), newHeight - height()); + if (move) { + parent->moveWindowsTop(bottom(), newHeight - height()); + } setHeight(newHeight); } diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index 179b61bc2..dc196b4f9 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -64,7 +64,7 @@ class ExpansionPanel: public FormGroup open(!isOpen); } - void open(bool state = true) + virtual void open(bool state = true) { isOpen = state; updateHeight(); @@ -82,7 +82,7 @@ class ExpansionPanel: public FormGroup enable(false); } - void updateHeight(); + void updateHeight(bool move = true); void setFocus(uint8_t flag) override; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0c76aa00e..a382f1e84 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -461,6 +461,10 @@ coord_t Window::adjustHeight() void Window::moveWindowsTop(coord_t y, coord_t delta) { + if (getWindowFlags() & FORWARD_SCROLL) { + parent->moveWindowsTop(bottom(), delta); + } + for (auto child: children) { if (child->rect.y >= y) { child->rect.y += delta; From d22d95fe9afbae28f1230ed486ce993e56da177f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 8 Jan 2021 10:33:26 +0100 Subject: [PATCH 312/714] [ExpansionPanel] Possibility to disable the toggle button --- radio/src/thirdparty/libopenui/src/expansion_panel.h | 7 +++++++ radio/src/thirdparty/libopenui/src/modal_window.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index dc196b4f9..a05842b26 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -70,6 +70,13 @@ class ExpansionPanel: public FormGroup updateHeight(); } + void setCloseAllowed(bool value = true) + { + if (value) { + header->disable(); + } + } + void enable(bool value = true) { FormGroup::enable(value); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index bdf6ba125..23367efd2 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -33,7 +33,7 @@ class ModalWindow: public Window } #endif - void deleteLater(bool detach = true, bool trash = true) override; + void deleteLater(bool detach = true, bool trash = true) override; // NOLINT(google-default-arguments) void paint(BitmapBuffer * dc) override; From 407656b065af4cc23790c51ff9aeb39bd54b8d6e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 8 Jan 2021 10:35:51 +0100 Subject: [PATCH 313/714] [ExpansionPanel] Possibility to disable the toggle button --- radio/src/thirdparty/libopenui/src/expansion_panel.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index a05842b26..ff22c859d 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -72,9 +72,7 @@ class ExpansionPanel: public FormGroup void setCloseAllowed(bool value = true) { - if (value) { - header->disable(); - } + header->enable(value); } void enable(bool value = true) From 09e47a35cbd537f7e847ce7dbde581ef4fc38494 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 21 Jan 2021 17:09:52 +0100 Subject: [PATCH 314/714] Added Window::getTextFlags() --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 3 ++- radio/src/thirdparty/libopenui/src/window.h | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 3606bd4b1..b076d120b 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -81,7 +81,8 @@ #define ARGB(a, r, g, b) (uint16_t)((((a) & 0xF0) << 8) + (((r) & 0xF0) << 4) + (((g) & 0xF0) << 0) + (((b) & 0xF0) >> 4)) #define COLOR(index) LcdFlags(unsigned(index) << 16u) -#define COLOR_IDX(att) uint8_t((att) >> 16u) +#define COLOR_IDX(flags) uint8_t((flags) >> 16u) +#define COLOR_MASK(flags) ((flags) & 0xFFFF0000u) #define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) #define DEFAULT_BGCOLOR COLOR(DEFAULT_BGCOLOR_INDEX) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 08e013840..89ca45a18 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -122,6 +122,11 @@ class Window windowFlags = flags; } + LcdFlags getTextFlags() const + { + return textFlags; + } + void setTextFlags(LcdFlags flags) { textFlags = flags; From 771445426483a415f849789804aa5d619507d247 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 22 Jan 2021 16:10:36 +0100 Subject: [PATCH 315/714] FormGroup::addField() is virtual now --- radio/src/thirdparty/libopenui/src/form.h | 2 +- .../src/thirdparty/libopenui/src/libopenui_helpers.h | 12 ++++++++++-- radio/src/thirdparty/libopenui/src/libopenui_types.h | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index eab6cfde7..602c5d155 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -133,7 +133,7 @@ class FormGroup: public FormField void setFocus(uint8_t flag) override; - void addField(FormField * field, bool front = false); + virtual void addField(FormField * field, bool front = false); // NOLINT(google-default-arguments) void setFirstField(FormField * field) { diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index 325d3c0df..fd2329100 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -45,7 +45,7 @@ inline T limit(T vmin, T x, T vmax) return ::min(::max(vmin, x), vmax); } -inline int divRoundClosest(const int n, const int d) +inline int divRoundClosest(int n, int d) { if (d == 0) return 0; @@ -53,10 +53,18 @@ inline int divRoundClosest(const int n, const int d) return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d); } +inline int multDivRoundClosest(int v, int n, int d) +{ + if (n == d) + return v; + else + return divRoundClosest(v * n, d); +} + template inline T sgn(T a) { - return a > 0 ? 1 : (a < 0 ? -1 : 0); + return a > 0 ? 1 : (a < 0 ? -1 : 0); } // TODO duplicated code diff --git a/radio/src/thirdparty/libopenui/src/libopenui_types.h b/radio/src/thirdparty/libopenui/src/libopenui_types.h index 4be8f4390..0fec188c8 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_types.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_types.h @@ -69,6 +69,7 @@ struct rect_t } }; +static const rect_t nullRect = {0, 0, 0, 0}; + typedef uint32_t LcdFlags; typedef uint16_t event_t; - From d47ca1994bf3d2c39e3a2ee4b3dcc4ce5ebf45fa Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Feb 2021 15:22:02 +0100 Subject: [PATCH 316/714] More beeps --- radio/src/thirdparty/libopenui/src/form.cpp | 12 +++++++++++- radio/src/thirdparty/libopenui/src/menu.cpp | 14 +++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 10e9ea624..eea53358c 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -39,19 +39,23 @@ void FormField::onEvent(event_t event) if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { if (next) { + onKeyPress(); next->setFocus(SET_FOCUS_FORWARD); } } else if (event == EVT_ROTARY_LEFT/*EVT_KEY_BREAK(KEY_UP)*/) { if (previous) { + onKeyPress(); previous->setFocus(SET_FOCUS_BACKWARD); } } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + onKeyPress(); setEditMode(!editMode); invalidate(); } else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { + onKeyPress(); setEditMode(false); invalidate(); } @@ -210,15 +214,19 @@ void FormGroup::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormGroup").c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { + onKeyPress(); setFocusOnFirstVisibleField(SET_FOCUS_FIRST); } else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus() && !(windowFlags & FORM_FORWARD_FOCUS)) { + onKeyPress(); setFocus(SET_FOCUS_DEFAULT); // opentx - model - timers settings } else if (event == EVT_ROTARY_RIGHT && !next) { + onKeyPress(); setFocusOnFirstVisibleField(SET_FOCUS_FIRST); } else if (event == EVT_ROTARY_LEFT && !previous) { + onKeyPress(); setFocusOnLastVisibleField(SET_FOCUS_BACKWARD); } else { @@ -245,10 +253,12 @@ void FormWindow::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormWindow").c_str(), event); if (event == EVT_KEY_BREAK(KEY_EXIT) && (windowFlags & FORM_FORWARD_FOCUS) && first) { + onKeyPress(); Window * currentFocus = getFocus(); first->setFocus(SET_FOCUS_FIRST); - if (getFocus() != currentFocus) + if (getFocus() != currentFocus) { return; + } } FormGroup::onEvent(event); diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 4ff454f63..22b5e3c0c 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -36,15 +36,20 @@ void MenuBody::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_ROTARY_RIGHT) { - if (!lines.empty()) + if (!lines.empty()) { select(int((selectedIndex + 1) % lines.size())); + onKeyPress(); + } } else if (event == EVT_ROTARY_LEFT) { - if (!lines.empty()) + if (!lines.empty()) { select(int(selectedIndex <= 0 ? lines.size() - 1 : selectedIndex - 1)); + onKeyPress(); + } } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { if (!lines.empty()) { + onKeyPress(); if (selectedIndex < 0) { select(0); } @@ -62,8 +67,10 @@ void MenuBody::onEvent(event_t event) } } else if (event == EVT_KEY_BREAK(KEY_EXIT)) { - if (onCancel) + onKeyPress(); + if (onCancel) { onCancel(); + } Window::onEvent(event); } else { @@ -78,6 +85,7 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) Menu * menu = getParentMenu(); int index = y / MENUS_LINE_HEIGHT; if (index < (int)lines.size()) { + onKeyPress(); if (menu->multiple) { if (selectedIndex == index) lines[index].onPress(); From 894fbd08abfa09e2e5dd9de02c1c56d1a6e130a3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Feb 2021 18:35:26 +0100 Subject: [PATCH 317/714] More beeps --- radio/src/thirdparty/libopenui/src/choice.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 366d0d03f..c3d8f1468 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -113,6 +113,7 @@ void Choice::onEvent(event_t event) TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); if (event == EVT_KEY_BREAK(KEY_ENTER)) { + onKeyPress(); openMenu(); } else { From 8f4bb417bae0f3aee808140773201ae9b3c1c298 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Feb 2021 18:37:51 +0100 Subject: [PATCH 318/714] More beeps --- radio/src/thirdparty/libopenui/src/button.cpp | 2 ++ radio/src/thirdparty/libopenui/src/modal_window.h | 1 + 2 files changed, 3 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 04efafa31..3f2b349db 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -38,6 +38,7 @@ void Button::onEvent(event_t event) if (event == EVT_KEY_BREAK(KEY_ENTER)) { if (enabled) { + onKeyPress(); onPress(); } } @@ -51,6 +52,7 @@ void Button::onEvent(event_t event) bool Button::onTouchEnd(coord_t x, coord_t y) { if (enabled) { + onKeyPress(); if (!(windowFlags & NO_FOCUS)) { setFocus(SET_FOCUS_DEFAULT); } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 23367efd2..75fd1b8ff 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -46,6 +46,7 @@ class ModalWindow: public Window bool onTouchEnd(coord_t x, coord_t y) override { if (!Window::onTouchEnd(x, y) && closeAllowed) { + onKeyPress(); deleteLater(); } return true; From e20f8564752c6a44f80e60ae2de6a0f52484c807 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 18 Feb 2021 18:42:31 +0100 Subject: [PATCH 319/714] More beeps --- radio/src/thirdparty/libopenui/src/table.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 06764889b..bbb5d2226 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -83,6 +83,7 @@ bool Table::Body::onTouchEnd(coord_t x, coord_t y) { unsigned index = y / TABLE_LINE_HEIGHT; if (index < lines.size()) { + onKeyPress(); setFocus(SET_FOCUS_DEFAULT); auto onPress = lines[index]->onPress; if (onPress) @@ -99,12 +100,14 @@ void Table::Body::onEvent(event_t event) if (event == EVT_KEY_BREAK(KEY_ENTER)) { if (selection >= 0) { + onKeyPress(); auto onPress = lines[selection]->onPress; if (onPress) onPress(); } } if (event == EVT_ROTARY_RIGHT) { + onKeyPress(); auto table = static_cast
(parent); if (table->getWindowFlags() & FORWARD_SCROLL) { auto index = selection + 1; @@ -126,6 +129,7 @@ void Table::Body::onEvent(event_t event) } } else if (event == EVT_ROTARY_LEFT) { + onKeyPress(); auto table = static_cast
(parent); if (table->getWindowFlags() & FORWARD_SCROLL) { auto index = selection - 1; From e71b959624e75a03daf9a394c8dcdf20dfdc19b6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Feb 2021 17:24:50 +0100 Subject: [PATCH 320/714] Prevents an infinite loop when a FormGroup has no widget which can accept the focus --- radio/src/thirdparty/libopenui/src/form.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index eea53358c..2bec6e80e 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -196,14 +196,14 @@ void FormGroup::setFocus(uint8_t flag) else if (next) { next->setFocus(SET_FOCUS_FORWARD); } - else { + else if (!(windowFlags & NO_FOCUS)) { clearFocus(); focusWindow = this; } break; } } - else { + else if (!(windowFlags & NO_FOCUS)) { FormField::setFocus(flag); } } From d0b517e9f716be3463744fcf555ad2b185a2bc1a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 22 Feb 2021 21:53:46 +0100 Subject: [PATCH 321/714] Prevents an infinite loop when a FormGroup has no widget which can accept the focus --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 2bec6e80e..d9bf66f2e 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -196,7 +196,7 @@ void FormGroup::setFocus(uint8_t flag) else if (next) { next->setFocus(SET_FOCUS_FORWARD); } - else if (!(windowFlags & NO_FOCUS)) { + else { clearFocus(); focusWindow = this; } From a4b8aafb36b825983b17d191a3b140a9b44bee47 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 23 Feb 2021 10:44:50 +0100 Subject: [PATCH 322/714] Slide improved (keep the energy for multipages scroll) --- .../thirdparty/libopenui/src/mainwindow.cpp | 18 ++++++++++++------ radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 259c3306b..03c761bd2 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -64,14 +64,20 @@ void MainWindow::checkEvents() } } else if (touchState.event == TE_SLIDE_END && slidingWindow) { - if (abs(touchState.lastDeltaX) >= 5 || abs(touchState.lastDeltaY) >= 5) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); - touchState.lastDeltaX = (touchState.lastDeltaX * 3) >> 2; - touchState.lastDeltaY = (touchState.lastDeltaY * 3) >> 2; - } - else { + if (touchState.lastDeltaX > SLIDE_SPEED_REDUCTION) + touchState.lastDeltaX -= SLIDE_SPEED_REDUCTION; + else if (touchState.lastDeltaX < -SLIDE_SPEED_REDUCTION) + touchState.lastDeltaX += SLIDE_SPEED_REDUCTION; + else touchState.lastDeltaX = 0; + if (touchState.lastDeltaY > SLIDE_SPEED_REDUCTION) + touchState.lastDeltaY -= SLIDE_SPEED_REDUCTION; + else if (touchState.lastDeltaY < -SLIDE_SPEED_REDUCTION) + touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; + else touchState.lastDeltaY = 0; + if (touchState.lastDeltaX || touchState.lastDeltaY) { + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); } } #endif diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index a382f1e84..7eff84a83 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -346,7 +346,7 @@ void Window::checkEvents() } #if defined(HARDWARE_TOUCH) - if (touchState.event != TE_SLIDE) { + if (touchState.event != TE_SLIDE && touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) { if (pageWidth) { coord_t relativeScrollPosition = scrollPositionX % pageWidth; if (relativeScrollPosition) { From 8d519c9fded835f336b18a4cad782f65a9837625 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 23 Feb 2021 12:12:35 +0100 Subject: [PATCH 323/714] Possibility to remove the fields wrapping within the same form --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index d9bf66f2e..586a6c333 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -124,7 +124,7 @@ void FormGroup::addField(FormField * field, bool front) last->setNextField(this); link(previous, first); } - else { + else if (WRAP_FORM_FIELDS_WITHIN_PAGE) { link(last, first); } if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { From c7c36d0652be6161c910bc57407534e14740b6af Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 23 Feb 2021 18:40:14 +0100 Subject: [PATCH 324/714] Roller fix --- .../thirdparty/libopenui/src/libopenui_helpers.h | 5 +++++ radio/src/thirdparty/libopenui/src/roller.h | 14 +++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index fd2329100..5c5597be4 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -61,6 +61,11 @@ inline int multDivRoundClosest(int v, int n, int d) return divRoundClosest(v * n, d); } +inline int mod(int k, int n) +{ + return ((k %= n) < 0) ? k + n : k; +} + template inline T sgn(T a) { diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index b4bf13fc9..d3a5ac846 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -68,7 +68,7 @@ class Roller: public Choice void paint(BitmapBuffer * dc) override { int32_t value = getValue(); - auto valuesCount = getValuesCount(); + auto valuesCount = (int)getValuesCount(); if (valuesCount == 0) { return; } @@ -76,11 +76,10 @@ class Roller: public Choice int index = (scrollPositionY - ROLLER_LINE_HEIGHT + 1) / ROLLER_LINE_HEIGHT; coord_t y = index * ROLLER_LINE_HEIGHT; coord_t yMax = scrollPositionY + 3 * ROLLER_LINE_HEIGHT; - while (index < 0) - index += valuesCount; + index = mod(index, valuesCount); while (y < yMax) { - auto displayedValue = getValueFromIndex(index % valuesCount); + auto displayedValue = getValueFromIndex(index); auto fgColor = DISABLE_COLOR; if (value == displayedValue) { @@ -99,7 +98,9 @@ class Roller: public Choice dc->drawSolidHorizontalLine(0, y - 10, width(), DISABLE_COLOR); } - index += 1; + if (++index == valuesCount) + index = 0; + y += ROLLER_LINE_HEIGHT; } } @@ -167,10 +168,9 @@ class Roller: public Choice lastScrollPositionY = scrollPositionY; auto valuesCount = getValuesCount(); - auto newValue = getValueFromIndex((((scrollPositionY / ROLLER_LINE_HEIGHT) + 1) + valuesCount) % valuesCount); + auto newValue = getValueFromIndex(mod((scrollPositionY / ROLLER_LINE_HEIGHT) + 1, valuesCount)); setValue(newValue); invalidate(); } }; - From 7d6d88113c7798810eb059c225bca6ea6f1cc60a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 24 Feb 2021 16:00:44 +0100 Subject: [PATCH 325/714] Focus issue: 'else' missing + refactoring --- radio/src/thirdparty/libopenui/src/form.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 586a6c333..865a35252 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -106,27 +106,30 @@ void FormGroup::addField(FormField * field, bool front) if (field->getWindowFlags() & FORM_DETACHED) return; - if (!first) { - first = field; - last = field; - } if (front) { if (first) link(field, first); first = field; + if (!last) + last = field; } else { if (last) link(last, field); last = field; + if (!first) + first = field; } - if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { + + if (windowFlags & FORM_FORWARD_FOCUS) { last->setNextField(this); - link(previous, first); + if (previous) + link(previous, first); } else if (WRAP_FORM_FIELDS_WITHIN_PAGE) { link(last, first); } + if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { field->setFocus(SET_FOCUS_DEFAULT); } From ca53750db1472ef7d4d6a6e3e7b3f6d007de1a15 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 24 Feb 2021 17:22:08 +0100 Subject: [PATCH 326/714] Wrapping within page repaired --- radio/src/thirdparty/libopenui/src/form.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 865a35252..5494960ff 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -121,13 +121,21 @@ void FormGroup::addField(FormField * field, bool front) first = field; } - if (windowFlags & FORM_FORWARD_FOCUS) { - last->setNextField(this); - if (previous) + if (WRAP_FORM_FIELDS_WITHIN_PAGE) { + if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { + last->setNextField(this); link(previous, first); + } + else { + link(last, first); + } } - else if (WRAP_FORM_FIELDS_WITHIN_PAGE) { - link(last, first); + else { + if (windowFlags & FORM_FORWARD_FOCUS) { + last->setNextField(this); + if (previous) + link(previous, first); + } } if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { From 8d8e7c395be6efcb36a22ab395f191812368a46a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 24 Feb 2021 18:51:45 +0100 Subject: [PATCH 327/714] Window::setFocus() refactoring --- radio/src/thirdparty/libopenui/src/checkbox.h | 11 +++-- .../libopenui/src/expansion_panel.cpp | 10 ++-- .../libopenui/src/expansion_panel.h | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 48 +++++++++---------- radio/src/thirdparty/libopenui/src/form.h | 8 ++-- radio/src/thirdparty/libopenui/src/table.cpp | 6 ++- radio/src/thirdparty/libopenui/src/table.h | 25 +++++++--- radio/src/thirdparty/libopenui/src/window.cpp | 3 +- radio/src/thirdparty/libopenui/src/window.h | 2 +- 9 files changed, 66 insertions(+), 49 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 411f588b6..444d7f381 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -19,14 +19,15 @@ #pragma once +#include #include "form.h" class CheckBox : public FormField { public: CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, WindowFlags flags = 0) : FormField(parent, rect, flags), - _getValue(getValue), - _setValue(setValue) + _getValue(std::move(getValue)), + _setValue(std::move(setValue)) { } @@ -62,9 +63,13 @@ class CheckBox : public FormField { return _getValue(); } + void setSetValueHandler(std::function handler) + { + _setValue = std::move(handler); + } + protected: std::string label; std::function _getValue; std::function _setValue; }; - diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp index 624931dd5..99a8c7e43 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp @@ -28,12 +28,12 @@ void ExpansionPanel::updateHeight(bool move) setHeight(newHeight); } -void ExpansionPanel::setFocus(uint8_t flag) +void ExpansionPanel::setFocus(uint8_t flag, Window * from) { if (!enabled || isOpen) - FormGroup::setFocus(flag); + FormGroup::setFocus(flag, from); else - header->setFocus(flag); + header->setFocus(flag, from); } ExpansionPanelHeader::ExpansionPanelHeader(ExpansionPanel * parent): @@ -51,12 +51,12 @@ void ExpansionPanelHeader::onEvent(event_t event) else if (event == EVT_ROTARY_RIGHT && !panel->isOpen) { auto next = panel->getNextField(); if (next) - next->setFocus(SET_FOCUS_FORWARD); + next->setFocus(SET_FOCUS_FORWARD, this); } else if (event == EVT_ROTARY_LEFT) { auto previous = panel->getPreviousField(); if (previous) { - previous->setFocus(SET_FOCUS_BACKWARD); + previous->setFocus(SET_FOCUS_BACKWARD, this); } } else { diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h index ff22c859d..9dd965ac5 100644 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ b/radio/src/thirdparty/libopenui/src/expansion_panel.h @@ -89,7 +89,7 @@ class ExpansionPanel: public FormGroup void updateHeight(bool move = true); - void setFocus(uint8_t flag) override; + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override; FormGroup * body = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 5494960ff..04a13a8bb 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -40,13 +40,13 @@ void FormField::onEvent(event_t event) if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { if (next) { onKeyPress(); - next->setFocus(SET_FOCUS_FORWARD); + next->setFocus(SET_FOCUS_FORWARD, this); } } else if (event == EVT_ROTARY_LEFT/*EVT_KEY_BREAK(KEY_UP)*/) { if (previous) { onKeyPress(); - previous->setFocus(SET_FOCUS_BACKWARD); + previous->setFocus(SET_FOCUS_BACKWARD, this); } } else if (event == EVT_KEY_BREAK(KEY_ENTER)) { @@ -65,22 +65,20 @@ void FormField::onEvent(event_t event) } #endif -void FormField::setFocus(uint8_t flag) +void FormField::setFocus(uint8_t flag, Window * from) { if (enabled) { - Window::setFocus(flag); + Window::setFocus(flag, from); } else { - clearFocus(); - focusWindow = this; if (flag == SET_FOCUS_BACKWARD) { if (previous) { - previous->setFocus(flag); + previous->setFocus(flag, this); } } else { if (next) { - next->setFocus(flag); + next->setFocus(flag, this); } } } @@ -146,29 +144,27 @@ void FormGroup::addField(FormField * field, bool front) } } -void FormGroup::setFocus(uint8_t flag) +void FormGroup::setFocus(uint8_t flag, Window * from) { TRACE_WINDOWS("%s setFocus(%d)", getWindowDebugString("FormGroup").c_str(), flag); if (windowFlags & FORM_FORWARD_FOCUS) { switch (flag) { case SET_FOCUS_BACKWARD: - if (focusWindow->isChild(first)) { + if (from && from->isChild(first)) { if (previous == this) { - last->setFocus(SET_FOCUS_BACKWARD); + last->setFocus(SET_FOCUS_BACKWARD, this); } else if (previous) { - previous->setFocus(SET_FOCUS_BACKWARD); + previous->setFocus(SET_FOCUS_BACKWARD, this); } } else { if (last) { - last->setFocus(SET_FOCUS_BACKWARD); + last->setFocus(SET_FOCUS_BACKWARD, this); } else if (previous) { - clearFocus(); - focusWindow = this; - previous->setFocus(SET_FOCUS_BACKWARD); + previous->setFocus(SET_FOCUS_BACKWARD, this); } } break; @@ -178,31 +174,33 @@ void FormGroup::setFocus(uint8_t flag) // no break; case SET_FOCUS_FORWARD: - if (focusWindow && focusWindow->isChild(this)) { + if (from && from->isChild(this)) { if (next == this) { - first->setFocus(SET_FOCUS_FORWARD); + first->setFocus(SET_FOCUS_FORWARD, this); } else if (next) { - next->setFocus(SET_FOCUS_FORWARD); + next->setFocus(SET_FOCUS_FORWARD, this); } } else { if (first) { - first->setFocus(SET_FOCUS_FORWARD); + first->setFocus(SET_FOCUS_FORWARD, this); } else if (next) { - clearFocus(); - focusWindow = this; - next->setFocus(SET_FOCUS_FORWARD); + next->setFocus(SET_FOCUS_FORWARD, this); } } break; default: - if (focusWindow == previous) { + if (from == previous) { if (first) { first->setFocus(SET_FOCUS_DEFAULT); } + else { + clearFocus(); + focusWindow = this; + } } else if (next) { next->setFocus(SET_FOCUS_FORWARD); @@ -215,7 +213,7 @@ void FormGroup::setFocus(uint8_t flag) } } else if (!(windowFlags & NO_FOCUS)) { - FormField::setFocus(flag); + FormField::setFocus(flag, from); } } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 602c5d155..8bbf4e517 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -79,7 +79,7 @@ class FormField: public Window enable(false); } - void setFocus(uint8_t flag) override; + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override; void onFocusLost() override { @@ -131,7 +131,7 @@ class FormGroup: public FormField } } - void setFocus(uint8_t flag) override; + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override; virtual void addField(FormField * field, bool front = false); // NOLINT(google-default-arguments) @@ -155,7 +155,7 @@ class FormGroup: public FormField return last; } - void setFocusOnFirstVisibleField(uint8_t flag) const + void setFocusOnFirstVisibleField(uint8_t flag = SET_FOCUS_DEFAULT) const { auto field = getFirstField(); while (field && !field->isInsideParentScrollingArea()) { @@ -166,7 +166,7 @@ class FormGroup: public FormField } } - void setFocusOnLastVisibleField(uint8_t flag) const + void setFocusOnLastVisibleField(uint8_t flag = SET_FOCUS_DEFAULT) const { auto field = getLastField(); while (field && !field->isInsideParentScrollingArea()) { diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index bbb5d2226..257eda06b 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -117,8 +117,10 @@ void Table::Body::onEvent(event_t event) else { auto next = table->getNextField(); if (next) { - select(-1, false); - next->setFocus(SET_FOCUS_FORWARD); + next->setFocus(SET_FOCUS_FORWARD, this); + if (!hasFocus()) { + select(-1, false); + } } } } diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 200ea9141..db392f906 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -239,9 +239,6 @@ class Table: public FormField header(this, {0, 0, width(), 0}, columnsCount), body(this, {0, 0, width(), height()}, windowFlags) { - if (hasFocus()) { - setFocus(SET_FOCUS_DEFAULT); - } } #if defined(DEBUG_WINDOWS) @@ -292,11 +289,25 @@ class Table: public FormField body.invalidate(); } - void setFocus(uint8_t flag) override + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override { - body.setFocus(flag); - if (body.selection < 0 && !body.lines.empty()) { - select(flag == SET_FOCUS_BACKWARD ? body.lines.size() - 1 : 0); + if (body.lines.empty()) { + if (flag == SET_FOCUS_BACKWARD) { + if (previous) { + previous->setFocus(flag, this); + } + } + else { + if (next) { + next->setFocus(flag, this); + } + } + } + else { + body.setFocus(flag, from); + if (body.selection < 0) { + select(flag == SET_FOCUS_BACKWARD ? body.lines.size() - 1 : 0); + } } } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7eff84a83..5fa606e96 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -122,7 +122,7 @@ void Window::clearFocus() } } -void Window::setFocus(uint8_t flag) +void Window::setFocus(uint8_t flag, Window * from) { if (deleted()) return; @@ -139,6 +139,7 @@ void Window::setFocus(uint8_t flag) parent->scrollTo(this); invalidate(); } + clearFocus(); focusWindow = this; if (focusHandler) { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 89ca45a18..888f3c245 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -164,7 +164,7 @@ class Window static void clearFocus(); - virtual void setFocus(uint8_t flag); + virtual void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr); void setRect(rect_t value) { From 549f4da051972c6ade75145a3e50e8a466b1b331 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 25 Feb 2021 15:57:27 +0100 Subject: [PATCH 328/714] libopenui TextButtons are not opaque by default anymore --- radio/src/thirdparty/libopenui/src/button.h | 4 ++-- .../thirdparty/libopenui/src/keyboard_number.cpp | 14 +++++++------- radio/src/thirdparty/libopenui/src/theme.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 5c9602ea3..78ce23de7 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -87,8 +87,8 @@ class Button: public FormField class TextButton: public Button { public: - TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND, LcdFlags textFlags = 0): - Button(parent, rect, std::move(pressHandler), windowFlags | OPAQUE, textFlags), + TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE, LcdFlags textFlags = 0): + Button(parent, rect, std::move(pressHandler), windowFlags, textFlags), text(std::move(text)) { } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index df578ebe1..a8891e048 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -32,43 +32,43 @@ NumberKeyboard::NumberKeyboard() : [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_BACKWARD); return 0; - }, BUTTON_BACKGROUND | NO_FOCUS); + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_MINUS); return 0; - }, BUTTON_BACKGROUND | NO_FOCUS); + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_PLUS); return 0; - }, BUTTON_BACKGROUND | NO_FOCUS); + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_FORWARD); return 0; - }, BUTTON_BACKGROUND | NO_FOCUS); + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_MIN); return 0; - }, BUTTON_BACKGROUND | NO_FOCUS); + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_MAX); return 0; - }, BUTTON_BACKGROUND | NO_FOCUS); + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 55, 50, 110, 30}, "DEFAULT", [=]() -> uint8_t { putEvent(EVT_VIRTUAL_KEY_DEFAULT); return 0; - }, BUTTON_BACKGROUND | NO_FOCUS); + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); } NumberKeyboard::~NumberKeyboard() diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index 5b5a36fc4..fc9c8759f 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -51,7 +51,7 @@ class Theme { virtual const BitmapBuffer * getIcon(uint8_t index, IconState state) const = 0; virtual const BitmapBuffer * getIconMask(uint8_t index) const = 0; - virtual TextButton * createTextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND) const + virtual TextButton * createTextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = OPAQUE | BUTTON_BACKGROUND) const { return new TextButton(parent, rect, text, pressHandler, windowFlags); } From 8a196f59822458ce77342e569d54c4151cc73edb Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 10 Mar 2021 09:41:37 +0100 Subject: [PATCH 329/714] Allow ModalWindows not to be closed when touching outside --- radio/src/thirdparty/libopenui/src/choice.h | 2 +- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- radio/src/thirdparty/libopenui/src/menu.h | 4 ++-- radio/src/thirdparty/libopenui/src/modal_window.cpp | 5 +++-- radio/src/thirdparty/libopenui/src/modal_window.h | 10 +++++----- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 9b606fb78..a92767ddd 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -45,7 +45,7 @@ class ChoiceBase : public FormField ChoiceType type; }; -class Choice : public ChoiceBase { +class Choice: public ChoiceBase { public: Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); Choice(FormGroup * parent, const rect_t & rect, std::vector values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 22b5e3c0c..3258d429c 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -154,7 +154,7 @@ void MenuWindowContent::paint(BitmapBuffer * dc) } Menu::Menu(Window * parent, bool multiple): - ModalWindow(parent), + ModalWindow(parent, true), content(createMenuWindow(this)), multiple(multiple) { diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index de9c65a36..b93486170 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -27,13 +27,13 @@ class Menu; class MenuWindowContent; -class MenuBody : public Window +class MenuBody: public Window { friend class MenuWindowContent; friend class Menu; class MenuLine { - friend class MenuBody; + friend class MenuBody; public: MenuLine(std::string text, std::function onPress, std::function isChecked): diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 5ddf49cc9..14c6888e2 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -22,8 +22,9 @@ #include "font.h" #include "layer.h" -ModalWindow::ModalWindow(Window * parent): - Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}) +ModalWindow::ModalWindow(Window * parent, bool closeWhenClickOutside): + Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}), + closeWhenClickOutside(closeWhenClickOutside) { Layer::push(this); } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 75fd1b8ff..e4580e689 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -24,7 +24,7 @@ class ModalWindow: public Window { public: - explicit ModalWindow(Window * parent); + explicit ModalWindow(Window * parent, bool closeWhenClickOutside = false); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -45,7 +45,7 @@ class ModalWindow: public Window bool onTouchEnd(coord_t x, coord_t y) override { - if (!Window::onTouchEnd(x, y) && closeAllowed) { + if (!Window::onTouchEnd(x, y) && closeWhenClickOutside) { onKeyPress(); deleteLater(); } @@ -59,13 +59,13 @@ class ModalWindow: public Window } #endif - void setCloseAllowed(bool value = true) + void setCloseWhenClickOutside(bool value = true) { - closeAllowed = value; + closeWhenClickOutside = value; } protected: - bool closeAllowed = true; + bool closeWhenClickOutside; }; class ModalWindowContent: public Window From f0d963386f9c2d6cbd1d68e1de411cef35adddfc Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 10 Mar 2021 10:43:36 +0100 Subject: [PATCH 330/714] Avoid a invalidate when not needed --- radio/src/thirdparty/libopenui/src/progress.h | 12 +++++++----- radio/src/thirdparty/libopenui/src/static.h | 9 ++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/progress.h b/radio/src/thirdparty/libopenui/src/progress.h index 7653382bb..5cd59cdc6 100644 --- a/radio/src/thirdparty/libopenui/src/progress.h +++ b/radio/src/thirdparty/libopenui/src/progress.h @@ -21,7 +21,8 @@ #include "window.h" -class Progress : public Window { +class Progress: public Window +{ public: Progress(Window * parent, const rect_t & rect): Window(parent, rect) @@ -35,10 +36,12 @@ class Progress : public Window { } #endif - void setValue(int value) + void setValue(int newValue) { - this->value = value; - invalidate(); + if (newValue != value) { + value = newValue; + invalidate(); + } } void paint(BitmapBuffer * dc) override; @@ -46,4 +49,3 @@ class Progress : public Window { protected: int value = 0; }; - diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index e66315ce5..d7baa0a5a 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -45,8 +45,10 @@ class StaticText: public Window void setText(std::string value) { - text = std::move(value); - invalidate(); + if (text != value) { + text = std::move(value); + invalidate(); + } } void setBackgroundColor(LcdFlags color) @@ -59,7 +61,8 @@ class StaticText: public Window LcdFlags bgColor = 0; }; -class Subtitle: public StaticText { +class Subtitle: public StaticText +{ public: Subtitle(Window * parent, const rect_t & rect, const char * text): StaticText(parent, rect, text, 0, FONT(BOLD)) From d8497a8bbc3a4583c822185a782a6b734a1a2c6b Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 10 Mar 2021 11:03:22 +0100 Subject: [PATCH 331/714] Cosmetics --- radio/src/thirdparty/libopenui/src/progress.h | 3 +-- radio/src/thirdparty/libopenui/src/static.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/progress.h b/radio/src/thirdparty/libopenui/src/progress.h index 5cd59cdc6..b69dc6f82 100644 --- a/radio/src/thirdparty/libopenui/src/progress.h +++ b/radio/src/thirdparty/libopenui/src/progress.h @@ -21,8 +21,7 @@ #include "window.h" -class Progress: public Window -{ +class Progress: public Window { public: Progress(Window * parent, const rect_t & rect): Window(parent, rect) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index d7baa0a5a..6ead718ec 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -61,8 +61,7 @@ class StaticText: public Window LcdFlags bgColor = 0; }; -class Subtitle: public StaticText -{ +class Subtitle: public StaticText { public: Subtitle(Window * parent, const rect_t & rect, const char * text): StaticText(parent, rect, text, 0, FONT(BOLD)) From 773e635bbd3c0ae587874892f8efaca7f6973786 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 12 Mar 2021 11:07:30 +0100 Subject: [PATCH 332/714] pushEvent insteado of putEvent --- .../thirdparty/libopenui/src/keyboard_number.cpp | 14 +++++++------- .../src/thirdparty/libopenui/src/keyboard_text.cpp | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index a8891e048..8a07dd038 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -30,43 +30,43 @@ NumberKeyboard::NumberKeyboard() : { new TextButton(this, {LCD_W / 2 - 115, 10, 50, 30}, "<<", [=]() -> uint8_t { - putEvent(EVT_VIRTUAL_KEY_BACKWARD); + pushEvent(EVT_VIRTUAL_KEY_BACKWARD); return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", [=]() -> uint8_t { - putEvent(EVT_VIRTUAL_KEY_MINUS); + pushEvent(EVT_VIRTUAL_KEY_MINUS); return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", [=]() -> uint8_t { - putEvent(EVT_VIRTUAL_KEY_PLUS); + pushEvent(EVT_VIRTUAL_KEY_PLUS); return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", [=]() -> uint8_t { - putEvent(EVT_VIRTUAL_KEY_FORWARD); + pushEvent(EVT_VIRTUAL_KEY_FORWARD); return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", [=]() -> uint8_t { - putEvent(EVT_VIRTUAL_KEY_MIN); + pushEvent(EVT_VIRTUAL_KEY_MIN); return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", [=]() -> uint8_t { - putEvent(EVT_VIRTUAL_KEY_MAX); + pushEvent(EVT_VIRTUAL_KEY_MAX); return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); new TextButton(this, {LCD_W / 2 - 55, 50, 110, 30}, "DEFAULT", [=]() -> uint8_t { - putEvent(EVT_VIRTUAL_KEY_DEFAULT); + pushEvent(EVT_VIRTUAL_KEY_DEFAULT); return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 67eabe4b2..717afde31 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -146,7 +146,7 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) } else if (*key == KEYBOARD_SPACE[0]) { if (x <= 135) { - putEvent(EVT_VIRTUAL_KEY(' ')); + pushEvent(EVT_VIRTUAL_KEY(' ')); return true; } x -= 135; @@ -164,7 +164,7 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) uint8_t specialKey = *key; if (specialKey == 128) { // backspace - putEvent(EVT_VIRTUAL_KEY(KEYBOARD_BACKSPACE[0])); + pushEvent(EVT_VIRTUAL_KEY(KEYBOARD_BACKSPACE[0])); } else { layout = KEYBOARD_LAYOUTS[specialKey - 129]; @@ -176,7 +176,7 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) } else { if (x <= 30) { - putEvent(EVT_VIRTUAL_KEY(*key)); + pushEvent(EVT_VIRTUAL_KEY(*key)); return true; } x -= 30; From 3abe7f7094eb963df181bfc2e0420b02390544b1 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 17 Mar 2021 11:43:35 +0100 Subject: [PATCH 333/714] Don't truncate the lines on chinese chars --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 ++ .../src/thirdparty/libopenui/src/libopenui_helpers.h | 11 +++++++++++ radio/src/thirdparty/libopenui/src/static.cpp | 5 ++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index e3ef9ed3f..4db3e7ed7 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -639,6 +639,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ while (len--) { unsigned int c = uint8_t(*s); + // TRACE("c = %d %o 0x%X '%c'", c, c, c, c); if (!c) { break; @@ -647,6 +648,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ // CJK char s++; c = uint8_t(*s) + ((c & 0x01u) << 8u) - 1; + // TRACE("CJK = %d", c); if (c >= 0x101) c -= 1; c += CJK_FIRST_LETTER_INDEX; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index 5c5597be4..b91388966 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -79,3 +79,14 @@ inline std::string TEXT_AT_INDEX(const char * val, uint8_t idx) return std::string(start, min(val[0], (uint8_t)strlen(start))); } +inline const char * findNextLine(const char * stack) +{ + while (true) { + const char * pos = strchr(stack, '\n'); + if (!pos) + return nullptr; + if (pos == stack || *((uint8_t *)(pos - 1)) < 0xFE) + return pos; + stack = pos + 1; + } +} diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 8d1ef9c40..4deb4e998 100755 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -37,13 +37,13 @@ void StaticText::paint(BitmapBuffer * dc) coord_t y = (textFlags & VCENTERED) ? (rect.h - getFontHeight(textFlags)) / 2 : FIELD_PADDING_TOP; auto start = text.c_str(); - auto nextline = strchr(start, '\n'); + auto nextline = findNextLine(start); if (nextline) { auto current = start; do { dc->drawText(x, y, text.substr(current - start, nextline - current).c_str(), textFlags); current = nextline + 1; - nextline = strchr(current, '\n'); + nextline = findNextLine(current); y += getFontHeight(textFlags) + 2; } while (nextline); dc->drawText(x, y, current, textFlags); @@ -51,5 +51,4 @@ void StaticText::paint(BitmapBuffer * dc) else { dc->drawText(x, y, start, textFlags); } - } From 7d0843c02115bc7356bb424fc915c652deaebf78 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 19 Mar 2021 10:22:56 +0100 Subject: [PATCH 334/714] TextEdit fix --- radio/src/thirdparty/libopenui/src/textedit.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index a7aa90e33..096a8fdf8 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -148,6 +148,10 @@ void TextEdit::onEvent(event_t event) case EVT_KEY_BREAK(KEY_ENTER): if (cursorPos < length - 1) { + if (value[cursorPos] == '\0') { + value[cursorPos] = ' '; + changed = true; + } cursorPos++; if (value[cursorPos] == '\0') { value[cursorPos] = ' '; From 6fbe5c454799b5f37c6bf6255e235b719c4e6ff6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 20 Mar 2021 12:46:13 +0100 Subject: [PATCH 335/714] Window::setInsideParentScrollingArea added --- radio/src/thirdparty/libopenui/src/window.cpp | 12 ++++++++++++ radio/src/thirdparty/libopenui/src/window.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 5fa606e96..f5022b4df 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -294,6 +294,18 @@ bool Window::isChildVisible(const Window * window) const return false; } +void Window::setInsideParentScrollingArea() +{ + Window * parent = getParent(); + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + if (parent) { + parent->scrollTo(this); + invalidate(); + } +} + void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) { coord_t x = dc->getOffsetX(); diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 888f3c245..0f8c15bbe 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -337,6 +337,8 @@ class Window return parent && right() >= parent->getScrollPositionX() && left() <= parent->getScrollPositionX() + parent->width(); } + void setInsideParentScrollingArea(); + void drawVerticalScrollbar(BitmapBuffer * dc); void drawHorizontalScrollbar(BitmapBuffer * dc); From 40770e6044d7928030f73a8fef5b3fcabe194ef4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 26 Mar 2021 19:15:45 +0100 Subject: [PATCH 336/714] Allow to delete a char in TextEdit with PAGE key --- radio/src/thirdparty/libopenui/src/textedit.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 096a8fdf8..864399127 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -204,6 +204,16 @@ void TextEdit::onEvent(event_t event) killEvents(KEY_LEFT); } break; + + case EVT_KEY_BREAK(KEY_PGDN): + memmove(&value[cursorPos], &value[cursorPos + 1], length - cursorPos - 1); + value[length - 1] = '\0'; + changed = true; + if (cursorPos > 0 && value[cursorPos] == '\0') { + cursorPos = cursorPos - 1; + } + invalidate(); + break; } if (c != v) { From 1513ab1bd5e3434c4cf147c229b61f2a6b3a0a85 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 29 Mar 2021 14:57:27 +0200 Subject: [PATCH 337/714] [NumberEdit] add an option to avoid instant change on modification (change is done later when edit mode is disabled) --- .../thirdparty/libopenui/src/basenumberedit.h | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 97061798c..cefe8c081 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -21,7 +21,7 @@ #include "form.h" -class BaseNumberEdit : public FormField +class BaseNumberEdit: public FormField { public: BaseNumberEdit(Window * parent, const rect_t &rect, int vmin, int vmax, @@ -76,10 +76,23 @@ class BaseNumberEdit : public FormField void setValue(int value) { - _setValue(limit(vmin, value, vmax)); + currentValue = limit(vmin, value, vmax); + if (instantChange) { + _setValue(currentValue); + } invalidate(); } + void enableInstantChange(bool value) + { + instantChange = value; + } + + void disableInstantChange() + { + enableInstantChange(false); + } + void setSetValueHandler(std::function handler) { _setValue = std::move(handler); @@ -87,7 +100,21 @@ class BaseNumberEdit : public FormField int32_t getValue() const { - return _getValue(); + return (editMode && !instantChange) ? currentValue : _getValue(); + } + + void setEditMode(bool newEditMode) override + { + auto previousEditMode = editMode; + FormField::setEditMode(newEditMode); + if (!instantChange) { + if (!previousEditMode && newEditMode) { + currentValue = _getValue(); + } + else if (previousEditMode && !newEditMode) { + _setValue(currentValue); + } + } } protected: @@ -95,7 +122,8 @@ class BaseNumberEdit : public FormField int vmin; int vmax; int step = 1; + int currentValue; + bool instantChange = true; std::function _getValue; std::function _setValue; }; - From 5c9304fcfd9d410de8dfb24b534146ed64c47691 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 29 Mar 2021 15:00:14 +0200 Subject: [PATCH 338/714] Cosmetics --- radio/src/thirdparty/libopenui/src/bitfield.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitfield.h b/radio/src/thirdparty/libopenui/src/bitfield.h index 093119613..2bcf1029d 100644 --- a/radio/src/thirdparty/libopenui/src/bitfield.h +++ b/radio/src/thirdparty/libopenui/src/bitfield.h @@ -29,10 +29,7 @@ inline T bfBit(uint8_t n) return T(1) << n; } -#define BFBIT_SET(y, mask) ( y |= (mask) ) -#define BFBIT_CLEAR(y, mask) ( y &= ~(mask) ) #define BFBIT_FLIP(y, mask) ( y ^= (mask) ) -#define BF_SINGLE_BIT_SET(y, i) BFBIT_SET(y, bfBit(i)) //! Create a bitmask of length 'len'. template From a8c61f740e22099ec0e17f2bd21128a029622df4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 2 Apr 2021 16:04:43 +0200 Subject: [PATCH 339/714] Warning fix --- radio/src/thirdparty/libopenui/src/layer.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index 379a8a267..b39e0457b 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -37,14 +37,14 @@ class Layer static void push(Window * window) { if (!stack.empty()) { - Layer &parentLayer = stack.back(); + Layer & parentLayer = stack.back(); parentLayer.focus = Window::getFocus(); } stack.emplace_back(Layer(window)); #if defined(DEBUG_WINDOWS) TRACE_WINDOWS("New layer added for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); - for (auto layer: stack) { + for (auto & layer: stack) { TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); } #endif @@ -71,8 +71,8 @@ class Layer #if defined(DEBUG_WINDOWS) TRACE_WINDOWS("Layer removed for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); - for (auto layer: stack) { - TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); + for (auto & layer: stack) { + TRACE(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); } #endif } From 791eec0235ea7f7c7313d6a397f180922672a7a0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 5 Apr 2021 14:58:30 +0200 Subject: [PATCH 340/714] fix offset applied twice in drawFilledRect() note: it seems there is no 'x' clipping in drawHorizontalLine(). This should probably be checked. --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 21 ++++++++++++++++--- .../thirdparty/libopenui/src/bitmapbuffer.h | 1 + 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 4db3e7ed7..b178dfe04 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -45,13 +45,20 @@ void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t p { APPLY_OFFSET(); + // line is off-screen if (y >= _height || y >= ymax || y < ymin) return; + // clip width if (x + w > _width) { w = _width - x; } + drawHorizontalLineAbs(x, y, w, pat, flags); +} + +void BitmapBuffer::drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags) +{ pixel_t * p = getPixelPtr(x, y); pixel_t color = lcdColorTable[COLOR_IDX(flags)]; uint8_t opacity = 0x0F - (flags >> 24); @@ -221,28 +228,36 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui { APPLY_OFFSET(); + // start is off-screen if (x >= xmax || y >= ymax) return; + // make height positive if (h < 0) { y += h; h = -h; } + + // clip y if (y < ymin) { h += y-ymin; y = ymin; } + + if (y + h > ymax) + h = ymax - y; + + // clip x if (x < xmin) { w += x - xmin; x = xmin; } - if (y + h > ymax) - h = ymax - y; + if (x + w > xmax) w = xmax - x; for (coord_t i = y; i < y + h; i++) { - drawHorizontalLine(x, i, w, pat, flags); + drawHorizontalLineAbs(x, i, w, pat, flags); } } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index e0243e56d..1c0c161d0 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -516,6 +516,7 @@ class BitmapBuffer: public BitmapBufferBase static BitmapBuffer * load_bmp(const char * filename); static BitmapBuffer * load_stb(const char * filename); uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); + void drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags); }; extern BitmapBuffer * lcd; From 9858ce217e35501cdca418d58809b4ed1c1b4de6 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 6 Apr 2021 08:47:09 +0200 Subject: [PATCH 341/714] [Checkbox] Fix when disabled --- radio/src/thirdparty/libopenui/src/button.cpp | 8 +++----- radio/src/thirdparty/libopenui/src/checkbox.cpp | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 3f2b349db..150381057 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -36,11 +36,9 @@ void Button::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("Button").c_str(), event); - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - if (enabled) { - onKeyPress(); - onPress(); - } + if (enabled && event == EVT_KEY_BREAK(KEY_ENTER)) { + onKeyPress(); + onPress(); } else { FormField::onEvent(event); diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 18f0cb458..8fea8fcb3 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -30,7 +30,7 @@ void CheckBox::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("CheckBox").c_str(), event); - if (event == EVT_KEY_BREAK(KEY_ENTER)) { + if (enabled && event == EVT_KEY_BREAK(KEY_ENTER)) { onKeyPress(); _setValue(!getValue()); invalidate(); From 9291675a2393a44d6c1b40b8d8f57f5bc373783f Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 6 Apr 2021 08:47:54 +0200 Subject: [PATCH 342/714] [Checkbox] Fix when disabled --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 8fea8fcb3..9dd22bcfd 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -44,10 +44,12 @@ void CheckBox::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool CheckBox::onTouchEnd(coord_t x, coord_t y) { - onKeyPress(); - _setValue(!getValue()); - setFocus(SET_FOCUS_DEFAULT); - invalidate(); + if (enabled) { + onKeyPress(); + _setValue(!getValue()); + setFocus(SET_FOCUS_DEFAULT); + invalidate(); + } return true; } #endif From d1ab02b5c9ad853f03bdfef3dafc90969ad85e45 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 6 Apr 2021 08:51:47 +0200 Subject: [PATCH 343/714] MainWindow is a singleton This prevents double instantiation by design rather than by convention. --- radio/src/thirdparty/libopenui/src/mainwindow.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 2b4368917..682d200ca 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -25,15 +25,16 @@ class MainWindow: public Window { - public: + // singleton MainWindow(): Window(nullptr, {0, 0, LCD_W, LCD_H}), invalidatedRect(rect) { Layer::push(this); - _instance = this; } + public: + ~MainWindow() override { children.clear(); @@ -41,6 +42,9 @@ class MainWindow: public Window static MainWindow * instance() { + if (!_instance) + _instance = new MainWindow(); + return _instance; } From 054411f53a89c33f11da5a4ab8d2a1eb70a200d3 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 6 Apr 2021 09:23:32 +0200 Subject: [PATCH 344/714] Update to latest libopenui --- radio/src/thirdparty/libopenui/src/mainwindow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 682d200ca..8a3a5f710 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -25,6 +25,7 @@ class MainWindow: public Window { + protected: // singleton MainWindow(): Window(nullptr, {0, 0, LCD_W, LCD_H}), @@ -34,7 +35,6 @@ class MainWindow: public Window } public: - ~MainWindow() override { children.clear(); From da2215d01e8c3c8579712379f585496d99a90bac Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 6 Apr 2021 11:19:14 +0200 Subject: [PATCH 345/714] MainWindow::create() added for automatic tests --- radio/src/thirdparty/libopenui/src/mainwindow.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 8a3a5f710..fc47bdbc3 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -48,6 +48,13 @@ class MainWindow: public Window return _instance; } +#if defined(TESTS) + static void create() + { + _instance = new MainWindow(); + } +#endif + #if defined(DEBUG_WINDOWS) std::string getName() const override { From e7bcd7b852a37b6e4b0cafb34d86721abcd05d99 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 6 Apr 2021 15:16:24 +0200 Subject: [PATCH 346/714] [Carousel] Protection --- radio/src/thirdparty/libopenui/src/carousel.cpp | 2 +- radio/src/thirdparty/libopenui/src/carousel.h | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp index 5495ba000..81c383071 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.cpp +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -25,7 +25,7 @@ void CarouselWindow::update() first = max(0, first); coord_t lastPosition = 0; coord_t spacing = 10; - if (count > 1) + if (items.size() > 0 && count > 0) spacing = (width() - items[0]->back->width() * (count - 1) - items[0]->front->width()) / (count - 1); int index = 0; diff --git a/radio/src/thirdparty/libopenui/src/carousel.h b/radio/src/thirdparty/libopenui/src/carousel.h index 661fa58b5..613548e9f 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.h +++ b/radio/src/thirdparty/libopenui/src/carousel.h @@ -55,7 +55,7 @@ class CarouselWindow: public Window { } - void deleteLater(bool detach = true, bool trash = true) override + void deleteLater(bool detach = true, bool trash = true) override // NOLINT(google-default-arguments) { if (_deleted) return; @@ -86,9 +86,11 @@ class CarouselWindow: public Window void select(int index, bool scroll = true) { selection = index; - auto item = items[selection]; - if (item->selectHandler) { - item->selectHandler(); + if (selection >= 0) { + auto item = items[selection]; + if (item->selectHandler) { + item->selectHandler(); + } } update(); } @@ -96,7 +98,7 @@ class CarouselWindow: public Window protected: std::vector items; int selection = 0; - uint8_t count; + unsigned count; void update(); }; From e3f56def2cc2b91a6a28f4ea34e5191c2cff5b9e Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 6 Apr 2021 15:22:10 +0200 Subject: [PATCH 347/714] [Carousel] Cosmetics --- radio/src/thirdparty/libopenui/src/carousel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp index 81c383071..1a6c3d371 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.cpp +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -25,7 +25,7 @@ void CarouselWindow::update() first = max(0, first); coord_t lastPosition = 0; coord_t spacing = 10; - if (items.size() > 0 && count > 0) + if (!items.empty() && count > 0) spacing = (width() - items[0]->back->width() * (count - 1) - items[0]->front->width()) / (count - 1); int index = 0; From 41c5a2fe7c6379ebe597626f9ad02b1d4f6f7dbe Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 7 Apr 2021 15:30:48 +0200 Subject: [PATCH 348/714] Merge with latest 2.3 --- radio/src/thirdparty/libopenui/src/layer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index b39e0457b..970b5e742 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -72,7 +72,7 @@ class Layer #if defined(DEBUG_WINDOWS) TRACE_WINDOWS("Layer removed for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); for (auto & layer: stack) { - TRACE(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); + TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); } #endif } From 125a0b826620bcdf361cfe6139dee98ae3afe176 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 11 Apr 2021 11:58:38 +0200 Subject: [PATCH 349/714] Add more clipping --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 78 ++----------------- .../thirdparty/libopenui/src/bitmapbuffer.h | 35 +++++++++ 2 files changed, 41 insertions(+), 72 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index b178dfe04..44d823a8a 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -45,15 +45,10 @@ void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t p { APPLY_OFFSET(); - // line is off-screen - if (y >= _height || y >= ymax || y < ymin) + coord_t h = 1; + if (!applyClippingRect(x, y, w, h)) return; - // clip width - if (x + w > _width) { - w = _width - x; - } - drawHorizontalLineAbs(x, y, w, pat, flags); } @@ -87,24 +82,8 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat { APPLY_OFFSET(); - if (x < xmin || x >= xmax) - return; - - if (h < 0) { - y += h; - h = -h; - } - - if (y < ymin) { - h += y - ymin; - y = ymin; - } - - if (y + h > ymax) { - h = ymax - y; - } - - if (h <= 0) + coord_t w = 1; + if (!applyClippingRect(x, y, w, h)) return; pixel_t color = lcdColorTable[COLOR_IDX(flags)]; @@ -198,27 +177,7 @@ void BitmapBuffer::drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t { APPLY_OFFSET(); - if (x >= xmax || y >= ymax) - return; - - if (h < 0) { - y += h; - h = -h; - } - if (y < ymin) { - h += y-ymin; - y = ymin; - } - if (x < xmin) { - w += x - xmin; - x = xmin; - } - if (y + h > ymax) - h = ymax - y; - if (x + w > xmax) - w = xmax - x; - - if (!data || h<=0 || w<=0) + if (!applyClippingRect(x, y, w, h)) return; DMAFillRect(data, _width, _height, x, y, w, h, lcdColorTable[COLOR_IDX(flags)]); @@ -228,34 +187,9 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui { APPLY_OFFSET(); - // start is off-screen - if (x >= xmax || y >= ymax) + if (!applyClippingRect(x, y, w, h)) return; - // make height positive - if (h < 0) { - y += h; - h = -h; - } - - // clip y - if (y < ymin) { - h += y-ymin; - y = ymin; - } - - if (y + h > ymax) - h = ymax - y; - - // clip x - if (x < xmin) { - w += x - xmin; - x = xmin; - } - - if (x + w > xmax) - w = xmax - x; - for (coord_t i = y; i < y + h; i++) { drawHorizontalLineAbs(x, i, w, pat, flags); } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 1c0c161d0..e2440c526 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -515,6 +515,41 @@ class BitmapBuffer: public BitmapBufferBase protected: static BitmapBuffer * load_bmp(const char * filename); static BitmapBuffer * load_stb(const char * filename); + + inline bool applyClippingRect(coord_t & x, coord_t & y, coord_t & w, coord_t & h) + { + if (h < 0) { + y += h; + h = -h; + } + + if (w < 0) { + x += w; + w = -w; + } + + if (x >= xmax || y >= ymax) + return false; + + if (y < ymin) { + h += y - ymin; + y = ymin; + } + + if (x < xmin) { + w += x - xmin; + x = xmin; + } + + if (y + h > ymax) + h = ymax - y; + + if (x + w > xmax) + w = xmax - x; + + return data && h > 0 && w > 0; + } + uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); void drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags); }; From 910d1420e76252261d9ef4418704854cf379c4c5 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 11 Apr 2021 13:03:21 +0200 Subject: [PATCH 350/714] Add more clipping --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 25 +++++++------- .../thirdparty/libopenui/src/bitmapbuffer.h | 33 ++++++++++++++++--- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 44d823a8a..89060ebda 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -91,7 +91,7 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat if (pat == SOLID) { while (h--) { - drawAlphaPixel(x, y, opacity, color); + drawAlphaPixelAbs(x, y, opacity, color); y++; } } @@ -101,7 +101,7 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat } while (h--) { if (pat & 1) { - drawAlphaPixel(x, y, opacity, color); + drawAlphaPixelAbs(x, y, opacity, color); pat = (pat >> 1) | 0x80; } else { @@ -137,7 +137,7 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint /* the line is more horizontal than vertical */ for (int i = 0; i <= dxabs; i++) { if ((1 << (px % 8)) & pat) { - drawPixel(px, py, color); + drawPixelAbs(px, py, color); } y += dyabs; if (y >= dxabs) { @@ -151,7 +151,7 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint /* the line is more vertical than horizontal */ for (int i = 0; i <= dyabs; i++) { if ((1 << (py % 8)) & pat) { - drawPixel(px, py, color); + drawPixelAbs(px, py, color); } x += dxabs; if (x >= dyabs) { @@ -220,8 +220,6 @@ void BitmapBuffer::drawCircle(coord_t x, coord_t y, coord_t radius, LcdFlags fla int decisionOver2 = 1 - x1; pixel_t color = lcdColorTable[COLOR_IDX(flags)]; - APPLY_OFFSET(); - while (y1 <= x1) { drawPixel(x1 + x, y1 + y, color); drawPixel(y1 + x, x1 + y, color); @@ -263,9 +261,11 @@ void BitmapBuffer::drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFla } } -class Slope { +class Slope +{ public: - explicit Slope(int angle) { + explicit Slope(int angle) + { if (angle < 0) angle += 360; if (angle > 360) @@ -354,7 +354,6 @@ void BitmapBuffer::drawBitmapPatternPie(coord_t x, coord_t y, const uint8_t * im Slope endSlope(endAngle); pixel_t color = lcdColorTable[COLOR_IDX(flags)]; - APPLY_OFFSET(); coord_t width = *((uint16_t *)img); coord_t height = *(((uint16_t *)img) + 1); @@ -403,13 +402,13 @@ void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadiu if (dist >= internalDist && dist <= externalDist) { Slope slope(false, x1 == 0 ? 99000 : y1 * 100 / x1); if (slope.isBetween(startSlope, endSlope)) - drawPixel(x + x1, y - y1, color); + drawPixelAbs(x + x1, y - y1, color); if (slope.invertVertical().isBetween(startSlope, endSlope)) - drawPixel(x + x1, y + y1, color); + drawPixelAbs(x + x1, y + y1, color); if (slope.invertHorizontal().isBetween(startSlope, endSlope)) - drawPixel(x - x1, y + y1, color); + drawPixelAbs(x - x1, y + y1, color); if (slope.invertVertical().isBetween(startSlope, endSlope)) - drawPixel(x - x1, y - y1, color); + drawPixelAbs(x - x1, y - y1, color); } } } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index e2440c526..21a7dca5d 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -311,16 +311,26 @@ class BitmapBuffer: public BitmapBufferBase inline void drawPixel(coord_t x, coord_t y, pixel_t value) { - pixel_t * p = getPixelPtr(x, y); - drawPixel(p, value); + APPLY_OFFSET(); + + coord_t w = 1, h = 1; + if (!applyClippingRect(x, y, w, h)) + return; + + drawPixelAbs(x, y, value); } void drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color); - inline void drawAlphaPixel(coord_t x, coord_t y, uint8_t opacity, uint16_t color) + inline void drawAlphaPixel(coord_t x, coord_t y, uint8_t opacity, pixel_t value) { - pixel_t * p = getPixelPtr(x, y); - drawAlphaPixel(p, opacity, color); + APPLY_OFFSET(); + + coord_t w = 1, h = 1; + if (!applyClippingRect(x, y, w, h)) + return; + + drawAlphaPixelAbs(x, y, opacity, value); } void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat = SOLID, LcdFlags flags = 0); @@ -551,6 +561,19 @@ class BitmapBuffer: public BitmapBufferBase } uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); + + inline void drawPixelAbs(coord_t x, coord_t y, pixel_t value) + { + pixel_t * p = getPixelPtr(x, y); + drawPixel(p, value); + } + + inline void drawAlphaPixelAbs(coord_t x, coord_t y, uint8_t opacity, uint16_t color) + { + pixel_t * p = getPixelPtr(x, y); + drawAlphaPixel(p, opacity, color); + } + void drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags); }; From 46a47f225be4e1148d0814c62d82680a110a92cc Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sun, 11 Apr 2021 16:21:11 +0200 Subject: [PATCH 351/714] Cosmetics --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 28 +++---- .../thirdparty/libopenui/src/bitmapbuffer.h | 76 +++++++++++-------- 2 files changed, 58 insertions(+), 46 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 89060ebda..4d9fde4d0 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -54,7 +54,7 @@ void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t p void BitmapBuffer::drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags) { - pixel_t * p = getPixelPtr(x, y); + pixel_t * p = getPixelPtrAbs(x, y); pixel_t color = lcdColorTable[COLOR_IDX(flags)]; uint8_t opacity = 0x0F - (flags >> 24); @@ -203,7 +203,7 @@ void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFla RGB_SPLIT(color, red, green, blue); for (int i = y; i < y + h; i++) { - pixel_t * p = getPixelPtr(x, i); + pixel_t * p = getPixelPtrAbs(x, i); for (int j = 0; j < w; j++) { // TODO ASSERT_IN_DISPLAY(p); RGB_SPLIT(*p, bgRed, bgGreen, bgBlue); @@ -446,8 +446,8 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd for (coord_t row = 0; row < height; row++) { if (y + row < ymin || y + row >= ymax) continue; - pixel_t * p = getPixelPtr(x, y + row); - const pixel_t * q = mask->getPixelPtr(offsetX, row); + pixel_t * p = getPixelPtrAbs(x, y + row); + const pixel_t * q = mask->getPixelPtrAbs(offsetX, row); for (coord_t col = 0; col < width; col++) { drawAlphaPixel(p, *((uint8_t *)q), color); MOVE_TO_NEXT_RIGHT_PIXEL(p); @@ -490,10 +490,10 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, con for (coord_t row = 0; row < height; row++) { if (y + row < ymin || y + row >= ymax) continue; - pixel_t * p = getPixelPtr(x, y + row); - const pixel_t * q = mask->getPixelPtr(offsetX, offsetY + row); + pixel_t * p = getPixelPtrAbs(x, y + row); + const pixel_t * q = mask->getPixelPtrAbs(offsetX, offsetY + row); for (coord_t col = 0; col < width; col++) { - drawAlphaPixel(p, *((uint8_t *)q), *srcBitmap->getPixelPtr(row, col)); + drawAlphaPixel(p, *((uint8_t *)q), *srcBitmap->getPixelPtrAbs(row, col)); MOVE_TO_NEXT_RIGHT_PIXEL(p); MOVE_TO_NEXT_RIGHT_PIXEL(q); } @@ -536,7 +536,7 @@ void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, ypixel = y + row; } if (xpixel >= xmin && xpixel < xmax) { - pixel_t * p = getPixelPtr(xpixel, ypixel); + pixel_t * p = getPixelPtrAbs(xpixel, ypixel); if (p) drawAlphaPixel(p, *q, color); } @@ -720,7 +720,7 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) { BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(filename); if (bitmap) { - pixel_t * p = bitmap->getPixelPtr(0, 0); + pixel_t * p = bitmap->getPixelPtrAbs(0, 0); for (int i = bitmap->width() * bitmap->height(); i > 0; i--) { *((uint8_t *)p) = OPACITY_MAX - ((*p) >> 12); MOVE_TO_NEXT_RIGHT_PIXEL(p); @@ -902,7 +902,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) switch (depth) { case 16: for (int i = h - 1; i >= 0; i--) { - pixel_t * dst = bmp->getPixelPtr(0, i); + pixel_t * dst = bmp->getPixelPtrAbs(0, i); for (unsigned int j = 0; j < w; j++) { result = f_read(&imgFile, (uint8_t *)dst, 2, &read); if (result != FR_OK || read != 2) { @@ -917,7 +917,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) case 32: for (int i = h - 1; i >= 0; i--) { - pixel_t * dst = bmp->getPixelPtr(0, i); + pixel_t * dst = bmp->getPixelPtrAbs(0, i); for (unsigned int j = 0; j < w; j++) { uint32_t pixel; result = f_read(&imgFile, (uint8_t *)&pixel, 4, &read); @@ -936,7 +936,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) else { hasAlpha = true; bmp->setFormat(BMP_ARGB4444); - for (pixel_t * p = bmp->getPixelPtr(j, i); p != bmp->getPixelPtr(0, h); MOVE_TO_NEXT_RIGHT_PIXEL(p)) { + for (pixel_t * p = bmp->getPixelPtrAbs(j, i); p != bmp->getPixelPtrAbs(0, h); MOVE_TO_NEXT_RIGHT_PIXEL(p)) { pixel_t tmp = *p; *p = ((tmp >> 1) & 0x0f) + (((tmp >> 7) & 0x0f) << 4) + (((tmp >> 12) & 0x0f) << 8); } @@ -960,7 +960,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) delete bmp; return nullptr; } - pixel_t * dst = bmp->getPixelPtr(0, i); + pixel_t * dst = bmp->getPixelPtrAbs(0, i); for (uint32_t j=0; j> ((j & 1) ? 0 : 4)) & 0x0F; uint8_t val = palette[index]; @@ -1079,7 +1079,7 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) #if 0 DMABitmapConvert(bmp->data, img, w, h, n == 4 ? DMA2D_ARGB4444 : DMA2D_RGB565); #else - pixel_t * dest = bmp->getPixelPtr(0, 0); + pixel_t * dest = bmp->getPixelPtrAbs(0, 0); const uint8_t * p = img; if (n == 4) { for (int row = 0; row < h; ++row) { diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 21a7dca5d..7aeda8e0e 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -153,7 +153,7 @@ class BitmapBufferBase return _width * _height * sizeof(T); } - inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const + inline const pixel_t * getPixelPtrAbs(coord_t x, coord_t y) const { #if defined(LCD_VERTICAL_INVERT) x = _width - x - 1; @@ -278,35 +278,15 @@ class BitmapBuffer: public BitmapBufferBase drawSolidFilledRect(0, 0, _width - offsetX, _height - offsetY, flags); } - inline void drawPixel(pixel_t * p, pixel_t value) - { - if (data && (data <= p || p < data_end)) { - *p = value; - } -#if defined(DEBUG) - else if (!leakReported) { - leakReported = true; - TRACE("BitmapBuffer(%p).drawPixel(): buffer overrun, data: %p, written at: %p", this, data, p); - } -#endif - } - inline const pixel_t * getPixelPtr(coord_t x, coord_t y) const { -#if defined(LCD_VERTICAL_INVERT) - x = _width - x - 1; - y = _height - y - 1; -#endif - return &data[y * _width + x]; - } + APPLY_OFFSET(); - inline pixel_t * getPixelPtr(coord_t x, coord_t y) - { -#if defined(LCD_VERTICAL_INVERT) - x = _width - x - 1; - y = _height - y - 1; -#endif - return &data[y * _width + x]; + coord_t w = 1, h = 1; + if (!applyClippingRect(x, y, w, h)) + return nullptr; + + return getPixelPtrAbs(x, y); } inline void drawPixel(coord_t x, coord_t y, pixel_t value) @@ -484,8 +464,8 @@ class BitmapBuffer: public BitmapBufferBase scaledh = _height - y; for (int i = 0; i < scaledh; i++) { - pixel_t * p = getPixelPtr(x, y + i); - const pixel_t * qstart = bmp->getPixelPtr(srcx, srcy + int(i / scale)); + pixel_t * p = getPixelPtrAbs(x, y + i); + const pixel_t * qstart = bmp->getPixelPtrAbs(srcx, srcy + int(i / scale)); for (int j = 0; j < scaledw; j++) { const pixel_t * q = qstart; MOVE_PIXEL_RIGHT(q, int(j / scale)); @@ -526,7 +506,7 @@ class BitmapBuffer: public BitmapBufferBase static BitmapBuffer * load_bmp(const char * filename); static BitmapBuffer * load_stb(const char * filename); - inline bool applyClippingRect(coord_t & x, coord_t & y, coord_t & w, coord_t & h) + inline bool applyClippingRect(coord_t & x, coord_t & y, coord_t & w, coord_t & h) const { if (h < 0) { y += h; @@ -562,15 +542,47 @@ class BitmapBuffer: public BitmapBufferBase uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); + inline void drawPixel(pixel_t * p, pixel_t value) + { + if (data && (data <= p || p < data_end)) { + *p = value; + } +#if defined(DEBUG) + else if (!leakReported) { + leakReported = true; + TRACE("BitmapBuffer(%p).drawPixel(): buffer overrun, data: %p, written at: %p", this, data, p); + } +#endif + } + + inline const pixel_t * getPixelPtrAbs(coord_t x, coord_t y) const + { +#if defined(LCD_VERTICAL_INVERT) + x = _width - x - 1; + y = _height - y - 1; +#endif + return &data[y * _width + x]; + } + + inline pixel_t * getPixelPtrAbs(coord_t x, coord_t y) + { +#if defined(LCD_VERTICAL_INVERT) + x = _width - x - 1; + y = _height - y - 1; +#endif + return &data[y * _width + x]; + } + + inline void drawPixelAbs(coord_t x, coord_t y, pixel_t value) { - pixel_t * p = getPixelPtr(x, y); + pixel_t * p = getPixelPtrAbs(x, y); drawPixel(p, value); } inline void drawAlphaPixelAbs(coord_t x, coord_t y, uint8_t opacity, uint16_t color) { - pixel_t * p = getPixelPtr(x, y); + pixel_t * p = getPixelPtrAbs(x, y); drawAlphaPixel(p, opacity, color); } From ff2a0754d46ac7d0d92bfc03de14d015c33c1520 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 16 Apr 2021 13:27:52 +0200 Subject: [PATCH 352/714] Make setScrollPosition[X/Y]() virtual This allows for effects implemented in a specific window, like a sticky component that follows the screen wherever it goes. --- radio/src/thirdparty/libopenui/src/window.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 0f8c15bbe..b77972104 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -319,9 +319,9 @@ class Window return scrollPositionY; } - void setScrollPositionX(coord_t value); + virtual void setScrollPositionX(coord_t value); - void setScrollPositionY(coord_t value); + virtual void setScrollPositionY(coord_t value); bool isChildVisible(const Window * window) const; From 2cc3c41505ab749ef2a6b467f2f982905b830088 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 16 Apr 2021 17:34:38 +0200 Subject: [PATCH 353/714] Merge of PR#12 --- radio/src/thirdparty/libopenui/src/window.cpp | 32 +++++++++++++++---- radio/src/thirdparty/libopenui/src/window.h | 2 ++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index f5022b4df..3bd69c198 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -338,6 +338,27 @@ void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) } } +#if defined(HARDWARE_TOUCH) +coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) +{ + coord_t result = 0; + if (relativeScrollPosition > pageSize / 2) { + // closer to next page + result = (pageSize - relativeScrollPosition); + } + else { + // closer to previous page + result = (0 - relativeScrollPosition); + } + + // do not get too slow + if (abs(result) > 32) + result /= 2; + + return result; +} +#endif + void Window::checkEvents() { auto copy = children; @@ -361,18 +382,15 @@ void Window::checkEvents() #if defined(HARDWARE_TOUCH) if (touchState.event != TE_SLIDE && touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) { if (pageWidth) { - coord_t relativeScrollPosition = scrollPositionX % pageWidth; + coord_t relativeScrollPosition = getScrollPositionX() % pageWidth; if (relativeScrollPosition) { - setScrollPositionX(getScrollPositionX() - relativeScrollPosition + (relativeScrollPosition > pageWidth / 2 ? pageWidth : 0)); + setScrollPositionX(getScrollPositionX() + getSnapStep(relativeScrollPosition, pageWidth)); } } if (pageHeight) { - coord_t relativeScrollPosition = scrollPositionY % pageHeight; + coord_t relativeScrollPosition = getScrollPositionY() % pageHeight; if (relativeScrollPosition) { - if (relativeScrollPosition > 0) - setScrollPositionY(getScrollPositionY() - relativeScrollPosition + (relativeScrollPosition > pageHeight / 2 ? pageHeight : 0)); - else - setScrollPositionY(getScrollPositionY() - relativeScrollPosition - (relativeScrollPosition > -pageHeight / 2 ? 0 : pageHeight)); + setScrollPositionY(getScrollPositionY() + getSnapStep(relativeScrollPosition, pageHeight)); } } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index b77972104..0afe516f1 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -427,6 +427,8 @@ class Window } #if defined(HARDWARE_TOUCH) + static coord_t getSnapStep(coord_t relativeScrollPosition, coord_t pageSize); + virtual bool onTouchStart(coord_t x, coord_t y); virtual bool onTouchEnd(coord_t x, coord_t y); From 53d73a7952c1da6977dc52114b0f51be75d07c9a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Tue, 27 Apr 2021 09:10:49 +0200 Subject: [PATCH 354/714] Cosmetics --- radio/src/thirdparty/libopenui/src/table.cpp | 2 +- radio/src/thirdparty/libopenui/src/table.h | 30 ++++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 257eda06b..7ea4fd9a5 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -69,7 +69,7 @@ void Table::Body::paint(BitmapBuffer * dc) for (unsigned i = 0; i < line->cells.size(); i++) { auto cell = line->cells[i]; if (cell) { - cell->paint(dc, x, y, line->flags | (highlight ? MENU_HIGHLIGHT_COLOR : DEFAULT_COLOR)); + cell->paint(dc, x, y, line->flags + (highlight ? MENU_HIGHLIGHT_COLOR - COLOR_MASK(line->flags) : DEFAULT_COLOR)); } x += static_cast
(parent)->columnsWidth[i]; } diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index db392f906..ecbcaca7e 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -28,7 +28,8 @@ class Table: public FormField { public: - class Cell { + class Cell + { public: virtual ~Cell() = default; @@ -37,7 +38,8 @@ class Table: public FormField virtual bool needsInvalidate() = 0; }; - class StringCell : public Cell { + class StringCell : public Cell + { public: StringCell() = default; @@ -60,7 +62,8 @@ class Table: public FormField std::string value; }; - class DynamicStringCell : public Cell { + class DynamicStringCell : public Cell + { public: explicit DynamicStringCell(std::function getText): getText(std::move(getText)) @@ -82,9 +85,10 @@ class Table: public FormField std::function getText; }; - class CustomCell : public Cell { + class CustomCell : public Cell + { public: - explicit CustomCell(std::function paintFunction): + explicit CustomCell(std::function paintFunction): paintFunction(std::move(paintFunction)) { } @@ -103,7 +107,8 @@ class Table: public FormField std::function paintFunction; }; - class Line { + class Line + { public: explicit Line(uint8_t columnsCount): cells(columnsCount, nullptr) @@ -122,7 +127,8 @@ class Table: public FormField LcdFlags flags = TABLE_BODY_FONT; }; - class Header: public Window, public Line { + class Header: public Window, public Line + { public: Header(Table * parent, const rect_t & rect, uint8_t columnsCount): Window(parent, rect, OPAQUE), @@ -165,7 +171,7 @@ class Table: public FormField void addLine(Line * line) { lines.push_back(line); - setInnerHeight(lines.size() * TABLE_LINE_HEIGHT - 2); + setInnerHeight((int)lines.size() * TABLE_LINE_HEIGHT - 2); if (hasFocus() && selection < 0) { select(0, true); } @@ -248,7 +254,7 @@ class Table: public FormField } #endif - void deleteLater(bool detach = true, bool trash = true) override + void deleteLater(bool detach = true, bool trash = true) override // NOLINT(google-default-arguments) { if (_deleted) return; @@ -261,7 +267,7 @@ class Table: public FormField void setColumnsWidth(const coord_t values[]) { - int8_t restColumn = -1; + int restColumn = -1; coord_t restWidth = width(); for (uint8_t i = 0; i < columnsCount; i++) { auto columnWidth = values[i]; @@ -289,7 +295,7 @@ class Table: public FormField body.invalidate(); } - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override // NOLINT(google-default-arguments) { if (body.lines.empty()) { if (flag == SET_FOCUS_BACKWARD) { @@ -306,7 +312,7 @@ class Table: public FormField else { body.setFocus(flag, from); if (body.selection < 0) { - select(flag == SET_FOCUS_BACKWARD ? body.lines.size() - 1 : 0); + select(flag == SET_FOCUS_BACKWARD ? (int)body.lines.size() - 1 : 0); } } } From 7117fe1ff50de266436d0f84d50a5e0f52ae8078 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Fri, 30 Apr 2021 12:04:19 +0200 Subject: [PATCH 355/714] Malloc alignment to 32bits for H7 --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 5 +++-- radio/src/thirdparty/libopenui/src/libopenui_helpers.h | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 7aeda8e0e..77e8dfd87 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -25,6 +25,7 @@ #include "libopenui_types.h" #include "libopenui_defines.h" #include "libopenui_depends.h" +#include "libopenui_helpers.h" #include "debug.h" constexpr uint8_t SOLID = 0xFF; @@ -188,7 +189,7 @@ class RLEBitmap: _width = *((uint16_t *)rle_data); _height = *(((uint16_t *)rle_data)+1); uint32_t pixels = _width * _height; - data = (uint16_t*)malloc(pixels * sizeof(uint16_t)); + data = (uint16_t*)malloc(align32(pixels * sizeof(uint16_t))); decode((uint8_t *)data, pixels * sizeof(uint16_t), rle_data+4); data_end = data + pixels; } @@ -248,7 +249,7 @@ class BitmapBuffer: public BitmapBufferBase , leakReported(false) #endif { - data = (uint16_t *)malloc(width * height * sizeof(uint16_t)); + data = (uint16_t *)malloc(align32(width * height * sizeof(uint16_t))); data_end = data + (width * height); } diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index b91388966..a5914a95a 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -66,6 +66,12 @@ inline int mod(int k, int n) return ((k %= n) < 0) ? k + n : k; } +inline unsigned align32(unsigned n) +{ + unsigned rest = (n & 3u); + return rest ? n + 4u - rest : n; +} + template inline T sgn(T a) { From c445be3dade6a922d2d87b992c5c52b8521eeb2a Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Mon, 3 May 2021 12:34:38 +0200 Subject: [PATCH 356/714] Use latest stb as submodule --- radio/src/thirdparty/libopenui/.gitmodules | 3 + .../thirdparty/libopenui/src/bitmapbuffer.cpp | 5 +- .../libopenui/src/thirdparty/Stb/stb_image.h | 6710 ----------------- .../thirdparty/libopenui/src/thirdparty/stb | 1 + 4 files changed, 8 insertions(+), 6711 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/.gitmodules delete mode 100644 radio/src/thirdparty/libopenui/src/thirdparty/Stb/stb_image.h create mode 160000 radio/src/thirdparty/libopenui/src/thirdparty/stb diff --git a/radio/src/thirdparty/libopenui/.gitmodules b/radio/src/thirdparty/libopenui/.gitmodules new file mode 100644 index 000000000..07807ffb0 --- /dev/null +++ b/radio/src/thirdparty/libopenui/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/thirdparty/stb"] + path = src/thirdparty/stb + url = https://github.com/nothings/stb.git diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 4d9fde4d0..3043bdbdb 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1017,7 +1017,10 @@ void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) } #endif // #if defined(TRACE_STB_MALLOC) -#include "thirdparty/Stb/stb_image.h" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#undef __I +#include "thirdparty/stb/stb_image.h" // fill 'data' with 'size' bytes. return number of bytes actually read int stbc_read(void *user, char * data, int size) diff --git a/radio/src/thirdparty/libopenui/src/thirdparty/Stb/stb_image.h b/radio/src/thirdparty/libopenui/src/thirdparty/Stb/stb_image.h deleted file mode 100644 index 836ad182a..000000000 --- a/radio/src/thirdparty/libopenui/src/thirdparty/Stb/stb_image.h +++ /dev/null @@ -1,6710 +0,0 @@ -/* stb_image - v2.10 - public domain image loader - http://nothings.org/stb_image.h - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8-bit-per-channel (16 bpc not supported) - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - - Revision 2.00 release notes: - - - Progressive JPEG is now supported. - - - PPM and PGM binary formats are now supported, thanks to Ken Miller. - - - x86 platforms now make use of SSE2 SIMD instructions for - JPEG decoding, and ARM platforms can use NEON SIMD if requested. - This work was done by Fabian "ryg" Giesen. SSE2 is used by - default, but NEON must be enabled explicitly; see docs. - - With other JPEG optimizations included in this version, we see - 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup - on a JPEG on an ARM machine, relative to previous versions of this - library. The same results will not obtain for all JPGs and for all - x86/ARM machines. (Note that progressive JPEGs are significantly - slower to decode than regular JPEGs.) This doesn't mean that this - is the fastest JPEG decoder in the land; rather, it brings it - closer to parity with standard libraries. If you want the fastest - decode, look elsewhere. (See "Philosophy" section of docs below.) - - See final bullet items below for more info on SIMD. - - - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing - the memory allocator. Unlike other STBI libraries, these macros don't - support a context parameter, so if you need to pass a context in to - the allocator, you'll have to store it in a global or a thread-local - variable. - - - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and - STBI_NO_LINEAR. - STBI_NO_HDR: suppress implementation of .hdr reader format - STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API - - - You can suppress implementation of any of the decoders to reduce - your code footprint by #defining one or more of the following - symbols before creating the implementation. - - STBI_NO_JPEG - STBI_NO_PNG - STBI_NO_BMP - STBI_NO_PSD - STBI_NO_TGA - STBI_NO_GIF - STBI_NO_HDR - STBI_NO_PIC - STBI_NO_PNM (.ppm and .pgm) - - - You can request *only* certain decoders and suppress all other ones - (this will be more forward-compatible, as addition of new decoders - doesn't require you to disable them explicitly): - - STBI_ONLY_JPEG - STBI_ONLY_PNG - STBI_ONLY_BMP - STBI_ONLY_PSD - STBI_ONLY_TGA - STBI_ONLY_GIF - STBI_ONLY_HDR - STBI_ONLY_PIC - STBI_ONLY_PNM (.ppm and .pgm) - - Note that you can define multiples of these, and you will get all - of them ("only x" and "only y" is interpreted to mean "only x&y"). - - - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still - want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - - - Compilation of all SIMD code can be suppressed with - #define STBI_NO_SIMD - It should not be necessary to disable SIMD unless you have issues - compiling (e.g. using an x86 compiler which doesn't support SSE - intrinsics or that doesn't support the method used to detect - SSE2 support at run-time), and even those can be reported as - bugs so I can refine the built-in compile-time checking to be - smarter. - - - The old STBI_SIMD system which allowed installing a user-defined - IDCT etc. has been removed. If you need this, don't upgrade. My - assumption is that almost nobody was doing this, and those who - were will find the built-in SIMD more satisfactory anyway. - - - RGB values computed for JPEG images are slightly different from - previous versions of stb_image. (This is due to using less - integer precision in SIMD.) The C code has been adjusted so - that the same RGB values will be computed regardless of whether - SIMD support is available, so your app should always produce - consistent results. But these results are slightly different from - previous versions. (Specifically, about 3% of available YCbCr values - will compute different RGB results from pre-1.49 versions by +-1; - most of the deviating values are one smaller in the G channel.) - - - If you must produce consistent results with previous versions of - stb_image, #define STBI_JPEG_OLD and you will get the same results - you used to; however, you will not get the SIMD speedups for - the YCbCr-to-RGB conversion step (although you should still see - significant JPEG speedup from the other changes). - - Please note that STBI_JPEG_OLD is a temporary feature; it will be - removed in future versions of the library. It is only intended for - near-term back-compatibility use. - - - Latest revision history: - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) partial animated GIF support - limited 16-bit PSD support - minor bugs, code cleanup, and compiler warnings - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) additional corruption checking - stbi_set_flip_vertically_on_load - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD - progressive JPEG - PGM/PPM support - STBI_MALLOC,STBI_REALLOC,STBI_FREE - STBI_NO_*, STBI_ONLY_* - GIF bugfix - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) - optimize PNG - fix bug in interlaced PNG with user-specified channel count - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - urraka@github (animated gif) Junggon Kim (PNM comments) - Daniel Gibson (16-bit TGA) - - Optimizations & bugfixes - Fabian "ryg" Giesen - Arseny Kapoulkine - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson - Dave Moore Roy Eltham Hayaki Saito Phil Jordan - Won Chun Luke Graham Johan Duparc Nathan Reed - the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis - Janez Zemva John Bartholomew Michal Cichon svdijk@github - Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson - Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github - Aruelien Pocheville Thibault Reuille Cass Everitt - Ryamond Barbiero Paul Du Bois Engin Manap - Blazej Dariusz Roszkowski - Michaelangel007@github - - -LICENSE - -This software is in the public domain. Where that dedication is not -recognized, you are granted a perpetual, irrevocable license to copy, -distribute, and modify this file as you see fit. - -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 16-bit-per-channel PNG -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - no 1-bit BMP -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *comp -- outputs # of image components in image file -// int req_comp -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. -// If req_comp is non-zero, *comp has the number of components that _would_ -// have been output otherwise. E.g. if you set req_comp to 4, you will always -// get RGBA output, but you can check *comp to see if it's trivially opaque -// because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() -// can be queried for an extremely brief, end-user unfriendly explanation -// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid -// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// The output of the JPEG decoder is slightly different from versions where -// SIMD support was introduced (that is, for versions before 1.49). The -// difference is only +-1 in the 8-bit RGB channels, and only on a small -// fraction of pixels. You can force the pre-1.49 behavior by defining -// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path -// and hence cost some performance. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for req_comp - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// NOTE: not clear do we actually need this for the 64-bit path? -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; -// this is just broken and gcc are jerks for not fixing it properly -// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -static int stbi__sse2_available() -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -static int stbi__sse2_available() -{ -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later - // GCC 4.8+ has a nice way to do this - return __builtin_cpu_supports("sse2"); -#else - // portable way to do this, preferably without using GCC inline ASM? - // just bail for now. - return 0; -#endif -} -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load = flag_true_if_should_flip; -} - -static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); - - if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - stbi_uc temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } - } - } - - return result; -} - -#ifndef STBI_NO_HDR -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int w = *x, h = *y; - int depth = req_comp ? req_comp : *comp; - int row,col,z; - float temp; - - // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once - for (row = 0; row < (h>>1); row++) { - for (col = 0; col < w; col++) { - for (z = 0; z < depth; z++) { - temp = result[(row * w + col) * depth + z]; - result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; - result[((h - row - 1) * w + col) * depth + z] = temp; - } - } - } - } -} -#endif - -#ifndef STBI_NO_STDIO - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_flip(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} -#endif //!STBI_NO_STDIO - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_flip(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_flip(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_flip(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_file(&s,f); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - - -#ifndef STBI_NO_HDR -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } -#endif // STBI_NO_HDR - - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -static void stbi__skip(stbi__context *s, int n) -{ - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc(req_comp * x * y); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define COMBO(a,b) ((a)*8+(b)) - #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (COMBO(img_n, req_comp)) { - CASE(1,2) (dest[0]=src[0], dest[1]=255); - break; - CASE(1,3) (dest[0]=dest[1]=dest[2]=src[0]); - break; - CASE(1,4) (dest[0]=dest[1]=dest[2]=src[0], dest[3]=255); - break; - CASE(2,1) (dest[0]=src[0]); - break; - CASE(2,3) (dest[0]=dest[1]=dest[2]=src[0]); - break; - CASE(2,4) (dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]); - break; - CASE(3,4) (dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255); - break; - CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); - break; - CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; - break; - CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); - break; - CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; - break; - CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; - break; - default: - STBI_ASSERT(0); - } - #undef CASE - } - - STBI_FREE(data); - return good; -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (powf(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) powf(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi_uc dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0,code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (-1 << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) << 12) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] << 2; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4; - int t = q & 15,i; - if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); - L -= 65; - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - stbi__skip(z->s, stbi__get16be(z->s)-2); - return 1; - } - return 0; -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - c = stbi__get8(s); - if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - for (i=0; i < s->img_n; ++i) { - z->img_comp[i].id = stbi__get8(s); - if (z->img_comp[i].id != i+1) // JFIF requires - if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! - return stbi__err("bad component ID","Corrupt JPEG"); - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); - - if (z->img_comp[i].raw_data == NULL) { - for(--i; i >= 0; --i) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - } - return stbi__err("outofmem", "Out of memory"); - } - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - z->img_comp[i].linebuf = NULL; - if (z->progressive) { - z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; - z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; - z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } else { - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } else if (x != 0) { - return stbi__err("junk before marker", "Corrupt JPEG"); - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -#ifdef STBI_JPEG_OLD -// this is the same YCbCr-to-RGB calculation that stb_image has used -// historically before the algorithm changes in 1.49 -#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 16) + 32768; // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr*float2fixed(1.40200f); - g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); - b = y_fixed + cb*float2fixed(1.77200f); - r >>= 16; - g >>= 16; - b >>= 16; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#else -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* float2fixed(1.40200f); - g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - #ifndef STBI_JPEG_OLD - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - #endif - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - int i; - for (i=0; i < j->s->img_n; ++i) { - if (j->img_comp[i].raw_data) { - STBI_FREE(j->img_comp[i].raw_data); - j->img_comp[i].raw_data = NULL; - j->img_comp[i].data = NULL; - } - if (j->img_comp[i].raw_coeff) { - STBI_FREE(j->img_comp[i].raw_coeff); - j->img_comp[i].raw_coeff = 0; - j->img_comp[i].coeff = 0; - } - if (j->img_comp[i].linebuf) { - STBI_FREE(j->img_comp[i].linebuf); - j->img_comp[i].linebuf = NULL; - } - } -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n; - - if (z->s->img_n == 3 && n < 3) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n; // report original components, not output - return output; - } -} - -static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char * r; - stbi__jpeg * j; - j = (stbi__jpeg *) stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return 0; - j->s = s; - stbi__setup_jpeg(j); - r = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg * j; - j = (stbi__jpeg *) stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return 0; - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int r; - stbi__jpeg * j; - j = (stbi__jpeg *) stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return 0; - j->s = s; - r = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return r; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < hlit + hdist) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else if (c == 16) { - c = stbi__zreceive(a,2)+3; - memset(lencodes+n, lencodes[n-1], c); - n += c; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - memset(lencodes+n, 0, c); - n += c; - } else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - memset(lencodes+n, 0, c); - n += c; - } - } - if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncomperssed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -// @TODO: should statically initialize these for optimal thread safety -static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; -static void stbi__init_zdefaults(void) -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncomperssed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - char * r; - stbi__zbuf * a; - a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); - if (!a) return 0; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) { - STBI_FREE(a); - return NULL; - } - a->zbuffer = (stbi_uc *) buffer; - a->zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a->zout - a->zout_start); - r = a->zout_start; - STBI_FREE(a); - return r; - } else { - STBI_FREE(a->zout_start); - STBI_FREE(a); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - char * r; - stbi__zbuf * a; - a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); - if (!a) return 0; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) { - STBI_FREE(a); - return NULL; - } - a->zbuffer = (stbi_uc *) buffer; - a->zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a->zout - a->zout_start); - r = a->zout_start; - STBI_FREE(a); - return r; - } else { - STBI_FREE(a->zout_start); - STBI_FREE(a); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - int r; - stbi__zbuf * a; - a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); - if (!a) return -1; - a->zbuffer = (stbi_uc *) ibuffer; - a->zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(a, obuffer, olen, 0, 1)) { - r = (int) (a->zout - a->zout_start); - STBI_FREE(a); - return r; - } - else { - STBI_FREE(a); - return -1; - } -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - char * r; - stbi__zbuf * a; - a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); - if (!a) return NULL; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) { - STBI_FREE(a); - return NULL; - } - a->zbuffer = (stbi_uc *) buffer; - a->zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a->zout - a->zout_start); - r = a->zout_start; - STBI_FREE(a); - return r; - } else { - STBI_FREE(a->zout_start); - STBI_FREE(a); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - int r; - stbi__zbuf * a; - a = (stbi__zbuf *) stbi__malloc(sizeof(stbi__zbuf)); - if (!a) return -1; - a->zbuffer = (stbi_uc *) ibuffer; - a->zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(a, obuffer, olen, 0, 0)) { - r = (int) (a->zout - a->zout_start); - STBI_FREE(a); - return r; - } - else { - STBI_FREE(a); - return -1; - } -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - s->img_out_n = 3; - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } else { // interlaced: - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - } - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior = cur - stride; - int filter = *raw++; - int filter_bytes = img_n; - int width = x; - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*img_n; - #define CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; - CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; - CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; - CASE(STBI__F_paeth) {cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; - CASE(STBI__F_avg_first) {cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; - CASE(STBI__F_paeth_first) {cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); if ((k&0x03) == 0x03 && cur[k]!=255) s->img_out_n = 4; } break; - } - #undef CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ - for (k=0; k < img_n; ++k) - switch (filter) { - CASE(STBI__F_none) cur[k] = raw[k]; - break; - CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); - break; - CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); - break; - CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); - break; - CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); - break; - CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); - break; - CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); - break; - } - #undef CASE - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, - a->out + (j*x+i)*out_n, out_n); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - p[0] = p[2] * 255 / a; - p[1] = p[1] * 255 / a; - p[2] = t * 255 / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - for (k=0; k < s->img_n; ++k) - tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; - if (has_trans) - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } - STBI_FREE(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) -{ - unsigned char *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - result = p->out; - p->out = NULL; -#if 0 - if (req_comp && req_comp != p->s->img_out_n) { - result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } -#endif - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_out_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -static int stbi__shiftsigned(int v, int shift, int bits) -{ - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - info->mr = info->mg = info->mb = 0; - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; - } - - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - s->img_n = 3; - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - s->img_n = 3; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) { - uint8_t alpha = STBI__BYTECAST(a); - if (alpha != 255) s->img_n = 4; - out[z++] = alpha; - } - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if(is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // else: fall-through - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fall-through - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (r * 255)/31; - out[1] = (g * 255)/31; - out[2] = (b * 255)/31; - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4]; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - int pixelCount; - int channelCount, compression; - int channel, i, count, len; - int bitdepth; - int w,h; - stbi_uc *out; - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Create the destination image. - out = (stbi_uc *) stbi__malloc(4 * w*h); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - count = 0; - while (count < pixelCount) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len ^= 0x0FF; - len += 2; - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out + channel; - if (channel >= channelCount) { - // Fill this channel with default data. - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } else { - // Read the data. - if (bitdepth == 16) { - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - - if (req_comp && req_comp != 4) { - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) -{ - stbi_uc *result; - int i, x,y; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc(x*y*4); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out, *old_out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags, delay; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif * g; - g = (stbi__gif *) stbi__malloc(sizeof(stbi__gif)); - if (!g) return 0; - if (!stbi__gif_header(s, g, comp, 1)) { - stbi__rewind( s ); - STBI_FREE(g); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; - - if (c[3] >= 128) { - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) -{ - int x, y; - stbi_uc *c = g->pal[g->bgindex]; - for (y = y0; y < y1; y += 4 * g->w) { - for (x = x0; x < x1; x += 4) { - stbi_uc *p = &g->out[y + x]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = 0; - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) -{ - int i; - stbi_uc *prev_out = 0; - - if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) - return 0; // stbi__g_failure_reason set by stbi__gif_header - - prev_out = g->out; - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - - switch ((g->eflags & 0x1C) >> 2) { - case 0: // unspecified (also always used on 1st frame) - stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); - break; - case 1: // do not dispose - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - g->old_out = prev_out; - break; - case 2: // dispose to background - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); - break; - case 3: // dispose to previous - if (g->old_out) { - for (i = g->start_y; i < g->max_y; i += 4 * g->w) - memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); - } - break; - } - - for (;;) { - switch (stbi__get8(s)) { - case 0x2C: /* Image Descriptor */ - { - int prev_trans = -1; - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - if (g->transparent >= 0 && (g->eflags & 0x01)) { - prev_trans = g->pal[g->transparent][3]; - g->pal[g->transparent][3] = 0; - } - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; - - if (prev_trans != -1) - g->pal[g->transparent][3] = (stbi_uc) prev_trans; - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = stbi__get16le(s); - g->transparent = stbi__get8(s); - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) - stbi__skip(s, len); - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } - - STBI_NOTUSED(req_comp); -} - -static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *u = 0; - stbi__gif * g; - g = (stbi__gif *) stbi__malloc(sizeof(stbi__gif)); - if (!g) return 0; - memset(g, 0, sizeof(stbi__gif)); - - u = stbi__gif_load_next(s, g, comp, req_comp); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g->w; - *y = g->h; - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g->w, g->h); - } - else if (g->out) - STBI_FREE(g->out); - - STBI_FREE(g); - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s) -{ - const char *signature = "#?RADIANCE\n"; - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s); - stbi__rewind(s); - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - - - // Check identifier - if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - // Read data - hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); - - for (k = 0; k < 4; ++k) { - i = 0; - while (i < width) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - *x = s->img_x; - *y = s->img_y; - *comp = info.ma ? 4 : 3; - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - if (stbi__get16be(s) != 8) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained; - stbi__pic_packet packets[10]; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi_uc *out; - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - *x = s->img_x; - *y = s->img_y; - *comp = s->img_n; - - out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv; - char c, p, t; - - stbi__rewind( s ); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ diff --git a/radio/src/thirdparty/libopenui/src/thirdparty/stb b/radio/src/thirdparty/libopenui/src/thirdparty/stb new file mode 160000 index 000000000..c9064e317 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/thirdparty/stb @@ -0,0 +1 @@ +Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a From 5d9f18a8afc9cd43a7e78f23a28708d428d52a47 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Wed, 5 May 2021 11:04:28 +0200 Subject: [PATCH 357/714] STB errors traced --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 3043bdbdb..1dfddada7 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1068,13 +1068,14 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) f_close(&imgFile); if (!img) { + TRACE("load_stb(%s) failed: %s", filename, stbi_failure_reason()); return nullptr; } // convert to RGB565 or ARGB4444 format BitmapBuffer * bmp = new BitmapBuffer(n == 4 ? BMP_ARGB4444 : BMP_RGB565, w, h); if (bmp == nullptr) { - TRACE("load_stb() malloc failed"); + TRACE("load_stb(%s) malloc failed", filename); stbi_image_free(img); return nullptr; } From 784bf73ae96ba88c5ec470530bda7b52a2febec4 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Thu, 6 May 2021 08:43:34 +0200 Subject: [PATCH 358/714] STB issue on H7 makes me revert to the latest working commit for now --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 8 ++++++++ radio/src/thirdparty/libopenui/src/thirdparty/stb | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 1dfddada7..0f81b3291 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1020,6 +1020,14 @@ void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) #pragma GCC diagnostic ignored "-Wsign-compare" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #undef __I +#define STBI_NO_HDR +#define STBI_NO_PIC +#define STBI_NO_GIF +#define STBI_NO_PNM +#define STBI_NO_PSD +#define STBI_NO_TGA +#define STBI_NO_BMP +#define STB_IMAGE_IMPLEMENTATION #include "thirdparty/stb/stb_image.h" // fill 'data' with 'size' bytes. return number of bytes actually read diff --git a/radio/src/thirdparty/libopenui/src/thirdparty/stb b/radio/src/thirdparty/libopenui/src/thirdparty/stb index c9064e317..7cce4c3ad 160000 --- a/radio/src/thirdparty/libopenui/src/thirdparty/stb +++ b/radio/src/thirdparty/libopenui/src/thirdparty/stb @@ -1 +1 @@ -Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a +Subproject commit 7cce4c3ad9a147c67258c5966f676d8436140939 From 8ec57dd39e3b2c9140518542db33b1e58f8b5039 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 23 Apr 2021 18:18:50 +0200 Subject: [PATCH 359/714] Move opacity in a separate parameter and use true color instead of palette index --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 58 +++++++++++-------- .../thirdparty/libopenui/src/bitmapbuffer.h | 10 ++-- .../libopenui/src/libopenui_defines.h | 26 ++------- 3 files changed, 44 insertions(+), 50 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 0f81b3291..1ad501fca 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -41,7 +41,7 @@ void BitmapBuffer::drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color) } } -void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags) +void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags, uint8_t opacity) { APPLY_OFFSET(); @@ -49,14 +49,19 @@ void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t p if (!applyClippingRect(x, y, w, h)) return; - drawHorizontalLineAbs(x, y, w, pat, flags); + drawHorizontalLineAbs(x, y, w, pat, flags, opacity); } -void BitmapBuffer::drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags) +void BitmapBuffer::drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags, uint8_t opacity) { pixel_t * p = getPixelPtrAbs(x, y); - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; - uint8_t opacity = 0x0F - (flags >> 24); + pixel_t color = COLOR_VAL(flags); + + // Opacity needs to be inverted: + // 0 : Opaque + // 15 : Translucid + // + opacity = 0x0F - opacity; if (pat == SOLID) { while (w--) { @@ -78,7 +83,7 @@ void BitmapBuffer::drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_ } } -void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags flags) +void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags flags, uint8_t opacity) { APPLY_OFFSET(); @@ -86,9 +91,13 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat if (!applyClippingRect(x, y, w, h)) return; - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; - uint8_t opacity = 0x0F - (flags >> 24); + // Opacity needs to be inverted: + // 0 : Opaque + // 15 : Translucid + // + opacity = 0x0F - opacity; + pixel_t color = COLOR_VAL(flags); if (pat == SOLID) { while (h--) { drawAlphaPixelAbs(x, y, opacity, color); @@ -120,7 +129,8 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint x2 += offsetX; y2 += offsetY; - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + // No 'opacity' here, only 'color' + pixel_t color = COLOR_VAL(flags); int dx = x2 - x1; /* the horizontal distance of the line */ int dy = y2 - y1; /* the vertical distance of the line */ @@ -163,13 +173,13 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint } } -void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags flags) +void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags flags, uint8_t opacity) { for (unsigned i = 0; i < thickness; i++) { - drawVerticalLine(x + i, y, h, pat, flags); - drawVerticalLine(x + w - 1 - i, y, h, pat, flags); - drawHorizontalLine(x, y + h - 1 - i, w, pat, flags); - drawHorizontalLine(x, y + i, w, pat, flags); + drawVerticalLine(x + i, y, h, pat, flags, opacity); + drawVerticalLine(x + w - 1 - i, y, h, pat, flags, opacity); + drawHorizontalLine(x, y + h - 1 - i, w, pat, flags, opacity); + drawHorizontalLine(x, y + i, w, pat, flags, opacity); } } @@ -180,10 +190,11 @@ void BitmapBuffer::drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t if (!applyClippingRect(x, y, w, h)) return; - DMAFillRect(data, _width, _height, x, y, w, h, lcdColorTable[COLOR_IDX(flags)]); + // No 'opacity' here, only 'color' + DMAFillRect(data, _width, _height, x, y, w, h, COLOR_VAL(flags)); } -void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags flags) +void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags flags, uint8_t opacity) { APPLY_OFFSET(); @@ -191,7 +202,7 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui return; for (coord_t i = y; i < y + h; i++) { - drawHorizontalLineAbs(x, i, w, pat, flags); + drawHorizontalLineAbs(x, i, w, pat, flags, opacity); } } @@ -199,7 +210,8 @@ void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFla { APPLY_OFFSET(); - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + // No 'opacity' here, only 'color' + pixel_t color = COLOR_VAL(flags); RGB_SPLIT(color, red, green, blue); for (int i = y; i < y + h; i++) { @@ -218,7 +230,7 @@ void BitmapBuffer::drawCircle(coord_t x, coord_t y, coord_t radius, LcdFlags fla int x1 = radius; int y1 = 0; int decisionOver2 = 1 - x1; - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + pixel_t color = COLOR_VAL(flags); while (y1 <= x1) { drawPixel(x1 + x, y1 + y, color); @@ -353,7 +365,7 @@ void BitmapBuffer::drawBitmapPatternPie(coord_t x, coord_t y, const uint8_t * im Slope startSlope(startAngle); Slope endSlope(endAngle); - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + pixel_t color = COLOR_VAL(flags); coord_t width = *((uint16_t *)img); coord_t height = *(((uint16_t *)img) + 1); @@ -390,7 +402,7 @@ void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadiu Slope startSlope(startAngle); Slope endSlope(endAngle); - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + pixel_t color = COLOR_VAL(flags); APPLY_OFFSET(); coord_t internalDist = internalRadius * internalRadius; @@ -441,7 +453,7 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) return; - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + pixel_t color = COLOR_VAL(flags); for (coord_t row = 0; row < height; row++) { if (y + row < ymin || y + row >= ymax) @@ -519,7 +531,7 @@ void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, return; } - pixel_t color = lcdColorTable[COLOR_IDX(flags)]; + pixel_t color = COLOR_VAL(flags); for (coord_t row=0; row= ymax) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 77e8dfd87..866f33336 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -314,9 +314,9 @@ class BitmapBuffer: public BitmapBufferBase drawAlphaPixelAbs(x, y, opacity, value); } - void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat = SOLID, LcdFlags flags = 0); + void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = OPACITY_MAX); - void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0); + void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = OPACITY_MAX); void drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att); @@ -330,7 +330,7 @@ class BitmapBuffer: public BitmapBufferBase drawSolidFilledRect(x, y, 1, h, flags); } - void drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, uint8_t pat = SOLID, LcdFlags flags = 0); + void drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = OPACITY_MAX); inline void drawSolidRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, LcdFlags flags = 0) { @@ -342,7 +342,7 @@ class BitmapBuffer: public BitmapBufferBase void drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); - void drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0); + void drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = OPACITY_MAX); void invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); @@ -587,7 +587,7 @@ class BitmapBuffer: public BitmapBufferBase drawAlphaPixel(p, opacity, color); } - void drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags); + void drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags, uint8_t opacity); }; extern BitmapBuffer * lcd; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index b076d120b..74b0c54d2 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -75,31 +75,13 @@ (((color) & 0x001F) << 3) #define OPACITY_MAX 0x0Fu -#define OPACITY(value) ((value) << 24u) +#define OPACITY(value) ((value) & OPACITY_MAX) #define RGB(r, g, b) (uint16_t)((((r) & 0xF8) << 8) + (((g) & 0xFC) << 3) + (((b) & 0xF8) >> 3)) #define ARGB(a, r, g, b) (uint16_t)((((a) & 0xF0) << 8) + (((r) & 0xF0) << 4) + (((g) & 0xF0) << 0) + (((b) & 0xF0) >> 4)) -#define COLOR(index) LcdFlags(unsigned(index) << 16u) -#define COLOR_IDX(flags) uint8_t((flags) >> 16u) +#define COLOR2FLAGS(color) LcdFlags(unsigned(color) << 16u) +#define COLOR_VAL(flags) ((flags) >> 16u) #define COLOR_MASK(flags) ((flags) & 0xFFFF0000u) - -#define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) -#define DEFAULT_BGCOLOR COLOR(DEFAULT_BGCOLOR_INDEX) -#define FOCUS_COLOR COLOR(FOCUS_COLOR_INDEX) -#define FOCUS_BGCOLOR COLOR(FOCUS_BGCOLOR_INDEX) -#define DISABLE_COLOR COLOR(DISABLE_COLOR_INDEX) -#define HIGHLIGHT_COLOR COLOR(HIGHLIGHT_COLOR_INDEX) -#define CHECKBOX_COLOR COLOR(CHECKBOX_COLOR_INDEX) -#define SCROLLBAR_COLOR COLOR(SCROLLBAR_COLOR_INDEX) -#define MENU_COLOR COLOR(MENU_COLOR_INDEX) -#define MENU_BGCOLOR COLOR(MENU_BGCOLOR_INDEX) -#define MENU_TITLE_BGCOLOR COLOR(MENU_TITLE_BGCOLOR_INDEX) -#define MENU_LINE_COLOR COLOR(MENU_LINE_COLOR_INDEX) -#define MENU_HIGHLIGHT_COLOR COLOR(MENU_HIGHLIGHT_COLOR_INDEX) -#define MENU_HIGHLIGHT_BGCOLOR COLOR(MENU_HIGHLIGHT_BGCOLOR_INDEX) -#define OVERLAY_COLOR COLOR(OVERLAY_COLOR_INDEX) -#define TABLE_BGCOLOR COLOR(TABLE_BGCOLOR_INDEX) -#define TABLE_HEADER_BGCOLOR COLOR(TABLE_HEADER_BGCOLOR_INDEX) -#define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX) +#define COLOR(index) (lcdColorTable[ unsigned(index) & 0xFF ] << 16u) From e251f63a35aca4beb505b02e818d28bfdbdd4e60 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 23 Apr 2021 18:50:18 +0200 Subject: [PATCH 360/714] Removed color table and added the list of colors to be defined --- .../libopenui/src/libopenui_defines.h | 1 - .../libopenui/src/libopenui_depends.h | 22 +++++++++++++++++++ .../libopenui/src/libopenui_globals.h | 6 ----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 74b0c54d2..dcf43c14b 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -83,5 +83,4 @@ #define COLOR2FLAGS(color) LcdFlags(unsigned(color) << 16u) #define COLOR_VAL(flags) ((flags) >> 16u) #define COLOR_MASK(flags) ((flags) & 0xFFFF0000u) -#define COLOR(index) (lcdColorTable[ unsigned(index) & 0xFF ] << 16u) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index df319d028..ebcc2c3ae 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -30,3 +30,25 @@ void onKeyError(); void killEvents(event_t event); event_t getWindowEvent(); +// +// These colors MUST be defined +// + +// #define DEFAULT_COLOR +// #define DEFAULT_BGCOLOR +// #define FOCUS_COLOR +// #define FOCUS_BGCOLOR +// #define DISABLE_COLOR +// #define HIGHLIGHT_COLOR +// #define CHECKBOX_COLOR +// #define SCROLLBAR_COLOR +// #define MENU_COLOR +// #define MENU_BGCOLOR +// #define MENU_TITLE_BGCOLOR +// #define MENU_LINE_COLOR +// #define MENU_HIGHLIGHT_COLOR +// #define MENU_HIGHLIGHT_BGCOLOR +// #define OVERLAY_COLOR +// #define TABLE_BGCOLOR +// #define TABLE_HEADER_BGCOLOR +// #define CUSTOM_COLOR diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.h b/radio/src/thirdparty/libopenui/src/libopenui_globals.h index 27d5b92a9..20a3e74e3 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_globals.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.h @@ -22,12 +22,6 @@ #include #include "libopenui_config.h" -extern uint16_t lcdColorTable[LCD_COLOR_COUNT]; extern const uint16_t * const fontspecsTable[FONTS_COUNT]; extern const uint8_t * fontsTable[FONTS_COUNT]; -inline void lcdSetColor(uint16_t color) -{ - lcdColorTable[CUSTOM_COLOR_INDEX] = color; -} - From 7ca1f0d74c73e6b4e3e01c1d65037007aa44fe0e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 23 Apr 2021 19:00:18 +0200 Subject: [PATCH 361/714] Fix modal window opacity --- radio/src/thirdparty/libopenui/src/modal_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 14c6888e2..e1557a17b 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -41,7 +41,7 @@ void ModalWindow::deleteLater(bool detach, bool trash) void ModalWindow::paint(BitmapBuffer * dc) { - dc->drawFilledRect(0, 0, width(), height(), SOLID, OVERLAY_COLOR | OPACITY(5)); + dc->drawFilledRect(0, 0, width(), height(), SOLID, OVERLAY_COLOR, OPACITY(5)); } void ModalWindowContent::paint(BitmapBuffer * dc) From 0faa1899845a251202f3694d7af128154209249e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 23 Apr 2021 22:29:48 +0200 Subject: [PATCH 362/714] Fixed 'opacity' default parameters --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 866f33336..622d9de6a 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -314,9 +314,9 @@ class BitmapBuffer: public BitmapBufferBase drawAlphaPixelAbs(x, y, opacity, value); } - void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = OPACITY_MAX); + void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = 0); - void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = OPACITY_MAX); + void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = 0); void drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att); @@ -330,7 +330,7 @@ class BitmapBuffer: public BitmapBufferBase drawSolidFilledRect(x, y, 1, h, flags); } - void drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = OPACITY_MAX); + void drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = 0); inline void drawSolidRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness = 1, LcdFlags flags = 0) { @@ -342,7 +342,7 @@ class BitmapBuffer: public BitmapBufferBase void drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); - void drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = OPACITY_MAX); + void drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat = SOLID, LcdFlags flags = 0, uint8_t opacity = 0); void invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); From d274f6ac7f32f7c202401b0e6bf9e8cba5ddfb33 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 23 Apr 2021 22:52:18 +0200 Subject: [PATCH 363/714] Remove the color table --- radio/src/thirdparty/libopenui/src/libopenui_globals.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp b/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp index 90cefa0ba..3bee77f51 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp @@ -19,5 +19,3 @@ #include "libopenui_globals.h" -uint16_t lcdColorTable[LCD_COLOR_COUNT]; - From 596ef31384a0ab543fe40a30309174f9efb67538 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 24 Apr 2021 11:04:15 +0200 Subject: [PATCH 364/714] ColorEdit shall use it's own color instead of modifying the color table --- radio/src/thirdparty/libopenui/src/coloredit.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index 708498433..b78ba7343 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -41,19 +41,18 @@ class ColorBox: public Window { void paint(BitmapBuffer * dc) override { - lcdSetColor(color); dc->drawSolidFilledRect(0, 0, width(), height(), DEFAULT_COLOR); - dc->drawSolidFilledRect(1, 1, width() - 2, height() - 2, CUSTOM_COLOR); + dc->drawSolidFilledRect(1, 1, width() - 2, height() - 2, color << 16); } - void setColor(LcdFlags value) + void setColor(pixel_t value) { color = value; invalidate(); } protected: - LcdFlags color; + pixel_t color; }; ColorEdit::ColorEdit(FormGroup * parent, const rect_t & rect, std::function getValue, std::function setValue): From 7510577dfcca05f938f77f11e0aeaccd844df88d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 25 Apr 2021 17:41:43 +0200 Subject: [PATCH 365/714] Use DMA ops for blending in drawFilledRect() --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 24 +++- .../thirdparty/libopenui/src/bitmapbuffer.h | 122 +++++++++--------- .../libopenui/src/libopenui_depends.h | 1 + 3 files changed, 87 insertions(+), 60 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 1ad501fca..98aed34e5 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -201,8 +201,28 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui if (!applyClippingRect(x, y, w, h)) return; - for (coord_t i = y; i < y + h; i++) { - drawHorizontalLineAbs(x, i, w, pat, flags, opacity); + // Use DMA instead... + if (SOLID != pat) { + for (coord_t i = y; i < y + h; i++) { + drawHorizontalLineAbs(x, i, w, pat, flags, opacity); + } + } + else { + // SOLID + + // Use the DMA2D to blend a scratch buffer filled with overlay color + BitmapBuffer scratch(BMP_ARGB4444, LCD_W, LCD_H, lcdGetScratchBuffer()); + RGB_SPLIT(COLOR_VAL(flags), r, g, b); + + LcdFlags flags = + uint32_t(ARGB((OPACITY_MAX - opacity) << 4, r << 3, g << 2, b << 3)) + << 16; + + // Fill the buffer + scratch.drawSolidFilledRect(0, 0, w, h, flags); + + // And blend + drawBitmapAbs(x, y, &scratch, 0, 0, w, h); } } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 622d9de6a..d11444086 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -394,6 +394,70 @@ class BitmapBuffer: public BitmapBufferBase if (x >= xmax || y >= ymax) return; + drawBitmapAbs(x, y, bmp, srcx, srcy, srcw, srch, scale); + } + + template + void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h) + { + if (bitmap) { + float vscale = float(h) / bitmap->height(); + float hscale = float(w) / bitmap->width(); + float scale = vscale < hscale ? vscale : hscale; + + int xshift = (w - (bitmap->width() * scale)) / 2; + int yshift = (h - (bitmap->height() * scale)) / 2; + drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); + } + } + + BitmapBuffer * horizontalFlip() const; + + BitmapBuffer * verticalFlip() const; + + BitmapBuffer * invertMask() const; + + protected: + static BitmapBuffer * load_bmp(const char * filename); + static BitmapBuffer * load_stb(const char * filename); + + inline bool applyClippingRect(coord_t & x, coord_t & y, coord_t & w, coord_t & h) const + { + if (h < 0) { + y += h; + h = -h; + } + + if (w < 0) { + x += w; + w = -w; + } + + if (x >= xmax || y >= ymax) + return false; + + if (y < ymin) { + h += y - ymin; + y = ymin; + } + + if (x < xmin) { + w += x - xmin; + x = xmin; + } + + if (y + h > ymax) + h = ymax - y; + + if (x + w > xmax) + w = xmax - x; + + return data && h > 0 && w > 0; + } + + template + void drawBitmapAbs(coord_t x, coord_t y, const T * bmp, coord_t srcx = 0, coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0, float scale = 0) + { coord_t bmpw = bmp->width(); coord_t bmph = bmp->height(); @@ -483,64 +547,6 @@ class BitmapBuffer: public BitmapBufferBase } } - template - void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h) - { - if (bitmap) { - float vscale = float(h) / bitmap->height(); - float hscale = float(w) / bitmap->width(); - float scale = vscale < hscale ? vscale : hscale; - - int xshift = (w - (bitmap->width() * scale)) / 2; - int yshift = (h - (bitmap->height() * scale)) / 2; - drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); - } - } - - BitmapBuffer * horizontalFlip() const; - - BitmapBuffer * verticalFlip() const; - - BitmapBuffer * invertMask() const; - - protected: - static BitmapBuffer * load_bmp(const char * filename); - static BitmapBuffer * load_stb(const char * filename); - - inline bool applyClippingRect(coord_t & x, coord_t & y, coord_t & w, coord_t & h) const - { - if (h < 0) { - y += h; - h = -h; - } - - if (w < 0) { - x += w; - w = -w; - } - - if (x >= xmax || y >= ymax) - return false; - - if (y < ymin) { - h += y - ymin; - y = ymin; - } - - if (x < xmin) { - w += x - xmin; - x = xmin; - } - - if (y + h > ymax) - h = ymax - y; - - if (x + w > xmax) - w = xmax - x; - - return data && h > 0 && w > 0; - } - uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); inline void drawPixel(pixel_t * p, pixel_t value) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index ebcc2c3ae..4a128f75b 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -23,6 +23,7 @@ #include "libopenui_config.h" void lcdNextLayer(); +uint16_t* lcdGetScratchBuffer(); void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void onKeyPress(); From 347bc76b2bda39b09d840815ac48cd5e7af644a1 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 25 Apr 2021 21:41:53 +0200 Subject: [PATCH 366/714] Move some big template methods into .cpp with explicit instantiation --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 212 +++++++++++++++++- .../thirdparty/libopenui/src/bitmapbuffer.h | 199 +--------------- 2 files changed, 223 insertions(+), 188 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 98aed34e5..745304208 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -25,7 +25,217 @@ #include "libopenui_file.h" #include "font.h" -void BitmapBuffer::drawAlphaPixel(pixel_t * p, uint8_t opacity, uint16_t color) +RLEBitmap::RLEBitmap(uint8_t format, const uint8_t* rle_data) : + BitmapBufferBase(format, 0, 0, nullptr) +{ + _width = *((uint16_t *)rle_data); + _height = *(((uint16_t *)rle_data)+1); + uint32_t pixels = _width * _height; + data = (uint16_t*)malloc(align32(pixels * sizeof(uint16_t))); + decode((uint8_t *)data, pixels * sizeof(uint16_t), rle_data+4); + data_end = data + pixels; +} + +RLEBitmap::~RLEBitmap() +{ + free(data); +} + +int RLEBitmap::decode(uint8_t *dest, unsigned int destSize, const uint8_t *src) +{ + uint8_t prevByte = 0; + bool prevByteValid = false; + + const uint8_t *destEnd = dest + destSize; + uint8_t *d = dest; + + while (d < destEnd) { + uint8_t byte = *src++; + *d++ = byte; + + if (prevByteValid && byte == prevByte) { + uint8_t count = *src++; + + if (d + count > destEnd) { + TRACE("rle_decode_8bit: destination overflow!\n"); + return -1; + } + + memset(d, byte, count); + d += count; + prevByteValid = false; + } else { + prevByte = byte; + prevByteValid = true; + } + } + + return d - dest; +} + +BitmapBuffer::BitmapBuffer(uint8_t format, uint16_t width, uint16_t height) : + BitmapBufferBase(format, width, height, nullptr), + dataAllocated(true) +#if defined(DEBUG) + , leakReported(false) +#endif +{ + data = (uint16_t *)malloc(align32(width * height * sizeof(uint16_t))); + data_end = data + (width * height); +} + +BitmapBuffer::BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, + uint16_t *data) : + BitmapBufferBase(format, width, height, data), + dataAllocated(false) +#if defined(DEBUG) + , leakReported(false) +#endif +{ +} + +BitmapBuffer::~BitmapBuffer() +{ + if (dataAllocated) { + free(data); + } +} + +template +void BitmapBuffer::drawBitmap(coord_t x, coord_t y, const T *bmp, coord_t srcx, + coord_t srcy, coord_t srcw, coord_t srch, + float scale) +{ + if (!data || !bmp) return; + APPLY_OFFSET(); + if (x >= xmax || y >= ymax) return; + drawBitmapAbs(x, y, bmp, srcx, srcy, srcw, srch, scale); +} + +template void BitmapBuffer::drawBitmap( + coord_t, coord_t, BitmapBufferBase const *, coord_t, + coord_t, coord_t, coord_t, float); + +template void BitmapBuffer::drawBitmap(coord_t, coord_t, const BitmapBuffer *, + coord_t, coord_t, coord_t, coord_t, + float); + +template void BitmapBuffer::drawBitmap(coord_t, coord_t, const RLEBitmap *, + coord_t, coord_t, coord_t, coord_t, + float); + +template +void BitmapBuffer::drawScaledBitmap(const T *bitmap, coord_t x, coord_t y, + coord_t w, coord_t h) +{ + if (bitmap) { + float vscale = float(h) / bitmap->height(); + float hscale = float(w) / bitmap->width(); + float scale = vscale < hscale ? vscale : hscale; + + int xshift = (w - (bitmap->width() * scale)) / 2; + int yshift = (h - (bitmap->height() * scale)) / 2; + drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); + } +} + +template void BitmapBuffer::drawScaledBitmap(const BitmapBuffer *, coord_t, + coord_t, coord_t, coord_t); + +template +void BitmapBuffer::drawBitmapAbs(coord_t x, coord_t y, const T *bmp, + coord_t srcx, coord_t srcy, + coord_t srcw, coord_t srch, + float scale) +{ + coord_t bmpw = bmp->width(); + coord_t bmph = bmp->height(); + + if (srcw == 0) srcw = bmpw; + if (srch == 0) srch = bmph; + if (srcx + srcw > bmpw) srcw = bmpw - srcx; + if (srcy + srch > bmph) srch = bmph - srcy; + + if (scale == 0) { + if (x < xmin) { + srcw += x - xmin; + srcx -= x - xmin; + x = xmin; + } + if (y < ymin) { + srch += y - ymin; + srcy -= y - ymin; + y = ymin; + } + if (x + srcw > xmax) { + srcw = xmax - x; + } + if (y + srch > ymax) { + srch = ymax - y; + } + } else { + if (x < xmin) { + srcw += (x - xmin) / scale; + srcx -= (x - xmin) / scale; + x = xmin; + } + if (y < ymin) { + srch += (y - ymin) / scale; + srcy -= (y - ymin) / scale; + y = ymin; + } + if (x + srcw * scale > xmax) { + srcw = (xmax - x) / scale; + } + if (y + srch * scale > ymax) { + srch = (ymax - y) / scale; + } + } + + if (srcw <= 0 || srch <= 0) { + return; + } + + if (scale == 0) { + if (bmp->getFormat() == BMP_ARGB4444) { + DMACopyAlphaBitmap(data, _width, _height, x, y, bmp->getData(), bmpw, + bmph, srcx, srcy, srcw, srch); + } else { + DMACopyBitmap(data, _width, _height, x, y, bmp->getData(), bmpw, bmph, + srcx, srcy, srcw, srch); + } + } else { + int scaledw = srcw * scale; + int scaledh = srch * scale; + + if (x + scaledw > _width) scaledw = _width - x; + if (y + scaledh > _height) scaledh = _height - y; + + for (int i = 0; i < scaledh; i++) { + pixel_t *p = getPixelPtrAbs(x, y + i); + const pixel_t *qstart = bmp->getPixelPtrAbs(srcx, srcy + int(i / scale)); + for (int j = 0; j < scaledw; j++) { + const pixel_t *q = qstart; + MOVE_PIXEL_RIGHT(q, int(j / scale)); + if (bmp->getFormat() == BMP_ARGB4444) { + ARGB_SPLIT(*q, a, r, g, b); + drawAlphaPixel(p, a, RGB_JOIN(r << 1, g << 2, b << 1)); + } else { + drawPixel(p, *q); + } + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + } +} + +template +void BitmapBuffer::drawBitmapAbs(coord_t, coord_t, const BitmapBuffer *, + coord_t srcx, coord_t srcy, + coord_t srcw, coord_t srch, + float scale); + +void BitmapBuffer::drawAlphaPixel(pixel_t *p, uint8_t opacity, uint16_t color) { if (opacity == OPACITY_MAX) { drawPixel(p, color); diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index d11444086..f447a6344 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -183,54 +183,10 @@ class RLEBitmap: public BitmapBufferBase { public: - RLEBitmap(uint8_t format, const uint8_t* rle_data) : - BitmapBufferBase(format, 0, 0, nullptr) - { - _width = *((uint16_t *)rle_data); - _height = *(((uint16_t *)rle_data)+1); - uint32_t pixels = _width * _height; - data = (uint16_t*)malloc(align32(pixels * sizeof(uint16_t))); - decode((uint8_t *)data, pixels * sizeof(uint16_t), rle_data+4); - data_end = data + pixels; - } + RLEBitmap(uint8_t format, const uint8_t* rle_data); + ~RLEBitmap(); - ~RLEBitmap() - { - free(data); - } - - static int decode(uint8_t * dest, unsigned int destSize, const uint8_t * src) - { - uint8_t prevByte = 0; - bool prevByteValid = false; - - const uint8_t * destEnd = dest + destSize; - uint8_t * d = dest; - - while (d < destEnd) { - uint8_t byte = *src++; - *d++ = byte; - - if (prevByteValid && byte == prevByte) { - uint8_t count = *src++; - - if (d + count > destEnd) { - TRACE("rle_decode_8bit: destination overflow!\n"); - return -1; - } - - memset(d, byte, count); - d += count; - prevByteValid = false; - } - else { - prevByte = byte; - prevByteValid = true; - } - } - - return d - dest; - } + static int decode(uint8_t * dest, unsigned int destSize, const uint8_t * src); }; class BitmapBuffer: public BitmapBufferBase @@ -242,32 +198,10 @@ class BitmapBuffer: public BitmapBufferBase #endif public: - BitmapBuffer(uint8_t format, uint16_t width, uint16_t height): - BitmapBufferBase(format, width, height, nullptr), - dataAllocated(true) -#if defined(DEBUG) - , leakReported(false) -#endif - { - data = (uint16_t *)malloc(align32(width * height * sizeof(uint16_t))); - data_end = data + (width * height); - } + BitmapBuffer(uint8_t format, uint16_t width, uint16_t height); + BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, uint16_t * data); - BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, uint16_t * data): - BitmapBufferBase(format, width, height, data), - dataAllocated(false) -#if defined(DEBUG) - , leakReported(false) -#endif - { - } - - ~BitmapBuffer() - { - if (dataAllocated) { - free(data); - } - } + ~BitmapBuffer(); inline void setFormat(uint8_t format) { @@ -384,32 +318,10 @@ class BitmapBuffer: public BitmapBufferBase coord_t drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); template - void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx = 0, coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0, float scale = 0) - { - if (!data || !bmp) - return; - - APPLY_OFFSET(); - - if (x >= xmax || y >= ymax) - return; - - drawBitmapAbs(x, y, bmp, srcx, srcy, srcw, srch, scale); - } + void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx = 0, coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0, float scale = 0); template - void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h) - { - if (bitmap) { - float vscale = float(h) / bitmap->height(); - float hscale = float(w) / bitmap->width(); - float scale = vscale < hscale ? vscale : hscale; - - int xshift = (w - (bitmap->width() * scale)) / 2; - int yshift = (h - (bitmap->height() * scale)) / 2; - drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); - } - } + void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h); BitmapBuffer * horizontalFlip() const; @@ -455,97 +367,10 @@ class BitmapBuffer: public BitmapBufferBase return data && h > 0 && w > 0; } - template - void drawBitmapAbs(coord_t x, coord_t y, const T * bmp, coord_t srcx = 0, coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0, float scale = 0) - { - coord_t bmpw = bmp->width(); - coord_t bmph = bmp->height(); - - if (srcw == 0) - srcw = bmpw; - if (srch == 0) - srch = bmph; - if (srcx + srcw > bmpw) - srcw = bmpw - srcx; - if (srcy + srch > bmph) - srch = bmph - srcy; - - if (scale == 0) { - if (x < xmin) { - srcw += x - xmin; - srcx -= x - xmin; - x = xmin; - } - if (y < ymin) { - srch += y - ymin; - srcy -= y - ymin; - y = ymin; - } - if (x + srcw > xmax) { - srcw = xmax - x; - } - if (y + srch > ymax) { - srch = ymax - y; - } - } - else { - if (x < xmin) { - srcw += (x - xmin) / scale; - srcx -= (x - xmin) / scale; - x = xmin; - } - if (y < ymin) { - srch += (y - ymin) / scale; - srcy -= (y - ymin) / scale; - y = ymin; - } - if (x + srcw * scale > xmax) { - srcw = (xmax - x) / scale; - } - if (y + srch * scale > ymax) { - srch = (ymax - y) / scale; - } - } - - if (srcw <= 0 || srch <= 0) { - return; - } - - if (scale == 0) { - if (bmp->getFormat() == BMP_ARGB4444) { - DMACopyAlphaBitmap(data, _width, _height, x, y, bmp->getData(), bmpw, bmph, srcx, srcy, srcw, srch); - } - else { - DMACopyBitmap(data, _width, _height, x, y, bmp->getData(), bmpw, bmph, srcx, srcy, srcw, srch); - } - } - else { - int scaledw = srcw * scale; - int scaledh = srch * scale; - - if (x + scaledw > _width) - scaledw = _width - x; - if (y + scaledh > _height) - scaledh = _height - y; - - for (int i = 0; i < scaledh; i++) { - pixel_t * p = getPixelPtrAbs(x, y + i); - const pixel_t * qstart = bmp->getPixelPtrAbs(srcx, srcy + int(i / scale)); - for (int j = 0; j < scaledw; j++) { - const pixel_t * q = qstart; - MOVE_PIXEL_RIGHT(q, int(j / scale)); - if (bmp->getFormat() == BMP_ARGB4444) { - ARGB_SPLIT(*q, a, r, g, b); - drawAlphaPixel(p, a, RGB_JOIN(r<<1, g<<2, b<<1)); - } - else { - drawPixel(p, *q); - } - MOVE_TO_NEXT_RIGHT_PIXEL(p); - } - } - } - } + template + void drawBitmapAbs(coord_t x, coord_t y, const T* bmp, coord_t srcx = 0, + coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0, + float scale = 0); uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); From ff71826fbcfde87429002d2305ffd31a9d32c8aa Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 26 Apr 2021 14:14:28 +0200 Subject: [PATCH 367/714] Use full range in 8bit encoding --- radio/src/thirdparty/libopenui/tools/encode-bitmap.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py index a6768a3cf..778324fd4 100755 --- a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py +++ b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py @@ -114,8 +114,7 @@ class ImageEncoder: for y in range(height): for x in range(width): value = self.get_pixel(image, x, y) - value = 0x0F - round(value / 16) - value = max(0x00, min(0x0F, value)) + value = 0xFF - value self.encode_byte(value) self.f.write("\n") self.encode_end() From a1cfe638b3821c4b2e96ce3cbe97e252d43c4bca Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 26 Apr 2021 14:14:49 +0200 Subject: [PATCH 368/714] WiP: works with radio --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 745304208..4fbe1e592 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -420,6 +420,9 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui else { // SOLID + + // TODO: can this be done in one step? (see drawBitmapPattern()) + // Use the DMA2D to blend a scratch buffer filled with overlay color BitmapBuffer scratch(BMP_ARGB4444, LCD_W, LCD_H, lcdGetScratchBuffer()); RGB_SPLIT(COLOR_VAL(flags), r, g, b); @@ -729,6 +732,16 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, con if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) return; + + // TODO: + // + // Try with DMA2D: + // - [ Fill ] copy scrBitmap into scratch buffer + // - ( +- [ Convert ] convert mask into ARGB4444 ) + // - [ Blend ] draw mask as alpha(ARGB4444) into scratch buffer + // - [ Blend ] blend into current buffer + // + for (coord_t row = 0; row < height; row++) { if (y + row < ymin || y + row >= ymax) continue; @@ -742,13 +755,24 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, con } } +// Apply a mask ('bmp') + color ('flags') on top of current pixels: +// +// drawAlphaPixel(bmp[x][y], pixel(x,y), color) +// +// +void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint8_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h, uint16_t bg_color); +// void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset, coord_t width) { APPLY_OFFSET(); - coord_t w = *((uint16_t *)bmp); - coord_t height = *(((uint16_t *)bmp)+1); + coord_t w = *((uint16_t *)bmp); // 'w' -> width of the font file + coord_t height = *(((uint16_t *)bmp)+1); // height of the font file + // skip header + bmp += 4; + + // 'width' -> font character we want if (!width || width > w) { width = w; } @@ -761,30 +785,11 @@ void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, return; } - pixel_t color = COLOR_VAL(flags); - - for (coord_t row=0; row= ymax) - continue; - const uint8_t * q = bmp + 4 + row*w + offset; - for (coord_t col=0; col= xmin && xpixel < xmax) { - pixel_t * p = getPixelPtrAbs(xpixel, ypixel); - if (p) - drawAlphaPixel(p, *q, color); - } - q++; - } - } + // TODO: + // - add some defs to libopenui_depends.h + // + DMACopyAlphaMask(data, _width, _height, x, y, bmp, w, height, + offset, 0, width, height, COLOR_VAL(flags)); } uint8_t BitmapBuffer::drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags) From 76cdb7f52790c7af1e30e8168ba61c12ece3c774 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 26 Apr 2021 17:04:56 +0200 Subject: [PATCH 369/714] Cleanup --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 10 +++------- radio/src/thirdparty/libopenui/src/libopenui_depends.h | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 4fbe1e592..354604818 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -420,19 +420,16 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, ui else { // SOLID - // TODO: can this be done in one step? (see drawBitmapPattern()) // Use the DMA2D to blend a scratch buffer filled with overlay color BitmapBuffer scratch(BMP_ARGB4444, LCD_W, LCD_H, lcdGetScratchBuffer()); - RGB_SPLIT(COLOR_VAL(flags), r, g, b); - LcdFlags flags = - uint32_t(ARGB((OPACITY_MAX - opacity) << 4, r << 3, g << 2, b << 3)) - << 16; + RGB_SPLIT(COLOR_VAL(flags), r, g, b); + pixel_t color_argb = ARGB((OPACITY_MAX - opacity) << 4, r << 3, g << 2, b << 3); // Fill the buffer - scratch.drawSolidFilledRect(0, 0, w, h, flags); + scratch.drawSolidFilledRect(0, 0, w, h, color_argb << 16); // And blend drawBitmapAbs(x, y, &scratch, 0, 0, w, h); @@ -760,7 +757,6 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, con // drawAlphaPixel(bmp[x][y], pixel(x,y), color) // // -void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint8_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h, uint16_t bg_color); // void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset, coord_t width) { diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index 4a128f75b..aa71e0fe6 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -26,6 +26,7 @@ void lcdNextLayer(); uint16_t* lcdGetScratchBuffer(); void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); +void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint8_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h, uint16_t bg_color); void onKeyPress(); void onKeyError(); void killEvents(event_t event); From 21940b15723608b601185a95cf10b3ca3b4090bb Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 26 Apr 2021 19:56:39 +0200 Subject: [PATCH 370/714] Fixed clipping in drawBitmapPattern() --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 354604818..237286757 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -762,30 +762,43 @@ void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, { APPLY_OFFSET(); - coord_t w = *((uint16_t *)bmp); // 'w' -> width of the font file - coord_t height = *(((uint16_t *)bmp)+1); // height of the font file + coord_t bmpw = *((uint16_t *)bmp); // 'w' -> width of the font file + coord_t bmph = *(((uint16_t *)bmp)+1); // height of the font file // skip header bmp += 4; - // 'width' -> font character we want - if (!width || width > w) { - width = w; + coord_t srcx = offset; + coord_t srcy = 0; + + coord_t srcw = (width != 0 ? width : bmpw); + coord_t srch = bmph; + if (srcx + srcw > bmpw) srcw = bmpw - srcx; + if (srcy + srch > bmph) srch = bmph - srcy; + + if (x < xmin) { + srcw += x - xmin; + srcx -= x - xmin; + x = xmin; + } + if (y < ymin) { + srch += y - ymin; + srcy -= y - ymin; + y = ymin; + } + if (x + srcw > xmax) { + srcw = xmax - x; + } + if (y + srch > ymax) { + srch = ymax - y; } - if (x + width > xmax) { - width = xmax - x; - } - - if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) { + if (srcw <= 0 || srch <= 0) { return; } - // TODO: - // - add some defs to libopenui_depends.h - // - DMACopyAlphaMask(data, _width, _height, x, y, bmp, w, height, - offset, 0, width, height, COLOR_VAL(flags)); + DMACopyAlphaMask(data, _width, _height, x, y, bmp, bmpw, bmph, + srcx, srcy, srcw, srch, COLOR_VAL(flags)); } uint8_t BitmapBuffer::drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags) From 7b634773003624975c2cbf7ecc5724983ae573f1 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 26 Apr 2021 23:17:33 +0200 Subject: [PATCH 371/714] Cleanup notes --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 237286757..c0949638b 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -683,6 +683,9 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd if (y >= ymax || x >= xmax || width <= 0 || x + width < xmin || y + height < ymin) return; + // TODO: This should be doable the same way as with + // drawBitmapPattern() (just with ARGB as input). + // pixel_t color = COLOR_VAL(flags); for (coord_t row = 0; row < height; row++) { @@ -730,15 +733,9 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, con return; - // TODO: + // TODO: This should be doable the same way as with + // drawBitmapPattern() (just with ARGB as input). // - // Try with DMA2D: - // - [ Fill ] copy scrBitmap into scratch buffer - // - ( +- [ Convert ] convert mask into ARGB4444 ) - // - [ Blend ] draw mask as alpha(ARGB4444) into scratch buffer - // - [ Blend ] blend into current buffer - // - for (coord_t row = 0; row < height; row++) { if (y + row < ymin || y + row >= ymax) continue; From c25d599393dbf90471e28b811a89801bd62a7a22 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 7 May 2021 23:58:20 +0200 Subject: [PATCH 372/714] Fixes for new Stb version As it seems, RGB888 bitmap are now declared as ARGB8888, as load_stb() requests 4 bytes / pixel. Now, real grayscale is used instead of just using "red" as alpha, as was previously done. --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index c0949638b..6254e0d89 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -974,9 +974,22 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(filename); if (bitmap) { pixel_t * p = bitmap->getPixelPtrAbs(0, 0); - for (int i = bitmap->width() * bitmap->height(); i > 0; i--) { - *((uint8_t *)p) = OPACITY_MAX - ((*p) >> 12); - MOVE_TO_NEXT_RIGHT_PIXEL(p); + if (bitmap->getFormat() == BMP_ARGB4444) { + for (int i = bitmap->width() * bitmap->height(); i > 0; i--) { + // invert red and use as alpha + ARGB_SPLIT(*p, a, r, g, b); + *((uint8_t *)p) = OPACITY_MAX - (r + g + b) / 3; + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } else { // BMP_RGB565 + for (int i = bitmap->width() * bitmap->height(); i > 0; i--) { + // invert msb 4 bits red and use as alpha + *((uint8_t *)p) = + OPACITY_MAX - + ((GET_RED(*p) >> 1) + (GET_GREEN(*p) >> 2) + (GET_BLUE(*p) >> 1)) / + 3; + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } } } return bitmap; @@ -1341,7 +1354,7 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) return nullptr; } -#if 0 +#if 0 // use Stb's "stbi__vertically_flip_on_load" instead of this? DMABitmapConvert(bmp->data, img, w, h, n == 4 ? DMA2D_ARGB4444 : DMA2D_RGB565); #else pixel_t * dest = bmp->getPixelPtrAbs(0, 0); @@ -1355,7 +1368,7 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) } } } - else { + else { // assume 3 bytes, packed in groups of 4, I guess for (int row = 0; row < h; ++row) { for (int col = 0; col < w; ++col) { *dest = RGB(p[0], p[1], p[2]); From 5403ab4b00bc39a48e9e3e35fa71174a7e20e524 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 8 May 2021 00:21:58 +0200 Subject: [PATCH 373/714] Filter out invalid chars to avoid out-of-bounds memory access --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 6254e0d89..230d3b8f9 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -856,7 +856,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ uint8_t width = drawChar(x, y, font, fontspecs, c, flags); INCREMENT_POS(width + CHAR_SPACING); } - else if (c >= 0x20) { + else if ((c >= 0x20) && (c < CJK_FIRST_LETTER_INDEX + 0x20)) { uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); if ((flags & SPACING_NUMBERS_CONST) && c >= '0' && c <= '9') INCREMENT_POS(getCharWidth('9', fontspecs) + CHAR_SPACING); From 1964d2ae8c287726685a63e45e098623561e465d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 8 May 2021 08:37:53 +0200 Subject: [PATCH 374/714] Check boundaries on characters / fonts --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 +- radio/src/thirdparty/libopenui/src/libopenui_globals.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 230d3b8f9..b7a342f3d 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -856,7 +856,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ uint8_t width = drawChar(x, y, font, fontspecs, c, flags); INCREMENT_POS(width + CHAR_SPACING); } - else if ((c >= 0x20) && (c < CJK_FIRST_LETTER_INDEX + 0x20)) { + else if ((c >= 0x20) && (c < fontCharactersTable[FONT_INDEX(flags)] + 0x20)) { uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); if ((flags & SPACING_NUMBERS_CONST) && c >= '0' && c <= '9') INCREMENT_POS(getCharWidth('9', fontspecs) + CHAR_SPACING); diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.h b/radio/src/thirdparty/libopenui/src/libopenui_globals.h index 20a3e74e3..c1352e747 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_globals.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_globals.h @@ -22,6 +22,7 @@ #include #include "libopenui_config.h" +extern const uint16_t fontCharactersTable[FONTS_COUNT]; extern const uint16_t * const fontspecsTable[FONTS_COUNT]; extern const uint8_t * fontsTable[FONTS_COUNT]; From 6a492d9777dba109c343fc00ea43d7544ad2e160 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 9 May 2021 09:29:11 +0200 Subject: [PATCH 375/714] Silence compiler warnings --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index b7a342f3d..4a611aa65 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -839,7 +839,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ const coord_t orig_pos = pos; while (len--) { - unsigned int c = uint8_t(*s); + unsigned c = uint8_t(*s); // TRACE("c = %d %o 0x%X '%c'", c, c, c, c); if (!c) { @@ -856,7 +856,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ uint8_t width = drawChar(x, y, font, fontspecs, c, flags); INCREMENT_POS(width + CHAR_SPACING); } - else if ((c >= 0x20) && (c < fontCharactersTable[FONT_INDEX(flags)] + 0x20)) { + else if ((c >= 0x20u) && (c < fontCharactersTable[FONT_INDEX(flags)] + 0x20u)) { uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); if ((flags & SPACING_NUMBERS_CONST) && c >= '0' && c <= '9') INCREMENT_POS(getCharWidth('9', fontspecs) + CHAR_SPACING); @@ -977,7 +977,8 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) if (bitmap->getFormat() == BMP_ARGB4444) { for (int i = bitmap->width() * bitmap->height(); i > 0; i--) { // invert red and use as alpha - ARGB_SPLIT(*p, a, r, g, b); + ARGB_SPLIT(*p, a __attribute__((unused)), r, g, b); + *((uint8_t *)p) = OPACITY_MAX - (r + g + b) / 3; MOVE_TO_NEXT_RIGHT_PIXEL(p); } From f603207a8ea7678018f88acf95ea71fa45396e97 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 15 May 2021 01:09:57 +0200 Subject: [PATCH 376/714] Added 8bit mask loading --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 29 +++++++++++++++++++ .../thirdparty/libopenui/src/bitmapbuffer.h | 2 ++ 2 files changed, 31 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 4a611aa65..9ab2724af 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -996,6 +996,20 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) return bitmap; } +BitmapBuffer * BitmapBuffer::load8bitMask(const uint8_t * lbm) +{ + BitmapBuffer * bitmap = new BitmapBuffer(BMP_RGB565,lbm[0],lbm[1]); + if (bitmap) { + pixel_t * p = bitmap->getPixelPtrAbs(0, 0); + const uint8_t * src = lbm + 2; + for (int i = bitmap->width() * bitmap->height(); i > 0; i--) { + *((uint8_t *)p) = (*(src++) >> 4); + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + return bitmap; +} + BitmapBuffer * BitmapBuffer::invertMask() const { BitmapBuffer * result = new BitmapBuffer(format, width(), height()); @@ -1052,6 +1066,21 @@ BitmapBuffer * BitmapBuffer::loadMaskOnBackground(const char * filename, LcdFlag return result; } +BitmapBuffer * BitmapBuffer::load8bitMaskOnBackground(const uint8_t * lbm, LcdFlags foreground, LcdFlags background) +{ + BitmapBuffer * result = nullptr; + BitmapBuffer * mask = BitmapBuffer::load8bitMask(lbm); + if (mask) { + result = new BitmapBuffer(BMP_RGB565, mask->width(), mask->height()); + if (result) { + result->clear(background); + result->drawMask(0, 0, mask, foreground); + } + delete mask; + } + return result; +} + FIL imgFile __DMA; BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index f447a6344..cc42766aa 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -293,8 +293,10 @@ class BitmapBuffer: public BitmapBufferBase static BitmapBuffer * loadBitmap(const char * filename); static BitmapBuffer * loadMask(const char * filename); + static BitmapBuffer * load8bitMask(const uint8_t * lbm); static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background); + static BitmapBuffer * load8bitMaskOnBackground(const uint8_t * lbm, LcdFlags foreground, LcdFlags background); void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offsetX = 0, coord_t width = 0); From 2d6ebc2ec18b4610c219ae88a61493a07cf41b95 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 15 May 2021 11:21:52 +0200 Subject: [PATCH 377/714] Added loading png bitmaps from memory --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 30 +++++++++++++++++-- .../thirdparty/libopenui/src/bitmapbuffer.h | 3 ++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 9ab2724af..20d9b0e26 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -969,6 +969,11 @@ BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename) return load_stb(filename); } +BitmapBuffer * BitmapBuffer::loadRamBitmap(const uint8_t * buffer, int len) +{ + return load_stb_buffer(buffer, len); +} + BitmapBuffer * BitmapBuffer::loadMask(const char * filename) { BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(filename); @@ -1376,11 +1381,31 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) return nullptr; } + BitmapBuffer * bmp = convert_stb_bitmap(img, w, h, n); + stbi_image_free(img); + return bmp; +} + +BitmapBuffer * BitmapBuffer::load_stb_buffer(const uint8_t * buffer, int len) +{ + int w, h, n; + unsigned char * img = stbi_load_from_memory(buffer, len, &w, &h, &n, 4); + if (!img) { + TRACE("load_stb_buffer(%p,%d) failed: %s", buffer, len, stbi_failure_reason()); + return nullptr; + } + + BitmapBuffer * bmp = convert_stb_bitmap(img, w, h, n); + stbi_image_free(img); + return bmp; +} + +BitmapBuffer * BitmapBuffer::convert_stb_bitmap(uint8_t * img, int w, int h, int n) +{ // convert to RGB565 or ARGB4444 format BitmapBuffer * bmp = new BitmapBuffer(n == 4 ? BMP_ARGB4444 : BMP_RGB565, w, h); if (bmp == nullptr) { - TRACE("load_stb(%s) malloc failed", filename); - stbi_image_free(img); + TRACE("convert_stn_bitmap: malloc failed"); return nullptr; } @@ -1409,6 +1434,5 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) } #endif - stbi_image_free(img); return bmp; } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index cc42766aa..770899e43 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -291,6 +291,7 @@ class BitmapBuffer: public BitmapBufferBase void drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle); static BitmapBuffer * loadBitmap(const char * filename); + static BitmapBuffer * loadRamBitmap(const uint8_t * buffer, int len); static BitmapBuffer * loadMask(const char * filename); static BitmapBuffer * load8bitMask(const uint8_t * lbm); @@ -334,6 +335,8 @@ class BitmapBuffer: public BitmapBufferBase protected: static BitmapBuffer * load_bmp(const char * filename); static BitmapBuffer * load_stb(const char * filename); + static BitmapBuffer * load_stb_buffer(const uint8_t * buffer, int len); + static BitmapBuffer * convert_stb_bitmap(uint8_t * img, int w, int h, int n); inline bool applyClippingRect(coord_t & x, coord_t & y, coord_t & w, coord_t & h) const { From 749d7d903718dc1df2b9fea8aaaa33a4a8ed74b0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 21 May 2021 20:59:33 +0200 Subject: [PATCH 378/714] Added 8bit PNG font loader --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 12 ++++++++++++ radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 20d9b0e26..42db65441 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1436,3 +1436,15 @@ BitmapBuffer * BitmapBuffer::convert_stb_bitmap(uint8_t * img, int w, int h, int return bmp; } + +uint8_t * BitmapBuffer::loadFont(const uint8_t * lbm, int len, int& w, int& h) +{ + int n; + unsigned char * font = stbi_load_from_memory(lbm, len, &w, &h, &n, 1); + if (!font) { + TRACE("load_stb_buffer(%p,%d) failed: %s", font, len, stbi_failure_reason()); + return nullptr; + } + + return font; +} diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 770899e43..e5b9b5299 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -299,6 +299,8 @@ class BitmapBuffer: public BitmapBufferBase static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background); static BitmapBuffer * load8bitMaskOnBackground(const uint8_t * lbm, LcdFlags foreground, LcdFlags background); + static uint8_t * loadFont(const uint8_t * lbm, int len, int& w, int& h); + void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offsetX = 0, coord_t width = 0); void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, const BitmapBuffer * srcBitmap, coord_t offsetX = 0, coord_t offsetY = 0, coord_t width = 0, coord_t height = 0); From e7b6fa412ce35a71b5aae9f6aed68ddaf1fcb34d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 22 May 2021 14:24:45 +0200 Subject: [PATCH 379/714] Apply clipping in BitmapBuffer::invertRect() --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 42db65441..eef66b2d1 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -440,6 +440,9 @@ void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFla { APPLY_OFFSET(); + if (!applyClippingRect(x, y, w, h)) + return; + // No 'opacity' here, only 'color' pixel_t color = COLOR_VAL(flags); RGB_SPLIT(color, red, green, blue); From 67ede465aabd3612a79e596fef7f10732347d814 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 22 May 2021 15:47:26 +0200 Subject: [PATCH 380/714] Added Liang-Barsky Line Clipping --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 95 ++++++++++++++++++- .../thirdparty/libopenui/src/bitmapbuffer.h | 2 + 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index eef66b2d1..4ed1b2c97 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -331,7 +331,97 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat } } -void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags flags) +// +// Liang-barsky clipping algo +// +static float lb_max(float arr[], int n) +{ + float m = 0; + for (int i = 0; i < n; ++i) + if (m < arr[i]) m = arr[i]; + return m; +} + +// this function gives the minimum +static float lb_min(float arr[], int n) +{ + float m = 1; + for (int i = 0; i < n; ++i) + if (m > arr[i]) m = arr[i]; + return m; +} + +bool BitmapBuffer::liangBarskyClipper(coord_t& x1, coord_t& y1, coord_t& x2, coord_t& y2) +{ + // defining variables + float p1 = -(x2 - x1); + float p2 = -p1; + float p3 = -(y2 - y1); + float p4 = -p3; + + float q1 = x1 - xmin; + float q2 = xmax - x1; + float q3 = y1 - ymin; + float q4 = ymax - y1; + + float posarr[5], negarr[5]; + int posind = 1, negind = 1; + posarr[0] = 1; + negarr[0] = 0; + + if ((p1 == 0 && q1 < 0) || (p2 == 0 && q2 < 0) || (p3 == 0 && q3 < 0) || + (p4 == 0 && q4 < 0)) { + return false; + } + + if (p1 != 0) { + float r1 = q1 / p1; + float r2 = q2 / p2; + if (p1 < 0) { + negarr[negind++] = r1; // for negative p1, add it to negative array + posarr[posind++] = r2; // and add p2 to positive array + } else { + negarr[negind++] = r2; + posarr[posind++] = r1; + } + } + if (p3 != 0) { + float r3 = q3 / p3; + float r4 = q4 / p4; + if (p3 < 0) { + negarr[negind++] = r3; + posarr[posind++] = r4; + } else { + negarr[negind++] = r4; + posarr[posind++] = r3; + } + } + + float xn1, yn1, xn2, yn2; + float rn1, rn2; + rn1 = lb_max(negarr, negind); // maximum of negative array + rn2 = lb_min(posarr, posind); // minimum of positive array + + if (rn1 > rn2) { // reject + return false; + } + + xn1 = x1 + p2 * rn1; + yn1 = y1 + p4 * rn1; // computing new points + + xn2 = x1 + p2 * rn2; + yn2 = y1 + p4 * rn2; + + x1 = coord_t(xn1); + y1 = coord_t(yn1); + x2 = coord_t(xn2); + y2 = coord_t(yn2); + + return true; +} + +void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, + uint8_t pat, LcdFlags flags) { // Offsets x1 += offsetX; @@ -339,6 +429,9 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint x2 += offsetX; y2 += offsetY; + if (!liangBarskyClipper(x1, y1, x2, y2)) + return; + // No 'opacity' here, only 'color' pixel_t color = COLOR_VAL(flags); diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index e5b9b5299..6d661ff16 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -426,6 +426,8 @@ class BitmapBuffer: public BitmapBufferBase } void drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags flags, uint8_t opacity); + + bool liangBarskyClipper(coord_t& x1, coord_t& y1, coord_t& x2, coord_t& y2); }; extern BitmapBuffer * lcd; From 52b3388062899225fd9a42e63340ea82fd986ce9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 22 May 2021 12:40:14 +0200 Subject: [PATCH 381/714] lcdRefresh() already does switch the layers, no need to do it 2 steps --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 4 ++++ radio/src/thirdparty/libopenui/src/mainwindow.cpp | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 6d661ff16..93b88a1e5 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -430,5 +430,9 @@ class BitmapBuffer: public BitmapBufferBase bool liangBarskyClipper(coord_t& x1, coord_t& y1, coord_t& x2, coord_t& y2); }; +// Back buffer to draw extern BitmapBuffer * lcd; +// Buffer currently on display +extern BitmapBuffer * lcdFront; + diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 03c761bd2..d535fdafc 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -104,13 +104,10 @@ bool MainWindow::refresh() if (invalidatedRect.w) { if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); - const BitmapBuffer * previous = lcd; - lcdNextLayer(); - lcdCopy(lcd->getData(), previous->getData()); + lcdCopy(lcd->getData(), lcdFront->getData()); } else { TRACE_WINDOWS("Refresh full screen"); - lcdNextLayer(); } lcd->setOffset(0, 0); lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); From 18025279c1bd3e64c39e0dc07739bc60cd2766eb Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 23 May 2021 08:48:17 +0200 Subject: [PATCH 382/714] Use EVT_KEY_FIRST on Button --- radio/src/thirdparty/libopenui/src/button.cpp | 3 ++- radio/src/thirdparty/libopenui/src/button.h | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 150381057..cf3e213b7 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -36,7 +36,8 @@ void Button::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("Button").c_str(), event); - if (enabled && event == EVT_KEY_BREAK(KEY_ENTER)) { + if (enabled && event == EVT_KEY_FIRST(KEY_ENTER)) { + killEvents(event); onKeyPress(); onPress(); } diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 78ce23de7..a60ab6476 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -28,9 +28,11 @@ constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3u; class Button: public FormField { public: - Button(FormGroup * parent, const rect_t & rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0): - FormField(parent, rect, windowFlags, textFlags), - pressHandler(std::move(pressHandler)) + Button(FormGroup* parent, const rect_t& rect, + std::function pressHandler = nullptr, + WindowFlags windowFlags = 0, LcdFlags textFlags = 0) : + FormField(parent, rect, windowFlags, textFlags), + pressHandler(std::move(pressHandler)) { } From a3923c35b9d81c756d7fa54798da0fa09d72d122 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 May 2021 19:10:02 +0200 Subject: [PATCH 383/714] FileChoice: openMenu() after setFocus() Otherwise, whatever is opened by the menu will loose the focus --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 162c2bb1b..02a8ccfed 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -140,9 +140,9 @@ void FileChoice::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool FileChoice::onTouchEnd(coord_t, coord_t) { - openMenu(); setFocus(SET_FOCUS_DEFAULT); setEditMode(true); + openMenu(); return true; } #endif From ff35b895431ba832d7a7a74c0f9c4618377ba5f7 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 27 May 2021 22:27:58 +0200 Subject: [PATCH 384/714] Reset touch state before calling onTouchEnd() In case something calls MainWindow::run() during the execution of this handler. Otherwise it will generate a second touch-end event, on whatever is on the screen at this moment. --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index d535fdafc..7cbe46111 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -51,8 +51,8 @@ void MainWindow::checkEvents() slidingWindow = nullptr; } else if (touchState.event == TE_UP) { - onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); touchState.event = TE_NONE; + onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); } else if (touchState.event == TE_SLIDE) { if (touchState.deltaX || touchState.deltaY) { From 8692796dd5ff6b3e9f0396e7bfd9893eeec578a8 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 30 May 2021 17:27:48 +0200 Subject: [PATCH 385/714] Add safeguards for drawText() & drawSizedText() --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 1 + radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 1 + 2 files changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 4ed1b2c97..b5b084766 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -908,6 +908,7 @@ uint8_t BitmapBuffer::drawChar(coord_t x, coord_t y, const uint8_t * font, const coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags) { + if (!s) return (flags & VERTICAL) ? y : x; MOVE_OFFSET(); int height = getFontHeight(flags); diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 93b88a1e5..51d244497 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -311,6 +311,7 @@ class BitmapBuffer: public BitmapBufferBase coord_t drawText(coord_t x, coord_t y, const char * s, LcdFlags flags = 0) { + if (!s) return (flags & VERTICAL) ? y : x; return drawSizedText(x, y, s, 255, flags); } From 675773bcfb82b1b2b6a09fd0a7a254a45c3a6167 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 30 May 2021 17:28:23 +0200 Subject: [PATCH 386/714] Disable Theme::drawMessageBox() --- radio/src/thirdparty/libopenui/src/theme.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index fc9c8759f..e7b138229 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -36,15 +36,20 @@ enum IconState { class Theme { public: - virtual void drawMessageBox(const char * title, const char * text, const char * action, LcdFlags flags) const = 0; - virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const = 0; - virtual void drawCheckBox(BitmapBuffer * dc, CheckBox * checkBox) const + // virtual void drawMessageBox(const char * title, const char * text, const + // char * action, LcdFlags flags) const = 0; + + virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, + coord_t w, coord_t h, int value) const = 0; + + virtual void drawCheckBox(BitmapBuffer* dc, CheckBox* checkBox) const { - drawCheckBox(dc, checkBox->getValue(), 0, FIELD_PADDING_TOP, checkBox->hasFocus()); - const char * label = checkBox->getLabel(); - if (label) - dc->drawText(22, FIELD_PADDING_TOP, label); + drawCheckBox(dc, checkBox->getValue(), 0, FIELD_PADDING_TOP, + checkBox->hasFocus()); + const char* label = checkBox->getLabel(); + if (label) dc->drawText(22, FIELD_PADDING_TOP, label); } + virtual void drawCheckBox(BitmapBuffer * dc, bool checked, coord_t x, coord_t y, bool focus = false) const = 0; virtual void drawChoice(BitmapBuffer * dc, ChoiceBase * choice, const char * str) const = 0; virtual void drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const = 0; From 32cf4739e128555e67a0fadb16dd4067f38c3469 Mon Sep 17 00:00:00 2001 From: olliw42 Date: Mon, 31 May 2021 21:53:37 +0200 Subject: [PATCH 387/714] Added drawFilledTriangle() --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 51 +++++++++++++++++++ .../thirdparty/libopenui/src/bitmapbuffer.h | 3 +- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index b5b084766..2e29f857e 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -551,6 +551,57 @@ void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFla } } +void BitmapBuffer::drawFilledTriangle(coord_t x0, coord_t y0, coord_t x1, coord_t y1, coord_t x2, coord_t y2, LcdFlags flags, uint8_t opacity) +{ + coord_t a, b; + + #define SWAP(a, b) {coord_t tmp = b; b = a; a = tmp;} + + if (y0 > y1) { SWAP(y0, y1); SWAP(x0, x1); } + if (y1 > y2) { SWAP(y2, y1); SWAP(x2, x1); } + if (y0 > y1) { SWAP(y0, y1); SWAP(x0, x1); } + + if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing + a = b = x0; + if (x1 < a) + a = x1; + else if (x1 > b) + b = x1; + if (x2 < a) + a = x2; + else if (x2 > b) + b = x2; + drawHorizontalLine(a, y0, b - a + 1, SOLID, flags, opacity); + return; + } + + coord_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, dx12 = x2 - x1, dy12 = y2 - y1; + coord_t sa = 0, sb = 0; + coord_t last = (y1 == y2) ? y1 : y1 - 1; + coord_t y; + + for (y = y0; y <= last; y++) { + a = x0 + sa / dy01; + b = x0 + sb / dy02; + sa += dx01; + sb += dx02; + if (a > b) SWAP(a, b); + drawHorizontalLine(a, y, b - a + 1, SOLID, flags, opacity); + } + + sa = dx12 * (y - y1); + sb = dx02 * (y - y0); + + for (; y <= y2; y++) { + a = x1 + sa / dy12; + b = x0 + sb / dy02; + sa += dx12; + sb += dx02; + if (a > b) SWAP(a, b); + drawHorizontalLine(a, y, b - a + 1, SOLID, flags, opacity); + } +} + void BitmapBuffer::drawCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags) { int x1 = radius; diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 51d244497..18c7df03a 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -280,6 +280,8 @@ class BitmapBuffer: public BitmapBufferBase void invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags = 0); + void drawFilledTriangle(coord_t x1, coord_t y1, coord_t x2, coord_t y2, coord_t x3, coord_t y3, LcdFlags flags = 0, uint8_t opacity = 0); + void drawCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags = 0); void drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFlags flags = 0); @@ -413,7 +415,6 @@ class BitmapBuffer: public BitmapBufferBase return &data[y * _width + x]; } - inline void drawPixelAbs(coord_t x, coord_t y, pixel_t value) { pixel_t * p = getPixelPtrAbs(x, y); From ae5a84cd6c1c43a4f60ad1bf078e6f31c450f9bc Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 6 Jun 2021 07:39:17 +0200 Subject: [PATCH 388/714] Fix drawBitmapPatternPie() Masks are stored in 8-bit format, but drawAlphaPixel() uses only 4-bit alpha. --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 2e29f857e..2dea49fd1 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -755,16 +755,16 @@ void BitmapBuffer::drawBitmapPatternPie(coord_t x, coord_t y, const uint8_t * im for (int x1 = w2 - 1; x1 >= 0; x1--) { Slope slope(false, x1 == 0 ? 99000 : y1 * 100 / x1); if (slope.isBetween(startSlope, endSlope)) { - drawAlphaPixel(x + w2 + x1, y + h2 - y1, q[(h2 - y1) * width + w2 + x1], color); + drawAlphaPixel(x + w2 + x1, y + h2 - y1, q[(h2 - y1) * width + w2 + x1]>>4, color); } if (slope.invertVertical().isBetween(startSlope, endSlope)) { - drawAlphaPixel(x + w2 + x1, y + h2 + y1, q[(h2 + y1) * width + w2 + x1], color); + drawAlphaPixel(x + w2 + x1, y + h2 + y1, q[(h2 + y1) * width + w2 + x1]>>4, color); } if (slope.invertHorizontal().isBetween(startSlope, endSlope)) { - drawAlphaPixel(x + w2 - x1, y + h2 + y1, q[(h2 + y1) * width + w2 - x1], color); + drawAlphaPixel(x + w2 - x1, y + h2 + y1, q[(h2 + y1) * width + w2 - x1]>>4, color); } if (slope.invertVertical().isBetween(startSlope, endSlope)) { - drawAlphaPixel(x + w2 - x1, y + h2 - y1, q[(h2 - y1) * width + w2 - x1], color); + drawAlphaPixel(x + w2 - x1, y + h2 - y1, q[(h2 - y1) * width + w2 - x1]>>4, color); } } } From 9962dd0092bfe29c0576433ce01df18a47ecac11 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 6 Jun 2021 10:13:01 +0200 Subject: [PATCH 389/714] [FileChoice] Added extension stripping (optional) --- .../thirdparty/libopenui/src/filechoice.cpp | 53 ++++++++++--------- .../src/thirdparty/libopenui/src/filechoice.h | 37 ++++++------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 02a8ccfed..9b07ebf9a 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -31,13 +31,18 @@ #define STR_NO_FILES_ON_SD "No files on SD Card!" #endif -FileChoice::FileChoice(FormGroup * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue): - ChoiceBase(parent, rect, CHOICE_TYPE_FOLDER), - folder(std::move(folder)), - extension(extension), - maxlen(maxlen), - getValue(std::move(getValue)), - setValue(std::move(setValue)) +FileChoice::FileChoice(FormGroup *parent, const rect_t &rect, + std::string folder, const char *extension, int maxlen, + std::function getValue, + std::function setValue, + bool stripExtension) : + ChoiceBase(parent, rect, CHOICE_TYPE_FOLDER), + folder(std::move(folder)), + extension(extension), + maxlen(maxlen), + getValue(std::move(getValue)), + setValue(std::move(setValue)), + stripExtension(stripExtension) { } @@ -52,30 +57,28 @@ bool FileChoice::openMenu() FILINFO fno; DIR dir; std::list files; - const char * fnExt; + const char *fnExt; uint8_t fnLen, extLen; - FRESULT res = f_opendir(&dir, folder.c_str()); // Open the directory + FRESULT res = f_opendir(&dir, folder.c_str()); // Open the directory if (res == FR_OK) { bool firstTime = true; for (;;) { res = sdReadDir(&dir, &fno, firstTime); if (res != FR_OK || fno.fname[0] == 0) - break; // break on error or end of dir - if (fno.fattrib & AM_DIR) - continue; // skip subfolders - if (fno.fattrib & AM_HID) - continue; // skip hidden files - if (fno.fattrib & AM_SYS) - continue; // skip system files + break; // break on error or end of dir + if (fno.fattrib & AM_DIR) continue; // skip subfolders + if (fno.fattrib & AM_HID) continue; // skip hidden files + if (fno.fattrib & AM_SYS) continue; // skip system files fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); - // fnLen -= extLen; - if (!fnLen || fnLen > maxlen) - continue; // wrong size if (extension && !isExtensionMatching(fnExt, extension)) - continue; // wrong extension + continue; // wrong extension + + if (stripExtension) fnLen -= extLen; + + if (!fnLen || fnLen > maxlen) continue; // wrong size files.emplace_back(fno.fname, fnLen); } @@ -83,18 +86,16 @@ bool FileChoice::openMenu() if (!files.empty()) { // sort files files.sort(compare_nocase); - files.push_front(""); auto menu = new Menu(this); int count = 0; int current = -1; std::string value = getValue(); - for (const auto &file: files) { - menu->addLine(file, [=]() { - setValue(file); - }); - // TRACE("%s %d %s %d", value.c_str(), value.size(), file.c_str(), file.size()); + for (const auto &file : files) { + menu->addLine(file, [=]() { setValue(file); }); + // TRACE("%s %d %s %d", value.c_str(), value.size(), file.c_str(), + // file.size()); if (value.compare(file) == 0) { // TRACE("OK"); current = count; diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index b1c0ee514..5e5f57c51 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -22,35 +22,36 @@ #include "choice.h" #include -class FileChoice: public ChoiceBase +class FileChoice : public ChoiceBase { - public: - FileChoice(FormGroup * parent, const rect_t & rect, std::string folder, const char * extension, int maxlen, std::function getValue, std::function setValue); +public: + FileChoice(FormGroup* parent, const rect_t& rect, std::string folder, + const char* extension, int maxlen, + std::function getValue, + std::function setValue, + bool stripExtension=false); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "FileChoice"; - } + std::string getName() const override { return "FileChoice"; } #endif - void paint(BitmapBuffer * dc) override; + void paint(BitmapBuffer* dc) override; #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; + bool onTouchEnd(coord_t x, coord_t y) override; #endif - protected: - std::string folder; - const char * extension; - int maxlen; - std::function getValue; - std::function setValue; +protected: + std::string folder; + const char* extension; + int maxlen; + std::function getValue; + std::function setValue; + bool stripExtension; - bool openMenu(); + bool openMenu(); }; - From 880a514f1873dbb87e544db88fa3f81e36d133ec Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 6 Jun 2021 12:21:50 +0200 Subject: [PATCH 390/714] [Menu] select the first element by default, even if hardware touch is supported --- radio/src/thirdparty/libopenui/src/menu.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index b93486170..da61996f0 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -122,11 +122,7 @@ class MenuBody: public Window protected: std::vector lines; -#if defined(HARDWARE_TOUCH) - int selectedIndex = -1; -#else int selectedIndex = 0; -#endif std::function onCancel; inline Menu * getParentMenu(); From 8d02f3884a8fcf6d6711fc5ca17c16319238b8c6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 6 Jun 2021 13:53:05 +0200 Subject: [PATCH 391/714] [Menu] added setFocusBody() --- radio/src/thirdparty/libopenui/src/menu.h | 5 +++++ radio/src/thirdparty/libopenui/src/menutoolbar.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index da61996f0..9e41eaa32 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -181,6 +181,11 @@ class Menu: public ModalWindow waitHandler = std::move(handler); } + void setFocusBody(uint8_t flag = SET_FOCUS_DEFAULT) + { + content->body.setFocus(flag); + } + void setToolbar(Window * window) { toolbar = window; diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index aae69d1a6..44365caed 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -156,9 +156,11 @@ class MenuToolbar: public FormGroup { choice->fillMenu(menu, [=](int16_t index) { return index >= filtermin && index <= filtermax; }); + menu->setFocusBody(); } else { choice->fillMenu(menu); + menu->setFocusBody(); } return 1; }); From 20f026eef56c4211811573df5eeb8c8ffbf235fc Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 7 Jun 2021 08:58:49 +0200 Subject: [PATCH 392/714] Added BSD license for the implementation of drawFilledTriangle() --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 2dea49fd1..0fad6ac81 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -551,7 +551,39 @@ void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFla } } -void BitmapBuffer::drawFilledTriangle(coord_t x0, coord_t y0, coord_t x1, coord_t y1, coord_t x2, coord_t y2, LcdFlags flags, uint8_t opacity) +/* + Notice: BitmapBuffer::drawFilledTriangle use the implementation + made for the Adafruit GFX library, which is licensed under BSD + license. + + Software License Agreement (BSD License) + + Copyright (c) 2012 Adafruit Industries. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ +void BitmapBuffer::drawFilledTriangle(coord_t x0, coord_t y0, coord_t x1, + coord_t y1, coord_t x2, coord_t y2, + LcdFlags flags, uint8_t opacity) { coord_t a, b; From 76a3886a037ece6785811c4d7c962f9a6537e645 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 07:22:33 +0200 Subject: [PATCH 393/714] Use EVT_KEY_FIRST(KEY_EXIT) instead of EVT_KEY_BREAK() in FormGroup This prevents the TabGroup from catching the event first, and thus quit the complete page. Resolves EdgeTX/edgetx#164 --- radio/src/thirdparty/libopenui/src/form.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 04a13a8bb..2c75aa53b 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -225,20 +225,18 @@ void FormGroup::onEvent(event_t event) if (event == EVT_KEY_BREAK(KEY_ENTER)) { onKeyPress(); setFocusOnFirstVisibleField(SET_FOCUS_FIRST); - } - else if (event == EVT_KEY_BREAK(KEY_EXIT) && !hasFocus() && !(windowFlags & FORM_FORWARD_FOCUS)) { + } else if (event == EVT_KEY_FIRST(KEY_EXIT) && !hasFocus() && + !(windowFlags & FORM_FORWARD_FOCUS)) { + killEvents(event); onKeyPress(); - setFocus(SET_FOCUS_DEFAULT); // opentx - model - timers settings - } - else if (event == EVT_ROTARY_RIGHT && !next) { + setFocus(SET_FOCUS_DEFAULT); // opentx - model - timers settings + } else if (event == EVT_ROTARY_RIGHT && !next) { onKeyPress(); setFocusOnFirstVisibleField(SET_FOCUS_FIRST); - } - else if (event == EVT_ROTARY_LEFT && !previous) { + } else if (event == EVT_ROTARY_LEFT && !previous) { onKeyPress(); setFocusOnLastVisibleField(SET_FOCUS_BACKWARD); - } - else { + } else { FormField::onEvent(event); } } From 02897d134e26d971c78eb9088cc63859e235f71f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 13:16:30 +0200 Subject: [PATCH 394/714] [Theme] drawProgressBar() should be given the total to avoid using only percentages --- radio/src/thirdparty/libopenui/src/progress.cpp | 2 +- radio/src/thirdparty/libopenui/src/theme.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/progress.cpp b/radio/src/thirdparty/libopenui/src/progress.cpp index a29707719..d17cd9a07 100644 --- a/radio/src/thirdparty/libopenui/src/progress.cpp +++ b/radio/src/thirdparty/libopenui/src/progress.cpp @@ -22,5 +22,5 @@ void Progress::paint(BitmapBuffer * dc) { - theme->drawProgressBar(dc, 0, 0, width(), height(), value); + theme->drawProgressBar(dc, 0, 0, width(), height(), value, 100); } diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index e7b138229..0de92ac33 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -40,7 +40,7 @@ class Theme { // char * action, LcdFlags flags) const = 0; virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, - coord_t w, coord_t h, int value) const = 0; + coord_t w, coord_t h, int value, int total) const = 0; virtual void drawCheckBox(BitmapBuffer* dc, CheckBox* checkBox) const { From 5c722be9ff7e3fe02bd2bbd427aa4074f902ee46 Mon Sep 17 00:00:00 2001 From: yaapu Date: Thu, 10 Jun 2021 17:18:18 +0000 Subject: [PATCH 395/714] fix INVERS in drawNumber --- .../src/thirdparty/libopenui/src/bitmapbuffer.cpp | 14 ++++++++++++-- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 0fad6ac81..f238223cb 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1059,8 +1059,12 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ return ((flags & RIGHT) ? orig_pos : pos) - offsetX; } -coord_t BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) +void BitmapBuffer::formatNumberAsString(char *buffer, uint8_t buffer_size, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) { + if (buffer == nullptr) { + return; + } + char str[48+1]; // max=16 for the prefix, 16 chars for the number, 16 chars for the suffix char *s = str + 32; *s = '\0'; @@ -1095,7 +1099,13 @@ coord_t BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags fla if (suffix) { strncpy(&str[32], suffix, 16); } - flags &= ~LEADING0; + strncpy(buffer, s, buffer_size); +} + +coord_t BitmapBuffer::drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix) +{ + char s[49]; + formatNumberAsString(s, 49, val, flags, len, prefix, suffix); return drawText(x, y, s, flags); } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 18c7df03a..146c81541 100755 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -323,6 +323,8 @@ class BitmapBuffer: public BitmapBufferBase return drawSizedText(x, y, s+length*idx, length, flags); } + static void formatNumberAsString(char *buffer, const uint8_t buffer_size, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); + coord_t drawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); template From b53982eebe8c04897a459628455c469e505c9d20 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 06:31:28 +0200 Subject: [PATCH 396/714] Fix potential NULL pointer access --- radio/src/thirdparty/libopenui/src/window.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 3bd69c198..7953367e6 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -53,10 +53,9 @@ Window::~Window() void Window::attach(Window * newParent) { - if (parent) - detach(); + if (parent) detach(); parent = newParent; - newParent->addChild(this); + if (newParent) newParent->addChild(this); } void Window::detach() From f24f218ebbdcc0de7cd0abd8ae2b417cbabbee38 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Jun 2021 17:49:01 +0200 Subject: [PATCH 397/714] Default Theme facelift --- radio/src/thirdparty/libopenui/src/button.cpp | 17 ++++--- radio/src/thirdparty/libopenui/src/button.h | 39 ++++++++------- radio/src/thirdparty/libopenui/src/form.cpp | 18 ++++--- radio/src/thirdparty/libopenui/src/menu.cpp | 48 ++++++++++++------- .../thirdparty/libopenui/src/numberedit.cpp | 29 ++++++----- radio/src/thirdparty/libopenui/src/static.h | 2 +- .../src/thirdparty/libopenui/src/textedit.cpp | 19 ++++---- 7 files changed, 99 insertions(+), 73 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index cf3e213b7..9ba76a6d7 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -77,22 +77,20 @@ void TextButton::paint(BitmapBuffer * dc) if (checked()) { if (hasFocus()) { dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); - dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, FOCUS_BGCOLOR); - } - else { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, FOCUS_BGCOLOR); + dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, EDIT_MARKER_COLOR); + } else { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, EDIT_MARKER_COLOR); } textColor = FOCUS_COLOR; } else { if (windowFlags & BUTTON_BACKGROUND) { if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, DISABLE_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FOCUS_BGCOLOR); + textColor = FOCUS_COLOR; } else { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, DISABLE_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_FRAME_COLOR); } } else { @@ -105,7 +103,8 @@ void TextButton::paint(BitmapBuffer * dc) } } - dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, text.c_str(), CENTERED | textFlags | textColor); + dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, + text.c_str(), CENTERED | textFlags | textColor); } void IconButton::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index a60ab6476..722ef3f18 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -89,31 +89,34 @@ class Button: public FormField class TextButton: public Button { public: - TextButton(FormGroup * parent, const rect_t & rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE, LcdFlags textFlags = 0): - Button(parent, rect, std::move(pressHandler), windowFlags, textFlags), - text(std::move(text)) - { - } + TextButton(FormGroup* parent, const rect_t& rect, std::string text, + std::function pressHandler = nullptr, + WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE, + LcdFlags textFlags = 0) : + Button(parent, rect, std::move(pressHandler), windowFlags, textFlags), + text(std::move(text)) + { + } #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "TextButton \"" + text + "\""; - } + std::string getName() const override + { + return "TextButton \"" + text + "\""; + } #endif - void setText(std::string value) - { - if (value != text) { - text = std::move(value); - invalidate(); - } - } + void setText(std::string value) + { + if (value != text) { + text = std::move(value); + invalidate(); + } + } - void paint(BitmapBuffer * dc) override; + void paint(BitmapBuffer* dc) override; protected: - std::string text; + std::string text; }; class IconButton: public Button diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 2c75aa53b..39198f0aa 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -87,14 +87,18 @@ void FormField::setFocus(uint8_t flag, Window * from) void FormField::paint(BitmapBuffer * dc) { if (editMode) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FOCUS_BGCOLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, EDIT_MARKER_COLOR); } - else if (!(windowFlags & FORM_NO_BORDER)) { - if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); - } - else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); + else { + + if (!(windowFlags & FORM_NO_BORDER)) { + + if (hasFocus()) { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FOCUS_BGCOLOR); + } else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } } } } diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 3258d429c..ee46dbe60 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -107,36 +107,43 @@ void MenuBody::paint(BitmapBuffer * dc) dc->clear(MENU_BGCOLOR); for (unsigned i = 0; i < lines.size(); i++) { - auto & line = lines[i]; + auto& line = lines[i]; LcdFlags flags = MENU_COLOR | MENU_FONT; if (selectedIndex == (int)i) { flags = MENU_HIGHLIGHT_COLOR | MENU_FONT; if (MENU_HIGHLIGHT_BGCOLOR != MENU_BGCOLOR) { - dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), MENUS_LINE_HEIGHT, MENU_HIGHLIGHT_BGCOLOR); + dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), + MENUS_LINE_HEIGHT, MENU_HIGHLIGHT_BGCOLOR); } } if (line.drawLine) { line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT, flags); - } - else { - const char * text = line.text.data(); - dc->drawText(10, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, text[0] == '\0' ? "---" : text, flags); + } else { + const char* text = line.text.data(); + dc->drawText(10, + i * MENUS_LINE_HEIGHT + + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, + text[0] == '\0' ? "---" : text, flags); } - Menu * menu = getParentMenu(); + Menu* menu = getParentMenu(); if (menu->multiple && line.isChecked) { - theme->drawCheckBox(dc, line.isChecked(), width() - 35, i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, 0); + theme->drawCheckBox(dc, line.isChecked(), width() - 35, + i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, + 0); } if (i > 0) { - dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); + dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT, MENUS_WIDTH, + MENU_LINE_COLOR); } } } -MenuWindowContent::MenuWindowContent(Menu * parent): - ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), - body(this, {0, 0, width(), height()}) +MenuWindowContent::MenuWindowContent(Menu* parent) : + ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, + (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), + body(this, {1, 1, width() - 2, height() - 2}) { body.setFocus(SET_FOCUS_DEFAULT); } @@ -148,9 +155,14 @@ void MenuWindowContent::paint(BitmapBuffer * dc) // the title if (!title.empty()) { - dc->drawText(MENUS_WIDTH / 2, (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); - dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); + dc->drawText(MENUS_WIDTH / 2, + (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, + title.c_str(), CENTERED | MENU_HEADER_FONT); + dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, + MENU_LINE_COLOR); } + + dc->drawSolidRect(0, 0, MENUS_WIDTH, height(), 1, MENU_LINE_COLOR); } Menu::Menu(Window * parent, bool multiple): @@ -165,10 +177,12 @@ void Menu::updatePosition() if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; - auto bodyHeight = limit(MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); + auto bodyHeight = limit( + MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT - 1, + MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); - content->setHeight(headerHeight + bodyHeight); - content->body.setTop(headerHeight); + content->setHeight(headerHeight + bodyHeight + 2); + content->body.setTop(headerHeight + 1); content->body.setHeight(bodyHeight); } content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT - 1); diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index cdbda1dad..e5ff1a38b 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -38,27 +38,30 @@ void NumberEdit::paint(BitmapBuffer * dc) if (editMode) textColor = FOCUS_COLOR; else if (hasFocus()) - textColor = FOCUS_BGCOLOR; - else if (enabled && (value != 0 || zeroText.empty())) - textColor = DEFAULT_COLOR; + textColor = FOCUS_COLOR; + // else if (enabled && (value != 0 || zeroText.empty())) + // textColor = DEFAULT_COLOR; else - textColor = DISABLE_COLOR; + textColor = DEFAULT_COLOR; if (displayFunction) { displayFunction(dc, textColor, value); - } - else if (value == 0 && !zeroText.empty()) { - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | textFlags); + } else if (value == 0 && !zeroText.empty()) { + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), + textColor | textFlags); if (textFlags & RIGHT) - dc->drawText(rect.w - FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | textFlags); + dc->drawText(rect.w - FIELD_PADDING_LEFT, FIELD_PADDING_TOP, + zeroText.c_str(), textColor | textFlags); else - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), textColor | textFlags); - } - else { + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), + textColor | textFlags); + } else { if (textFlags & RIGHT) - dc->drawNumber(rect.w - FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); + dc->drawNumber(rect.w - FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, + textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); else - dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, + textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); } } diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 6ead718ec..15f1bcf5b 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -30,7 +30,7 @@ class StaticText: public Window text(std::move(text)) { if (windowFlags & BUTTON_BACKGROUND) { - setBackgroundColor(DISABLE_COLOR); + setBackgroundColor(FIELD_FRAME_COLOR); } } diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 864399127..3edcfaafb 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -47,24 +47,27 @@ void TextEdit::paint(BitmapBuffer * dc) FormField::paint(dc); if (editMode) { - dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, FOCUS_COLOR); + dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, + FOCUS_COLOR); coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); #if defined(SOFTWARE_KEYBOARD) dc->drawSolidFilledRect(left + 2, 2, 2, height() - 4, FOCUS_COLOR); #else - char s[] = { value[cursorPos], '\0' }; - dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP - 1, getTextWidth(s, 1) + 1, height() - 2, FOCUS_COLOR); - dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, DEFAULT_COLOR); + char s[] = {value[cursorPos], '\0'}; + dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, + FIELD_PADDING_TOP - 1, getTextWidth(s, 1) + 1, + height() - 2, FOCUS_COLOR); + dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, + DEFAULT_COLOR); #endif - } - else { + } else { const char * displayedValue = value; - LcdFlags textColor; + LcdFlags textColor = FOCUS_COLOR; if (hasFocus()) { if (strlen(value) == 0) { displayedValue = "---"; } - textColor = FOCUS_BGCOLOR; + textColor = FOCUS_COLOR; } else { if (strlen(value) == 0) { From 2b3df9aea994d2a2ad52e2965064088e2c24d525 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 08:08:31 +0200 Subject: [PATCH 398/714] [TextButton] added bgColorHandler --- radio/src/thirdparty/libopenui/src/button.cpp | 29 ++++++++++--------- radio/src/thirdparty/libopenui/src/button.h | 6 ++++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 9ba76a6d7..d0996c2b9 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -70,36 +70,37 @@ void Button::checkEvents() void TextButton::paint(BitmapBuffer * dc) { - FormField::paint(dc); - auto textColor = DEFAULT_COLOR; + auto bgColor = FIELD_FRAME_COLOR; + + if (bgColorHandler) { + bgColor = bgColorHandler(); + } else if (checked()) { + bgColor = EDIT_MARKER_COLOR; + } else if (hasFocus()) { + bgColor = FOCUS_BGCOLOR; + } if (checked()) { if (hasFocus()) { dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); - dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, EDIT_MARKER_COLOR); + dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, bgColor); } else { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, EDIT_MARKER_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); } textColor = FOCUS_COLOR; } else { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); if (windowFlags & BUTTON_BACKGROUND) { if (hasFocus()) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FOCUS_BGCOLOR); textColor = FOCUS_COLOR; } - else { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_FRAME_COLOR); - } + } else if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } else { - if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); - } - else { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); - } + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); } } diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 722ef3f18..78d09463b 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -113,10 +113,16 @@ class TextButton: public Button } } + void setBgColorHandler(std::function handler = nullptr) + { + bgColorHandler = std::move(handler); + } + void paint(BitmapBuffer* dc) override; protected: std::string text; + std::function bgColorHandler = nullptr; }; class IconButton: public Button From f8042ee2e06fc115fa43991193fe9497c56ea4a9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 08:33:41 +0200 Subject: [PATCH 399/714] [Menu] tuned look of menus with toolbar - use DEFAULT_BGCOLOR for toolbar - draw line around the complete menu --- radio/src/thirdparty/libopenui/src/menu.cpp | 20 +++++++++++++------ radio/src/thirdparty/libopenui/src/menu.h | 2 ++ .../thirdparty/libopenui/src/menutoolbar.cpp | 17 ++++++++++------ .../thirdparty/libopenui/src/menutoolbar.h | 2 +- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index ee46dbe60..9db5f5d0c 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -143,7 +143,7 @@ void MenuBody::paint(BitmapBuffer * dc) MenuWindowContent::MenuWindowContent(Menu* parent) : ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), - body(this, {1, 1, width() - 2, height() - 2}) + body(this, {0, 0, width(), height()}) { body.setFocus(SET_FOCUS_DEFAULT); } @@ -161,8 +161,6 @@ void MenuWindowContent::paint(BitmapBuffer * dc) dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, MENU_LINE_COLOR); } - - dc->drawSolidRect(0, 0, MENUS_WIDTH, height(), 1, MENU_LINE_COLOR); } Menu::Menu(Window * parent, bool multiple): @@ -181,11 +179,11 @@ void Menu::updatePosition() MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT - 1, MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); - content->setHeight(headerHeight + bodyHeight + 2); - content->body.setTop(headerHeight + 1); + content->setHeight(headerHeight + bodyHeight); + content->body.setTop(headerHeight); content->body.setHeight(bodyHeight); } - content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT - 1); + content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT); } void Menu::setTitle(std::string text) @@ -226,3 +224,13 @@ void Menu::onEvent(event_t event) } } #endif + +void Menu::paint(BitmapBuffer * dc) +{ + rect_t r(content->getRect()); + if (toolbar) { + r.x = toolbar->left(); + r.w += toolbar->width(); + } + dc->drawSolidRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, 1, MENU_LINE_COLOR); +} diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 9e41eaa32..8b8e361b2 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -224,6 +224,8 @@ class Menu: public ModalWindow } } + void paint(BitmapBuffer * dc) override; + protected: MenuWindowContent * content; bool multiple; diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index f6cceee29..7a27ae2d0 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -23,10 +23,15 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) { if (checked()) { - dc->drawSolidFilledRect(MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, FOCUS_BGCOLOR); - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, &picto, 1, CENTERED | FOCUS_COLOR); + dc->drawSolidFilledRect( + MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, + MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, + MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, + FOCUS_BGCOLOR); + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, + &picto, 1, CENTERED | FOCUS_COLOR); + } else { + dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, + &picto, 1, CENTERED | DEFAULT_COLOR); } - else { - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, &picto, 1, CENTERED | DEFAULT_COLOR); - } -} \ No newline at end of file +} diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 44365caed..de493a04f 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -78,7 +78,7 @@ class MenuToolbar: public FormGroup { void paint(BitmapBuffer * dc) override { - dc->clear(DISABLE_COLOR); + dc->clear(DEFAULT_BGCOLOR); } #if defined(HARDWARE_KEYS) From 54a87bc180dde53afde94a187c0f1fa41484fec0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 18:14:30 +0200 Subject: [PATCH 400/714] A bit more tuning --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- radio/src/thirdparty/libopenui/src/static.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 39198f0aa..cff139eae 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -253,7 +253,7 @@ void FormGroup::paint(BitmapBuffer * dc) dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, DISABLE_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); } } } diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 4deb4e998..64d5f62c6 100755 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -34,21 +34,23 @@ void StaticText::paint(BitmapBuffer * dc) else x = 0; - coord_t y = (textFlags & VCENTERED) ? (rect.h - getFontHeight(textFlags)) / 2 : FIELD_PADDING_TOP; + coord_t y = (textFlags & VCENTERED) ? (rect.h - getFontHeight(textFlags)) / 2 + : FIELD_PADDING_TOP; auto start = text.c_str(); auto nextline = findNextLine(start); if (nextline) { auto current = start; do { - dc->drawText(x, y, text.substr(current - start, nextline - current).c_str(), textFlags); + dc->drawText(x, y, + text.substr(current - start, nextline - current).c_str(), + textFlags); current = nextline + 1; nextline = findNextLine(current); y += getFontHeight(textFlags) + 2; } while (nextline); dc->drawText(x, y, current, textFlags); - } - else { + } else { dc->drawText(x, y, start, textFlags); } } From 6d2d740d95bb22354a95249a2d982e57e62fab4f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 11:57:37 +0200 Subject: [PATCH 401/714] Small fixes --- radio/src/thirdparty/libopenui/src/button.cpp | 3 +-- radio/src/thirdparty/libopenui/src/choice.cpp | 4 +++- radio/src/thirdparty/libopenui/src/menu.cpp | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index d0996c2b9..e9f1f71fc 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -76,7 +76,7 @@ void TextButton::paint(BitmapBuffer * dc) if (bgColorHandler) { bgColor = bgColorHandler(); } else if (checked()) { - bgColor = EDIT_MARKER_COLOR; + bgColor = HIGHLIGHT_COLOR; } else if (hasFocus()) { bgColor = FOCUS_BGCOLOR; } @@ -88,7 +88,6 @@ void TextButton::paint(BitmapBuffer * dc) } else { dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); } - textColor = FOCUS_COLOR; } else { dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index c3d8f1468..9be769153 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -104,7 +104,9 @@ void Choice::setValues(const char * const values[]) void Choice::paint(BitmapBuffer * dc) { FormField::paint(dc); - theme->drawChoice(dc, this, textHandler ? textHandler(getValue()).c_str() : values[getValue() - vmin].c_str()); + theme->drawChoice(dc, this, + textHandler ? textHandler(getValue()).c_str() + : values[getValue() - vmin].c_str()); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 9db5f5d0c..478dd6a13 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -227,6 +227,8 @@ void Menu::onEvent(event_t event) void Menu::paint(BitmapBuffer * dc) { + ModalWindow::paint(dc); + rect_t r(content->getRect()); if (toolbar) { r.x = toolbar->left(); From d4c23446e558777e5084fb8e3d8c2bf6ec3c1dec Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 17 Jun 2021 09:02:28 +0200 Subject: [PATCH 402/714] Fixed menu body height --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 478dd6a13..de1beffd7 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -176,7 +176,7 @@ void Menu::updatePosition() // there is no navigation bar at the left, we may center the window on screen auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; auto bodyHeight = limit( - MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT - 1, + MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT, MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); content->setHeight(headerHeight + bodyHeight); From 8a6d288cf6fd71f5530f51431ce98499dd929941 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Jun 2021 12:00:52 +0200 Subject: [PATCH 403/714] [FormGroup] added removeField() to properly remove fields --- radio/src/thirdparty/libopenui/src/form.cpp | 21 +++++++++++++++++++++ radio/src/thirdparty/libopenui/src/form.h | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index cff139eae..4e49eaced 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -103,6 +103,27 @@ void FormField::paint(BitmapBuffer * dc) } } +void FormGroup::removeField(FormField * field) +{ + FormField* prev = field->getPreviousField(); + FormField* next = field->getNextField(); + + if (prev) { prev->setNextField(next); } + if (next) { next->setPreviousField(prev); } + + if (first == field) { + if (prev && (prev != field)) first = prev; + else if (next && (next != field)) first = next; + else first = nullptr; + } + + if (last == field) { + if (next && (next != field)) last = next; + else if (prev && (prev != field)) last = prev; + else last = nullptr; + } +} + void FormGroup::addField(FormField * field, bool front) { if (field->getWindowFlags() & FORM_DETACHED) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 8bbf4e517..5faceea0c 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -133,7 +133,9 @@ class FormGroup: public FormField void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override; - virtual void addField(FormField * field, bool front = false); // NOLINT(google-default-arguments) + virtual void addField(FormField * field, bool front = false); + + virtual void removeField(FormField * field); void setFirstField(FormField * field) { From dd02b4419c896d75d204e1a2d5207125528b27b0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 09:31:20 +0200 Subject: [PATCH 404/714] Removed obsolete EXPANDED --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index dcf43c14b..d8a300f0c 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -24,7 +24,6 @@ /* obsolete flags */ #define BLINK 0 -#define EXPANDED 0 #define TIMEHOUR 0 /* drawText flags */ From 3dc11d5bf8686793afb97d92fd6c8cc40686f6af Mon Sep 17 00:00:00 2001 From: eshifri Date: Tue, 22 Jun 2021 16:51:19 -0700 Subject: [PATCH 405/714] Fix for #197 --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 3 +++ radio/src/thirdparty/libopenui/src/slider.cpp | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index e5ff1a38b..248cdbb74 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -103,6 +103,9 @@ void NumberEdit::onEvent(event_t event) } #endif + case EVT_KEY_FIRST(KEY_EXIT): + return; + #if defined(HARDWARE_TOUCH) case EVT_VIRTUAL_KEY_PLUS: setValue(getValue() + getStep()); diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index fd53fb488..fb6c6a7c8 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -45,7 +45,8 @@ void Slider::onEvent(event_t event) setValue(getValue() - ROTARY_ENCODER_SPEED()); onKeyPress(); return; - } + } else if (event == EVT_KEY_FIRST(KEY_EXIT)) + return; } FormField::onEvent(event); From 381487d61063191740f603ab4e47567e951644a6 Mon Sep 17 00:00:00 2001 From: olliw42 Date: Sat, 10 Jul 2021 15:02:53 +0200 Subject: [PATCH 406/714] add extra characters capability to TextEdit class --- radio/src/thirdparty/libopenui/src/textedit.h | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 013bbc202..32d8dc256 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -21,15 +21,18 @@ #include "form.h" +const char extra_chars_default[] = ":;<=>"; + class TextEdit : public FormField { friend class TextKeyboard; public: - TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0) : + TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0, const char * _extra_chars = nullptr) : FormField(parent, rect, windowFlags), value(value), length(length) { + extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; } #if defined(DEBUG_WINDOWS) @@ -68,6 +71,7 @@ class TextEdit : public FormField { char * value; bool changed = false; uint8_t length; + const char * extra_chars; uint8_t cursorPos = 0; std::function changeHandler = nullptr; @@ -85,32 +89,50 @@ class TextEdit : public FormField { } } - static uint8_t getNextChar(uint8_t c) + uint8_t getNextChar(uint8_t c) { - if (c == ' ' || c == 0) + if (c == ' ' || c == '\0') return 'A'; + else if (c >= 'A' && c < 'Z') + return c + 1; else if (c == 'Z') return 'a'; + else if (c >= 'a' && c < 'z') + return c + 1; else if (c == 'z') return '0'; - else if (c == '>') - return ' '; - else + else if (c >= '0' && c < '9') return c + 1; + else if (c == '9') + return extra_chars[0]; + else { + for (uint8_t n = 0; n < strlen(extra_chars) - 1; n++) + if (c == extra_chars[n]) return extra_chars[n + 1]; + return ' '; + } } - static uint8_t getPreviousChar(uint8_t c) + uint8_t getPreviousChar(uint8_t c) { - if (c == 'A') + if (c == ' ' || c == '\0') + return extra_chars[strlen(extra_chars) - 1]; + else if (c == 'A') return ' '; + else if (c > 'A' && c <= 'Z') + return c - 1; else if (c == 'a') return 'Z'; + else if (c > 'a' && c <= 'z') + return c - 1; else if (c == '0') return 'z'; - else if (c == ' ' || c == 0) - return '>'; - else + else if (c > '0' && c <= '9') return c - 1; + else { + for (uint8_t n = 1; n < strlen(extra_chars); n++) + if (c == extra_chars[n]) return extra_chars[n - 1]; + return '9'; + } } static uint8_t toggleCase(uint8_t c) From 3f6f488180c9376923e3925517af1c05b0c874b3 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 13 Jul 2021 08:42:34 +0200 Subject: [PATCH 407/714] [FileChoice] eject duplicates --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 9b07ebf9a..07f14d33c 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -80,7 +80,12 @@ bool FileChoice::openMenu() if (!fnLen || fnLen > maxlen) continue; // wrong size - files.emplace_back(fno.fname, fnLen); + // eject duplicates + std::string newFile(fno.fname, fnLen); + if (std::find(files.begin(), files.end(), newFile) != files.end()) + continue; + + files.emplace_back(newFile); } if (!files.empty()) { From 4aade1d0da209901ef39913ff553069d36434705 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 13 Jul 2021 09:12:55 +0200 Subject: [PATCH 408/714] [FileChoice] added missing include --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 07f14d33c..a0fa3e25b 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -23,6 +23,8 @@ #include "theme.h" #include "message_dialog.h" +#include + #if !defined(STR_SDCARD) #define STR_SDCARD "SD" #endif From b951dbf9de53a65c4fe17cc31ceb7c22481289ee Mon Sep 17 00:00:00 2001 From: eshifri Date: Mon, 19 Jul 2021 15:17:31 -0700 Subject: [PATCH 409/714] fix for #444: Accidentally altering a checkbox. --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 9dd22bcfd..33f08aa52 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -44,11 +44,15 @@ void CheckBox::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool CheckBox::onTouchEnd(coord_t x, coord_t y) { - if (enabled) { - onKeyPress(); - _setValue(!getValue()); - setFocus(SET_FOCUS_DEFAULT); - invalidate(); + if (enabled) + { + if (x < (rect.w >> 2)) + { + onKeyPress(); + _setValue(!getValue()); + setFocus(SET_FOCUS_DEFAULT); + invalidate(); + } } return true; } From 9b061261e9ba8dd2e9837d8f34a05ae829cffd5d Mon Sep 17 00:00:00 2001 From: eshifri Date: Tue, 20 Jul 2021 14:02:56 -0700 Subject: [PATCH 410/714] Force checkbox to be square. --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 14 +++++--------- radio/src/thirdparty/libopenui/src/checkbox.h | 3 +++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 33f08aa52..9dd22bcfd 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -44,15 +44,11 @@ void CheckBox::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool CheckBox::onTouchEnd(coord_t x, coord_t y) { - if (enabled) - { - if (x < (rect.w >> 2)) - { - onKeyPress(); - _setValue(!getValue()); - setFocus(SET_FOCUS_DEFAULT); - invalidate(); - } + if (enabled) { + onKeyPress(); + _setValue(!getValue()); + setFocus(SET_FOCUS_DEFAULT); + invalidate(); } return true; } diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 444d7f381..8bdd51756 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -29,6 +29,9 @@ class CheckBox : public FormField { _getValue(std::move(getValue)), _setValue(std::move(setValue)) { + coord_t size = min(rect.w, rect.h); + setWidth(size); + setHeight(size); } #if defined(DEBUG_WINDOWS) From 2b24cb2fc3995521585266af143141372a1e1a83 Mon Sep 17 00:00:00 2001 From: eshifri Date: Wed, 28 Jul 2021 20:38:11 -0700 Subject: [PATCH 411/714] Add +/- button to virtual number_edir keyboard. --- radio/src/thirdparty/libopenui/src/keyboard_number.cpp | 10 ++++++++-- radio/src/thirdparty/libopenui/src/numberedit.cpp | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index 8a07dd038..daf04c1f4 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -64,11 +64,17 @@ NumberKeyboard::NumberKeyboard() : return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - new TextButton(this, {LCD_W / 2 - 55, 50, 110, 30}, "DEFAULT", + new TextButton(this, { LCD_W/2 - 55, 50, 50, 30 }, "DEF", [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_DEFAULT); + pushEvent(EVT_VIRTUAL_KEY_DEFAULT); return 0; }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + + new TextButton(this, { LCD_W/2 + 5, 50, 50, 30 }, "+/-", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_SIGN); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); } NumberKeyboard::~NumberKeyboard() diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 248cdbb74..e309764ea 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -134,6 +134,10 @@ void NumberEdit::onEvent(event_t event) case EVT_VIRTUAL_KEY_MIN: setValue(getMin()); break; + + case EVT_VIRTUAL_KEY_SIGN: + setValue(-getValue()); + break; #endif } } From e487273ce91785300c55997d1fb4c3bc70a281e7 Mon Sep 17 00:00:00 2001 From: WismyYao Date: Mon, 2 Aug 2021 15:58:14 +0800 Subject: [PATCH 412/714] Fix the out of bounds of array --- radio/src/thirdparty/libopenui/src/textedit.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 3edcfaafb..8f3a8ba08 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -209,13 +209,15 @@ void TextEdit::onEvent(event_t event) break; case EVT_KEY_BREAK(KEY_PGDN): - memmove(&value[cursorPos], &value[cursorPos + 1], length - cursorPos - 1); - value[length - 1] = '\0'; - changed = true; - if (cursorPos > 0 && value[cursorPos] == '\0') { - cursorPos = cursorPos - 1; + if (cursorPos < length) { + memmove(&value[cursorPos], &value[cursorPos + 1], length - cursorPos - 1); + value[length - 1] = '\0'; + changed = true; + if (cursorPos > 0 && value[cursorPos] == '\0') { + cursorPos = cursorPos - 1; + } + invalidate(); } - invalidate(); break; } From 8314bb645e588e4513fa32a1337ffe0f85512603 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 4 Aug 2021 07:15:17 +0200 Subject: [PATCH 413/714] [filechoice] Fixed extension-matching in case a file without extension is present --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index a0fa3e25b..0e289b9be 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -75,7 +75,7 @@ bool FileChoice::openMenu() fnExt = getFileExtension(fno.fname, 0, 0, &fnLen, &extLen); - if (extension && !isExtensionMatching(fnExt, extension)) + if (extension && (!fnExt || !isExtensionMatching(fnExt, extension))) continue; // wrong extension if (stripExtension) fnLen -= extLen; From 398969df19cd9017b5abe55bc4de1b4ce811b608 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 4 Aug 2021 10:11:33 +0200 Subject: [PATCH 414/714] [keyboard] Fix nullptr access while trying to find a suitable full screen window --- .../thirdparty/libopenui/src/keyboard_base.h | 23 ++++++++----------- radio/src/thirdparty/libopenui/src/window.h | 6 +++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index bd7376ee6..fb6859eb8 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -53,11 +53,15 @@ class Keyboard: public FormWindow activeKeyboard = this; attach(MainWindow::instance()); fieldContainer = getFieldContainer(newField); - fieldContainer->setHeight(LCD_H - height() - fieldContainer->top()); - fieldContainer->scrollTo(newField); - invalidate(); - newField->setEditMode(true); - field = newField; + if (fieldContainer) { + fieldContainer->setHeight(LCD_H - height() - fieldContainer->top()); + fieldContainer->scrollTo(newField); + invalidate(); + newField->setEditMode(true); + field = newField; + } else { + clearField(); + } } void clearField() @@ -75,14 +79,7 @@ class Keyboard: public FormWindow Window * getFieldContainer(FormField * field) { - Window * parent = field; - while (true) { - Window * tmp = parent->getParent(); - if ((tmp->getWindowFlags() & OPAQUE) && tmp->width() == LCD_W && tmp->height() == LCD_H) { - return parent; - } - parent = tmp; - } + return field->getFullScreenWindow(); } }; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 0afe516f1..a3cbbb964 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -107,9 +107,11 @@ class Window return window == this || (parent && parent->isChild(window)); } - Window * getFullScreenWindow() + Window *getFullScreenWindow() { - return (width() == LCD_W && height() == LCD_H) ? this : parent->getFullScreenWindow(); + if (width() == LCD_W && height() == LCD_H) return this; + if (parent) return parent->getFullScreenWindow(); + return nullptr; } WindowFlags getWindowFlags() const From 27c067553bc671cb436eebedf00cf7aec514f2c7 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Thu, 5 Aug 2021 21:00:13 -0400 Subject: [PATCH 415/714] OS theme colors as suggested by JimB40 in #506 #540 Did the scripted search and replace of theme colors to introduce 11 new colors replacing 40 old colors. --- radio/src/thirdparty/libopenui/src/button.cpp | 16 ++++----- .../thirdparty/libopenui/src/coloredit.cpp | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 12 +++---- .../libopenui/src/keyboard_text.cpp | 6 ++-- .../libopenui/src/libopenui_depends.h | 34 +++++++++---------- radio/src/thirdparty/libopenui/src/menu.cpp | 18 +++++----- .../thirdparty/libopenui/src/menutoolbar.cpp | 6 ++-- .../thirdparty/libopenui/src/menutoolbar.h | 2 +- .../thirdparty/libopenui/src/modal_window.cpp | 8 ++--- .../thirdparty/libopenui/src/numberedit.cpp | 8 ++--- radio/src/thirdparty/libopenui/src/roller.h | 6 ++-- radio/src/thirdparty/libopenui/src/static.h | 2 +- radio/src/thirdparty/libopenui/src/table.cpp | 8 ++--- .../src/thirdparty/libopenui/src/textedit.cpp | 16 ++++----- radio/src/thirdparty/libopenui/src/window.cpp | 4 +-- 15 files changed, 74 insertions(+), 74 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index e9f1f71fc..f2b9ad0e2 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -70,20 +70,20 @@ void Button::checkEvents() void TextButton::paint(BitmapBuffer * dc) { - auto textColor = DEFAULT_COLOR; - auto bgColor = FIELD_FRAME_COLOR; + auto textColor = COLOR_THEME_SECONDARY1; + auto bgColor = COLOR_THEME_SECONDARY2; if (bgColorHandler) { bgColor = bgColorHandler(); } else if (checked()) { - bgColor = HIGHLIGHT_COLOR; + bgColor = COLOR_THEME_ACTIVE; } else if (hasFocus()) { - bgColor = FOCUS_BGCOLOR; + bgColor = COLOR_THEME_FOCUS; } if (checked()) { if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, bgColor); } else { dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); @@ -93,13 +93,13 @@ void TextButton::paint(BitmapBuffer * dc) dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); if (windowFlags & BUTTON_BACKGROUND) { if (hasFocus()) { - textColor = FOCUS_COLOR; + textColor = COLOR_THEME_PRIMARY2; } } else if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); } else { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); } } diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index b78ba7343..35b7d6c0e 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -41,7 +41,7 @@ class ColorBox: public Window { void paint(BitmapBuffer * dc) override { - dc->drawSolidFilledRect(0, 0, width(), height(), DEFAULT_COLOR); + dc->drawSolidFilledRect(0, 0, width(), height(), COLOR_THEME_SECONDARY1); dc->drawSolidFilledRect(1, 1, width() - 2, height() - 2, color << 16); } diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 4e49eaced..a168f009f 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -87,17 +87,17 @@ void FormField::setFocus(uint8_t flag, Window * from) void FormField::paint(BitmapBuffer * dc) { if (editMode) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, EDIT_MARKER_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR_THEME_EDIT); } else { if (!(windowFlags & FORM_NO_BORDER)) { if (hasFocus()) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FOCUS_BGCOLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR_THEME_FOCUS); } else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR_THEME_PRIMARY2); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); } } } @@ -271,10 +271,10 @@ void FormGroup::paint(BitmapBuffer * dc) { if (!(windowFlags & (FORM_NO_BORDER | FORM_FORWARD_FOCUS))) { if (!editMode && hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); } else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); } } } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 717afde31..cbf3860a5 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -112,17 +112,17 @@ void TextKeyboard::paint(BitmapBuffer * dc) } else if (*c == KEYBOARD_SPACE[0]) { // spacebar - dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, DEFAULT_COLOR); + dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, COLOR_THEME_SECONDARY1); x += 135; } else if (*c == KEYBOARD_ENTER[0]) { // enter - dc->drawSolidFilledRect(x, y-2, 80, 25, TEXT_DISABLE_COLOR); + dc->drawSolidFilledRect(x, y-2, 80, 25, COLOR_THEME_DISABLED); dc->drawText(x+40, y, "ENTER", CENTERED); x += 80; } else if (int8_t(*c) < 0) { - dc->drawBitmapPattern(x, y, LBM_SPECIAL_KEYS[uint8_t(*c - 128)], DEFAULT_COLOR); + dc->drawBitmapPattern(x, y, LBM_SPECIAL_KEYS[uint8_t(*c - 128)], COLOR_THEME_SECONDARY1); x += 45; } else { diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index aa71e0fe6..4a9c2800d 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -36,21 +36,21 @@ event_t getWindowEvent(); // These colors MUST be defined // -// #define DEFAULT_COLOR -// #define DEFAULT_BGCOLOR -// #define FOCUS_COLOR -// #define FOCUS_BGCOLOR -// #define DISABLE_COLOR -// #define HIGHLIGHT_COLOR -// #define CHECKBOX_COLOR -// #define SCROLLBAR_COLOR -// #define MENU_COLOR -// #define MENU_BGCOLOR -// #define MENU_TITLE_BGCOLOR -// #define MENU_LINE_COLOR -// #define MENU_HIGHLIGHT_COLOR -// #define MENU_HIGHLIGHT_BGCOLOR -// #define OVERLAY_COLOR -// #define TABLE_BGCOLOR -// #define TABLE_HEADER_BGCOLOR +// #define COLOR_THEME_SECONDARY1 +// #define COLOR_THEME_SECONDARY3 +// #define COLOR_THEME_PRIMARY2 +// #define COLOR_THEME_FOCUS +// #define COLOR_THEME_DISABLED +// #define COLOR_THEME_ACTIVE +// #define COLOR_THEME_FOCUS +// #define COLOR_THEME_PRIMARY3 +// #define COLOR_THEME_SECONDARY1 +// #define COLOR_THEME_PRIMARY2 +// #define COLOR_THEME_SECONDARY1_THEME_SECONDARY1 +// #define COLOR_THEME_SECONDARY2 +// #define COLOR_THEME_PRIMARY2 +// #define COLOR_THEME_FOCUS +// #define COLOR_THEME_PRIMARY1 +// #define COLOR_THEME_SECONDARY3 +// #define COLOR_THEME_FOCUS // #define CUSTOM_COLOR diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index de1beffd7..258b13487 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -104,16 +104,16 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) void MenuBody::paint(BitmapBuffer * dc) { - dc->clear(MENU_BGCOLOR); + dc->clear(COLOR_THEME_PRIMARY2); for (unsigned i = 0; i < lines.size(); i++) { auto& line = lines[i]; - LcdFlags flags = MENU_COLOR | MENU_FONT; + LcdFlags flags = COLOR_THEME_SECONDARY1 | MENU_FONT; if (selectedIndex == (int)i) { - flags = MENU_HIGHLIGHT_COLOR | MENU_FONT; - if (MENU_HIGHLIGHT_BGCOLOR != MENU_BGCOLOR) { + flags = COLOR_THEME_PRIMARY2 | MENU_FONT; + if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), - MENUS_LINE_HEIGHT, MENU_HIGHLIGHT_BGCOLOR); + MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); } } if (line.drawLine) { @@ -135,7 +135,7 @@ void MenuBody::paint(BitmapBuffer * dc) if (i > 0) { dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT, MENUS_WIDTH, - MENU_LINE_COLOR); + COLOR_THEME_SECONDARY2); } } } @@ -151,7 +151,7 @@ MenuWindowContent::MenuWindowContent(Menu* parent) : void MenuWindowContent::paint(BitmapBuffer * dc) { // the background - dc->clear(MENU_BGCOLOR); + dc->clear(COLOR_THEME_PRIMARY2); // the title if (!title.empty()) { @@ -159,7 +159,7 @@ void MenuWindowContent::paint(BitmapBuffer * dc) (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, title.c_str(), CENTERED | MENU_HEADER_FONT); dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, - MENU_LINE_COLOR); + COLOR_THEME_SECONDARY2); } } @@ -234,5 +234,5 @@ void Menu::paint(BitmapBuffer * dc) r.x = toolbar->left(); r.w += toolbar->width(); } - dc->drawSolidRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, 1, MENU_LINE_COLOR); + dc->drawSolidRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, 1, COLOR_THEME_SECONDARY2); } diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 7a27ae2d0..334098d52 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -27,11 +27,11 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, - FOCUS_BGCOLOR); + COLOR_THEME_FOCUS); dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, - &picto, 1, CENTERED | FOCUS_COLOR); + &picto, 1, CENTERED | COLOR_THEME_PRIMARY2); } else { dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, - &picto, 1, CENTERED | DEFAULT_COLOR); + &picto, 1, CENTERED | COLOR_THEME_SECONDARY1); } } diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index de493a04f..484b6ba48 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -78,7 +78,7 @@ class MenuToolbar: public FormGroup { void paint(BitmapBuffer * dc) override { - dc->clear(DEFAULT_BGCOLOR); + dc->clear(COLOR_THEME_SECONDARY3); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index e1557a17b..2c1222890 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -41,12 +41,12 @@ void ModalWindow::deleteLater(bool detach, bool trash) void ModalWindow::paint(BitmapBuffer * dc) { - dc->drawFilledRect(0, 0, width(), height(), SOLID, OVERLAY_COLOR, OPACITY(5)); + dc->drawFilledRect(0, 0, width(), height(), SOLID, COLOR_THEME_PRIMARY1, OPACITY(5)); } void ModalWindowContent::paint(BitmapBuffer * dc) { - dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, FOCUS_BGCOLOR); - dc->drawText(FIELD_PADDING_LEFT, (POPUP_HEADER_HEIGHT - getFontHeight(FONT(STD))) / 2, title.c_str(), FOCUS_COLOR); - dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, width(), height() - POPUP_HEADER_HEIGHT, DEFAULT_BGCOLOR); + dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, COLOR_THEME_FOCUS); + dc->drawText(FIELD_PADDING_LEFT, (POPUP_HEADER_HEIGHT - getFontHeight(FONT(STD))) / 2, title.c_str(), COLOR_THEME_PRIMARY2); + dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, width(), height() - POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY3); } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 248cdbb74..facfdfb06 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -36,13 +36,13 @@ void NumberEdit::paint(BitmapBuffer * dc) LcdFlags textColor; if (editMode) - textColor = FOCUS_COLOR; + textColor = COLOR_THEME_PRIMARY2; else if (hasFocus()) - textColor = FOCUS_COLOR; + textColor = COLOR_THEME_PRIMARY2; // else if (enabled && (value != 0 || zeroText.empty())) - // textColor = DEFAULT_COLOR; + // textColor = COLOR_THEME_SECONDARY1; else - textColor = DEFAULT_COLOR; + textColor = COLOR_THEME_SECONDARY1; if (displayFunction) { displayFunction(dc, textColor, value); diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index d3a5ac846..7135e509a 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -81,9 +81,9 @@ class Roller: public Choice while (y < yMax) { auto displayedValue = getValueFromIndex(index); - auto fgColor = DISABLE_COLOR; + auto fgColor = COLOR_THEME_DISABLED; if (value == displayedValue) { - fgColor = FOCUS_COLOR | FONT(STD); + fgColor = COLOR_THEME_PRIMARY2 | FONT(STD); } unsigned valueIndex = displayedValue - vmin; @@ -95,7 +95,7 @@ class Roller: public Choice } if (windowFlags & ROLLER_SEPARATION_LINES) { - dc->drawSolidHorizontalLine(0, y - 10, width(), DISABLE_COLOR); + dc->drawSolidHorizontalLine(0, y - 10, width(), COLOR_THEME_DISABLED); } if (++index == valuesCount) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 15f1bcf5b..bceae5ba9 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -30,7 +30,7 @@ class StaticText: public Window text(std::move(text)) { if (windowFlags & BUTTON_BACKGROUND) { - setBackgroundColor(FIELD_FRAME_COLOR); + setBackgroundColor(COLOR_THEME_SECONDARY2); } } diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 7ea4fd9a5..a4401d838 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -24,7 +24,7 @@ void Table::Header::paint(BitmapBuffer * dc) { coord_t x = 10; if (!cells.empty()) { - dc->clear(TABLE_HEADER_BGCOLOR); + dc->clear(COLOR_THEME_FOCUS); for (unsigned i = 0; i < cells.size(); i++) { auto cell = cells[i]; if (cell) { @@ -61,15 +61,15 @@ void Table::Body::paint(BitmapBuffer * dc) { coord_t y = 0; int index = 0; - dc->clear(DEFAULT_BGCOLOR); + dc->clear(COLOR_THEME_SECONDARY3); for (auto line: lines) { bool highlight = (index == selection); - dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, highlight ? MENU_HIGHLIGHT_BGCOLOR : TABLE_BGCOLOR); + dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, highlight ? COLOR_THEME_FOCUS : COLOR_THEME_SECONDARY3); coord_t x = 10; for (unsigned i = 0; i < line->cells.size(); i++) { auto cell = line->cells[i]; if (cell) { - cell->paint(dc, x, y, line->flags + (highlight ? MENU_HIGHLIGHT_COLOR - COLOR_MASK(line->flags) : DEFAULT_COLOR)); + cell->paint(dc, x, y, line->flags + (highlight ? COLOR_THEME_PRIMARY2 - COLOR_MASK(line->flags) : COLOR_THEME_SECONDARY1)); } x += static_cast
(parent)->columnsWidth[i]; } diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 3edcfaafb..d52cdd150 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -48,34 +48,34 @@ void TextEdit::paint(BitmapBuffer * dc) if (editMode) { dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, - FOCUS_COLOR); + COLOR_THEME_PRIMARY2); coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); #if defined(SOFTWARE_KEYBOARD) - dc->drawSolidFilledRect(left + 2, 2, 2, height() - 4, FOCUS_COLOR); + dc->drawSolidFilledRect(left + 2, 2, 2, height() - 4, COLOR_THEME_PRIMARY2); #else char s[] = {value[cursorPos], '\0'}; dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, FIELD_PADDING_TOP - 1, getTextWidth(s, 1) + 1, - height() - 2, FOCUS_COLOR); + height() - 2, COLOR_THEME_PRIMARY2); dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, - DEFAULT_COLOR); + COLOR_THEME_SECONDARY1); #endif } else { const char * displayedValue = value; - LcdFlags textColor = FOCUS_COLOR; + LcdFlags textColor = COLOR_THEME_PRIMARY2; if (hasFocus()) { if (strlen(value) == 0) { displayedValue = "---"; } - textColor = FOCUS_COLOR; + textColor = COLOR_THEME_PRIMARY2; } else { if (strlen(value) == 0) { displayedValue = "---"; - textColor = DISABLE_COLOR; + textColor = COLOR_THEME_DISABLED; } else { - textColor = DEFAULT_COLOR; + textColor = COLOR_THEME_SECONDARY1; } } dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7953367e6..096b14b28 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -520,7 +520,7 @@ void Window::drawVerticalScrollbar(BitmapBuffer * dc) yhgt = 15; if (yhgt + yofs > rect.h) yhgt = rect.h - yofs; - dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, SCROLLBAR_COLOR); + dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, COLOR_THEME_PRIMARY3); } } @@ -533,6 +533,6 @@ void Window::drawHorizontalScrollbar(BitmapBuffer * dc) xwdth = 15; if (xwdth + xofs > rect.w) xwdth = rect.w - xofs; - dc->drawSolidFilledRect(scrollPositionX + xofs, rect.h - SCROLLBAR_WIDTH, xwdth, SCROLLBAR_WIDTH, SCROLLBAR_COLOR); + dc->drawSolidFilledRect(scrollPositionX + xofs, rect.h - SCROLLBAR_WIDTH, xwdth, SCROLLBAR_WIDTH, COLOR_THEME_PRIMARY3); } } From efb228c8e204c18f623d7e0a66eb1319326c5fd7 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Sat, 14 Aug 2021 09:32:49 -0400 Subject: [PATCH 416/714] Assigned unused flags for Lua use Please see lua/api_colorlcd.h --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index d8a300f0c..0f6346daf 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -28,14 +28,15 @@ /* drawText flags */ #define LEFT 0x00u /* align left */ +// 0x01u used by Lua in api_colorlcd.h #define VCENTERED 0x02u /* align center vertically */ #define CENTERED 0x04u /* align center */ #define RIGHT 0x08u /* align right */ #define SHADOWED 0x80u /* black copy at +1 +1 */ -// unused 0x1000u +// 0x1000u used by Lua in api_colorlcd.h #define SPACING_NUMBERS_CONST 0x2000u #define VERTICAL 0x4000u -// unused 0x8000u +// 0x8000u used by Lua in api_colorlcd.h /* drawNumber flags */ #define LEADING0 0x10u From 1c1d5428394bb3cb683892d70a91e05b726e4259 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 25 Aug 2021 12:28:22 -0400 Subject: [PATCH 417/714] Updated software keyboard to center, and provice press indicators --- .../libopenui/src/keyboard_text.cpp | 173 +++++++++++++++--- .../thirdparty/libopenui/src/keyboard_text.h | 19 ++ .../src/thirdparty/libopenui/src/textedit.cpp | 3 + 3 files changed, 166 insertions(+), 29 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index cbf3860a5..92553d743 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -79,7 +79,8 @@ const char * const KEYBOARD_NUMBERS[] = { KEYBOARD_SET_LETTERS KEYBOARD_SPACE KEYBOARD_ENTER }; -const char * const * const KEYBOARD_LAYOUTS[] = { +#define NUMBER_OF_KEYBOARD_LAYOUTS 4 +const char * const * const KEYBOARD_LAYOUTS[NUMBER_OF_KEYBOARD_LAYOUTS] = { KEYBOARD_UPPERCASE, KEYBOARD_LOWERCASE, KEYBOARD_LOWERCASE, @@ -97,70 +98,169 @@ TextKeyboard::~TextKeyboard() _instance = nullptr; } +int TextKeyboard::getCharWidth(const char c) +{ + if (int8_t(c) < 0) return KEYBOARD_BITMAP_WIDTH; + + switch (c) + { + case ' ': return 15; + case KEYBOARD_SPACE[0]: return KEYBOARD_SPACE_WIDTH; + case KEYBOARD_ENTER[0]: return KEYBOARD_ENTER_WIDTH; + default: return KEYBOARD_CHAR_WIDTH; + } +} + + +int TextKeyboard::calculateMaxWidth() +{ + int maxWidth = 0; + for (uint8_t i = 0; i < NUMBER_OF_KEYBOARD_LAYOUTS; i++) { + int lineWidth = 0; + const char * c = layout[i]; + while (*c) { + lineWidth += getCharWidth(*c); + maxWidth = max(lineWidth, maxWidth); + c++; + } + } + + return maxWidth; +} + void TextKeyboard::paint(BitmapBuffer * dc) { lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); dc->clear(CUSTOM_COLOR); - for (uint8_t i=0; i<4; i++) { - coord_t y = 15 + i * 40; - coord_t x = 15; + // center the keyboard + coord_t start_x = (dc->width() - calculateMaxWidth()) / 2; + + for (uint8_t i = 0; i < NUMBER_OF_KEYBOARD_LAYOUTS; i++) { + coord_t y = KEYBOARD_OFFSET_Y + i * KEYBOARD_ROW_HEIGHT; + coord_t x = start_x; const char * c = layout[i]; + while (*c) { - if (*c == ' ') { - x += 15; - } - else if (*c == KEYBOARD_SPACE[0]) { + if (*c == KEYBOARD_SPACE[0]) { // spacebar dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, COLOR_THEME_SECONDARY1); - x += 135; } else if (*c == KEYBOARD_ENTER[0]) { // enter - dc->drawSolidFilledRect(x, y-2, 80, 25, COLOR_THEME_DISABLED); - dc->drawText(x+40, y, "ENTER", CENTERED); - x += 80; + dc->drawSolidFilledRect(x, y - 2, KEYBOARD_ENTER_WIDTH, 25, COLOR_THEME_DISABLED); + dc->drawText(x + KEYBOARD_ENTER_WIDTH / 2, y + 2, "ENTER", CENTERED); } else if (int8_t(*c) < 0) { - dc->drawBitmapPattern(x, y, LBM_SPECIAL_KEYS[uint8_t(*c - 128)], COLOR_THEME_SECONDARY1); - x += 45; + const uint8_t *pBitmap = LBM_SPECIAL_KEYS[uint8_t(*c - 128)]; + uint8_t bitmapWidth = *pBitmap, bitmapHeight = *(pBitmap + 2); + if (touched && *c == touch_key) { + dc->drawSolidFilledRect(x - 2, y + 3, bitmapWidth + 4, bitmapHeight + 4, COLOR_THEME_SECONDARY2); + } + + dc->drawBitmapPattern(x, y + 5, pBitmap, COLOR_THEME_SECONDARY1); } else { - dc->drawSizedText(x, y, c, 1); - x += 30; + if (touched && *c == touch_key) { + dc->drawSolidFilledRect(x - KEYBOARD_CHAR_WIDTH / 2, y - ((KEYBOARD_ROW_HEIGHT - 16) / 2), KEYBOARD_CHAR_WIDTH, KEYBOARD_ROW_HEIGHT, COLOR_THEME_FOCUS); + } + dc->drawSizedText(x, y, c, 1, CENTERED); } + x += getCharWidth(*c); c++; } } } +bool TextKeyboard::onTouchStart(coord_t x, coord_t y) +{ + // remove the centering. If x < 0 then this is an invalid touch event + x -= (this->width() - calculateMaxWidth()) / 2; + if (x < 0) { + if (touched) { + touched = false; + invalidate(); + } + return true; + } + + touched = false; + + uint8_t row = max(0, y - 5) / KEYBOARD_ROW_HEIGHT; + const char * key = layout[row]; + + while(*key) { + if (*key == KEYBOARD_SPACE[0] && x <= KEYBOARD_SPACE_WIDTH) { + touch_key = KEYBOARD_SPACE[0]; + touched = true; + break; + } + else if (*key == KEYBOARD_ENTER[0] && x <= KEYBOARD_ENTER_WIDTH) { + touch_key = KEYBOARD_ENTER[0]; + touched = true; + break; + } + else if (int8_t(*key) < 0 && x <= KEYBOARD_BITMAP_WIDTH) { + if (uint8_t(*key) == 128) { + touch_key = KEYBOARD_BACKSPACE[0]; + touched = true; + break; + } else { + touched = false; + break; + } + } + else if (*key != ' ' && (x >= -(KEYBOARD_CHAR_WIDTH / 2) && x <= KEYBOARD_CHAR_WIDTH / 2)) { + touch_key = *key; + touched = true; + break; + } + + x -= getCharWidth(*key); + key++; + + if (touched) { + invalidate(); + break; + } + } + return true; +} + bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) { + // remove the centering width, if x < 0 then this is an invalid touch on a centered keyboard. + x -= (this->width() - calculateMaxWidth()) / 2; + if (x < 0) { + touched = false; + return true; + } + onKeyPress(); - uint8_t row = max(0, y - 5) / 40; + if (touched) + invalidate(); + touched = false; + + uint8_t row = max(0, y - 5) / KEYBOARD_ROW_HEIGHT; + const char * key = layout[row]; while (*key) { - if (*key == ' ') { - x -= 15; - } - else if (*key == KEYBOARD_SPACE[0]) { - if (x <= 135) { + if (*key == KEYBOARD_SPACE[0]) { + if (x <= KEYBOARD_SPACE_WIDTH) { pushEvent(EVT_VIRTUAL_KEY(' ')); return true; } - x -= 135; } else if (*key == KEYBOARD_ENTER[0]) { - if (x <= 80) { + if (x <= KEYBOARD_ENTER_WIDTH) { // enter hide(); return true; } - x -= 80; } else if (int8_t(*key) < 0) { - if (x <= 45) { + if (x <= KEYBOARD_BITMAP_WIDTH) { uint8_t specialKey = *key; if (specialKey == 128) { // backspace @@ -172,17 +272,32 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) } break; } - x -= 45; } else { - if (x <= 30) { + if (x <= KEYBOARD_CHAR_WIDTH && *key != ' ') { pushEvent(EVT_VIRTUAL_KEY(*key)); return true; } - x -= 30; } + x -= getCharWidth(*key); key++; } return true; } + +bool TextKeyboard::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + if (touched) + invalidate(); + + touched = false; + return false; +} + +#if defined(HARDWARE_KEYS) +void TextKeyboard::onEvent(event_t event) +{ + +} +#endif diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index afc8cfef2..732e6e2c3 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -29,6 +29,13 @@ #define KEYBOARD_SET_LETTERS "\203" #define KEYBOARD_SET_NUMBERS "\204" +#define KEYBOARD_OFFSET_Y 15 +#define KEYBOARD_ROW_HEIGHT 40 +#define KEYBOARD_CHAR_WIDTH 30 +#define KEYBOARD_SPACE_WIDTH 135 +#define KEYBOARD_ENTER_WIDTH 80 +#define KEYBOARD_BITMAP_WIDTH 45 + class TextKeyboard : public Keyboard { public: TextKeyboard(); @@ -53,10 +60,22 @@ class TextKeyboard : public Keyboard { #if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t x, coord_t y) override; + bool onTouchStart(coord_t x, coord_t y) override; + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; #endif protected: +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + static TextKeyboard * _instance; const char * const * layout; + private: + int calculateMaxWidth(); + int getCharWidth(const char c); + + char touch_key = -128; + bool touched = false; }; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 45d4997dd..0e023d354 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -170,6 +170,9 @@ void TextEdit::onEvent(event_t event) case EVT_KEY_BREAK(KEY_EXIT): changeEnd(); +#if defined(SOFTWARE_KEYBOARD) + Keyboard::hide(); +#endif FormField::onEvent(event); break; From 9ee4d2b726ccab7f128ee682c9ab1d272c01ed0f Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 25 Aug 2021 15:31:32 -0400 Subject: [PATCH 418/714] hide numeric keyboard on RETURN key --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 91d87e3bf..bba57e9e0 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -106,6 +106,15 @@ void NumberEdit::onEvent(event_t event) case EVT_KEY_FIRST(KEY_EXIT): return; + case EVT_KEY_BREAK(KEY_EXIT): + changeEnd(); +#if defined(SOFTWARE_KEYBOARD) + Keyboard::hide(); +#endif + FormField::onEvent(event); + break; + + #if defined(HARDWARE_TOUCH) case EVT_VIRTUAL_KEY_PLUS: setValue(getValue() + getStep()); From 2f30482115a235f5abad12b4b90090081a3d0b4b Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 25 Aug 2021 15:56:59 -0400 Subject: [PATCH 419/714] fix issue with compile --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index bba57e9e0..63792a42b 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -107,7 +107,6 @@ void NumberEdit::onEvent(event_t event) return; case EVT_KEY_BREAK(KEY_EXIT): - changeEnd(); #if defined(SOFTWARE_KEYBOARD) Keyboard::hide(); #endif From 75110d2f0dbc41582ef083e4a6b9dfc48fd2470f Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 25 Aug 2021 15:59:38 -0400 Subject: [PATCH 420/714] modified to fix issue with return --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 63792a42b..f6e9934b1 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -110,7 +110,6 @@ void NumberEdit::onEvent(event_t event) #if defined(SOFTWARE_KEYBOARD) Keyboard::hide(); #endif - FormField::onEvent(event); break; From 36e048cde3c48c01b2fdbe26d1daccfe893ece88 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 25 Aug 2021 16:53:47 -0400 Subject: [PATCH 421/714] fixing stuff --- .../src/thirdparty/libopenui/src/numberedit.cpp | 17 +++-------------- radio/src/thirdparty/libopenui/src/numberedit.h | 2 -- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index f6e9934b1..eacc2b86e 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -103,14 +103,11 @@ void NumberEdit::onEvent(event_t event) } #endif - case EVT_KEY_FIRST(KEY_EXIT): - return; - - case EVT_KEY_BREAK(KEY_EXIT): + case EVT_KEY_FIRST(KEY_EXIT): #if defined(SOFTWARE_KEYBOARD) - Keyboard::hide(); + Keyboard::hide(); #endif - break; + break; #if defined(HARDWARE_TOUCH) @@ -174,11 +171,3 @@ bool NumberEdit::onTouchEnd(coord_t, coord_t) } #endif -void NumberEdit::onFocusLost() -{ -#if defined(SOFTWARE_KEYBOARD) - Keyboard::hide(); -#endif - - FormField::onFocusLost(); -} diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 67ff5da90..180889c90 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -67,8 +67,6 @@ class NumberEdit : public BaseNumberEdit bool onTouchEnd(coord_t x, coord_t y) override; #endif - void onFocusLost() override; - protected: std::function displayFunction; std::string prefix; From 41b40f4afd5fa4b0f3a5af7be881ff77c86e7d33 Mon Sep 17 00:00:00 2001 From: eshifri Date: Wed, 25 Aug 2021 20:52:09 -0700 Subject: [PATCH 422/714] do not change window size when moving the top --- radio/src/thirdparty/libopenui/src/window.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 096b14b28..7f0222f4a 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -501,7 +501,6 @@ void Window::moveWindowsTop(coord_t y, coord_t delta) invalidate(); } } - setInnerHeight(innerHeight + delta); } void Window::invalidate(const rect_t & rect) From 8a1089538fa2b2a9eaaf4ce10e52ce513353ebf7 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 26 Aug 2021 17:26:28 -0400 Subject: [PATCH 423/714] trying to fix scrolling to correct positon when keyboard pops up --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + .../libopenui/src/keyboard_base.cpp | 103 ++++++++++++++++++ .../thirdparty/libopenui/src/keyboard_base.h | 58 ++-------- .../thirdparty/libopenui/src/numberedit.cpp | 1 + .../src/thirdparty/libopenui/src/textedit.cpp | 4 +- radio/src/thirdparty/libopenui/src/window.h | 5 + 6 files changed, 122 insertions(+), 50 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/keyboard_base.cpp diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 98bcf5f78..c1a9671d3 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -42,5 +42,6 @@ if(SOFTWARE_KEYBOARD) ${LIBOPENUI_SRC} keyboard_text.cpp keyboard_number.cpp + keyboard_base.cpp ) endif() diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp new file mode 100644 index 000000000..a74e6516b --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +#include "keyboard_base.h" + +void Keyboard::hide() +{ + if (activeKeyboard) { + activeKeyboard->clearField(); + activeKeyboard = nullptr; + } +} + +void Keyboard::clearField() +{ + detach(); + if (fields) { + fields->setHeight(oldHeight); + fields = nullptr; + } + if (fieldContainer) { + fieldContainer->setHeight(LCD_H - 0 - fieldContainer->top()); + fieldContainer = nullptr; + } + if (field) { + field->setEditMode(false); + field = nullptr; + } +} + +void Keyboard::setField(FormField* newField) +{ + if (activeKeyboard) { + if (activeKeyboard == this) return; + activeKeyboard->clearField(); + } + activeKeyboard = this; + attach(MainWindow::instance()); + + fieldContainer = getFieldContainer(newField); + if (fieldContainer) { + coord_t newWindowHeight = LCD_H - height(); + + fieldContainer->setHeight(newWindowHeight); + + std::list children = fieldContainer->getChildren(); + fields = *std::next(children.begin()); + + rect_t scrollRect = { + fieldContainer->left(), + fields->top(), // this will remove the title from the screen to provide extra visible space. + fieldContainer->width(), + fieldContainer->height() + }; + // both should be the same height as we are scrolled to the right position + fieldContainer->scrollTo(scrollRect); + oldHeight = fields->height(); + fields->setHeight(newWindowHeight); + + // now we need to calculate the offset of the field in the fields scroll container + coord_t offsetX = 0, offsetY = 0; + Window* parentWindow = newField->getParent(); + while (parentWindow && parentWindow != fieldContainer) { + offsetX += parentWindow->left(); + offsetY += parentWindow->top(); + parentWindow = parentWindow->getParent(); + } + + scrollRect = { + offsetX, + offsetY + 69, + fieldContainer->width(), + fieldContainer->height() + }; + fields->scrollTo(scrollRect); + + invalidate(); + newField->setEditMode(true); + field = newField; + } else { + clearField(); + } +} + +Window* Keyboard::getFieldContainer(FormField* field) +{ + return field->getFullScreenWindow(); +} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index fb6859eb8..2240d183c 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -25,61 +25,21 @@ class Keyboard: public FormWindow { public: - explicit Keyboard(coord_t height): - FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) + explicit Keyboard(coord_t height) : FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) { } - static void hide() - { - if (activeKeyboard) { - activeKeyboard->clearField(); - activeKeyboard = nullptr; - } - } + static void hide(); protected: static Keyboard * activeKeyboard; - FormField * field = nullptr; - Window * fieldContainer = nullptr; + FormField *field = nullptr; + Window *fieldContainer = nullptr; + Window *fields = nullptr; + coord_t oldHeight = 0; - void setField(FormField * newField) - { - if (activeKeyboard) { - if (activeKeyboard == this) - return; - activeKeyboard->clearField(); - } - activeKeyboard = this; - attach(MainWindow::instance()); - fieldContainer = getFieldContainer(newField); - if (fieldContainer) { - fieldContainer->setHeight(LCD_H - height() - fieldContainer->top()); - fieldContainer->scrollTo(newField); - invalidate(); - newField->setEditMode(true); - field = newField; - } else { - clearField(); - } - } - - void clearField() - { - detach(); - if (fieldContainer) { - fieldContainer->setHeight(LCD_H - 0 - fieldContainer->top()); - fieldContainer = nullptr; - } - if (field) { - field->setEditMode(false); - field = nullptr; - } - } - - Window * getFieldContainer(FormField * field) - { - return field->getFullScreenWindow(); - } + void setField(FormField *newField); + void clearField(); + Window *getFieldContainer(FormField * field); }; diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index eacc2b86e..f9d56fbbd 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -106,6 +106,7 @@ void NumberEdit::onEvent(event_t event) case EVT_KEY_FIRST(KEY_EXIT): #if defined(SOFTWARE_KEYBOARD) Keyboard::hide(); + return; #endif break; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 0e023d354..b2aa9ef94 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -170,10 +170,12 @@ void TextEdit::onEvent(event_t event) case EVT_KEY_BREAK(KEY_EXIT): changeEnd(); + FormField::onEvent(event); + #if defined(SOFTWARE_KEYBOARD) Keyboard::hide(); #endif - FormField::onEvent(event); + setFocus(SET_FOCUS_DEFAULT, this); break; case EVT_KEY_LONG(KEY_ENTER): diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index a3cbbb964..6902eb4e4 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -144,6 +144,11 @@ class Window focusHandler = std::move(handler); } + const std::list getChildren() + { + return children; + } + virtual void deleteLater(bool detach = true, bool trash = true); void clear(); From 95f696bfefa8ce74bff297625aa894b4bddd7020 Mon Sep 17 00:00:00 2001 From: kevinkoenig Date: Fri, 27 Aug 2021 13:11:11 -0400 Subject: [PATCH 424/714] fix scroll position --- .../libopenui/src/keyboard_base.cpp | 91 ++++++++++++------- .../thirdparty/libopenui/src/keyboard_base.h | 12 ++- .../libopenui/src/keyboard_text.cpp | 4 +- 3 files changed, 66 insertions(+), 41 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index a74e6516b..e8444eda2 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -1,8 +1,8 @@ /* - * Copyright (C) OpenTX + * Copyright (C) EdgeTX * * Source: - * https://github.com/opentx/libopenui + * https://github.com/edgetx/libopenui * * This file is a part of libopenui library. * @@ -43,7 +43,24 @@ void Keyboard::clearField() } } -void Keyboard::setField(FormField* newField) +coord_t calcScrollOffsetForField(FormField *newField, Window *topWindow) +{ + // now we need to calculate the offset of the field in the fields scroll container + // start with the current fields top and walk the hierarchy to calculate positon + coord_t offsetY = newField->top(); + + Window* parentWindow = newField->getParent(); + while (parentWindow && parentWindow != topWindow) { + offsetY += parentWindow->top(); + parentWindow = parentWindow->getParent(); + } + + // try and place it in the middle of the screen. The containing window MUST have + // already been resized + return offsetY - topWindow->height() / 2; +} + +void Keyboard::attachKeyboard() { if (activeKeyboard) { if (activeKeyboard == this) return; @@ -51,44 +68,50 @@ void Keyboard::setField(FormField* newField) } activeKeyboard = this; attach(MainWindow::instance()); +} + + +// this routine finds the window that is a FormWindow. This is the window +// that contains all of the editable fields. This is the window that needs +// to be scrolled into view. +FormWindow *Keyboard::findFormWindow() +{ + if (fieldContainer) { + auto children = fieldContainer->getChildren(); + auto k = children.begin(); + while (k != children.end()) + { + FormWindow *a = dynamic_cast(*k); + if (a) + return a; + k++; + } + } + + return nullptr; +} + +void Keyboard::setField(FormField* newField) +{ + attachKeyboard(); fieldContainer = getFieldContainer(newField); if (fieldContainer) { - coord_t newWindowHeight = LCD_H - height(); + coord_t newHeight = LCD_H - height(); + fieldContainer->setHeight(newHeight); - fieldContainer->setHeight(newWindowHeight); + fields = findFormWindow(); + if (fields) { + // scroll the header of the window out of view to get more space to + // see the field being edited + fieldContainer->setScrollPositionY(fields->top()); + oldHeight = fields->height(); + fields->setHeight(newHeight); - std::list children = fieldContainer->getChildren(); - fields = *std::next(children.begin()); - - rect_t scrollRect = { - fieldContainer->left(), - fields->top(), // this will remove the title from the screen to provide extra visible space. - fieldContainer->width(), - fieldContainer->height() - }; - // both should be the same height as we are scrolled to the right position - fieldContainer->scrollTo(scrollRect); - oldHeight = fields->height(); - fields->setHeight(newWindowHeight); - - // now we need to calculate the offset of the field in the fields scroll container - coord_t offsetX = 0, offsetY = 0; - Window* parentWindow = newField->getParent(); - while (parentWindow && parentWindow != fieldContainer) { - offsetX += parentWindow->left(); - offsetY += parentWindow->top(); - parentWindow = parentWindow->getParent(); + coord_t offsetY = calcScrollOffsetForField(newField, fields); + fields->setScrollPositionY(offsetY); } - scrollRect = { - offsetX, - offsetY + 69, - fieldContainer->width(), - fieldContainer->height() - }; - fields->scrollTo(scrollRect); - invalidate(); newField->setEditMode(true); field = newField; diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 2240d183c..d55c55239 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -1,8 +1,8 @@ /* - * Copyright (C) OpenTX + * Copyright (C) EdgeTX * * Source: - * https://github.com/opentx/libopenui + * https://github.com/edgetx/libopenui * * This file is a part of libopenui library. * @@ -34,12 +34,14 @@ class Keyboard: public FormWindow protected: static Keyboard * activeKeyboard; FormField *field = nullptr; - Window *fieldContainer = nullptr; - Window *fields = nullptr; - coord_t oldHeight = 0; + Window *fieldContainer = nullptr; + FormWindow *fields = nullptr; + coord_t oldHeight = 0; void setField(FormField *newField); void clearField(); Window *getFieldContainer(FormField * field); + void attachKeyboard(); + FormWindow *findFormWindow(); }; diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 92553d743..9f827bbbf 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -174,7 +174,7 @@ void TextKeyboard::paint(BitmapBuffer * dc) bool TextKeyboard::onTouchStart(coord_t x, coord_t y) { - // remove the centering. If x < 0 then this is an invalid touch event + // remove the centering. If x < 0 then this is an invalid touch event x -= (this->width() - calculateMaxWidth()) / 2; if (x < 0) { if (touched) { @@ -274,7 +274,7 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) } } else { - if (x <= KEYBOARD_CHAR_WIDTH && *key != ' ') { + if (*key != ' ' && (x >= -(KEYBOARD_CHAR_WIDTH / 2) && x <= KEYBOARD_CHAR_WIDTH / 2)) { pushEvent(EVT_VIRTUAL_KEY(*key)); return true; } From 6c16afa28b7f49137729a39a24adc14d92478f26 Mon Sep 17 00:00:00 2001 From: jfrickmann Date: Thu, 26 Aug 2021 10:46:15 -0400 Subject: [PATCH 425/714] Applied flags to SubTitle --- radio/src/thirdparty/libopenui/src/static.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index bceae5ba9..f7beee158 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -63,8 +63,8 @@ class StaticText: public Window class Subtitle: public StaticText { public: - Subtitle(Window * parent, const rect_t & rect, const char * text): - StaticText(parent, rect, text, 0, FONT(BOLD)) + Subtitle(Window * parent, const rect_t & rect, const char * text, WindowFlags windowFlags = 0, LcdFlags textFlags = 0): + StaticText(parent, rect, text, windowFlags, textFlags | FONT(BOLD)) { } From 47585b882956646abbc04b503a888a78e075f323 Mon Sep 17 00:00:00 2001 From: kevinkoenig Date: Mon, 30 Aug 2021 04:35:55 -0400 Subject: [PATCH 426/714] fixed potential compilation issue --- .../libopenui/src/keyboard_base.cpp | 25 ----------------- .../thirdparty/libopenui/src/keyboard_base.h | 27 +++++++++++++++++-- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index e8444eda2..81efb7a2f 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -18,31 +18,6 @@ */ #include "keyboard_base.h" -void Keyboard::hide() -{ - if (activeKeyboard) { - activeKeyboard->clearField(); - activeKeyboard = nullptr; - } -} - -void Keyboard::clearField() -{ - detach(); - if (fields) { - fields->setHeight(oldHeight); - fields = nullptr; - } - if (fieldContainer) { - fieldContainer->setHeight(LCD_H - 0 - fieldContainer->top()); - fieldContainer = nullptr; - } - if (field) { - field->setEditMode(false); - field = nullptr; - } -} - coord_t calcScrollOffsetForField(FormField *newField, Window *topWindow) { // now we need to calculate the offset of the field in the fields scroll container diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index d55c55239..4f3cfcd78 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -29,7 +29,31 @@ class Keyboard: public FormWindow { } - static void hide(); + void clearField() + { + detach(); + if (fields) { + fields->setHeight(oldHeight); + fields = nullptr; + } + if (fieldContainer) { + fieldContainer->setHeight(LCD_H - 0 - fieldContainer->top()); + fieldContainer = nullptr; + } + if (field) { + field->setEditMode(false); + field = nullptr; + } + } + + static void hide() + { + if (activeKeyboard) { + activeKeyboard->clearField(); + activeKeyboard = nullptr; + } + } + protected: static Keyboard * activeKeyboard; @@ -39,7 +63,6 @@ class Keyboard: public FormWindow coord_t oldHeight = 0; void setField(FormField *newField); - void clearField(); Window *getFieldContainer(FormField * field); void attachKeyboard(); FormWindow *findFormWindow(); From 2c5ed7e3ad332a7fa380089649155c9a8c1e13fa Mon Sep 17 00:00:00 2001 From: kevinkoenig Date: Wed, 1 Sep 2021 07:20:56 -0400 Subject: [PATCH 427/714] fix crash in number kebyoard when NumericKeyboard looses focus --- .../libopenui/src/keyboard_base.cpp | 39 ++++++++++++++----- .../thirdparty/libopenui/src/keyboard_base.h | 2 +- .../thirdparty/libopenui/src/numberedit.cpp | 10 +++++ .../src/thirdparty/libopenui/src/numberedit.h | 1 + 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 81efb7a2f..10c80d526 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -49,17 +49,25 @@ void Keyboard::attachKeyboard() // this routine finds the window that is a FormWindow. This is the window // that contains all of the editable fields. This is the window that needs // to be scrolled into view. -FormWindow *Keyboard::findFormWindow() +FormWindow *Keyboard::findFormWindow(Window *parent) { - if (fieldContainer) { - auto children = fieldContainer->getChildren(); - auto k = children.begin(); - while (k != children.end()) + if (parent) { + auto children = parent->getChildren(); + auto childIterator = children.begin(); + while (childIterator != children.end()) { - FormWindow *a = dynamic_cast(*k); - if (a) - return a; - k++; +#if defined(DEBUG_WINDOWS) + std::string windowName; + Window *window = dynamic_cast(*childIterator); + if (window) + windowName = window->getName(); +#endif + + FormWindow *formWindow = dynamic_cast(*childIterator); + if (formWindow) + return formWindow; + + childIterator++; } } @@ -72,11 +80,18 @@ void Keyboard::setField(FormField* newField) fieldContainer = getFieldContainer(newField); if (fieldContainer) { +#if defined(DEBUG_WINDOWS) + auto windowName = fieldContainer->getName(); +#endif coord_t newHeight = LCD_H - height(); fieldContainer->setHeight(newHeight); - fields = findFormWindow(); + fields = findFormWindow(fieldContainer); + if (fields) { +#if defined(DEBUG_WINDOWS) + windowName = fields->getName(); +#endif // scroll the header of the window out of view to get more space to // see the field being edited fieldContainer->setScrollPositionY(fields->top()); @@ -85,6 +100,10 @@ void Keyboard::setField(FormField* newField) coord_t offsetY = calcScrollOffsetForField(newField, fields); fields->setScrollPositionY(offsetY); + } else { + // TODO: what happens if field container is null. + // we will need to interrogate the fieldContainer for its + // FormGroup. That is really how it should work anyway. } invalidate(); diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 4f3cfcd78..b30c76a05 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -65,6 +65,6 @@ class Keyboard: public FormWindow void setField(FormField *newField); Window *getFieldContainer(FormField * field); void attachKeyboard(); - FormWindow *findFormWindow(); + FormWindow *findFormWindow(Window *parent); }; diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index f9d56fbbd..48cd71e44 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -150,6 +150,16 @@ void NumberEdit::onEvent(event_t event) FormField::onEvent(event); } +void NumberEdit::onFocusLost() +{ +#if defined(SOFTWARE_KEYBOARD) + Keyboard::hide(); +#endif + + FormField::onFocusLost(); +} + + #if defined(HARDWARE_TOUCH) bool NumberEdit::onTouchEnd(coord_t, coord_t) { diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 180889c90..a44d35fbd 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -62,6 +62,7 @@ class NumberEdit : public BaseNumberEdit } void onEvent(event_t event) override; + void onFocusLost() override; #if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t x, coord_t y) override; From 8bde45bb8669727c6bd265923f777aedb2dc7aff Mon Sep 17 00:00:00 2001 From: Cliff Date: Mon, 6 Sep 2021 09:44:19 -0700 Subject: [PATCH 428/714] Add get current selection index --- radio/src/thirdparty/libopenui/src/menu.cpp | 1 + radio/src/thirdparty/libopenui/src/menu.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 258b13487..753fc9ccd 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -94,6 +94,7 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) menu->invalidate(); } else { + select(index); menu->deleteLater(); lines[index].onPress(); } diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 8b8e361b2..9fdc382e5 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -207,6 +207,11 @@ class Menu: public ModalWindow return content->body.count(); } + int selection() const + { + return content->body.selection(); + } + void select(int index) { content->body.select(index); From b611071855ac10667835423e522d29bd3fd662cf Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Fri, 10 Sep 2021 20:59:17 +1000 Subject: [PATCH 429/714] Add clang format config Same format as edgetx repo --- radio/src/thirdparty/libopenui/.clang-format | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/.clang-format diff --git a/radio/src/thirdparty/libopenui/.clang-format b/radio/src/thirdparty/libopenui/.clang-format new file mode 100644 index 000000000..913141780 --- /dev/null +++ b/radio/src/thirdparty/libopenui/.clang-format @@ -0,0 +1,6 @@ +--- +Language: Cpp +BasedOnStyle: Google +BreakBeforeBraces: Linux +BreakConstructorInitializers: AfterColon +... From 89978ff17c548a0145d4d8496d44b0751dc6de05 Mon Sep 17 00:00:00 2001 From: Kevin Koenig Date: Sun, 12 Sep 2021 22:20:24 -0400 Subject: [PATCH 430/714] Move ChoiceEx to libopenui + fix keyboard crash (#19) * moved choiceex.* to libopenui where it belongs * added choiceex to build * enhanced system * removed construtor parameter for getTicks. Turns out you can just access the timer directly in libopenui * fixed keyboard crash for widget settings * code cleanup Co-authored-by: kkoenig1@optonline.com --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + .../thirdparty/libopenui/src/bitmapbuffer.cpp | 0 .../thirdparty/libopenui/src/bitmapbuffer.h | 0 radio/src/thirdparty/libopenui/src/choice.cpp | 5 + radio/src/thirdparty/libopenui/src/choice.h | 8 ++ .../src/thirdparty/libopenui/src/choiceex.cpp | 111 ++++++++++++++++++ radio/src/thirdparty/libopenui/src/choiceex.h | 60 ++++++++++ .../libopenui/src/keyboard_base.cpp | 12 +- .../thirdparty/libopenui/src/keyboard_base.h | 3 +- radio/src/thirdparty/libopenui/src/static.cpp | 0 10 files changed, 196 insertions(+), 4 deletions(-) mode change 100755 => 100644 radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp mode change 100755 => 100644 radio/src/thirdparty/libopenui/src/bitmapbuffer.h create mode 100644 radio/src/thirdparty/libopenui/src/choiceex.cpp create mode 100644 radio/src/thirdparty/libopenui/src/choiceex.h mode change 100755 => 100644 radio/src/thirdparty/libopenui/src/static.cpp diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index c1a9671d3..661dee69e 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -26,6 +26,7 @@ set(LIBOPENUI_SRC menu.cpp menutoolbar.cpp choice.cpp + choiceex.cpp filechoice.cpp numberedit.cpp textedit.cpp diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp old mode 100755 new mode 100644 diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h old mode 100755 new mode 100644 diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 9be769153..0d935b245 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -162,6 +162,11 @@ void Choice::openMenu() menu->select(current); } + if (beforeDisplayMenuHandler) { + beforeDisplayMenuHandler(menu); + } + + menu->setCloseHandler([=]() { setEditMode(false); }); diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index a92767ddd..bea30052a 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -22,6 +22,8 @@ #include #include "form.h" +class Menu; + enum ChoiceType { CHOICE_TYPE_DROPOWN, CHOICE_TYPE_FOLDER, @@ -77,6 +79,11 @@ class Choice: public ChoiceBase { bool onTouchEnd(coord_t x, coord_t y) override; #endif + void setBeforeDisplayMenuHandler(std::function handler) + { + beforeDisplayMenuHandler = std::move(handler); + } + void setSetValueHandler(std::function handler) { setValue = std::move(handler); @@ -155,6 +162,7 @@ class Choice: public ChoiceBase { std::function setValue; std::function isValueAvailable; std::function textHandler; + std::function beforeDisplayMenuHandler; virtual void openMenu(); }; diff --git a/radio/src/thirdparty/libopenui/src/choiceex.cpp b/radio/src/thirdparty/libopenui/src/choiceex.cpp new file mode 100644 index 000000000..d8a87698d --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/choiceex.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include "choiceex.h" + +constexpr int LONG_PRESS_10MS = 40; + +extern inline tmr10ms_t getTicks() +{ + return g_tmr10ms; +} + + + +void ChoiceEx::setLongPressHandler(std::function handler) +{ + longPressHandler = handler; +} + +ChoiceEx::ChoiceEx(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, + std::function setValue, WindowFlags windowFlags) : + Choice(parent, rect, vmin, vmax, getValue, setValue, windowFlags) +{ +#if defined(HARDWARE_TOUCH) + duration10ms = 0; +#endif +} + +#if defined(HARDWARE_KEYS) +void ChoiceEx::onEvent(event_t event) +{ + if (event == EVT_KEY_LONG(KEY_ENTER)) { + if (longPressHandler) { + killEvents(event); + longPressHandler(event); + return; + } + } + + Choice::onEvent(event); +} +#endif + +#if defined(HARDWARE_TOUCH) +bool ChoiceEx::isLongPress() +{ + unsigned int curTimer = getTicks(); + return (!longPressed && duration10ms != 0 && curTimer - duration10ms > LONG_PRESS_10MS); +} + +void ChoiceEx::checkEvents(void) +{ + event_t event = getEvent(); + + if (isLongPress()) { + if (!longPressed && longPressHandler) { + longPressHandler(event); + killEvents(event); + duration10ms = 0; + longPressed = true; + return; + } + } + + if (hasFocus()) + onEvent(event); + else + pushEvent(event); +} + +bool ChoiceEx::onTouchStart(coord_t x, coord_t y) +{ + if (!longPressed && duration10ms == 0) { + duration10ms = getTicks(); + } + + return Choice::onTouchStart(x, y); +} + + +bool ChoiceEx::onTouchEnd(coord_t x, coord_t y) +{ + if (longPressed) { + longPressed = false; + return false; + } + + duration10ms = 0; + return Choice::onTouchEnd(x,y); +} +#endif + + + diff --git a/radio/src/thirdparty/libopenui/src/choiceex.h b/radio/src/thirdparty/libopenui/src/choiceex.h new file mode 100644 index 000000000..4a63d13cb --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/choiceex.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef _CHOICEEX_H_ +#define _CHOICEEX_H_ +#include "choice.h" + +class ChoiceEx : public Choice +{ + public: + ChoiceEx(FormGroup * parent, const rect_t &rect, int16_t vmin, int16_t vmax, std::function getValue, + std::function setValue = nullptr, WindowFlags windowFlags = 0); + + void setLongPressHandler(std::function handler); + +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; + bool onTouchStart(coord_t x, coord_t y) override; + void checkEvents(void) override; + bool isLongPress(); +#endif + +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "ChoceEx"; + } +#endif + +protected: + std::function longPressHandler = nullptr; + +#if defined(HARDWARE_TOUCH) + uint32_t duration10ms; + bool longPressed = false; +#endif +}; + +#endif \ No newline at end of file diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 10c80d526..414c0bd42 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -35,14 +35,17 @@ coord_t calcScrollOffsetForField(FormField *newField, Window *topWindow) return offsetY - topWindow->height() / 2; } -void Keyboard::attachKeyboard() +bool Keyboard::attachKeyboard() { + TRACE("ATTACH KEYBOARD"); if (activeKeyboard) { - if (activeKeyboard == this) return; + if (activeKeyboard == this) return false; activeKeyboard->clearField(); } + activeKeyboard = this; attach(MainWindow::instance()); + return true; } @@ -76,7 +79,10 @@ FormWindow *Keyboard::findFormWindow(Window *parent) void Keyboard::setField(FormField* newField) { - attachKeyboard(); + TRACE("SET FIELD"); + + if (!attachKeyboard()) + return; fieldContainer = getFieldContainer(newField); if (fieldContainer) { diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index b30c76a05..5374b9d5c 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -31,6 +31,7 @@ class Keyboard: public FormWindow void clearField() { + TRACE("CLEAR FIELD"); detach(); if (fields) { fields->setHeight(oldHeight); @@ -64,7 +65,7 @@ class Keyboard: public FormWindow void setField(FormField *newField); Window *getFieldContainer(FormField * field); - void attachKeyboard(); + bool attachKeyboard(); FormWindow *findFormWindow(Window *parent); }; diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp old mode 100755 new mode 100644 From 218e8bed02d0d53753e7761bb590d851e6092931 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 30 Aug 2021 15:47:22 +0200 Subject: [PATCH 431/714] Remove harmful & useless MinGW32 compatibility definitions --- .../src/thirdparty/libopenui/src/libopenui_compat.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_compat.h b/radio/src/thirdparty/libopenui/src/libopenui_compat.h index ac94b9670..7c8006bd8 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_compat.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_compat.h @@ -19,16 +19,3 @@ #pragma once -#if defined(__MINGW32__) || !defined(__GNUC__) - #include - #include - #define sleep(x) Sleep(x) - #define strcasecmp _stricmp - #define strncasecmp _strnicmp - #define chdir _chdir - #define getcwd _getcwd - // remove windows default definitions - #undef OPAQUE - #undef RGB - #undef EXTERN_C -#endif From c8f79c95a2e102bb96e4bfc9a2c1465f39349742 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Mon, 13 Sep 2021 19:18:07 +1000 Subject: [PATCH 432/714] Create README.md --- radio/src/thirdparty/libopenui/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/README.md diff --git a/radio/src/thirdparty/libopenui/README.md b/radio/src/thirdparty/libopenui/README.md new file mode 100644 index 000000000..6d17b6d92 --- /dev/null +++ b/radio/src/thirdparty/libopenui/README.md @@ -0,0 +1,4 @@ +# libopenui +C++ UI Library primarily used in EdgeTX for Colour LCD targets. + +Was forked from [OpenTX/libopenui](https://github.com/opentx/libopenui) and detached so that contributors can easily work on both if they wish to do so. From 06fb5b0e1809de5732d35259aca769e65e2dada0 Mon Sep 17 00:00:00 2001 From: "Cliff(dlktdr)" Date: Wed, 15 Sep 2021 13:10:13 -0700 Subject: [PATCH 433/714] Use a virtual event so enter on keyboard calls changeHandler() immediately --- radio/src/thirdparty/libopenui/src/keyboard_text.cpp | 1 + radio/src/thirdparty/libopenui/src/textedit.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 9f827bbbf..19a0ca44b 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -254,6 +254,7 @@ bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) } else if (*key == KEYBOARD_ENTER[0]) { if (x <= KEYBOARD_ENTER_WIDTH) { + pushEvent(EVT_VIRTUAL_KEY(KEYBOARD_ENTER[0])); // enter hide(); return true; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index b2aa9ef94..c82a56605 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -108,6 +108,9 @@ void TextEdit::onEvent(event_t event) changed = true; } } + else if (c == (uint8_t)KEYBOARD_ENTER[0]) { + changeEnd(); + } else if (cursorPos < length) { memmove(value + cursorPos + 1, value + cursorPos, length - cursorPos - 1); value[cursorPos++] = c; From ede2dba2eaa78663451a338b25c25416d74d895f Mon Sep 17 00:00:00 2001 From: kevinkoenig Date: Thu, 16 Sep 2021 08:51:38 -0500 Subject: [PATCH 434/714] Fixes the defect that the software keyboard does not call onChangeEnd if the keyboard is hidden via touch and not via an esc. It changes TextEdit and FormField to move the onChange event to the base class instead of in TextEdit. Now the keyboard calls onChange when it is hidden. --- radio/src/thirdparty/libopenui/src/form.h | 13 +++++++++++++ .../src/thirdparty/libopenui/src/keyboard_base.cpp | 1 - radio/src/thirdparty/libopenui/src/keyboard_base.h | 1 + radio/src/thirdparty/libopenui/src/textedit.h | 8 +------- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 5faceea0c..88c2ff56f 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -32,6 +32,18 @@ class FormField: public Window public: FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); + virtual void changeEnd(bool forceChanged = false) + { + if (changeHandler) { + changeHandler(); + } + } + + void setChangeHandler(std::function handler) + { + changeHandler = std::move(handler); + } + inline void setNextField(FormField *field) { next = field; @@ -104,6 +116,7 @@ class FormField: public Window FormField * previous = nullptr; bool editMode = false; bool enabled = true; + std::function changeHandler = nullptr; }; class FormGroup: public FormField diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 414c0bd42..50bb07051 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -37,7 +37,6 @@ coord_t calcScrollOffsetForField(FormField *newField, Window *topWindow) bool Keyboard::attachKeyboard() { - TRACE("ATTACH KEYBOARD"); if (activeKeyboard) { if (activeKeyboard == this) return false; activeKeyboard->clearField(); diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 5374b9d5c..cf0e42df4 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -43,6 +43,7 @@ class Keyboard: public FormWindow } if (field) { field->setEditMode(false); + field->changeEnd(); field = nullptr; } } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 32d8dc256..975f40e1a 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -42,11 +42,6 @@ class TextEdit : public FormField { } #endif - void setChangeHandler(std::function handler) - { - changeHandler = std::move(handler); - } - uint8_t getMaxLength() const { return length; @@ -73,11 +68,10 @@ class TextEdit : public FormField { uint8_t length; const char * extra_chars; uint8_t cursorPos = 0; - std::function changeHandler = nullptr; void trim(); - void changeEnd(bool forceChanged = false) + void changeEnd(bool forceChanged = false) override { cursorPos = 0; if (changed || forceChanged) { From 05a858d4c2d9c194fdabc80c6f95f4c22ea1fc24 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 19 Sep 2021 10:18:25 +0200 Subject: [PATCH 435/714] [menutoolbar] pass current value to fillMenu() --- .../src/thirdparty/libopenui/src/menutoolbar.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 484b6ba48..a26e70b98 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -147,19 +147,24 @@ class MenuToolbar: public FormGroup { if (vmin > filtermin || vmax < filtermin) return; - if (choice->isValueAvailable && getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == 0) + if (choice->isValueAvailable && + getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == + 0) return; - auto button = new MenuToolbarButton(this, {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}, picto); + auto button = new MenuToolbarButton( + this, {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}, + picto); + button->setPressHandler([=]() { + auto val = choice->getValue(); if (button->checked()) { - choice->fillMenu(menu, [=](int16_t index) { + choice->fillMenu(menu, val, [=](int16_t index) { return index >= filtermin && index <= filtermax; }); menu->setFocusBody(); - } - else { - choice->fillMenu(menu); + } else { + choice->fillMenu(menu, val); menu->setFocusBody(); } return 1; From 5ed1d63d6e950bb1fd3463229d439a8479134b82 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 22 Sep 2021 12:54:35 +0200 Subject: [PATCH 436/714] [choice] check boundaries when accessing values vector EdgeTX/edgetx#767 --- radio/src/thirdparty/libopenui/src/choice.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 0d935b245..536c19f05 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -104,9 +104,19 @@ void Choice::setValues(const char * const values[]) void Choice::paint(BitmapBuffer * dc) { FormField::paint(dc); - theme->drawChoice(dc, this, - textHandler ? textHandler(getValue()).c_str() - : values[getValue() - vmin].c_str()); + auto val = getValue(); + + const char* str = ""; + if (textHandler) { + str = textHandler(val).c_str(); + } else { + val -= vmin; + if (val > 0 && val < values.size()) { + values[val - vmin].c_str(); + } + } + + theme->drawChoice(dc, this, str); } #if defined(HARDWARE_KEYS) From 5ced0de44a4832a2f52cf1b283238ea7d1e6a918 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Wed, 22 Sep 2021 21:53:26 +1000 Subject: [PATCH 437/714] [choice] fixup bounding and showing current value --- radio/src/thirdparty/libopenui/src/choice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 536c19f05..33f8103f6 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -111,8 +111,8 @@ void Choice::paint(BitmapBuffer * dc) str = textHandler(val).c_str(); } else { val -= vmin; - if (val > 0 && val < values.size()) { - values[val - vmin].c_str(); + if (val >= 0 && val < (int)values.size()) { + str = values[val].c_str(); } } From 672dc8389b97d8ea9c7153110f40ce7185ce5013 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Thu, 23 Sep 2021 22:19:08 +1000 Subject: [PATCH 438/714] [choice] Resolve textHandler pointer scoping --- radio/src/thirdparty/libopenui/src/choice.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 33f8103f6..d7fbf825e 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -106,17 +106,17 @@ void Choice::paint(BitmapBuffer * dc) FormField::paint(dc); auto val = getValue(); - const char* str = ""; + std::string str = ""; if (textHandler) { - str = textHandler(val).c_str(); + str = textHandler(val); } else { val -= vmin; if (val >= 0 && val < (int)values.size()) { - str = values[val].c_str(); + str = values[val]; } } - theme->drawChoice(dc, this, str); + theme->drawChoice(dc, this, str.c_str()); } #if defined(HARDWARE_KEYS) From 461b8971a27f94c5d63faf4b5e184b79142a037a Mon Sep 17 00:00:00 2001 From: Kevin Koenig Date: Tue, 28 Sep 2021 05:01:15 -0400 Subject: [PATCH 439/714] Fix colors used for TextButtons, MenuToolbarButton and ModalWindow (#25) * fix colors for modal window and toolbar buttons in menu popuos * fix TextButton correctly so all TextButtons in app just work * fix TextButton correctly so the app just worksX Co-authored-by: kkoenig1@optonline.com --- radio/src/thirdparty/libopenui/src/button.cpp | 5 +++-- radio/src/thirdparty/libopenui/src/button.h | 1 + radio/src/thirdparty/libopenui/src/menutoolbar.cpp | 2 +- radio/src/thirdparty/libopenui/src/modal_window.cpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index f2b9ad0e2..0ff6156e2 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -70,7 +70,7 @@ void Button::checkEvents() void TextButton::paint(BitmapBuffer * dc) { - auto textColor = COLOR_THEME_SECONDARY1; + auto textColor = getTextFlags(); auto bgColor = COLOR_THEME_SECONDARY2; if (bgColorHandler) { @@ -79,6 +79,7 @@ void TextButton::paint(BitmapBuffer * dc) bgColor = COLOR_THEME_ACTIVE; } else if (hasFocus()) { bgColor = COLOR_THEME_FOCUS; + textColor = COLOR_THEME_PRIMARY2; } if (checked()) { @@ -104,7 +105,7 @@ void TextButton::paint(BitmapBuffer * dc) } dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, - text.c_str(), CENTERED | textFlags | textColor); + text.c_str(), CENTERED | textColor); } void IconButton::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 78d09463b..07a8db3e1 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -96,6 +96,7 @@ class TextButton: public Button Button(parent, rect, std::move(pressHandler), windowFlags, textFlags), text(std::move(text)) { + setTextFlags(textFlags | COLOR_THEME_PRIMARY1); } #if defined(DEBUG_WINDOWS) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 334098d52..1c5181c63 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -32,6 +32,6 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) &picto, 1, CENTERED | COLOR_THEME_PRIMARY2); } else { dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, - &picto, 1, CENTERED | COLOR_THEME_SECONDARY1); + &picto, 1, CENTERED | COLOR_THEME_PRIMARY1); } } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 2c1222890..8fba5c93f 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -41,7 +41,7 @@ void ModalWindow::deleteLater(bool detach, bool trash) void ModalWindow::paint(BitmapBuffer * dc) { - dc->drawFilledRect(0, 0, width(), height(), SOLID, COLOR_THEME_PRIMARY1, OPACITY(5)); + dc->drawFilledRect(0, 0, width(), height(), SOLID, COLOR_THEME_SECONDARY3, OPACITY(5)); } void ModalWindowContent::paint(BitmapBuffer * dc) From d862cd6a8cea76f56701e0fb6813e31ab4e5df57 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Tue, 28 Sep 2021 10:52:29 -0400 Subject: [PATCH 440/714] Enhance FormField paint to allow instantiator to set a background color handler. Used in FlightModes() to set background color. Enhance GridLayout to allow whitespace at top. changed modal to use hard coded BLACK instead of theme color as per JimB40 --- radio/src/thirdparty/libopenui/src/form.cpp | 29 ++++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index a168f009f..8d88327fc 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -86,21 +86,24 @@ void FormField::setFocus(uint8_t flag, Window * from) void FormField::paint(BitmapBuffer * dc) { - if (editMode) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR_THEME_EDIT); - } - else { - - if (!(windowFlags & FORM_NO_BORDER)) { - - if (hasFocus()) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR_THEME_FOCUS); - } else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR_THEME_PRIMARY2); - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); - } + uint32_t bgColor = 0; + if (backgroundHandler) { + bgColor = backgroundHandler(this); + } else { + if (editMode) { + bgColor = COLOR_THEME_EDIT; + } else if (hasFocus()) { + bgColor = COLOR_THEME_FOCUS; + } else { + bgColor = COLOR_THEME_PRIMARY2; } } + + if (editMode || ! (windowFlags & FORM_NO_BORDER)) { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); + if (!editMode) + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); + } } void FormGroup::removeField(FormField * field) From 3ebd4321eb74f4a1cdb88bf9c2e6011f14901d63 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Tue, 28 Sep 2021 10:55:11 -0400 Subject: [PATCH 441/714] More color fixes, set background of FormField --- radio/src/thirdparty/libopenui/src/form.h | 6 ++++++ radio/src/thirdparty/libopenui/src/gridlayout.h | 9 +++++---- radio/src/thirdparty/libopenui/src/modal_window.cpp | 4 ++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 88c2ff56f..f04483c46 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -44,6 +44,11 @@ class FormField: public Window changeHandler = std::move(handler); } + void setBackgroundHandler(std::function handler) + { + backgroundHandler = std::move(handler); + } + inline void setNextField(FormField *field) { next = field; @@ -117,6 +122,7 @@ class FormField: public Window bool editMode = false; bool enabled = true; std::function changeHandler = nullptr; + std::function backgroundHandler = nullptr; }; class FormGroup: public FormField diff --git a/radio/src/thirdparty/libopenui/src/gridlayout.h b/radio/src/thirdparty/libopenui/src/gridlayout.h index a606f5796..60e6024bc 100644 --- a/radio/src/thirdparty/libopenui/src/gridlayout.h +++ b/radio/src/thirdparty/libopenui/src/gridlayout.h @@ -23,8 +23,9 @@ class GridLayout { public: - explicit GridLayout(coord_t width): - width(width) + explicit GridLayout(coord_t width, coord_t top = 0): + width(width), + currentY(top) { } @@ -68,8 +69,8 @@ class GridLayout { class FormGridLayout: public GridLayout { public: - explicit FormGridLayout(coord_t width = LCD_W): - GridLayout(width) + explicit FormGridLayout(coord_t width = LCD_W, coord_t top = 0): + GridLayout(width, top) { } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 8fba5c93f..b33983164 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -41,12 +41,12 @@ void ModalWindow::deleteLater(bool detach, bool trash) void ModalWindow::paint(BitmapBuffer * dc) { - dc->drawFilledRect(0, 0, width(), height(), SOLID, COLOR_THEME_SECONDARY3, OPACITY(5)); + dc->drawFilledRect(0, 0, width(), height(), SOLID, BLACK, OPACITY(6)); } void ModalWindowContent::paint(BitmapBuffer * dc) { - dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, COLOR_THEME_FOCUS); + dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY1); dc->drawText(FIELD_PADDING_LEFT, (POPUP_HEADER_HEIGHT - getFontHeight(FONT(STD))) / 2, title.c_str(), COLOR_THEME_PRIMARY2); dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, width(), height() - POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY3); } From 324622e11ea890c8582a9ebf14307872e89af158 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 21 Oct 2021 05:11:19 -0400 Subject: [PATCH 442/714] fix touchend and slide processing to make it consistent. --- radio/src/thirdparty/libopenui/mainwindow.cpp | 141 +++++ radio/src/thirdparty/libopenui/window.cpp | 564 ++++++++++++++++++ radio/src/thirdparty/libopenui/window.h | 451 ++++++++++++++ 3 files changed, 1156 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/mainwindow.cpp create mode 100644 radio/src/thirdparty/libopenui/window.cpp create mode 100644 radio/src/thirdparty/libopenui/window.h diff --git a/radio/src/thirdparty/libopenui/mainwindow.cpp b/radio/src/thirdparty/libopenui/mainwindow.cpp new file mode 100644 index 000000000..de96d7fa9 --- /dev/null +++ b/radio/src/thirdparty/libopenui/mainwindow.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "mainwindow.h" +#include "keyboard_base.h" + +#if defined(HARDWARE_TOUCH) +#include "touch.h" +#endif + +MainWindow * MainWindow::_instance = nullptr; + +#if defined(HARDWARE_TOUCH) +TouchState touchState; +Keyboard * Keyboard::activeKeyboard = nullptr; +#endif + +void MainWindow::emptyTrash() +{ + for (auto window: trash) { + delete window; + } + trash.clear(); +} + +void MainWindow::checkEvents() +{ +#if defined(HARDWARE_TOUCH) + if (touchPanelEventOccured()) { + touchPanelRead(); + } + + if (touchState.event == TE_DOWN) { + onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); + slidingWindow = nullptr; + } + else if (touchState.event == TE_UP) { + touchState.event = TE_NONE; + onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); + } + else if (touchState.event == TE_SLIDE) { + if (touchState.deltaX || touchState.deltaY) { + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); + touchState.lastDeltaX = touchState.deltaX; + touchState.lastDeltaY = touchState.deltaY; + touchState.deltaX = 0; + touchState.deltaY = 0; + } + } + else if (touchState.event == TE_SLIDE_END && slidingWindow) { + if (touchState.lastDeltaX > SLIDE_SPEED_REDUCTION) + touchState.lastDeltaX -= SLIDE_SPEED_REDUCTION; + else if (touchState.lastDeltaX < -SLIDE_SPEED_REDUCTION) + touchState.lastDeltaX += SLIDE_SPEED_REDUCTION; + else + touchState.lastDeltaX = 0; + if (touchState.lastDeltaY > SLIDE_SPEED_REDUCTION) + touchState.lastDeltaY -= SLIDE_SPEED_REDUCTION; + else if (touchState.lastDeltaY < -SLIDE_SPEED_REDUCTION) + touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; + else + touchState.lastDeltaY = 0; + + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); + slidingWindow = nullptr; + } +#endif + + Window::checkEvents(); +} + +void MainWindow::invalidate(const rect_t & rect) +{ + if (invalidatedRect.w) { + auto left = limit(0, rect.left(), invalidatedRect.left()); + auto right = limit(invalidatedRect.right(), rect.right(), LCD_W); + auto top = limit(0, rect.top(), invalidatedRect.top()); + auto bottom = limit(rect.bottom(), invalidatedRect.bottom(), LCD_H); + invalidatedRect = {left, top, right - left, bottom - top}; + } + else { + invalidatedRect = rect; + } +} + +bool MainWindow::refresh() +{ + if (invalidatedRect.w) { + if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { + TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); + lcdCopy(lcd->getData(), lcdFront->getData()); + } + else { + TRACE_WINDOWS("Refresh full screen"); + } + lcd->setOffset(0, 0); + lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); + fullPaint(lcd); + invalidatedRect.w = 0; + return true; + } + else { + return false; + } +} + +void MainWindow::run(bool trash) +{ + auto start = ticksNow(); + + checkEvents(); + + if (trash) { + emptyTrash(); + } + + if (refresh()) { + lcdRefresh(); + } + + auto delta = ticksNow() - start; + if (delta > 10 * SYSTEM_TICKS_1MS) { + TRACE_WINDOWS("MainWindow::run took %dms", (ticksNow() - start) / SYSTEM_TICKS_1MS); + } +} diff --git a/radio/src/thirdparty/libopenui/window.cpp b/radio/src/thirdparty/libopenui/window.cpp new file mode 100644 index 000000000..46f5d4ce4 --- /dev/null +++ b/radio/src/thirdparty/libopenui/window.cpp @@ -0,0 +1,564 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "window.h" +#include "touch.h" + +Window * Window::focusWindow = nullptr; +Window * Window::slidingWindow = nullptr; +Window * Window::touchedWindow = nullptr; +std::list Window::trash; + +Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): + parent(parent), + rect(rect), + innerWidth(rect.w), + innerHeight(rect.h), + windowFlags(windowFlags), + textFlags(textFlags) +{ + if (parent) { + parent->addChild(this, windowFlags & PUSH_FRONT); + if (!(windowFlags & TRANSPARENT)) { + invalidate(); + } + } +} + +Window::~Window() +{ + TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); + + if (focusWindow == this) { + focusWindow = nullptr; + } + + deleteChildren(); +} + +void Window::attach(Window * newParent) +{ + if (parent) detach(); + parent = newParent; + if (newParent) newParent->addChild(this); +} + +void Window::detach() +{ + if (parent) { + parent->removeChild(this); + parent = nullptr; + } +} + +void Window::deleteLater(bool detach, bool trash) +{ + if (_deleted) + return; + + TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); + + _deleted = true; + + if (static_cast(focusWindow) == static_cast(this)) { + focusWindow = nullptr; + } + + if (detach) + this->detach(); + else + parent = nullptr; + + deleteChildren(); + + if (closeHandler) { + closeHandler(); + } + + if (trash) { + Window::trash.push_back(this); + } +} + +void Window::clear() +{ + scrollPositionX = 0; + scrollPositionY = 0; + innerWidth = rect.w; + innerHeight = rect.h; + deleteChildren(); + invalidate(); +} + +void Window::deleteChildren() +{ + for (auto window: children) { + window->deleteLater(false); + } + children.clear(); +} + +void Window::clearFocus() +{ + if (focusWindow) { + focusWindow->onFocusLost(); + focusWindow = nullptr; + } +} + +void Window::setFocus(uint8_t flag, Window * from) +{ + if (deleted()) + return; + + TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); + + if (focusWindow != this) { + // scroll before calling focusHandler so that the window can adjust the scroll position if needed + Window * parent = this->parent; + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + if (parent) { + parent->scrollTo(this); + invalidate(); + } + + clearFocus(); + focusWindow = this; + if (focusHandler) { + focusHandler(true); + } + } +} + +void Window::setScrollPositionX(coord_t value) +{ + auto newScrollPosition = max(0, min(innerWidth - width(), value)); + if (newScrollPosition != scrollPositionX) { + scrollPositionX = newScrollPosition; + invalidate(); + } +} + +void Window::setScrollPositionY(coord_t value) +{ + auto newScrollPosition = min(innerHeight - height(), value); + + if (newScrollPosition < 0 && innerHeight != INFINITE_HEIGHT) { + newScrollPosition = 0; + } + + if (newScrollPosition != scrollPositionY) { + scrollPositionY = newScrollPosition; + invalidate(); + } +} + +void Window::scrollTo(Window * child) +{ + coord_t offsetX = 0; + coord_t offsetY = 0; + + Window * parentWindow = child->getParent(); + while (parentWindow && parentWindow != this) { + offsetX += parentWindow->left(); + offsetY += parentWindow->top(); + parentWindow = parentWindow->getParent(); + } + + const rect_t scrollRect = { + offsetX + child->left(), + offsetY + child->top(), + min(child->width(), width()), + min(child->height(), height()) + }; + + scrollTo(scrollRect); +} + +void Window::scrollTo(const rect_t & rect) +{ + if (rect.top() < scrollPositionY) { + setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.top() - 5); + } + else if (rect.bottom() > scrollPositionY + height() - 5) { + setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.bottom() - height() + 5); + } + + if (rect.left() < scrollPositionX) { + setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.left() - 5); + } + else if (rect.right() > scrollPositionX + width() - 5) { + setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.right() - width() + 5); + } +} + +bool Window::hasOpaqueRect(const rect_t & testRect) const +{ + if (!rect.contains(testRect)) + return false; + + if (windowFlags & OPAQUE) { + return true; + } + + rect_t relativeRect = {testRect.x - rect.x, testRect.y - rect.y, testRect.w, testRect.h}; + for (auto child: children) { + if (child->hasOpaqueRect(relativeRect)) + return true; + } + + return false; +} + +void Window::fullPaint(BitmapBuffer * dc) +{ + bool paintNeeded = true; + std::list::iterator firstChild; + + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + + if (windowFlags & PAINT_CHILDREN_FIRST) { + paintChildren(dc, children.begin()); + dc->setOffset(x, y); + dc->setClippingRect(xmin, xmax, ymin, ymax); + } + else { + firstChild = children.end(); + rect_t relativeRect = {xmin - x, ymin - y, xmax - xmin, ymax - ymin}; + while (firstChild != children.begin()) { + auto child = *(--firstChild); + if (child->hasOpaqueRect(relativeRect)) { + paintNeeded = false; + break; + } + } + } + + if (paintNeeded) { + TRACE_WINDOWS_INDENT("%s%s", getWindowDebugString().c_str(), hasFocus() ? " (*)" : ""); + paint(dc); +#if defined(WINDOWS_INSPECT_BORDER_COLOR) + dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR); +#endif + } + else { + TRACE_WINDOWS_INDENT("%s (skipped)", getWindowDebugString().c_str()); + } + + if (!(windowFlags & NO_SCROLLBAR)) { + drawVerticalScrollbar(dc); + } + + if (!(windowFlags & PAINT_CHILDREN_FIRST)) { + paintChildren(dc, firstChild); + } +} + +bool Window::isChildFullSize(const Window * child) const +{ + return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width(); +} + +bool Window::isChildVisible(const Window * window) const +{ + for (auto rit = children.rbegin(); rit != children.rend(); rit++) { + auto child = *rit; + if (child == window) { + return true; + } + if ((child->getWindowFlags() & OPAQUE) & isChildFullSize(child)) { + return false; + } + } + return false; +} + +void Window::setInsideParentScrollingArea() +{ + Window * parent = getParent(); + while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { + parent = parent->parent; + } + if (parent) { + parent->scrollTo(this); + invalidate(); + } +} + +void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) +{ + coord_t x = dc->getOffsetX(); + coord_t y = dc->getOffsetY(); + coord_t xmin, xmax, ymin, ymax; + dc->getClippingRect(xmin, xmax, ymin, ymax); + + for (; it != children.end(); it++) { + auto child = *it; + + coord_t child_xmin = x + child->rect.x; + if (child_xmin >= xmax) + continue; + coord_t child_ymin = y + child->rect.y; + if (child_ymin >= ymax) + continue; + coord_t child_xmax = child_xmin + child->rect.w; + if (child_xmax <= xmin) + continue; + coord_t child_ymax = child_ymin + child->rect.h; + if (child_ymax <= ymin) + continue; + + dc->setOffset(x + child->rect.x - child->scrollPositionX, y + child->rect.y - child->scrollPositionY); + dc->setClippingRect(max(xmin, x + child->rect.left()), + min(xmax, x + child->rect.right()), + max(ymin, y + child->rect.top()), + min(ymax, y + child->rect.bottom())); + child->fullPaint(dc); + } +} + +#if defined(HARDWARE_TOUCH) +coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) +{ + coord_t result = 0; + if (relativeScrollPosition > pageSize / 2) { + // closer to next page + result = (pageSize - relativeScrollPosition); + } + else { + // closer to previous page + result = (0 - relativeScrollPosition); + } + + // do not get too slow + if (abs(result) > 32) + result /= 2; + + return result; +} +#endif + +void Window::checkEvents() +{ + auto copy = children; + for (auto child: copy) { + if (!child->deleted()) { + child->checkEvents(); + } + } + + if (this == Window::focusWindow) { + event_t event = getWindowEvent(); + if (event) { + this->onEvent(event); + } + } + + if (windowFlags & REFRESH_ALWAYS) { + invalidate(); + } + +#if defined(HARDWARE_TOUCH) + if (touchState.event != TE_SLIDE && touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) { + if (pageWidth) { + coord_t relativeScrollPosition = getScrollPositionX() % pageWidth; + if (relativeScrollPosition) { + setScrollPositionX(getScrollPositionX() + getSnapStep(relativeScrollPosition, pageWidth)); + } + } + if (pageHeight) { + coord_t relativeScrollPosition = getScrollPositionY() % pageHeight; + if (relativeScrollPosition) { + setScrollPositionY(getScrollPositionY() + getSnapStep(relativeScrollPosition, pageHeight)); + } + } + } +#endif +} + +void Window::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString("Window").c_str(), event); + if (parent) { + parent->onEvent(event); + } +} + +#if defined(HARDWARE_TOUCH) +bool Window::onTouchStart(coord_t x, coord_t y) +{ + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (child->rect.contains(x, y)) { + if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + // remember the first window that processed the touch start event. We will give that window + // the opportunity to process the touchend first, and also after a slide end so the touched window + // always gets a touchEnd if it processed the touch start. + if (touchedWindow == nullptr) { + touchedWindow = child; + } + return true; + } + } + } + + return false; +} + +bool Window::forwardTouchEnd(coord_t x, coord_t y) +{ + // give the touched window the opportunity to process the touch first before it is + // forwarded: See on touchstart + if (touchedWindow != nullptr) { + auto savedTouchWindow = touchedWindow; + touchedWindow = nullptr; + // transalte x and y through the parent hierarchy + auto myParent = savedTouchWindow->parent; + while(myParent != nullptr) { + x = x - myParent->rect.x + myParent->scrollPositionX; + y = y - myParent->rect.y + myParent->scrollPositionY; + myParent = myParent->parent; + } + + if (savedTouchWindow->onTouchEnd( + x - savedTouchWindow->rect.x + savedTouchWindow->scrollPositionX, + y - savedTouchWindow->rect.y + savedTouchWindow->scrollPositionY)) { + return true; + } + } + + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (child->rect.contains(x, y)) { + if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + return true; + } + } + } + + return false; +} + +bool Window::onTouchEnd(coord_t x, coord_t y) +{ + return forwardTouchEnd(x, y) ? true : (windowFlags & OPAQUE); +} + +bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + startX += getScrollPositionX(); + startY += getScrollPositionY(); + + for (auto it = children.rbegin(); it != children.rend(); ++it) { + auto child = *it; + if (child->rect.contains(startX, startY)) { + if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { + return true; + } + } + } + + if (slidingWindow && slidingWindow != this) { + return false; + } + + if (slideY && innerHeight > rect.h) { + setScrollPositionY(scrollPositionY - slideY); + slidingWindow = this; + return true; + } + + if (slideX && innerWidth > rect.w) { + setScrollPositionX(scrollPositionX - slideX); + slidingWindow = this; + return true; + } + + return false; +} +#endif + +void Window::adjustInnerHeight() +{ + coord_t bottomMax = 0; + for (auto child: children) { + bottomMax = max(bottomMax, child->rect.y + child->rect.h); + } + setInnerHeight(bottomMax); +} + +coord_t Window::adjustHeight() +{ + coord_t old = rect.h; + adjustInnerHeight(); + rect.h = innerHeight; + return rect.h - old; +} + +void Window::moveWindowsTop(coord_t y, coord_t delta) +{ + if (getWindowFlags() & FORWARD_SCROLL) { + parent->moveWindowsTop(bottom(), delta); + } + + for (auto child: children) { + if (child->rect.y >= y) { + child->rect.y += delta; + invalidate(); + } + } +} + +void Window::invalidate(const rect_t & rect) +{ + if (isVisible()) { + parent->invalidate({this->rect.x + rect.x - parent->scrollPositionX, this->rect.y + rect.y - parent->scrollPositionY, rect.w, rect.h}); + } +} + +void Window::drawVerticalScrollbar(BitmapBuffer * dc) +{ + if (innerHeight > rect.h) { + coord_t yofs = divRoundClosest(rect.h * scrollPositionY, innerHeight); + coord_t yhgt = divRoundClosest(rect.h * rect.h, innerHeight); + if (yhgt < 15) + yhgt = 15; + if (yhgt + yofs > rect.h) + yhgt = rect.h - yofs; + dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, COLOR_THEME_PRIMARY3); + } +} + +void Window::drawHorizontalScrollbar(BitmapBuffer * dc) +{ + if (innerWidth > rect.w) { + coord_t xofs = divRoundClosest(rect.w * scrollPositionX, innerWidth); + coord_t xwdth = divRoundClosest(rect.w * rect.w, innerWidth); + if (xwdth < 15) + xwdth = 15; + if (xwdth + xofs > rect.w) + xwdth = rect.w - xofs; + dc->drawSolidFilledRect(scrollPositionX + xofs, rect.h - SCROLLBAR_WIDTH, xwdth, SCROLLBAR_WIDTH, COLOR_THEME_PRIMARY3); + } +} diff --git a/radio/src/thirdparty/libopenui/window.h b/radio/src/thirdparty/libopenui/window.h new file mode 100644 index 000000000..2b9b045e6 --- /dev/null +++ b/radio/src/thirdparty/libopenui/window.h @@ -0,0 +1,451 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "bitmapbuffer.h" +#include "libopenui_defines.h" +#include "libopenui_helpers.h" +#include "libopenui_config.h" + +typedef uint32_t WindowFlags; + +#if !defined(_GNUC_) + #undef OPAQUE + #undef TRANSPARENT +#endif + +constexpr int INFINITE_HEIGHT = INT32_MAX; + +constexpr WindowFlags OPAQUE = 1u << 0u; +constexpr WindowFlags TRANSPARENT = 1u << 1u; +constexpr WindowFlags NO_SCROLLBAR = 1u << 2u; +constexpr WindowFlags NO_FOCUS = 1u << 3u; +constexpr WindowFlags FORWARD_SCROLL = 1u << 4u; +constexpr WindowFlags REFRESH_ALWAYS = 1u << 5u; +constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6u; +constexpr WindowFlags PUSH_FRONT = 1u << 7u; +constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; + +enum SetFocusFlag +{ + SET_FOCUS_DEFAULT, + SET_FOCUS_FORWARD, + SET_FOCUS_BACKWARD, + SET_FOCUS_FIRST +}; + +class Window +{ + friend class GridLayout; + + public: + Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); + + virtual ~Window(); + +#if defined(DEBUG_WINDOWS) + virtual std::string getName() const + { + return "Window"; + } + + std::string getRectString() const + { + char result[32]; + sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); + return result; + } + + std::string getIndentString() const + { + std::string result; + auto tmp = parent; + while (tmp) { + result += " "; + tmp = tmp->getParent(); + } + return result; + } + + std::string getWindowDebugString(const char * name = nullptr) const + { + return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); + } +#endif + + Window * getParent() const + { + return parent; + } + + bool isChild(Window * window) const + { + return window == this || (parent && parent->isChild(window)); + } + + Window *getFullScreenWindow() + { + if (width() == LCD_W && height() == LCD_H) return this; + if (parent) return parent->getFullScreenWindow(); + return nullptr; + } + + WindowFlags getWindowFlags() const + { + return windowFlags; + } + + void setWindowFlags(WindowFlags flags) + { + windowFlags = flags; + } + + LcdFlags getTextFlags() const + { + return textFlags; + } + + void setTextFlags(LcdFlags flags) + { + textFlags = flags; + } + + void setCloseHandler(std::function handler) + { + closeHandler = std::move(handler); + } + + void setFocusHandler(std::function handler) + { + focusHandler = std::move(handler); + } + + const std::list getChildren() + { + return children; + } + + virtual void deleteLater(bool detach = true, bool trash = true); + + void clear(); + + void deleteChildren(); + + bool hasFocus() const + { + return focusWindow == this; + } + + static Window * getFocus() + { + return focusWindow; + } + + void scrollTo(const rect_t & rect); + + void scrollTo(Window * child); + + static void clearFocus(); + + virtual void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr); + + void setRect(rect_t value) + { + rect = value; + invalidate(); + } + + void setWidth(coord_t value) + { + rect.w = value; + invalidate(); + } + + void setWindowCentered() + { + rect.x = (parent->width() - width()) / 2; + rect.y = (parent->height() - height()) / 2; + } + + void setHeight(coord_t value) + { + rect.h = value; + if (windowFlags & FORWARD_SCROLL) + innerHeight = value; + else if (innerHeight <= value) { + setScrollPositionY(0); + } + invalidate(); + } + + void setLeft(coord_t x) + { + rect.x = x; + invalidate(); + } + + void setTop(coord_t y) + { + rect.y = y; + invalidate(); + } + + coord_t left() const + { + return rect.x; + } + + coord_t right() const + { + return rect.x + rect.w; + } + + coord_t top() const + { + return rect.y; + } + + coord_t bottom() const + { + return rect.y + rect.h; + } + + coord_t width() const + { + return rect.w; + } + + coord_t height() const + { + return rect.h; + } + + rect_t getRect() const + { + return rect; + } + + coord_t getInnerWidth() const + { + return innerWidth; + } + + void setInnerWidth(coord_t w) + { + innerWidth = w; + if (width() >= w) { + scrollPositionX = 0; + } + } + + void setPageWidth(coord_t w) + { + pageWidth = w; + } + + void setPageHeight(coord_t h) + { + pageHeight = h; + } + + uint8_t getPageCount() const + { + if (pageWidth) + return innerWidth / pageWidth; + else if (pageHeight) + return innerHeight / pageHeight; + else + return 1; + } + + uint8_t getPageIndex() const + { + if (pageWidth) + return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; + else if (pageHeight) + return (getScrollPositionY() + (pageHeight / 2)) / pageHeight; + else + return 0; + } + + coord_t getInnerHeight() const + { + return innerHeight; + } + + void setInnerHeight(coord_t h) + { + innerHeight = h; + if (windowFlags & FORWARD_SCROLL) { + rect.h = innerHeight; + parent->adjustInnerHeight(); + } + else if (height() >= h) { + setScrollPositionY(0); + } + else { + coord_t maxScrollPosition = h - height(); + if (scrollPositionY > maxScrollPosition) { + setScrollPositionY(maxScrollPosition); + } + } + invalidate(); + } + + coord_t getScrollPositionX() const + { + return scrollPositionX; + } + + coord_t getScrollPositionY() const + { + return scrollPositionY; + } + + virtual void setScrollPositionX(coord_t value); + + virtual void setScrollPositionY(coord_t value); + + bool isChildVisible(const Window * window) const; + + bool isChildFullSize(const Window * window) const; + + bool isVisible() const + { + return parent && parent->isChildVisible(this); + } + + bool isInsideParentScrollingArea() const + { + return parent && right() >= parent->getScrollPositionX() && left() <= parent->getScrollPositionX() + parent->width(); + } + + void setInsideParentScrollingArea(); + + void drawVerticalScrollbar(BitmapBuffer * dc); + + void drawHorizontalScrollbar(BitmapBuffer * dc); + + virtual void onEvent(event_t event); + + void adjustInnerHeight(); + + coord_t adjustHeight(); + + void moveWindowsTop(coord_t y, coord_t delta); + + void invalidate() + { + invalidate({0, 0, rect.w, rect.h}); + } + + void bringToTop() + { + attach(parent); // does a detach + attach + } + + virtual void checkEvents(); + + void attach(Window * window); + + void detach(); + + bool deleted() const + { + return _deleted; + } + + protected: + Window * parent; + std::list children; + rect_t rect; + coord_t innerWidth; + coord_t innerHeight; + coord_t pageWidth = 0; + coord_t pageHeight = 0; + coord_t scrollPositionX = 0; + coord_t scrollPositionY = 0; + WindowFlags windowFlags; + LcdFlags textFlags; + bool _deleted = false; + + static Window * focusWindow; + static Window * slidingWindow; + static Window * touchedWindow; + static std::list trash; + + std::function closeHandler; + std::function focusHandler; + + void addChild(Window * window, bool front = false) + { + if (front) + children.push_front(window); + else + children.push_back(window); + } + + void removeChild(Window * window) + { + children.remove(window); + invalidate(); + } + + virtual void invalidate(const rect_t & rect); + + void paintChildren(BitmapBuffer * dc, std::list::iterator it); + + void fullPaint(BitmapBuffer * dc); + + virtual void paint(BitmapBuffer *) + { + } + + virtual void onFocusLost() + { + TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); + if (focusHandler) { + focusHandler(false); + } + invalidate(); + } + +#if defined(HARDWARE_TOUCH) + static coord_t getSnapStep(coord_t relativeScrollPosition, coord_t pageSize); + + virtual bool onTouchStart(coord_t x, coord_t y); + + virtual bool onTouchEnd(coord_t x, coord_t y); + + virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); +#endif + + bool forwardTouchEnd(coord_t x, coord_t y); + + bool hasOpaqueRect(const rect_t & testRect) const; +}; + From 8ea6f964fa7d3f255adccbb5a6a8f3e2cb267a4b Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 21 Oct 2021 05:30:49 -0400 Subject: [PATCH 443/714] put files in wrong place --- radio/src/thirdparty/libopenui/mainwindow.cpp | 141 ----- radio/src/thirdparty/libopenui/window.cpp | 564 ------------------ radio/src/thirdparty/libopenui/window.h | 451 -------------- 3 files changed, 1156 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/mainwindow.cpp delete mode 100644 radio/src/thirdparty/libopenui/window.cpp delete mode 100644 radio/src/thirdparty/libopenui/window.h diff --git a/radio/src/thirdparty/libopenui/mainwindow.cpp b/radio/src/thirdparty/libopenui/mainwindow.cpp deleted file mode 100644 index de96d7fa9..000000000 --- a/radio/src/thirdparty/libopenui/mainwindow.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "mainwindow.h" -#include "keyboard_base.h" - -#if defined(HARDWARE_TOUCH) -#include "touch.h" -#endif - -MainWindow * MainWindow::_instance = nullptr; - -#if defined(HARDWARE_TOUCH) -TouchState touchState; -Keyboard * Keyboard::activeKeyboard = nullptr; -#endif - -void MainWindow::emptyTrash() -{ - for (auto window: trash) { - delete window; - } - trash.clear(); -} - -void MainWindow::checkEvents() -{ -#if defined(HARDWARE_TOUCH) - if (touchPanelEventOccured()) { - touchPanelRead(); - } - - if (touchState.event == TE_DOWN) { - onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); - slidingWindow = nullptr; - } - else if (touchState.event == TE_UP) { - touchState.event = TE_NONE; - onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); - } - else if (touchState.event == TE_SLIDE) { - if (touchState.deltaX || touchState.deltaY) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); - touchState.lastDeltaX = touchState.deltaX; - touchState.lastDeltaY = touchState.deltaY; - touchState.deltaX = 0; - touchState.deltaY = 0; - } - } - else if (touchState.event == TE_SLIDE_END && slidingWindow) { - if (touchState.lastDeltaX > SLIDE_SPEED_REDUCTION) - touchState.lastDeltaX -= SLIDE_SPEED_REDUCTION; - else if (touchState.lastDeltaX < -SLIDE_SPEED_REDUCTION) - touchState.lastDeltaX += SLIDE_SPEED_REDUCTION; - else - touchState.lastDeltaX = 0; - if (touchState.lastDeltaY > SLIDE_SPEED_REDUCTION) - touchState.lastDeltaY -= SLIDE_SPEED_REDUCTION; - else if (touchState.lastDeltaY < -SLIDE_SPEED_REDUCTION) - touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; - else - touchState.lastDeltaY = 0; - - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); - slidingWindow = nullptr; - } -#endif - - Window::checkEvents(); -} - -void MainWindow::invalidate(const rect_t & rect) -{ - if (invalidatedRect.w) { - auto left = limit(0, rect.left(), invalidatedRect.left()); - auto right = limit(invalidatedRect.right(), rect.right(), LCD_W); - auto top = limit(0, rect.top(), invalidatedRect.top()); - auto bottom = limit(rect.bottom(), invalidatedRect.bottom(), LCD_H); - invalidatedRect = {left, top, right - left, bottom - top}; - } - else { - invalidatedRect = rect; - } -} - -bool MainWindow::refresh() -{ - if (invalidatedRect.w) { - if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { - TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); - lcdCopy(lcd->getData(), lcdFront->getData()); - } - else { - TRACE_WINDOWS("Refresh full screen"); - } - lcd->setOffset(0, 0); - lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); - fullPaint(lcd); - invalidatedRect.w = 0; - return true; - } - else { - return false; - } -} - -void MainWindow::run(bool trash) -{ - auto start = ticksNow(); - - checkEvents(); - - if (trash) { - emptyTrash(); - } - - if (refresh()) { - lcdRefresh(); - } - - auto delta = ticksNow() - start; - if (delta > 10 * SYSTEM_TICKS_1MS) { - TRACE_WINDOWS("MainWindow::run took %dms", (ticksNow() - start) / SYSTEM_TICKS_1MS); - } -} diff --git a/radio/src/thirdparty/libopenui/window.cpp b/radio/src/thirdparty/libopenui/window.cpp deleted file mode 100644 index 46f5d4ce4..000000000 --- a/radio/src/thirdparty/libopenui/window.cpp +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "window.h" -#include "touch.h" - -Window * Window::focusWindow = nullptr; -Window * Window::slidingWindow = nullptr; -Window * Window::touchedWindow = nullptr; -std::list Window::trash; - -Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): - parent(parent), - rect(rect), - innerWidth(rect.w), - innerHeight(rect.h), - windowFlags(windowFlags), - textFlags(textFlags) -{ - if (parent) { - parent->addChild(this, windowFlags & PUSH_FRONT); - if (!(windowFlags & TRANSPARENT)) { - invalidate(); - } - } -} - -Window::~Window() -{ - TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); - - if (focusWindow == this) { - focusWindow = nullptr; - } - - deleteChildren(); -} - -void Window::attach(Window * newParent) -{ - if (parent) detach(); - parent = newParent; - if (newParent) newParent->addChild(this); -} - -void Window::detach() -{ - if (parent) { - parent->removeChild(this); - parent = nullptr; - } -} - -void Window::deleteLater(bool detach, bool trash) -{ - if (_deleted) - return; - - TRACE_WINDOWS("Delete %p %s", this, getWindowDebugString().c_str()); - - _deleted = true; - - if (static_cast(focusWindow) == static_cast(this)) { - focusWindow = nullptr; - } - - if (detach) - this->detach(); - else - parent = nullptr; - - deleteChildren(); - - if (closeHandler) { - closeHandler(); - } - - if (trash) { - Window::trash.push_back(this); - } -} - -void Window::clear() -{ - scrollPositionX = 0; - scrollPositionY = 0; - innerWidth = rect.w; - innerHeight = rect.h; - deleteChildren(); - invalidate(); -} - -void Window::deleteChildren() -{ - for (auto window: children) { - window->deleteLater(false); - } - children.clear(); -} - -void Window::clearFocus() -{ - if (focusWindow) { - focusWindow->onFocusLost(); - focusWindow = nullptr; - } -} - -void Window::setFocus(uint8_t flag, Window * from) -{ - if (deleted()) - return; - - TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); - - if (focusWindow != this) { - // scroll before calling focusHandler so that the window can adjust the scroll position if needed - Window * parent = this->parent; - while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { - parent = parent->parent; - } - if (parent) { - parent->scrollTo(this); - invalidate(); - } - - clearFocus(); - focusWindow = this; - if (focusHandler) { - focusHandler(true); - } - } -} - -void Window::setScrollPositionX(coord_t value) -{ - auto newScrollPosition = max(0, min(innerWidth - width(), value)); - if (newScrollPosition != scrollPositionX) { - scrollPositionX = newScrollPosition; - invalidate(); - } -} - -void Window::setScrollPositionY(coord_t value) -{ - auto newScrollPosition = min(innerHeight - height(), value); - - if (newScrollPosition < 0 && innerHeight != INFINITE_HEIGHT) { - newScrollPosition = 0; - } - - if (newScrollPosition != scrollPositionY) { - scrollPositionY = newScrollPosition; - invalidate(); - } -} - -void Window::scrollTo(Window * child) -{ - coord_t offsetX = 0; - coord_t offsetY = 0; - - Window * parentWindow = child->getParent(); - while (parentWindow && parentWindow != this) { - offsetX += parentWindow->left(); - offsetY += parentWindow->top(); - parentWindow = parentWindow->getParent(); - } - - const rect_t scrollRect = { - offsetX + child->left(), - offsetY + child->top(), - min(child->width(), width()), - min(child->height(), height()) - }; - - scrollTo(scrollRect); -} - -void Window::scrollTo(const rect_t & rect) -{ - if (rect.top() < scrollPositionY) { - setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.top() - 5); - } - else if (rect.bottom() > scrollPositionY + height() - 5) { - setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.bottom() - height() + 5); - } - - if (rect.left() < scrollPositionX) { - setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.left() - 5); - } - else if (rect.right() > scrollPositionX + width() - 5) { - setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.right() - width() + 5); - } -} - -bool Window::hasOpaqueRect(const rect_t & testRect) const -{ - if (!rect.contains(testRect)) - return false; - - if (windowFlags & OPAQUE) { - return true; - } - - rect_t relativeRect = {testRect.x - rect.x, testRect.y - rect.y, testRect.w, testRect.h}; - for (auto child: children) { - if (child->hasOpaqueRect(relativeRect)) - return true; - } - - return false; -} - -void Window::fullPaint(BitmapBuffer * dc) -{ - bool paintNeeded = true; - std::list::iterator firstChild; - - coord_t xmin, xmax, ymin, ymax; - dc->getClippingRect(xmin, xmax, ymin, ymax); - coord_t x = dc->getOffsetX(); - coord_t y = dc->getOffsetY(); - - if (windowFlags & PAINT_CHILDREN_FIRST) { - paintChildren(dc, children.begin()); - dc->setOffset(x, y); - dc->setClippingRect(xmin, xmax, ymin, ymax); - } - else { - firstChild = children.end(); - rect_t relativeRect = {xmin - x, ymin - y, xmax - xmin, ymax - ymin}; - while (firstChild != children.begin()) { - auto child = *(--firstChild); - if (child->hasOpaqueRect(relativeRect)) { - paintNeeded = false; - break; - } - } - } - - if (paintNeeded) { - TRACE_WINDOWS_INDENT("%s%s", getWindowDebugString().c_str(), hasFocus() ? " (*)" : ""); - paint(dc); -#if defined(WINDOWS_INSPECT_BORDER_COLOR) - dc->drawSolidRect(0, 0, width(), height(), 1, WINDOWS_INSPECT_BORDER_COLOR); -#endif - } - else { - TRACE_WINDOWS_INDENT("%s (skipped)", getWindowDebugString().c_str()); - } - - if (!(windowFlags & NO_SCROLLBAR)) { - drawVerticalScrollbar(dc); - } - - if (!(windowFlags & PAINT_CHILDREN_FIRST)) { - paintChildren(dc, firstChild); - } -} - -bool Window::isChildFullSize(const Window * child) const -{ - return child->top() == 0 && child->height() == height() && child->left() == 0 && child->width() == width(); -} - -bool Window::isChildVisible(const Window * window) const -{ - for (auto rit = children.rbegin(); rit != children.rend(); rit++) { - auto child = *rit; - if (child == window) { - return true; - } - if ((child->getWindowFlags() & OPAQUE) & isChildFullSize(child)) { - return false; - } - } - return false; -} - -void Window::setInsideParentScrollingArea() -{ - Window * parent = getParent(); - while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { - parent = parent->parent; - } - if (parent) { - parent->scrollTo(this); - invalidate(); - } -} - -void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) -{ - coord_t x = dc->getOffsetX(); - coord_t y = dc->getOffsetY(); - coord_t xmin, xmax, ymin, ymax; - dc->getClippingRect(xmin, xmax, ymin, ymax); - - for (; it != children.end(); it++) { - auto child = *it; - - coord_t child_xmin = x + child->rect.x; - if (child_xmin >= xmax) - continue; - coord_t child_ymin = y + child->rect.y; - if (child_ymin >= ymax) - continue; - coord_t child_xmax = child_xmin + child->rect.w; - if (child_xmax <= xmin) - continue; - coord_t child_ymax = child_ymin + child->rect.h; - if (child_ymax <= ymin) - continue; - - dc->setOffset(x + child->rect.x - child->scrollPositionX, y + child->rect.y - child->scrollPositionY); - dc->setClippingRect(max(xmin, x + child->rect.left()), - min(xmax, x + child->rect.right()), - max(ymin, y + child->rect.top()), - min(ymax, y + child->rect.bottom())); - child->fullPaint(dc); - } -} - -#if defined(HARDWARE_TOUCH) -coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) -{ - coord_t result = 0; - if (relativeScrollPosition > pageSize / 2) { - // closer to next page - result = (pageSize - relativeScrollPosition); - } - else { - // closer to previous page - result = (0 - relativeScrollPosition); - } - - // do not get too slow - if (abs(result) > 32) - result /= 2; - - return result; -} -#endif - -void Window::checkEvents() -{ - auto copy = children; - for (auto child: copy) { - if (!child->deleted()) { - child->checkEvents(); - } - } - - if (this == Window::focusWindow) { - event_t event = getWindowEvent(); - if (event) { - this->onEvent(event); - } - } - - if (windowFlags & REFRESH_ALWAYS) { - invalidate(); - } - -#if defined(HARDWARE_TOUCH) - if (touchState.event != TE_SLIDE && touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) { - if (pageWidth) { - coord_t relativeScrollPosition = getScrollPositionX() % pageWidth; - if (relativeScrollPosition) { - setScrollPositionX(getScrollPositionX() + getSnapStep(relativeScrollPosition, pageWidth)); - } - } - if (pageHeight) { - coord_t relativeScrollPosition = getScrollPositionY() % pageHeight; - if (relativeScrollPosition) { - setScrollPositionY(getScrollPositionY() + getSnapStep(relativeScrollPosition, pageHeight)); - } - } - } -#endif -} - -void Window::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString("Window").c_str(), event); - if (parent) { - parent->onEvent(event); - } -} - -#if defined(HARDWARE_TOUCH) -bool Window::onTouchStart(coord_t x, coord_t y) -{ - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(x, y)) { - if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { - // remember the first window that processed the touch start event. We will give that window - // the opportunity to process the touchend first, and also after a slide end so the touched window - // always gets a touchEnd if it processed the touch start. - if (touchedWindow == nullptr) { - touchedWindow = child; - } - return true; - } - } - } - - return false; -} - -bool Window::forwardTouchEnd(coord_t x, coord_t y) -{ - // give the touched window the opportunity to process the touch first before it is - // forwarded: See on touchstart - if (touchedWindow != nullptr) { - auto savedTouchWindow = touchedWindow; - touchedWindow = nullptr; - // transalte x and y through the parent hierarchy - auto myParent = savedTouchWindow->parent; - while(myParent != nullptr) { - x = x - myParent->rect.x + myParent->scrollPositionX; - y = y - myParent->rect.y + myParent->scrollPositionY; - myParent = myParent->parent; - } - - if (savedTouchWindow->onTouchEnd( - x - savedTouchWindow->rect.x + savedTouchWindow->scrollPositionX, - y - savedTouchWindow->rect.y + savedTouchWindow->scrollPositionY)) { - return true; - } - } - - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(x, y)) { - if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { - return true; - } - } - } - - return false; -} - -bool Window::onTouchEnd(coord_t x, coord_t y) -{ - return forwardTouchEnd(x, y) ? true : (windowFlags & OPAQUE); -} - -bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) -{ - startX += getScrollPositionX(); - startY += getScrollPositionY(); - - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(startX, startY)) { - if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { - return true; - } - } - } - - if (slidingWindow && slidingWindow != this) { - return false; - } - - if (slideY && innerHeight > rect.h) { - setScrollPositionY(scrollPositionY - slideY); - slidingWindow = this; - return true; - } - - if (slideX && innerWidth > rect.w) { - setScrollPositionX(scrollPositionX - slideX); - slidingWindow = this; - return true; - } - - return false; -} -#endif - -void Window::adjustInnerHeight() -{ - coord_t bottomMax = 0; - for (auto child: children) { - bottomMax = max(bottomMax, child->rect.y + child->rect.h); - } - setInnerHeight(bottomMax); -} - -coord_t Window::adjustHeight() -{ - coord_t old = rect.h; - adjustInnerHeight(); - rect.h = innerHeight; - return rect.h - old; -} - -void Window::moveWindowsTop(coord_t y, coord_t delta) -{ - if (getWindowFlags() & FORWARD_SCROLL) { - parent->moveWindowsTop(bottom(), delta); - } - - for (auto child: children) { - if (child->rect.y >= y) { - child->rect.y += delta; - invalidate(); - } - } -} - -void Window::invalidate(const rect_t & rect) -{ - if (isVisible()) { - parent->invalidate({this->rect.x + rect.x - parent->scrollPositionX, this->rect.y + rect.y - parent->scrollPositionY, rect.w, rect.h}); - } -} - -void Window::drawVerticalScrollbar(BitmapBuffer * dc) -{ - if (innerHeight > rect.h) { - coord_t yofs = divRoundClosest(rect.h * scrollPositionY, innerHeight); - coord_t yhgt = divRoundClosest(rect.h * rect.h, innerHeight); - if (yhgt < 15) - yhgt = 15; - if (yhgt + yofs > rect.h) - yhgt = rect.h - yofs; - dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, COLOR_THEME_PRIMARY3); - } -} - -void Window::drawHorizontalScrollbar(BitmapBuffer * dc) -{ - if (innerWidth > rect.w) { - coord_t xofs = divRoundClosest(rect.w * scrollPositionX, innerWidth); - coord_t xwdth = divRoundClosest(rect.w * rect.w, innerWidth); - if (xwdth < 15) - xwdth = 15; - if (xwdth + xofs > rect.w) - xwdth = rect.w - xofs; - dc->drawSolidFilledRect(scrollPositionX + xofs, rect.h - SCROLLBAR_WIDTH, xwdth, SCROLLBAR_WIDTH, COLOR_THEME_PRIMARY3); - } -} diff --git a/radio/src/thirdparty/libopenui/window.h b/radio/src/thirdparty/libopenui/window.h deleted file mode 100644 index 2b9b045e6..000000000 --- a/radio/src/thirdparty/libopenui/window.h +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include "bitmapbuffer.h" -#include "libopenui_defines.h" -#include "libopenui_helpers.h" -#include "libopenui_config.h" - -typedef uint32_t WindowFlags; - -#if !defined(_GNUC_) - #undef OPAQUE - #undef TRANSPARENT -#endif - -constexpr int INFINITE_HEIGHT = INT32_MAX; - -constexpr WindowFlags OPAQUE = 1u << 0u; -constexpr WindowFlags TRANSPARENT = 1u << 1u; -constexpr WindowFlags NO_SCROLLBAR = 1u << 2u; -constexpr WindowFlags NO_FOCUS = 1u << 3u; -constexpr WindowFlags FORWARD_SCROLL = 1u << 4u; -constexpr WindowFlags REFRESH_ALWAYS = 1u << 5u; -constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6u; -constexpr WindowFlags PUSH_FRONT = 1u << 7u; -constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; - -enum SetFocusFlag -{ - SET_FOCUS_DEFAULT, - SET_FOCUS_FORWARD, - SET_FOCUS_BACKWARD, - SET_FOCUS_FIRST -}; - -class Window -{ - friend class GridLayout; - - public: - Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); - - virtual ~Window(); - -#if defined(DEBUG_WINDOWS) - virtual std::string getName() const - { - return "Window"; - } - - std::string getRectString() const - { - char result[32]; - sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); - return result; - } - - std::string getIndentString() const - { - std::string result; - auto tmp = parent; - while (tmp) { - result += " "; - tmp = tmp->getParent(); - } - return result; - } - - std::string getWindowDebugString(const char * name = nullptr) const - { - return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); - } -#endif - - Window * getParent() const - { - return parent; - } - - bool isChild(Window * window) const - { - return window == this || (parent && parent->isChild(window)); - } - - Window *getFullScreenWindow() - { - if (width() == LCD_W && height() == LCD_H) return this; - if (parent) return parent->getFullScreenWindow(); - return nullptr; - } - - WindowFlags getWindowFlags() const - { - return windowFlags; - } - - void setWindowFlags(WindowFlags flags) - { - windowFlags = flags; - } - - LcdFlags getTextFlags() const - { - return textFlags; - } - - void setTextFlags(LcdFlags flags) - { - textFlags = flags; - } - - void setCloseHandler(std::function handler) - { - closeHandler = std::move(handler); - } - - void setFocusHandler(std::function handler) - { - focusHandler = std::move(handler); - } - - const std::list getChildren() - { - return children; - } - - virtual void deleteLater(bool detach = true, bool trash = true); - - void clear(); - - void deleteChildren(); - - bool hasFocus() const - { - return focusWindow == this; - } - - static Window * getFocus() - { - return focusWindow; - } - - void scrollTo(const rect_t & rect); - - void scrollTo(Window * child); - - static void clearFocus(); - - virtual void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr); - - void setRect(rect_t value) - { - rect = value; - invalidate(); - } - - void setWidth(coord_t value) - { - rect.w = value; - invalidate(); - } - - void setWindowCentered() - { - rect.x = (parent->width() - width()) / 2; - rect.y = (parent->height() - height()) / 2; - } - - void setHeight(coord_t value) - { - rect.h = value; - if (windowFlags & FORWARD_SCROLL) - innerHeight = value; - else if (innerHeight <= value) { - setScrollPositionY(0); - } - invalidate(); - } - - void setLeft(coord_t x) - { - rect.x = x; - invalidate(); - } - - void setTop(coord_t y) - { - rect.y = y; - invalidate(); - } - - coord_t left() const - { - return rect.x; - } - - coord_t right() const - { - return rect.x + rect.w; - } - - coord_t top() const - { - return rect.y; - } - - coord_t bottom() const - { - return rect.y + rect.h; - } - - coord_t width() const - { - return rect.w; - } - - coord_t height() const - { - return rect.h; - } - - rect_t getRect() const - { - return rect; - } - - coord_t getInnerWidth() const - { - return innerWidth; - } - - void setInnerWidth(coord_t w) - { - innerWidth = w; - if (width() >= w) { - scrollPositionX = 0; - } - } - - void setPageWidth(coord_t w) - { - pageWidth = w; - } - - void setPageHeight(coord_t h) - { - pageHeight = h; - } - - uint8_t getPageCount() const - { - if (pageWidth) - return innerWidth / pageWidth; - else if (pageHeight) - return innerHeight / pageHeight; - else - return 1; - } - - uint8_t getPageIndex() const - { - if (pageWidth) - return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; - else if (pageHeight) - return (getScrollPositionY() + (pageHeight / 2)) / pageHeight; - else - return 0; - } - - coord_t getInnerHeight() const - { - return innerHeight; - } - - void setInnerHeight(coord_t h) - { - innerHeight = h; - if (windowFlags & FORWARD_SCROLL) { - rect.h = innerHeight; - parent->adjustInnerHeight(); - } - else if (height() >= h) { - setScrollPositionY(0); - } - else { - coord_t maxScrollPosition = h - height(); - if (scrollPositionY > maxScrollPosition) { - setScrollPositionY(maxScrollPosition); - } - } - invalidate(); - } - - coord_t getScrollPositionX() const - { - return scrollPositionX; - } - - coord_t getScrollPositionY() const - { - return scrollPositionY; - } - - virtual void setScrollPositionX(coord_t value); - - virtual void setScrollPositionY(coord_t value); - - bool isChildVisible(const Window * window) const; - - bool isChildFullSize(const Window * window) const; - - bool isVisible() const - { - return parent && parent->isChildVisible(this); - } - - bool isInsideParentScrollingArea() const - { - return parent && right() >= parent->getScrollPositionX() && left() <= parent->getScrollPositionX() + parent->width(); - } - - void setInsideParentScrollingArea(); - - void drawVerticalScrollbar(BitmapBuffer * dc); - - void drawHorizontalScrollbar(BitmapBuffer * dc); - - virtual void onEvent(event_t event); - - void adjustInnerHeight(); - - coord_t adjustHeight(); - - void moveWindowsTop(coord_t y, coord_t delta); - - void invalidate() - { - invalidate({0, 0, rect.w, rect.h}); - } - - void bringToTop() - { - attach(parent); // does a detach + attach - } - - virtual void checkEvents(); - - void attach(Window * window); - - void detach(); - - bool deleted() const - { - return _deleted; - } - - protected: - Window * parent; - std::list children; - rect_t rect; - coord_t innerWidth; - coord_t innerHeight; - coord_t pageWidth = 0; - coord_t pageHeight = 0; - coord_t scrollPositionX = 0; - coord_t scrollPositionY = 0; - WindowFlags windowFlags; - LcdFlags textFlags; - bool _deleted = false; - - static Window * focusWindow; - static Window * slidingWindow; - static Window * touchedWindow; - static std::list trash; - - std::function closeHandler; - std::function focusHandler; - - void addChild(Window * window, bool front = false) - { - if (front) - children.push_front(window); - else - children.push_back(window); - } - - void removeChild(Window * window) - { - children.remove(window); - invalidate(); - } - - virtual void invalidate(const rect_t & rect); - - void paintChildren(BitmapBuffer * dc, std::list::iterator it); - - void fullPaint(BitmapBuffer * dc); - - virtual void paint(BitmapBuffer *) - { - } - - virtual void onFocusLost() - { - TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); - if (focusHandler) { - focusHandler(false); - } - invalidate(); - } - -#if defined(HARDWARE_TOUCH) - static coord_t getSnapStep(coord_t relativeScrollPosition, coord_t pageSize); - - virtual bool onTouchStart(coord_t x, coord_t y); - - virtual bool onTouchEnd(coord_t x, coord_t y); - - virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); -#endif - - bool forwardTouchEnd(coord_t x, coord_t y); - - bool hasOpaqueRect(const rect_t & testRect) const; -}; - From 582f953e500a9e37fe0990a75e06d1e7cc63e11b Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 21 Oct 2021 05:32:03 -0400 Subject: [PATCH 444/714] put files in right place --- .../thirdparty/libopenui/src/mainwindow.cpp | 6 ++--- radio/src/thirdparty/libopenui/src/window.cpp | 27 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/window.h | 1 + 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 7cbe46111..de96d7fa9 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -76,9 +76,9 @@ void MainWindow::checkEvents() touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; else touchState.lastDeltaY = 0; - if (touchState.lastDeltaX || touchState.lastDeltaY) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); - } + + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); + slidingWindow = nullptr; } #endif diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7f0222f4a..46f5d4ce4 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -22,6 +22,7 @@ Window * Window::focusWindow = nullptr; Window * Window::slidingWindow = nullptr; +Window * Window::touchedWindow = nullptr; std::list Window::trash; Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): @@ -411,6 +412,12 @@ bool Window::onTouchStart(coord_t x, coord_t y) auto child = *it; if (child->rect.contains(x, y)) { if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { + // remember the first window that processed the touch start event. We will give that window + // the opportunity to process the touchend first, and also after a slide end so the touched window + // always gets a touchEnd if it processed the touch start. + if (touchedWindow == nullptr) { + touchedWindow = child; + } return true; } } @@ -421,6 +428,26 @@ bool Window::onTouchStart(coord_t x, coord_t y) bool Window::forwardTouchEnd(coord_t x, coord_t y) { + // give the touched window the opportunity to process the touch first before it is + // forwarded: See on touchstart + if (touchedWindow != nullptr) { + auto savedTouchWindow = touchedWindow; + touchedWindow = nullptr; + // transalte x and y through the parent hierarchy + auto myParent = savedTouchWindow->parent; + while(myParent != nullptr) { + x = x - myParent->rect.x + myParent->scrollPositionX; + y = y - myParent->rect.y + myParent->scrollPositionY; + myParent = myParent->parent; + } + + if (savedTouchWindow->onTouchEnd( + x - savedTouchWindow->rect.x + savedTouchWindow->scrollPositionX, + y - savedTouchWindow->rect.y + savedTouchWindow->scrollPositionY)) { + return true; + } + } + for (auto it = children.rbegin(); it != children.rend(); ++it) { auto child = *it; if (child->rect.contains(x, y)) { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 6902eb4e4..2b9b045e6 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -395,6 +395,7 @@ class Window static Window * focusWindow; static Window * slidingWindow; + static Window * touchedWindow; static std::list trash; std::function closeHandler; From bd3781c95e15e2051310705d98b8d12050f90ea1 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 23 Sep 2021 07:13:27 -0400 Subject: [PATCH 445/714] added menu separators --- radio/src/thirdparty/libopenui/src/menu.cpp | 123 +++++- radio/src/thirdparty/libopenui/src/menu.h | 454 +++++++++++--------- 2 files changed, 362 insertions(+), 215 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 753fc9ccd..8283e6bc3 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,15 +21,59 @@ #include "font.h" #include "theme.h" -void MenuBody::select(int index) + +// ensure index is in range and also handle wrapping index +int MenuBody::rangeCheck(int index) +{ + if (index < 0) + index = lines.size() - 1; + else if (index > lines.size() - 1) + index = 0; + + return index; +} + +void MenuBody::setIndex(int index) { selectedIndex = index; + // calculate the correct scroll postion based on there being separators + int scrollY = 0; + for (int i = 0; i < selectedIndex; i++) + scrollY += lines[i].lineHeight(); + if (innerHeight > height()) { - setScrollPositionY(MENUS_LINE_HEIGHT * index - 3 * MENUS_LINE_HEIGHT); + setScrollPositionY(scrollY - 3 * MENUS_LINE_HEIGHT); } + invalidate(); } +void MenuBody::select(int index) +{ + // adjust the selection based on separators + for (int i = 0; i <= index; i++) { + if (lines[i].isSeparator) + index++; + index = rangeCheck(index); + } + + setIndex(index); +} + +void MenuBody::selectNext(MENU_DIRECTION direction) +{ + // look for the next non separator line + int index = selectedIndex + direction; + index = rangeCheck(index); + while (lines[index].isSeparator) { + index += direction; + index = rangeCheck(index); + } + + setIndex(index); +} + + #if defined(HARDWARE_KEYS) void MenuBody::onEvent(event_t event) { @@ -37,13 +81,13 @@ void MenuBody::onEvent(event_t event) if (event == EVT_ROTARY_RIGHT) { if (!lines.empty()) { - select(int((selectedIndex + 1) % lines.size())); + selectNext(DIRECTION_UP); onKeyPress(); } } else if (event == EVT_ROTARY_LEFT) { if (!lines.empty()) { - select(int(selectedIndex <= 0 ? lines.size() - 1 : selectedIndex - 1)); + selectNext(DIRECTION_DOWN); onKeyPress(); } } @@ -83,22 +127,39 @@ void MenuBody::onEvent(event_t event) bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { Menu * menu = getParentMenu(); - int index = y / MENUS_LINE_HEIGHT; + + int index = 0; + + // dynamically find the selected element based on variable line heights + coord_t yAccumulator = 0; + for (int i = 0; i < lines.size(); i++) { + if (y >= yAccumulator && y <= yAccumulator + lines[i].lineHeight()) { + index = i; + break; + } + yAccumulator += lines[i].lineHeight(); + } + + // dont allow selecting separators + if (lines[index].isSeparator) + return false; + if (index < (int)lines.size()) { onKeyPress(); if (menu->multiple) { if (selectedIndex == index) lines[index].onPress(); else - select(index); + setIndex(index); menu->invalidate(); } else { - select(index); + setIndex(index); menu->deleteLater(); lines[index].onPress(); } } + return true; } #endif @@ -107,37 +168,43 @@ void MenuBody::paint(BitmapBuffer * dc) { dc->clear(COLOR_THEME_PRIMARY2); - for (unsigned i = 0; i < lines.size(); i++) { + int y = 0; + for (int i = 0; i < lines.size(); i++) { auto& line = lines[i]; LcdFlags flags = COLOR_THEME_SECONDARY1 | MENU_FONT; - if (selectedIndex == (int)i) { + + // draw selection if appropriate + if (selectedIndex == i && ! line.isSeparator) { flags = COLOR_THEME_PRIMARY2 | MENU_FONT; if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { - dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), + dc->drawSolidFilledRect(0, y, width(), MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); } } - if (line.drawLine) { - line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT, flags); + + if (line.isSeparator) { + dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY2); + } else if (line.drawLine) { + line.drawLine(dc, 0, y, flags); } else { const char* text = line.text.data(); dc->drawText(10, - i * MENUS_LINE_HEIGHT + - (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, + y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, text[0] == '\0' ? "---" : text, flags); } Menu* menu = getParentMenu(); if (menu->multiple && line.isChecked) { theme->drawCheckBox(dc, line.isChecked(), width() - 35, - i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, + y + (MENUS_LINE_HEIGHT - 20) / 2, 0); } - if (i > 0) { - dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT, MENUS_WIDTH, - COLOR_THEME_SECONDARY2); - } + // if (i > 0 && ! lines[i].isSeparator && ! lines[i-1].isSeparator) { + // dc->drawSolidHorizontalLine(0, y, MENUS_WIDTH, COLOR_THEME_SECONDARY2); + // } + + y += line.lineHeight(); } } @@ -173,18 +240,22 @@ Menu::Menu(Window * parent, bool multiple): void Menu::updatePosition() { + // calcualte the correct menu height given that the line heights are variable + coord_t height = 0; + for (int i = 0; i < content->body.lines.size(); i++) + height += content->body.lines[i].lineHeight(); + if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; - auto bodyHeight = limit( - MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT, - MENUS_MAX_HEIGHT); + auto bodyHeight = limit(MENUS_MIN_HEIGHT, height, MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); content->setHeight(headerHeight + bodyHeight); content->body.setTop(headerHeight); content->body.setHeight(bodyHeight); } - content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT); + + content->body.setInnerHeight(height); } void Menu::setTitle(std::string text) @@ -199,6 +270,12 @@ void Menu::addLine(const std::string & text, std::function onPress, std: updatePosition(); } +void Menu::addSeparator() +{ + content->body.addSeparator(); + updatePosition(); +} + void Menu::addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) { content->body.addCustomLine(std::move(drawLine), std::move(onPress), std::move(isChecked)); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 9fdc382e5..8283e6bc3 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -17,230 +17,300 @@ * Lesser General Public License for more details. */ -#pragma once +#include "menu.h" +#include "font.h" +#include "theme.h" -#include -#include -#include -#include "modal_window.h" -class Menu; -class MenuWindowContent; - -class MenuBody: public Window +// ensure index is in range and also handle wrapping index +int MenuBody::rangeCheck(int index) { - friend class MenuWindowContent; - friend class Menu; + if (index < 0) + index = lines.size() - 1; + else if (index > lines.size() - 1) + index = 0; - class MenuLine { - friend class MenuBody; + return index; +} - public: - MenuLine(std::string text, std::function onPress, std::function isChecked): - text(std::move(text)), - onPress(std::move(onPress)), - isChecked(std::move(isChecked)) - { - } +void MenuBody::setIndex(int index) +{ + selectedIndex = index; + // calculate the correct scroll postion based on there being separators + int scrollY = 0; + for (int i = 0; i < selectedIndex; i++) + scrollY += lines[i].lineHeight(); - MenuLine(std::function drawLine, std::function onPress, std::function isChecked): - drawLine(std::move(drawLine)), - onPress(std::move(onPress)), - isChecked(std::move(isChecked)) - { - } + if (innerHeight > height()) { + setScrollPositionY(scrollY - 3 * MENUS_LINE_HEIGHT); + } - MenuLine(MenuLine &) = delete; + invalidate(); +} - MenuLine(MenuLine &&) = default; +void MenuBody::select(int index) +{ + // adjust the selection based on separators + for (int i = 0; i <= index; i++) { + if (lines[i].isSeparator) + index++; + index = rangeCheck(index); + } - protected: - std::string text; - std::function drawLine; - std::function onPress; - std::function isChecked; - }; + setIndex(index); +} - public: - MenuBody(Window * parent, const rect_t & rect): - Window(parent, rect, OPAQUE) - { - setPageHeight(MENUS_LINE_HEIGHT); - } +void MenuBody::selectNext(MENU_DIRECTION direction) +{ + // look for the next non separator line + int index = selectedIndex + direction; + index = rangeCheck(index); + while (lines[index].isSeparator) { + index += direction; + index = rangeCheck(index); + } -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "MenuBody"; - } -#endif + setIndex(index); +} - void select(int index); - - int selection() const - { - return selectedIndex; - } - - int count() const - { - return lines.size(); - } #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; +void MenuBody::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + + if (event == EVT_ROTARY_RIGHT) { + if (!lines.empty()) { + selectNext(DIRECTION_UP); + onKeyPress(); + } + } + else if (event == EVT_ROTARY_LEFT) { + if (!lines.empty()) { + selectNext(DIRECTION_DOWN); + onKeyPress(); + } + } + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + if (!lines.empty()) { + onKeyPress(); + if (selectedIndex < 0) { + select(0); + } + else { + Menu * menu = getParentMenu(); + if (menu->multiple) { + lines[selectedIndex].onPress(); + menu->invalidate(); + } + else { + menu->deleteLater(); + lines[selectedIndex].onPress(); + } + } + } + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + onKeyPress(); + if (onCancel) { + onCancel(); + } + Window::onEvent(event); + } + else { + Window::onEvent(event); + } +} #endif #if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif - - void addLine(const std::string & text, std::function onPress, std::function isChecked) - { - lines.emplace_back(text, std::move(onPress), std::move(isChecked)); - invalidate(); - } - - void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) - { - lines.emplace_back(std::move(drawLine), std::move(onPress), std::move(isChecked)); - invalidate(); - } - - void removeLines() - { - lines.clear(); - invalidate(); - } - - void setCancelHandler(std::function handler) - { - onCancel = std::move(handler); - } - - void paint(BitmapBuffer * dc) override; - - protected: - std::vector lines; - int selectedIndex = 0; - std::function onCancel; - - inline Menu * getParentMenu(); -}; - -class MenuWindowContent: public ModalWindowContent +bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { - friend class Menu; + Menu * menu = getParentMenu(); - public: - explicit MenuWindowContent(Menu * parent); + int index = 0; - void deleteLater(bool detach = true, bool trash = true) override - { - if (_deleted) - return; - - body.deleteLater(true, false); - ModalWindowContent::deleteLater(detach, trash); + // dynamically find the selected element based on variable line heights + coord_t yAccumulator = 0; + for (int i = 0; i < lines.size(); i++) { + if (y >= yAccumulator && y <= yAccumulator + lines[i].lineHeight()) { + index = i; + break; } + yAccumulator += lines[i].lineHeight(); + } -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "MenuWindowContent"; + // dont allow selecting separators + if (lines[index].isSeparator) + return false; + + if (index < (int)lines.size()) { + onKeyPress(); + if (menu->multiple) { + if (selectedIndex == index) + lines[index].onPress(); + else + setIndex(index); + menu->invalidate(); } + else { + setIndex(index); + menu->deleteLater(); + lines[index].onPress(); + } + } + + return true; +} #endif - void paint(BitmapBuffer * dc) override; - - protected: - MenuBody body; -}; - -class Menu: public ModalWindow +void MenuBody::paint(BitmapBuffer * dc) { - friend class MenuBody; + dc->clear(COLOR_THEME_PRIMARY2); - public: - explicit Menu(Window * parent, bool multiple = false); + int y = 0; + for (int i = 0; i < lines.size(); i++) { + auto& line = lines[i]; + LcdFlags flags = COLOR_THEME_SECONDARY1 | MENU_FONT; -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "Menu"; - } -#endif - - void setCancelHandler(std::function handler) - { - content->body.setCancelHandler(std::move(handler)); - } - - void setWaitHandler(std::function handler) - { - waitHandler = std::move(handler); - } - - void setFocusBody(uint8_t flag = SET_FOCUS_DEFAULT) - { - content->body.setFocus(flag); - } - - void setToolbar(Window * window) - { - toolbar = window; - content->setLeft(toolbar->right()); - content->setTop(toolbar->top()); - content->setHeight(toolbar->height()); - } - - void setTitle(std::string text); - - void addLine(const std::string & text, std::function onPress, std::function isChecked = nullptr); - - void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked = nullptr); - - void removeLines(); - - unsigned count() const - { - return content->body.count(); - } - - int selection() const - { - return content->body.selection(); - } - - void select(int index) - { - content->body.select(index); - } - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - - void checkEvents() override - { - ModalWindow::checkEvents(); - if (waitHandler) { - waitHandler(); + // draw selection if appropriate + if (selectedIndex == i && ! line.isSeparator) { + flags = COLOR_THEME_PRIMARY2 | MENU_FONT; + if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { + dc->drawSolidFilledRect(0, y, width(), + MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); } } - void paint(BitmapBuffer * dc) override; + if (line.isSeparator) { + dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY2); + } else if (line.drawLine) { + line.drawLine(dc, 0, y, flags); + } else { + const char* text = line.text.data(); + dc->drawText(10, + y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, + text[0] == '\0' ? "---" : text, flags); + } - protected: - MenuWindowContent * content; - bool multiple; - Window * toolbar = nullptr; - std::function waitHandler; - void updatePosition(); -}; + Menu* menu = getParentMenu(); + if (menu->multiple && line.isChecked) { + theme->drawCheckBox(dc, line.isChecked(), width() - 35, + y + (MENUS_LINE_HEIGHT - 20) / 2, + 0); + } -Menu * MenuBody::getParentMenu() -{ - return static_cast(getParent()->getParent()); + // if (i > 0 && ! lines[i].isSeparator && ! lines[i-1].isSeparator) { + // dc->drawSolidHorizontalLine(0, y, MENUS_WIDTH, COLOR_THEME_SECONDARY2); + // } + + y += line.lineHeight(); + } } +MenuWindowContent::MenuWindowContent(Menu* parent) : + ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, + (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), + body(this, {0, 0, width(), height()}) +{ + body.setFocus(SET_FOCUS_DEFAULT); +} + +void MenuWindowContent::paint(BitmapBuffer * dc) +{ + // the background + dc->clear(COLOR_THEME_PRIMARY2); + + // the title + if (!title.empty()) { + dc->drawText(MENUS_WIDTH / 2, + (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, + title.c_str(), CENTERED | MENU_HEADER_FONT); + dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, + COLOR_THEME_SECONDARY2); + } +} + +Menu::Menu(Window * parent, bool multiple): + ModalWindow(parent, true), + content(createMenuWindow(this)), + multiple(multiple) +{ +} + +void Menu::updatePosition() +{ + // calcualte the correct menu height given that the line heights are variable + coord_t height = 0; + for (int i = 0; i < content->body.lines.size(); i++) + height += content->body.lines[i].lineHeight(); + + if (!toolbar) { + // there is no navigation bar at the left, we may center the window on screen + auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; + auto bodyHeight = limit(MENUS_MIN_HEIGHT, height, MENUS_MAX_HEIGHT); + content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); + content->setHeight(headerHeight + bodyHeight); + content->body.setTop(headerHeight); + content->body.setHeight(bodyHeight); + } + + content->body.setInnerHeight(height); +} + +void Menu::setTitle(std::string text) +{ + content->setTitle(std::move(text)); + updatePosition(); +} + +void Menu::addLine(const std::string & text, std::function onPress, std::function isChecked) +{ + content->body.addLine(text, std::move(onPress), std::move(isChecked)); + updatePosition(); +} + +void Menu::addSeparator() +{ + content->body.addSeparator(); + updatePosition(); +} + +void Menu::addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) +{ + content->body.addCustomLine(std::move(drawLine), std::move(onPress), std::move(isChecked)); + updatePosition(); +} + +void Menu::removeLines() +{ + content->body.removeLines(); + updatePosition(); +} + +#if defined(HARDWARE_KEYS) +void Menu::onEvent(event_t event) +{ + if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN))) { + toolbar->onEvent(event); + } + else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + deleteLater(); + } + else if (event == EVT_KEY_BREAK(KEY_ENTER) && !multiple) { + deleteLater(); + } +} +#endif + +void Menu::paint(BitmapBuffer * dc) +{ + ModalWindow::paint(dc); + + rect_t r(content->getRect()); + if (toolbar) { + r.x = toolbar->left(); + r.w += toolbar->width(); + } + dc->drawSolidRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, 1, COLOR_THEME_SECONDARY2); +} From cda649db1f811e0bea81a8b62e5ea18df9784f4e Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 23 Sep 2021 07:57:36 -0400 Subject: [PATCH 446/714] Revert "added menu separators" This reverts commit 35a4ebc7fe5b86eec23ddf8a9291d539f44e14b4. --- radio/src/thirdparty/libopenui/src/menu.cpp | 127 ++---- radio/src/thirdparty/libopenui/src/menu.h | 464 +++++++++----------- 2 files changed, 222 insertions(+), 369 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 8283e6bc3..753fc9ccd 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,59 +21,15 @@ #include "font.h" #include "theme.h" - -// ensure index is in range and also handle wrapping index -int MenuBody::rangeCheck(int index) -{ - if (index < 0) - index = lines.size() - 1; - else if (index > lines.size() - 1) - index = 0; - - return index; -} - -void MenuBody::setIndex(int index) -{ - selectedIndex = index; - // calculate the correct scroll postion based on there being separators - int scrollY = 0; - for (int i = 0; i < selectedIndex; i++) - scrollY += lines[i].lineHeight(); - - if (innerHeight > height()) { - setScrollPositionY(scrollY - 3 * MENUS_LINE_HEIGHT); - } - - invalidate(); -} - void MenuBody::select(int index) { - // adjust the selection based on separators - for (int i = 0; i <= index; i++) { - if (lines[i].isSeparator) - index++; - index = rangeCheck(index); + selectedIndex = index; + if (innerHeight > height()) { + setScrollPositionY(MENUS_LINE_HEIGHT * index - 3 * MENUS_LINE_HEIGHT); } - - setIndex(index); + invalidate(); } -void MenuBody::selectNext(MENU_DIRECTION direction) -{ - // look for the next non separator line - int index = selectedIndex + direction; - index = rangeCheck(index); - while (lines[index].isSeparator) { - index += direction; - index = rangeCheck(index); - } - - setIndex(index); -} - - #if defined(HARDWARE_KEYS) void MenuBody::onEvent(event_t event) { @@ -81,13 +37,13 @@ void MenuBody::onEvent(event_t event) if (event == EVT_ROTARY_RIGHT) { if (!lines.empty()) { - selectNext(DIRECTION_UP); + select(int((selectedIndex + 1) % lines.size())); onKeyPress(); } } else if (event == EVT_ROTARY_LEFT) { if (!lines.empty()) { - selectNext(DIRECTION_DOWN); + select(int(selectedIndex <= 0 ? lines.size() - 1 : selectedIndex - 1)); onKeyPress(); } } @@ -127,39 +83,22 @@ void MenuBody::onEvent(event_t event) bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { Menu * menu = getParentMenu(); - - int index = 0; - - // dynamically find the selected element based on variable line heights - coord_t yAccumulator = 0; - for (int i = 0; i < lines.size(); i++) { - if (y >= yAccumulator && y <= yAccumulator + lines[i].lineHeight()) { - index = i; - break; - } - yAccumulator += lines[i].lineHeight(); - } - - // dont allow selecting separators - if (lines[index].isSeparator) - return false; - + int index = y / MENUS_LINE_HEIGHT; if (index < (int)lines.size()) { onKeyPress(); if (menu->multiple) { if (selectedIndex == index) lines[index].onPress(); else - setIndex(index); + select(index); menu->invalidate(); } else { - setIndex(index); + select(index); menu->deleteLater(); lines[index].onPress(); } } - return true; } #endif @@ -168,43 +107,37 @@ void MenuBody::paint(BitmapBuffer * dc) { dc->clear(COLOR_THEME_PRIMARY2); - int y = 0; - for (int i = 0; i < lines.size(); i++) { + for (unsigned i = 0; i < lines.size(); i++) { auto& line = lines[i]; LcdFlags flags = COLOR_THEME_SECONDARY1 | MENU_FONT; - - // draw selection if appropriate - if (selectedIndex == i && ! line.isSeparator) { + if (selectedIndex == (int)i) { flags = COLOR_THEME_PRIMARY2 | MENU_FONT; if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { - dc->drawSolidFilledRect(0, y, width(), + dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); } } - - if (line.isSeparator) { - dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY2); - } else if (line.drawLine) { - line.drawLine(dc, 0, y, flags); + if (line.drawLine) { + line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT, flags); } else { const char* text = line.text.data(); dc->drawText(10, - y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, + i * MENUS_LINE_HEIGHT + + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, text[0] == '\0' ? "---" : text, flags); } Menu* menu = getParentMenu(); if (menu->multiple && line.isChecked) { theme->drawCheckBox(dc, line.isChecked(), width() - 35, - y + (MENUS_LINE_HEIGHT - 20) / 2, + i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, 0); } - // if (i > 0 && ! lines[i].isSeparator && ! lines[i-1].isSeparator) { - // dc->drawSolidHorizontalLine(0, y, MENUS_WIDTH, COLOR_THEME_SECONDARY2); - // } - - y += line.lineHeight(); + if (i > 0) { + dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT, MENUS_WIDTH, + COLOR_THEME_SECONDARY2); + } } } @@ -240,22 +173,18 @@ Menu::Menu(Window * parent, bool multiple): void Menu::updatePosition() { - // calcualte the correct menu height given that the line heights are variable - coord_t height = 0; - for (int i = 0; i < content->body.lines.size(); i++) - height += content->body.lines[i].lineHeight(); - if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; - auto bodyHeight = limit(MENUS_MIN_HEIGHT, height, MENUS_MAX_HEIGHT); + auto bodyHeight = limit( + MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT, + MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); content->setHeight(headerHeight + bodyHeight); content->body.setTop(headerHeight); content->body.setHeight(bodyHeight); } - - content->body.setInnerHeight(height); + content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT); } void Menu::setTitle(std::string text) @@ -270,12 +199,6 @@ void Menu::addLine(const std::string & text, std::function onPress, std: updatePosition(); } -void Menu::addSeparator() -{ - content->body.addSeparator(); - updatePosition(); -} - void Menu::addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) { content->body.addCustomLine(std::move(drawLine), std::move(onPress), std::move(isChecked)); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 8283e6bc3..9fdc382e5 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -17,300 +17,230 @@ * Lesser General Public License for more details. */ -#include "menu.h" -#include "font.h" -#include "theme.h" +#pragma once +#include +#include +#include +#include "modal_window.h" -// ensure index is in range and also handle wrapping index -int MenuBody::rangeCheck(int index) +class Menu; +class MenuWindowContent; + +class MenuBody: public Window { - if (index < 0) - index = lines.size() - 1; - else if (index > lines.size() - 1) - index = 0; + friend class MenuWindowContent; + friend class Menu; - return index; -} + class MenuLine { + friend class MenuBody; -void MenuBody::setIndex(int index) -{ - selectedIndex = index; - // calculate the correct scroll postion based on there being separators - int scrollY = 0; - for (int i = 0; i < selectedIndex; i++) - scrollY += lines[i].lineHeight(); + public: + MenuLine(std::string text, std::function onPress, std::function isChecked): + text(std::move(text)), + onPress(std::move(onPress)), + isChecked(std::move(isChecked)) + { + } - if (innerHeight > height()) { - setScrollPositionY(scrollY - 3 * MENUS_LINE_HEIGHT); - } + MenuLine(std::function drawLine, std::function onPress, std::function isChecked): + drawLine(std::move(drawLine)), + onPress(std::move(onPress)), + isChecked(std::move(isChecked)) + { + } - invalidate(); -} + MenuLine(MenuLine &) = delete; -void MenuBody::select(int index) -{ - // adjust the selection based on separators - for (int i = 0; i <= index; i++) { - if (lines[i].isSeparator) - index++; - index = rangeCheck(index); - } + MenuLine(MenuLine &&) = default; - setIndex(index); -} + protected: + std::string text; + std::function drawLine; + std::function onPress; + std::function isChecked; + }; -void MenuBody::selectNext(MENU_DIRECTION direction) -{ - // look for the next non separator line - int index = selectedIndex + direction; - index = rangeCheck(index); - while (lines[index].isSeparator) { - index += direction; - index = rangeCheck(index); - } + public: + MenuBody(Window * parent, const rect_t & rect): + Window(parent, rect, OPAQUE) + { + setPageHeight(MENUS_LINE_HEIGHT); + } - setIndex(index); -} +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "MenuBody"; + } +#endif + void select(int index); + + int selection() const + { + return selectedIndex; + } + + int count() const + { + return lines.size(); + } #if defined(HARDWARE_KEYS) -void MenuBody::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_ROTARY_RIGHT) { - if (!lines.empty()) { - selectNext(DIRECTION_UP); - onKeyPress(); - } - } - else if (event == EVT_ROTARY_LEFT) { - if (!lines.empty()) { - selectNext(DIRECTION_DOWN); - onKeyPress(); - } - } - else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - if (!lines.empty()) { - onKeyPress(); - if (selectedIndex < 0) { - select(0); - } - else { - Menu * menu = getParentMenu(); - if (menu->multiple) { - lines[selectedIndex].onPress(); - menu->invalidate(); - } - else { - menu->deleteLater(); - lines[selectedIndex].onPress(); - } - } - } - } - else if (event == EVT_KEY_BREAK(KEY_EXIT)) { - onKeyPress(); - if (onCancel) { - onCancel(); - } - Window::onEvent(event); - } - else { - Window::onEvent(event); - } -} + void onEvent(event_t event) override; #endif #if defined(HARDWARE_TOUCH) -bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) -{ - Menu * menu = getParentMenu(); - - int index = 0; - - // dynamically find the selected element based on variable line heights - coord_t yAccumulator = 0; - for (int i = 0; i < lines.size(); i++) { - if (y >= yAccumulator && y <= yAccumulator + lines[i].lineHeight()) { - index = i; - break; - } - yAccumulator += lines[i].lineHeight(); - } - - // dont allow selecting separators - if (lines[index].isSeparator) - return false; - - if (index < (int)lines.size()) { - onKeyPress(); - if (menu->multiple) { - if (selectedIndex == index) - lines[index].onPress(); - else - setIndex(index); - menu->invalidate(); - } - else { - setIndex(index); - menu->deleteLater(); - lines[index].onPress(); - } - } - - return true; -} + bool onTouchEnd(coord_t x, coord_t y) override; #endif -void MenuBody::paint(BitmapBuffer * dc) + void addLine(const std::string & text, std::function onPress, std::function isChecked) + { + lines.emplace_back(text, std::move(onPress), std::move(isChecked)); + invalidate(); + } + + void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) + { + lines.emplace_back(std::move(drawLine), std::move(onPress), std::move(isChecked)); + invalidate(); + } + + void removeLines() + { + lines.clear(); + invalidate(); + } + + void setCancelHandler(std::function handler) + { + onCancel = std::move(handler); + } + + void paint(BitmapBuffer * dc) override; + + protected: + std::vector lines; + int selectedIndex = 0; + std::function onCancel; + + inline Menu * getParentMenu(); +}; + +class MenuWindowContent: public ModalWindowContent { - dc->clear(COLOR_THEME_PRIMARY2); + friend class Menu; - int y = 0; - for (int i = 0; i < lines.size(); i++) { - auto& line = lines[i]; - LcdFlags flags = COLOR_THEME_SECONDARY1 | MENU_FONT; + public: + explicit MenuWindowContent(Menu * parent); - // draw selection if appropriate - if (selectedIndex == i && ! line.isSeparator) { - flags = COLOR_THEME_PRIMARY2 | MENU_FONT; - if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { - dc->drawSolidFilledRect(0, y, width(), - MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); + void deleteLater(bool detach = true, bool trash = true) override + { + if (_deleted) + return; + + body.deleteLater(true, false); + ModalWindowContent::deleteLater(detach, trash); + } + +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "MenuWindowContent"; + } +#endif + + void paint(BitmapBuffer * dc) override; + + protected: + MenuBody body; +}; + +class Menu: public ModalWindow +{ + friend class MenuBody; + + public: + explicit Menu(Window * parent, bool multiple = false); + +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "Menu"; + } +#endif + + void setCancelHandler(std::function handler) + { + content->body.setCancelHandler(std::move(handler)); + } + + void setWaitHandler(std::function handler) + { + waitHandler = std::move(handler); + } + + void setFocusBody(uint8_t flag = SET_FOCUS_DEFAULT) + { + content->body.setFocus(flag); + } + + void setToolbar(Window * window) + { + toolbar = window; + content->setLeft(toolbar->right()); + content->setTop(toolbar->top()); + content->setHeight(toolbar->height()); + } + + void setTitle(std::string text); + + void addLine(const std::string & text, std::function onPress, std::function isChecked = nullptr); + + void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked = nullptr); + + void removeLines(); + + unsigned count() const + { + return content->body.count(); + } + + int selection() const + { + return content->body.selection(); + } + + void select(int index) + { + content->body.select(index); + } + +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + + void checkEvents() override + { + ModalWindow::checkEvents(); + if (waitHandler) { + waitHandler(); } } - if (line.isSeparator) { - dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY2); - } else if (line.drawLine) { - line.drawLine(dc, 0, y, flags); - } else { - const char* text = line.text.data(); - dc->drawText(10, - y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, - text[0] == '\0' ? "---" : text, flags); - } + void paint(BitmapBuffer * dc) override; - Menu* menu = getParentMenu(); - if (menu->multiple && line.isChecked) { - theme->drawCheckBox(dc, line.isChecked(), width() - 35, - y + (MENUS_LINE_HEIGHT - 20) / 2, - 0); - } + protected: + MenuWindowContent * content; + bool multiple; + Window * toolbar = nullptr; + std::function waitHandler; + void updatePosition(); +}; - // if (i > 0 && ! lines[i].isSeparator && ! lines[i-1].isSeparator) { - // dc->drawSolidHorizontalLine(0, y, MENUS_WIDTH, COLOR_THEME_SECONDARY2); - // } - - y += line.lineHeight(); - } -} - -MenuWindowContent::MenuWindowContent(Menu* parent) : - ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, - (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), - body(this, {0, 0, width(), height()}) +Menu * MenuBody::getParentMenu() { - body.setFocus(SET_FOCUS_DEFAULT); + return static_cast(getParent()->getParent()); } -void MenuWindowContent::paint(BitmapBuffer * dc) -{ - // the background - dc->clear(COLOR_THEME_PRIMARY2); - - // the title - if (!title.empty()) { - dc->drawText(MENUS_WIDTH / 2, - (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, - title.c_str(), CENTERED | MENU_HEADER_FONT); - dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, - COLOR_THEME_SECONDARY2); - } -} - -Menu::Menu(Window * parent, bool multiple): - ModalWindow(parent, true), - content(createMenuWindow(this)), - multiple(multiple) -{ -} - -void Menu::updatePosition() -{ - // calcualte the correct menu height given that the line heights are variable - coord_t height = 0; - for (int i = 0; i < content->body.lines.size(); i++) - height += content->body.lines[i].lineHeight(); - - if (!toolbar) { - // there is no navigation bar at the left, we may center the window on screen - auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; - auto bodyHeight = limit(MENUS_MIN_HEIGHT, height, MENUS_MAX_HEIGHT); - content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); - content->setHeight(headerHeight + bodyHeight); - content->body.setTop(headerHeight); - content->body.setHeight(bodyHeight); - } - - content->body.setInnerHeight(height); -} - -void Menu::setTitle(std::string text) -{ - content->setTitle(std::move(text)); - updatePosition(); -} - -void Menu::addLine(const std::string & text, std::function onPress, std::function isChecked) -{ - content->body.addLine(text, std::move(onPress), std::move(isChecked)); - updatePosition(); -} - -void Menu::addSeparator() -{ - content->body.addSeparator(); - updatePosition(); -} - -void Menu::addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) -{ - content->body.addCustomLine(std::move(drawLine), std::move(onPress), std::move(isChecked)); - updatePosition(); -} - -void Menu::removeLines() -{ - content->body.removeLines(); - updatePosition(); -} - -#if defined(HARDWARE_KEYS) -void Menu::onEvent(event_t event) -{ - if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN))) { - toolbar->onEvent(event); - } - else if (event == EVT_KEY_BREAK(KEY_EXIT)) { - deleteLater(); - } - else if (event == EVT_KEY_BREAK(KEY_ENTER) && !multiple) { - deleteLater(); - } -} -#endif - -void Menu::paint(BitmapBuffer * dc) -{ - ModalWindow::paint(dc); - - rect_t r(content->getRect()); - if (toolbar) { - r.x = toolbar->left(); - r.w += toolbar->width(); - } - dc->drawSolidRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, 1, COLOR_THEME_SECONDARY2); -} From 45e4f05b165ca0efe4d3d456aede7012072046b8 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 23 Sep 2021 08:03:00 -0400 Subject: [PATCH 447/714] readded separators correctly this time --- radio/src/thirdparty/libopenui/src/menu.cpp | 123 ++++++++++++++++---- radio/src/thirdparty/libopenui/src/menu.h | 34 +++++- 2 files changed, 133 insertions(+), 24 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 753fc9ccd..8283e6bc3 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,15 +21,59 @@ #include "font.h" #include "theme.h" -void MenuBody::select(int index) + +// ensure index is in range and also handle wrapping index +int MenuBody::rangeCheck(int index) +{ + if (index < 0) + index = lines.size() - 1; + else if (index > lines.size() - 1) + index = 0; + + return index; +} + +void MenuBody::setIndex(int index) { selectedIndex = index; + // calculate the correct scroll postion based on there being separators + int scrollY = 0; + for (int i = 0; i < selectedIndex; i++) + scrollY += lines[i].lineHeight(); + if (innerHeight > height()) { - setScrollPositionY(MENUS_LINE_HEIGHT * index - 3 * MENUS_LINE_HEIGHT); + setScrollPositionY(scrollY - 3 * MENUS_LINE_HEIGHT); } + invalidate(); } +void MenuBody::select(int index) +{ + // adjust the selection based on separators + for (int i = 0; i <= index; i++) { + if (lines[i].isSeparator) + index++; + index = rangeCheck(index); + } + + setIndex(index); +} + +void MenuBody::selectNext(MENU_DIRECTION direction) +{ + // look for the next non separator line + int index = selectedIndex + direction; + index = rangeCheck(index); + while (lines[index].isSeparator) { + index += direction; + index = rangeCheck(index); + } + + setIndex(index); +} + + #if defined(HARDWARE_KEYS) void MenuBody::onEvent(event_t event) { @@ -37,13 +81,13 @@ void MenuBody::onEvent(event_t event) if (event == EVT_ROTARY_RIGHT) { if (!lines.empty()) { - select(int((selectedIndex + 1) % lines.size())); + selectNext(DIRECTION_UP); onKeyPress(); } } else if (event == EVT_ROTARY_LEFT) { if (!lines.empty()) { - select(int(selectedIndex <= 0 ? lines.size() - 1 : selectedIndex - 1)); + selectNext(DIRECTION_DOWN); onKeyPress(); } } @@ -83,22 +127,39 @@ void MenuBody::onEvent(event_t event) bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { Menu * menu = getParentMenu(); - int index = y / MENUS_LINE_HEIGHT; + + int index = 0; + + // dynamically find the selected element based on variable line heights + coord_t yAccumulator = 0; + for (int i = 0; i < lines.size(); i++) { + if (y >= yAccumulator && y <= yAccumulator + lines[i].lineHeight()) { + index = i; + break; + } + yAccumulator += lines[i].lineHeight(); + } + + // dont allow selecting separators + if (lines[index].isSeparator) + return false; + if (index < (int)lines.size()) { onKeyPress(); if (menu->multiple) { if (selectedIndex == index) lines[index].onPress(); else - select(index); + setIndex(index); menu->invalidate(); } else { - select(index); + setIndex(index); menu->deleteLater(); lines[index].onPress(); } } + return true; } #endif @@ -107,37 +168,43 @@ void MenuBody::paint(BitmapBuffer * dc) { dc->clear(COLOR_THEME_PRIMARY2); - for (unsigned i = 0; i < lines.size(); i++) { + int y = 0; + for (int i = 0; i < lines.size(); i++) { auto& line = lines[i]; LcdFlags flags = COLOR_THEME_SECONDARY1 | MENU_FONT; - if (selectedIndex == (int)i) { + + // draw selection if appropriate + if (selectedIndex == i && ! line.isSeparator) { flags = COLOR_THEME_PRIMARY2 | MENU_FONT; if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { - dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), + dc->drawSolidFilledRect(0, y, width(), MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); } } - if (line.drawLine) { - line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT, flags); + + if (line.isSeparator) { + dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY2); + } else if (line.drawLine) { + line.drawLine(dc, 0, y, flags); } else { const char* text = line.text.data(); dc->drawText(10, - i * MENUS_LINE_HEIGHT + - (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, + y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, text[0] == '\0' ? "---" : text, flags); } Menu* menu = getParentMenu(); if (menu->multiple && line.isChecked) { theme->drawCheckBox(dc, line.isChecked(), width() - 35, - i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, + y + (MENUS_LINE_HEIGHT - 20) / 2, 0); } - if (i > 0) { - dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT, MENUS_WIDTH, - COLOR_THEME_SECONDARY2); - } + // if (i > 0 && ! lines[i].isSeparator && ! lines[i-1].isSeparator) { + // dc->drawSolidHorizontalLine(0, y, MENUS_WIDTH, COLOR_THEME_SECONDARY2); + // } + + y += line.lineHeight(); } } @@ -173,18 +240,22 @@ Menu::Menu(Window * parent, bool multiple): void Menu::updatePosition() { + // calcualte the correct menu height given that the line heights are variable + coord_t height = 0; + for (int i = 0; i < content->body.lines.size(); i++) + height += content->body.lines[i].lineHeight(); + if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; - auto bodyHeight = limit( - MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT, - MENUS_MAX_HEIGHT); + auto bodyHeight = limit(MENUS_MIN_HEIGHT, height, MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); content->setHeight(headerHeight + bodyHeight); content->body.setTop(headerHeight); content->body.setHeight(bodyHeight); } - content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT); + + content->body.setInnerHeight(height); } void Menu::setTitle(std::string text) @@ -199,6 +270,12 @@ void Menu::addLine(const std::string & text, std::function onPress, std: updatePosition(); } +void Menu::addSeparator() +{ + content->body.addSeparator(); + updatePosition(); +} + void Menu::addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) { content->body.addCustomLine(std::move(drawLine), std::move(onPress), std::move(isChecked)); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 9fdc382e5..cf16aff9e 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -32,6 +32,12 @@ class MenuBody: public Window friend class MenuWindowContent; friend class Menu; + enum MENU_DIRECTION + { + DIRECTION_UP = 1, + DIRECTION_DOWN = -1 + }; + class MenuLine { friend class MenuBody; @@ -50,11 +56,21 @@ class MenuBody: public Window { } + MenuLine(bool isSeparator) : + isSeparator(true), + height(MENUS_SEPARATOR_HEIGHT) + { + } + + inline coord_t lineHeight() { return height; } + MenuLine(MenuLine &) = delete; MenuLine(MenuLine &&) = default; protected: + bool isSeparator = false; + coord_t height = MENUS_LINE_HEIGHT; std::string text; std::function drawLine; std::function onPress; @@ -79,7 +95,12 @@ class MenuBody: public Window int selection() const { - return selectedIndex; + int index = selectedIndex; + for (int i = 0; i < selectedIndex; i++) + if (lines[i].isSeparator) + index--; + + return index; } int count() const @@ -107,6 +128,11 @@ class MenuBody: public Window invalidate(); } + void addSeparator() + { + lines.emplace_back(true); + } + void removeLines() { lines.clear(); @@ -121,6 +147,10 @@ class MenuBody: public Window void paint(BitmapBuffer * dc) override; protected: + void selectNext(MENU_DIRECTION direction); + int rangeCheck(int); + void setIndex(int index); + std::vector lines; int selectedIndex = 0; std::function onCancel; @@ -200,6 +230,8 @@ class Menu: public ModalWindow void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked = nullptr); + void addSeparator(); + void removeLines(); unsigned count() const From 28f224e64adedf0e612cdbb7bd722884db0f7063 Mon Sep 17 00:00:00 2001 From: runsys Date: Sat, 23 Oct 2021 03:07:03 +0200 Subject: [PATCH 448/714] Fix drawBitmapAbs transparency (i.e. PNGs) (#27) * fix tansparency: when a bitmap change dimension, the function drawBitmapAbs() is called. this function respect the original format now ! * A BitmapBuffer can be draw inside another BitmaBuffer (ex: function paint( BitmapBuffer * dc )) If the format is different, now the drawing is correct. Co-authored-by: Runsys16 --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 76 +++++++++++++++---- .../libopenui/src/libopenui_defines.h | 3 + 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index f238223cb..f0e1f3a98 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -135,6 +135,7 @@ void BitmapBuffer::drawScaledBitmap(const T *bitmap, coord_t x, coord_t y, int xshift = (w - (bitmap->width() * scale)) / 2; int yshift = (h - (bitmap->height() * scale)) / 2; + //TRACE(" BitmapBuffer::drawScaledBitmap()---- scale %f", scale); drawBitmap(x + xshift, y + yshift, bitmap, 0, 0, 0, 0, scale); } } @@ -150,6 +151,9 @@ void BitmapBuffer::drawBitmapAbs(coord_t x, coord_t y, const T *bmp, { coord_t bmpw = bmp->width(); coord_t bmph = bmp->height(); + //TRACE(" BitmapBuffer::drawBitmapAbs %dx%d %s->%s", bmpw, bmph, + // bmp->getFormat() == BMP_RGB565 ? "BMP_RGB565" : "BMP_ARGB4444", + // getFormat() == BMP_RGB565 ? "BMP_RGB565" : "BMP_ARGB4444"); if (srcw == 0) srcw = bmpw; if (srch == 0) srch = bmph; @@ -205,28 +209,54 @@ void BitmapBuffer::drawBitmapAbs(coord_t x, coord_t y, const T *bmp, srcx, srcy, srcw, srch); } } else { + int scaledw = srcw * scale; int scaledh = srch * scale; if (x + scaledw > _width) scaledw = _width - x; if (y + scaledh > _height) scaledh = _height - y; - for (int i = 0; i < scaledh; i++) { - pixel_t *p = getPixelPtrAbs(x, y + i); - const pixel_t *qstart = bmp->getPixelPtrAbs(srcx, srcy + int(i / scale)); - for (int j = 0; j < scaledw; j++) { - const pixel_t *q = qstart; - MOVE_PIXEL_RIGHT(q, int(j / scale)); - if (bmp->getFormat() == BMP_ARGB4444) { - ARGB_SPLIT(*q, a, r, g, b); - drawAlphaPixel(p, a, RGB_JOIN(r << 1, g << 2, b << 1)); - } else { - drawPixel(p, *q); + if ( format == BMP_ARGB4444) { + + for (int i = 0; i < scaledh; i++) { + pixel_t *p = getPixelPtrAbs(x, y + i); + const pixel_t *qstart = bmp->getPixelPtrAbs(srcx, srcy + int(i / scale)); + + for (int j = 0; j < scaledw; j++) { + const pixel_t *q = qstart; + MOVE_PIXEL_RIGHT(q, int(j / scale)); + + if (bmp->getFormat() == BMP_RGB565) { + RGB_SPLIT(*q, r, g, b); + drawPixel(p, ARGB_JOIN(0xF, r>>1, g>>2, b>>1)); + + } else { // bmp->getFormat() == BMP_ARGB4444 + drawPixel(p, *q); + } + MOVE_TO_NEXT_RIGHT_PIXEL(p); } - MOVE_TO_NEXT_RIGHT_PIXEL(p); } - } - } + } else { // format == BM_RGB565 + + for (int i = 0; i < scaledh; i++) { + pixel_t *p = getPixelPtrAbs(x, y + i); + const pixel_t *qstart = bmp->getPixelPtrAbs(srcx, srcy + int(i / scale)); + + for (int j = 0; j < scaledw; j++) { + const pixel_t *q = qstart; + MOVE_PIXEL_RIGHT(q, int(j / scale)); + + if (bmp->getFormat() == BMP_RGB565) { + drawPixel(p, *q); + } else { // bmp->getFormat() == BMP_ARGB4444 + ARGB_SPLIT(*q, a, r, g, b); + drawAlphaPixel(p, a, RGB_JOIN(r << 1, g << 2, b << 1)); + } + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } // for j + } //for i + } // if format + } // else (scale != 0) { } template @@ -237,6 +267,7 @@ void BitmapBuffer::drawBitmapAbs(coord_t, coord_t, const BitmapBuffer *, void BitmapBuffer::drawAlphaPixel(pixel_t *p, uint8_t opacity, uint16_t color) { + //TRACE("BitmapBuffer::drawAlphaPixel()"); if (opacity == OPACITY_MAX) { drawPixel(p, color); } @@ -1152,6 +1183,7 @@ void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename) { + //TRACE(" BitmapBuffer::loadBitmap(%s)", filename); const char * ext = getFileExtension(filename); if (ext && !strcmp(ext, ".bmp")) return load_bmp(filename); @@ -1355,7 +1387,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) f_close(&imgFile); return nullptr; } - + //TRACE(" BitmapBuffer::load_bmp() %dx%d", w, h); if (*((uint16_t *)&buf[0]) != 1) { /* planes */ f_close(&imgFile); return nullptr; @@ -1557,11 +1589,23 @@ const stbi_io_callbacks stbCallbacks = { BitmapBuffer * BitmapBuffer::load_stb(const char * filename) { + //TRACE(" BitmapBuffer::load_stb(%s)", filename); + FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); if (result != FR_OK) { return nullptr; } + int x, y, nn; + stbi_info_from_callbacks(&stbCallbacks, &imgFile, &x, &y, &nn); + f_close(&imgFile); + //TRACE(" BitmapBuffer::load_stb()----Info File %s, %d, %d, %d", filename, x, y, nn); + + result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); + if (result != FR_OK) { + return nullptr; + } + int w, h, n; unsigned char * img = stbi_load_from_callbacks(&stbCallbacks, &imgFile, &w, &h, &n, 4); f_close(&imgFile); @@ -1571,6 +1615,7 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) return nullptr; } + //TRACE(" BitmapBuffer::load_stb()----Info File %s, %d, %d, %d/%d", filename, x, y, nn, n); BitmapBuffer * bmp = convert_stb_bitmap(img, w, h, n); stbi_image_free(img); return bmp; @@ -1593,6 +1638,7 @@ BitmapBuffer * BitmapBuffer::load_stb_buffer(const uint8_t * buffer, int len) BitmapBuffer * BitmapBuffer::convert_stb_bitmap(uint8_t * img, int w, int h, int n) { // convert to RGB565 or ARGB4444 format + //TRACE(" BitmapBuffer::convert_stb_bitmap(%d)", n); BitmapBuffer * bmp = new BitmapBuffer(n == 4 ? BMP_ARGB4444 : BMP_RGB565, w, h); if (bmp == nullptr) { TRACE("convert_stn_bitmap: malloc failed"); diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 0f6346daf..b766f035e 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -62,6 +62,9 @@ uint16_t g = ((color) & 0x07E0) >> 5; \ uint16_t b = ((color) & 0x001F) +#define ARGB_JOIN(a, r, g, b) \ + (((a&0xF) << 12) + ((r&0xF) << 8) + ((g&0xF) << 4) + (b&0xF)) + #define RGB_JOIN(r, g, b) \ (((r) << 11) + ((g) << 5) + (b)) From 45db1393d66c36e00fa9f0f455f76ecd611a0307 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 24 Oct 2021 11:44:26 +0200 Subject: [PATCH 449/714] added a way to disable touch screen event handling Needed for EdgeTX issue #185 --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 3 +++ radio/src/thirdparty/libopenui/src/mainwindow.h | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 7cbe46111..16f9135d5 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -46,6 +46,9 @@ void MainWindow::checkEvents() touchPanelRead(); } + if(!touchEnabled) + touchState.event = TE_NONE; + if (touchState.event == TE_DOWN) { onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); slidingWindow = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index fc47bdbc3..009c86454 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -64,6 +64,13 @@ class MainWindow: public Window void checkEvents() override; +#if defined(HARDWARE_TOUCH) + void setTouchEnabled(bool enable) + { + touchEnabled = enable; + } +#endif + void invalidate() { invalidate({0, 0, rect.w, rect.h}); @@ -85,4 +92,7 @@ class MainWindow: public Window static void emptyTrash(); rect_t invalidatedRect; const char * shutdown = nullptr; +#if defined(HARDWARE_TOUCH) + bool touchEnabled = true; +#endif }; From 4c0518d2d9796c28d38348d105149c9b5411a75b Mon Sep 17 00:00:00 2001 From: Kevin Koenig Date: Thu, 4 Nov 2021 03:57:37 -0700 Subject: [PATCH 450/714] Fix continuously touch end events and ensuretouchEnd if no scroll * minor changes to touch and slide processing. set event to TE_NONE so you don't continuously get touch end events. also make sure you get touchEnd if you slid but there is no sliding window (e.g. no scroll happened) * revert slide operations back to their original state * fix merge issue * one more try and it should be right now Co-authored-by: kkoenig1@optonline.com --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 03241345f..27c459bf2 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -47,7 +47,7 @@ void MainWindow::checkEvents() } if(!touchEnabled) - touchState.event = TE_NONE; + touchState.event = TE_NONE; if (touchState.event == TE_DOWN) { onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); @@ -81,7 +81,9 @@ void MainWindow::checkEvents() touchState.lastDeltaY = 0; onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); - slidingWindow = nullptr; + } else if (touchState.event == TE_SLIDE_END && slidingWindow == nullptr) { + onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); + touchState.event = TE_NONE; } #endif From 40432653bdc30eb7e77952f13b96dba7f69038cb Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Wed, 10 Nov 2021 11:58:11 +0100 Subject: [PATCH 451/714] Make sliders honor the enable bool (#33) --- radio/src/thirdparty/libopenui/src/slider.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index fb6c6a7c8..610af4639 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -56,6 +56,9 @@ void Slider::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool Slider::onTouchStart(coord_t x, coord_t y) { + if(!enabled) + return true; + if (!hasFocus()) { onKeyPress(); setFocus(SET_FOCUS_DEFAULT); @@ -66,6 +69,9 @@ bool Slider::onTouchStart(coord_t x, coord_t y) bool Slider::onTouchEnd(coord_t x, coord_t y) { + if(!enabled) + return true; + setValue(value(x)); invalidate(); onKeyPress(); @@ -74,6 +80,9 @@ bool Slider::onTouchEnd(coord_t x, coord_t y) bool Slider::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) { + if(!enabled) + return true; + if (hasFocus() && sliding) { int newValue = value(x); if (getValue() != newValue) { From 01f9e8311768293437e77215532c073a04a45884 Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Mon, 15 Nov 2021 11:07:56 +0100 Subject: [PATCH 452/714] Implement touch disable/enable statemachine (#34) * implemented a touch disable/enable statemachine works as follows: if touch is enabled, everything is processed if touch is disabled, nothing is processed when switching from disabled to enabled, and the display touched before, the TE_UP/TE_SLIDE_END ist ignored when switching from enabled to disabled and the display is touched, the next TE_UP/TW_SLIDE_END will be processed, to leave nobody in a touch started but never ended state * fix touch screen handling when staring touch while disabled and releasing touch when enabled * touch on/off with backlight on/off is working * setTouchEnable has to be #if defed for touch radios --- .../thirdparty/libopenui/src/mainwindow.cpp | 73 ++++++++++++++++++- .../src/thirdparty/libopenui/src/mainwindow.h | 24 +++++- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 27c459bf2..589722c51 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -39,15 +39,84 @@ void MainWindow::emptyTrash() trash.clear(); } +#if defined(HARDWARE_TOUCH) +void MainWindow::setTouchEnabled(bool enable) +{ + switch(touchEnableState) + { + case TouchOn: + if(!enable) + touchEnableState = TouchOff; + break; + case TouchOn_Touching: + if(!enable) + touchEnableState = TouchOnToOff_Touching; + break; + case TouchOnToOff_Touching: + if(enable) + touchEnableState = TouchOn_Touching; + break; + case TouchOff: + if(enable) + touchEnableState = TouchOn; + break; + case TouchOff_Touching: + if(enable) + touchEnableState = TouchOffToOn_Touching; + break; + case TouchOffToOn_Touching: + if(!enable) + touchEnableState = TouchOff_Touching; + break; + } +} +#endif + void MainWindow::checkEvents() { #if defined(HARDWARE_TOUCH) + if (touchPanelEventOccured()) { - touchPanelRead(); + touchState = touchPanelRead(); + } else { + touchState.event = TE_NONE; } - if(!touchEnabled) + TouchEnableState currentState = touchEnableState; + + switch(touchState.event) + { + case TE_DOWN: + case TE_SLIDE: + if(touchEnableState == TouchOn) + touchEnableState = TouchOn_Touching; + else if (touchEnableState == TouchOff) + touchEnableState = TouchOff_Touching; + break; + case TE_UP: + case TE_SLIDE_END: + if(touchEnableState == TouchOn_Touching) + touchEnableState = TouchOn; + else if (touchEnableState == TouchOff_Touching) + touchEnableState = TouchOff; + else if(touchEnableState == TouchOnToOff_Touching) + touchEnableState = TouchOff; + else if(touchEnableState == TouchOffToOn_Touching) + touchEnableState = TouchOn; + break; + default: + break; + } + + if(touchState.event != TE_NONE) + _touchEventOccured = true; + + if ( currentState == TouchOff + || currentState == TouchOff_Touching + || currentState == TouchOffToOn_Touching) + { touchState.event = TE_NONE; + } if (touchState.event == TE_DOWN) { onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 009c86454..d8bf8f62b 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -65,9 +65,14 @@ class MainWindow: public Window void checkEvents() override; #if defined(HARDWARE_TOUCH) - void setTouchEnabled(bool enable) - { - touchEnabled = enable; + void setTouchEnabled(bool enable); + bool touchEventOccured() { + if(_touchEventOccured) + { + _touchEventOccured = false; + return true; + } + return false; } #endif @@ -93,6 +98,17 @@ class MainWindow: public Window rect_t invalidatedRect; const char * shutdown = nullptr; #if defined(HARDWARE_TOUCH) - bool touchEnabled = true; + bool lastTouchState = false; + bool _touchEventOccured = false; + + enum TouchEnableState { + TouchOn, + TouchOn_Touching, + TouchOnToOff_Touching, + TouchOff, + TouchOff_Touching, + TouchOffToOn_Touching + }; + TouchEnableState touchEnableState = TouchOn; #endif }; From dd8c122db5d72461b8c7238d4b69c34db0444437 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Mon, 15 Nov 2021 22:04:31 +1100 Subject: [PATCH 453/714] Adjust theme constants use by Menu --- radio/src/thirdparty/libopenui/src/menu.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 8283e6bc3..c52635dd2 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -171,7 +171,7 @@ void MenuBody::paint(BitmapBuffer * dc) int y = 0; for (int i = 0; i < lines.size(); i++) { auto& line = lines[i]; - LcdFlags flags = COLOR_THEME_SECONDARY1 | MENU_FONT; + LcdFlags flags = COLOR_THEME_PRIMARY3 | MENU_FONT; // draw selection if appropriate if (selectedIndex == i && ! line.isSeparator) { @@ -183,9 +183,9 @@ void MenuBody::paint(BitmapBuffer * dc) } if (line.isSeparator) { - dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY2); + dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY1); } else if (line.drawLine) { - line.drawLine(dc, 0, y, flags); + line.drawLine(dc, 0, y, COLOR_THEME_SECONDARY3); } else { const char* text = line.text.data(); dc->drawText(10, @@ -200,10 +200,6 @@ void MenuBody::paint(BitmapBuffer * dc) 0); } - // if (i > 0 && ! lines[i].isSeparator && ! lines[i-1].isSeparator) { - // dc->drawSolidHorizontalLine(0, y, MENUS_WIDTH, COLOR_THEME_SECONDARY2); - // } - y += line.lineHeight(); } } @@ -219,13 +215,13 @@ MenuWindowContent::MenuWindowContent(Menu* parent) : void MenuWindowContent::paint(BitmapBuffer * dc) { // the background - dc->clear(COLOR_THEME_PRIMARY2); + dc->clear(COLOR_THEME_SECONDARY1); // the title if (!title.empty()) { dc->drawText(MENUS_WIDTH / 2, (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, - title.c_str(), CENTERED | MENU_HEADER_FONT); + title.c_str(), CENTERED | MENU_HEADER_FONT | COLOR_THEME_PRIMARY2); dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, COLOR_THEME_SECONDARY2); } @@ -312,5 +308,5 @@ void Menu::paint(BitmapBuffer * dc) r.x = toolbar->left(); r.w += toolbar->width(); } - dc->drawSolidRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, 1, COLOR_THEME_SECONDARY2); + dc->drawSolidRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, 1, COLOR_THEME_SECONDARY1); } From b211ce5af6c11cb16eaaa8a5c9cc4b29da396489 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Tue, 16 Nov 2021 19:53:31 +1000 Subject: [PATCH 454/714] If scroll value out of range, set min or max number_edit value --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 48cd71e44..70e6d6889 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -82,6 +82,7 @@ void NumberEdit::onEvent(event_t event) onKeyPress(); } else { + setValue(vmax); onKeyError(); } return; @@ -97,6 +98,7 @@ void NumberEdit::onEvent(event_t event) onKeyPress(); } else { + setValue(vmin); onKeyError(); } return; From 4e1cba2bad15a06ba40c663dcee761f6ecbff358 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sat, 20 Nov 2021 15:03:00 +0100 Subject: [PATCH 455/714] fix scroll inertia --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 589722c51..fea41da9a 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -77,9 +77,13 @@ void MainWindow::checkEvents() #if defined(HARDWARE_TOUCH) if (touchPanelEventOccured()) { + short lastDeltaX = touchState.lastDeltaX; + short lastDeltaY = touchState.lastDeltaY;; touchState = touchPanelRead(); + touchState.lastDeltaX = lastDeltaX; + touchState.lastDeltaY = lastDeltaY; } else { - touchState.event = TE_NONE; + touchState.event = TE_NONE; } TouchEnableState currentState = touchEnableState; From 1a886dee5f1c4577805c9d5b8e9081edfa2d1eba Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Fri, 26 Nov 2021 11:37:29 +0100 Subject: [PATCH 456/714] Fix scroll inertia (again) (#38) * fix scroll inertia * scroll inertia now works like before touch on/off changes --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index fea41da9a..09cc3d37e 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -82,8 +82,6 @@ void MainWindow::checkEvents() touchState = touchPanelRead(); touchState.lastDeltaX = lastDeltaX; touchState.lastDeltaY = lastDeltaY; - } else { - touchState.event = TE_NONE; } TouchEnableState currentState = touchEnableState; From 758c56a4cf2d09d87e04313d804e0359a38b9077 Mon Sep 17 00:00:00 2001 From: Kevin Koenig Date: Tue, 30 Nov 2021 06:14:57 -0500 Subject: [PATCH 457/714] Fix being able to enable and disable popup menu filters (#39) * fix-228 * removed commented codes Co-authored-by: kkoenig1@optonline.com --- radio/src/thirdparty/libopenui/src/menu.cpp | 3 +- .../thirdparty/libopenui/src/menutoolbar.h | 94 ++++++++----------- 2 files changed, 40 insertions(+), 57 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index c52635dd2..e4c6ce189 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -287,7 +287,8 @@ void Menu::removeLines() #if defined(HARDWARE_KEYS) void Menu::onEvent(event_t event) { - if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN))) { + if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || + event == EVT_KEY_BREAK(KEY_PGUP))) { toolbar->onEvent(event); } else if (event == EVT_KEY_BREAK(KEY_EXIT)) { diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index a26e70b98..f6a360a8a 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -21,6 +21,7 @@ #include "button.h" #include "menu.h" +#include int getFirstAvailable(int min, int max, std::functionisValueAvailable); @@ -28,7 +29,7 @@ class MenuToolbarButton: public Button { public: MenuToolbarButton(FormGroup * window, const rect_t & rect, char picto): #if defined(HARDWARE_TOUCH) - Button(window, rect, nullptr, BUTTON_CHECKED_ON_FOCUS), + Button(window, rect, nullptr), #else Button(window, rect, nullptr), #endif @@ -38,28 +39,6 @@ class MenuToolbarButton: public Button { void paint(BitmapBuffer * dc) override; -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override - { - if (hasFocus()) { - check(false); - clearFocus(); - } - else { - setFocus(SET_FOCUS_DEFAULT); - } - onPress(); - return true; - } -#endif - - void check(bool checked=true) - { - Button::check(checked); - onPress(); - Button::check(checked); - } - protected: char picto; }; @@ -84,38 +63,27 @@ class MenuToolbar: public FormGroup { #if defined(HARDWARE_KEYS) void onEvent(event_t event) override { - if (event == EVT_KEY_BREAK(KEY_PGDN)) { - if (current == children.end()) { - current = children.begin(); - } - else { - static_cast(*current)->check(false); - ++current; + if (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || event == EVT_KEY_BREAK(KEY_PGUP)) { + if (current != children.end()) { + static_cast(*current)->onTouchEnd(0,0); } + + current = IS_KEY_LONG(event) || event == EVT_KEY_BREAK(KEY_PGUP) ? + current == children.end() ? + --current: + current == children.begin() ? + children.end() : + --current : + current == children.end() ? + children.begin() : + ++current; + + if (IS_KEY_LONG(event)) + killEvents(event); + if (current != children.end()) { auto button = static_cast(*current); - button->check(true); - scrollTo(button); - } - else { - setScrollPositionY(0); - } - } - else if (event == EVT_KEY_LONG(KEY_PGDN)) { - killEvents(event); - if (current == children.end()) { - --current; - } - else { - static_cast(*current)->check(false); - if (current == children.begin()) - current = children.end(); - else - --current; - } - if (current != children.end()) { - auto button = static_cast(*current); - button->check(true); + button->onTouchEnd(0,0); scrollTo(button); } else { @@ -138,6 +106,7 @@ class MenuToolbar: public FormGroup { T * choice; Menu * menu; coord_t y = 0; + std::vector menuButtons; void addButton(char picto, int16_t filtermin, int16_t filtermax) { @@ -155,21 +124,34 @@ class MenuToolbar: public FormGroup { auto button = new MenuToolbarButton( this, {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}, picto); - button->setPressHandler([=]() { auto val = choice->getValue(); + button->check(!button->checked()); + if (button->checked()) { choice->fillMenu(menu, val, [=](int16_t index) { return index >= filtermin && index <= filtermax; }); - menu->setFocusBody(); } else { choice->fillMenu(menu, val); - menu->setFocusBody(); } - return 1; + menu->setFocusBody(); + + // set current for page key procesing + current = std::find_if(children.begin(), children.end(), [button](Window *o) { return o == button; }); + + // clear all checked but the current button + for (auto b : menuButtons) { + if (b != button) + b->check(false); + } + + return button->checked(); }); + menuButtons.emplace_back(button); + + y += MENUS_TOOLBAR_BUTTON_WIDTH; setInnerHeight(y); From cd52eb9737fc41f2afeddefe366dfd8dd1e4ab05 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Tue, 30 Nov 2021 22:17:31 +1000 Subject: [PATCH 458/714] Fix #39 hardware touch whoopsie --- radio/src/thirdparty/libopenui/src/menutoolbar.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index f6a360a8a..a4f57834e 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -64,10 +64,11 @@ class MenuToolbar: public FormGroup { void onEvent(event_t event) override { if (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || event == EVT_KEY_BREAK(KEY_PGUP)) { +#if defined(HARDWARE_TOUCH) if (current != children.end()) { static_cast(*current)->onTouchEnd(0,0); } - +#endif current = IS_KEY_LONG(event) || event == EVT_KEY_BREAK(KEY_PGUP) ? current == children.end() ? --current: @@ -83,7 +84,9 @@ class MenuToolbar: public FormGroup { if (current != children.end()) { auto button = static_cast(*current); +#if defined(HARDWARE_TOUCH) button->onTouchEnd(0,0); +#endif scrollTo(button); } else { From faae2fff8ce9b4e8e4a04e4bdec75bbad33c4edd Mon Sep 17 00:00:00 2001 From: Kevin Koenig Date: Wed, 1 Dec 2021 18:41:08 -0500 Subject: [PATCH 459/714] Fix touch slide end segfault (#40) * fix touch slide/end issue * Revert "fix touch slide/end issue" This reverts commit e9f11fe9905ff3e175e2f37aeafac0fa20956450. * fix tocuh slide/end issue * Revert "fix tocuh slide/end issue" This reverts commit 82264c8382736a1c7b60a0e41b7188c429d2c43f. * put the file in the wrong place Co-authored-by: kkoenig1@optonline.com --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 09cc3d37e..40d5bd1d4 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -151,6 +151,8 @@ void MainWindow::checkEvents() else touchState.lastDeltaY = 0; + touchedWindow = nullptr; // this is also an end of a touch so reset the touched window. Fixes scroll and touch bug + onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); } else if (touchState.event == TE_SLIDE_END && slidingWindow == nullptr) { onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); From 929847209d61d797ca38edfdd0cd411e82a40205 Mon Sep 17 00:00:00 2001 From: Risto Date: Thu, 2 Dec 2021 01:28:13 +0100 Subject: [PATCH 460/714] Fixed compiler warnings (#41) Fixed 4 compiler warnings about comparison of signed and unsigned int. --- radio/src/thirdparty/libopenui/src/menu.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index e4c6ce189..28d46105e 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -27,7 +27,7 @@ int MenuBody::rangeCheck(int index) { if (index < 0) index = lines.size() - 1; - else if (index > lines.size() - 1) + else if (index > (signed)lines.size() - 1) index = 0; return index; @@ -132,7 +132,7 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) // dynamically find the selected element based on variable line heights coord_t yAccumulator = 0; - for (int i = 0; i < lines.size(); i++) { + for (int i = 0; i < (signed)lines.size(); i++) { if (y >= yAccumulator && y <= yAccumulator + lines[i].lineHeight()) { index = i; break; @@ -169,7 +169,7 @@ void MenuBody::paint(BitmapBuffer * dc) dc->clear(COLOR_THEME_PRIMARY2); int y = 0; - for (int i = 0; i < lines.size(); i++) { + for (int i = 0; i < (signed)lines.size(); i++) { auto& line = lines[i]; LcdFlags flags = COLOR_THEME_PRIMARY3 | MENU_FONT; @@ -238,7 +238,7 @@ void Menu::updatePosition() { // calcualte the correct menu height given that the line heights are variable coord_t height = 0; - for (int i = 0; i < content->body.lines.size(); i++) + for (int i = 0; i < (signed)content->body.lines.size(); i++) height += content->body.lines[i].lineHeight(); if (!toolbar) { From a557b5d2fe5f676d1e6e6ac483ed6eca570acadf Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Mon, 6 Dec 2021 11:52:34 +1000 Subject: [PATCH 461/714] Let buttons catch touchStart Resolves https://github.com/EdgeTX/edgetx/issues/1087 --- radio/src/thirdparty/libopenui/src/button.cpp | 8 ++++++++ radio/src/thirdparty/libopenui/src/button.h | 1 + 2 files changed, 9 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 0ff6156e2..22107365a 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -47,6 +47,14 @@ void Button::onEvent(event_t event) } #endif +#if defined(HARDWARE_TOUCH) +bool Button::onTouchStart(coord_t x, coord_t y) +{ + Window::onTouchStart(x, y); + return true; +} +#endif + #if defined(HARDWARE_TOUCH) bool Button::onTouchEnd(coord_t x, coord_t y) { diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 07a8db3e1..725cd3ce7 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -74,6 +74,7 @@ class Button: public FormField #endif #if defined(HARDWARE_TOUCH) + bool onTouchStart(coord_t x, coord_t y) override; bool onTouchEnd(coord_t x, coord_t y) override; #endif From b8f17aa3f9ce5a6486f27f695c841ea98859adcd Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Sun, 12 Dec 2021 10:00:28 +0100 Subject: [PATCH 462/714] Fix repeated touch events (#44) * fix repeated touch event execution * handle all touch events only once, except for TE_SLIDE_END for touch inertia --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 40d5bd1d4..23679dfee 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -158,6 +158,9 @@ void MainWindow::checkEvents() onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); touchState.event = TE_NONE; } + if(touchState.event != TE_SLIDE_END) { + touchState.event = TE_NONE; + } #endif Window::checkEvents(); From 580849386b680edcd6f2837ba940f68084f51c5a Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Sat, 11 Dec 2021 08:01:23 -0500 Subject: [PATCH 463/714] fix touch end after slide --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 23679dfee..406829613 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -155,7 +155,7 @@ void MainWindow::checkEvents() onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); } else if (touchState.event == TE_SLIDE_END && slidingWindow == nullptr) { - onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); + onTouchEnd(touchState.x + scrollPositionX, touchState.y + scrollPositionY); touchState.event = TE_NONE; } if(touchState.event != TE_SLIDE_END) { From f81fa679d79dec9c83b4a42c8ecaf249e448e29d Mon Sep 17 00:00:00 2001 From: Kevin Koenig Date: Sun, 12 Dec 2021 16:39:44 -0500 Subject: [PATCH 464/714] Change touch end processing (#45) 1. removed the generic touchedWindow processing 2. replaced it with an implicit capture processing 3. therefore it works like normal in all instances except ListBox() which explicitely captures the up. --- .../thirdparty/libopenui/src/mainwindow.cpp | 4 +-- radio/src/thirdparty/libopenui/src/window.cpp | 31 ++++--------------- radio/src/thirdparty/libopenui/src/window.h | 7 ++++- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 406829613..c256d0b06 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -150,8 +150,8 @@ void MainWindow::checkEvents() touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; else touchState.lastDeltaY = 0; - - touchedWindow = nullptr; // this is also an end of a touch so reset the touched window. Fixes scroll and touch bug + + Window::capturedWindow = nullptr; onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); } else if (touchState.event == TE_SLIDE_END && slidingWindow == nullptr) { diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 46f5d4ce4..28ba8c027 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -22,7 +22,7 @@ Window * Window::focusWindow = nullptr; Window * Window::slidingWindow = nullptr; -Window * Window::touchedWindow = nullptr; +Window * Window::capturedWindow = nullptr; std::list Window::trash; Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): @@ -412,12 +412,6 @@ bool Window::onTouchStart(coord_t x, coord_t y) auto child = *it; if (child->rect.contains(x, y)) { if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { - // remember the first window that processed the touch start event. We will give that window - // the opportunity to process the touchend first, and also after a slide end so the touched window - // always gets a touchEnd if it processed the touch start. - if (touchedWindow == nullptr) { - touchedWindow = child; - } return true; } } @@ -428,24 +422,11 @@ bool Window::onTouchStart(coord_t x, coord_t y) bool Window::forwardTouchEnd(coord_t x, coord_t y) { - // give the touched window the opportunity to process the touch first before it is - // forwarded: See on touchstart - if (touchedWindow != nullptr) { - auto savedTouchWindow = touchedWindow; - touchedWindow = nullptr; - // transalte x and y through the parent hierarchy - auto myParent = savedTouchWindow->parent; - while(myParent != nullptr) { - x = x - myParent->rect.x + myParent->scrollPositionX; - y = y - myParent->rect.y + myParent->scrollPositionY; - myParent = myParent->parent; - } - - if (savedTouchWindow->onTouchEnd( - x - savedTouchWindow->rect.x + savedTouchWindow->scrollPositionX, - y - savedTouchWindow->rect.y + savedTouchWindow->scrollPositionY)) { - return true; - } + if (capturedWindow != nullptr) + { + capturedWindow->onTouchEnd(x, y); + capturedWindow = nullptr; + return true; } for (auto it = children.rbegin(); it != children.rend(); ++it) { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 2b9b045e6..8f731010f 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -321,6 +321,11 @@ class Window return scrollPositionX; } + void captureWindow(Window *window) + { + capturedWindow = window; + } + coord_t getScrollPositionY() const { return scrollPositionY; @@ -395,7 +400,7 @@ class Window static Window * focusWindow; static Window * slidingWindow; - static Window * touchedWindow; + static Window * capturedWindow; static std::list trash; std::function closeHandler; From 987bc073cd4c7256e432f9f148dbffee7b9b1fa1 Mon Sep 17 00:00:00 2001 From: richardclli Date: Fri, 17 Dec 2021 06:48:42 +0800 Subject: [PATCH 465/714] Fixed wrong index calculation (chinese character display) (#46) Companion for EdgeTX/edgetx#1257 --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index f0e1f3a98..b7484e10e 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1061,7 +1061,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ s++; c = uint8_t(*s) + ((c & 0x01u) << 8u) - 1; // TRACE("CJK = %d", c); - if (c >= 0x101) + if (c >= 0x100) c -= 1; c += CJK_FIRST_LETTER_INDEX; uint8_t width = drawChar(x, y, font, fontspecs, c, flags); From 0458ef4537ecf7109c6d91f40a56e94b4e5eef3d Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Thu, 23 Dec 2021 10:18:48 +1000 Subject: [PATCH 466/714] [menu] Change theme color used for drawLine --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 28d46105e..9d25a6d51 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -185,7 +185,7 @@ void MenuBody::paint(BitmapBuffer * dc) if (line.isSeparator) { dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY1); } else if (line.drawLine) { - line.drawLine(dc, 0, y, COLOR_THEME_SECONDARY3); + line.drawLine(dc, 0, y, COLOR_THEME_PRIMARY3); } else { const char* text = line.text.data(); dc->drawText(10, From b624ffc631d2d3c4a091226844715237d159e38d Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Fri, 21 Jan 2022 10:56:33 +0100 Subject: [PATCH 467/714] Move TE_SLIDE_END inertia handling to completely to libopenui (#50) stop continuing TW_SLIDE_END events after sliding stops --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index c256d0b06..f9cc467ba 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -79,7 +79,9 @@ void MainWindow::checkEvents() if (touchPanelEventOccured()) { short lastDeltaX = touchState.lastDeltaX; short lastDeltaY = touchState.lastDeltaY;; - touchState = touchPanelRead(); + TouchState newTouchState = touchPanelRead(); + if(touchState.event != TE_SLIDE_END || newTouchState.event != TE_NONE) + touchState = newTouchState; touchState.lastDeltaX = lastDeltaX; touchState.lastDeltaY = lastDeltaY; } @@ -154,6 +156,11 @@ void MainWindow::checkEvents() Window::capturedWindow = nullptr; onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); + if(touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) + { + touchState.event = TE_NONE; + slidingWindow = nullptr; + } } else if (touchState.event == TE_SLIDE_END && slidingWindow == nullptr) { onTouchEnd(touchState.x + scrollPositionX, touchState.y + scrollPositionY); touchState.event = TE_NONE; From e538138adefc1bf1d8c39640d1401377aebba34b Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Thu, 30 Dec 2021 20:19:54 +0100 Subject: [PATCH 468/714] made change to be able to use libopenui with lvgl as underlying lib --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 8 ++++---- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 14 +++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 146c81541..f5ce24548 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -36,7 +36,7 @@ constexpr uint8_t STASHED = 0x33; #define APPLY_OFFSET() x += this->offsetX; y += this->offsetY #define RESTORE_OFFSET() this->offsetX = offsetX, this->offsetY = offsetY -#if defined(LCD_VERTICAL_INVERT) +#if defined(LCD_VERTICAL_INVERT) && 0 #define MOVE_PIXEL_RIGHT(p, count) p -= count #else #define MOVE_PIXEL_RIGHT(p, count) p += count @@ -156,7 +156,7 @@ class BitmapBufferBase inline const pixel_t * getPixelPtrAbs(coord_t x, coord_t y) const { -#if defined(LCD_VERTICAL_INVERT) +#if defined(LCD_VERTICAL_INVERT) && 0 x = _width - x - 1; y = _height - y - 1; #endif @@ -401,7 +401,7 @@ class BitmapBuffer: public BitmapBufferBase inline const pixel_t * getPixelPtrAbs(coord_t x, coord_t y) const { -#if defined(LCD_VERTICAL_INVERT) +#if defined(LCD_VERTICAL_INVERT) && 0 x = _width - x - 1; y = _height - y - 1; #endif @@ -410,7 +410,7 @@ class BitmapBuffer: public BitmapBufferBase inline pixel_t * getPixelPtrAbs(coord_t x, coord_t y) { -#if defined(LCD_VERTICAL_INVERT) +#if defined(LCD_VERTICAL_INVERT) && 0 x = _width - x - 1; y = _height - y - 1; #endif diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index f9cc467ba..7d2de6336 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -186,9 +186,21 @@ void MainWindow::invalidate(const rect_t & rect) invalidatedRect = rect; } } - +#include "lvgl/lvgl.h" +extern lv_obj_t * canvas; +extern BitmapBuffer lcdBackup; bool MainWindow::refresh() { + if (invalidatedRect.w) { + lcdBackup.setOffset(0, 0); + lcdBackup.setClippingRect(0,LCD_W,0, LCD_H); + fullPaint(&lcdBackup); + lv_canvas_copy_buf(canvas, lcdBackup.getData(), 0, 0, LCD_W, LCD_H); + lv_img_cache_invalidate_src(lcdBackup.getData()); + lv_obj_invalidate(canvas); + } + return false; + if (invalidatedRect.w) { if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); From 6b70baf63ae61f01742a2308a72ccac4efae80a8 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Sat, 8 Jan 2022 04:23:23 -0500 Subject: [PATCH 469/714] 1. basic changes to mirror libopnui window hierararchy to lvgl object hierarchy 2. basic scroll support 3. Basic layer support (still pretty broken), multiple screen support 4. Preliminary impelemntation of TextEdit using lvgl. 5. Preliminary implementation of StaticText using lvgl. 6. Preliminary implementation of TextButton using lvgl. --- radio/src/thirdparty/libopenui/src/button.cpp | 104 ++++++++---- radio/src/thirdparty/libopenui/src/button.h | 21 +-- radio/src/thirdparty/libopenui/src/form.cpp | 4 +- radio/src/thirdparty/libopenui/src/form.h | 8 +- radio/src/thirdparty/libopenui/src/layer.h | 15 ++ .../src/thirdparty/libopenui/src/mainwindow.h | 2 +- .../thirdparty/libopenui/src/modal_window.cpp | 2 +- radio/src/thirdparty/libopenui/src/static.cpp | 56 +++---- radio/src/thirdparty/libopenui/src/static.h | 17 +- .../src/thirdparty/libopenui/src/textedit.cpp | 149 +++++++++++++----- radio/src/thirdparty/libopenui/src/textedit.h | 10 +- radio/src/thirdparty/libopenui/src/window.cpp | 37 ++++- radio/src/thirdparty/libopenui/src/window.h | 6 +- 13 files changed, 302 insertions(+), 129 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 22107365a..f5f61f1e0 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -76,44 +76,82 @@ void Button::checkEvents() checkHandler(); } + +TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, + std::function pressHandler, + WindowFlags windowFlags, + LcdFlags textFlags) : + Button(parent, rect, std::move(pressHandler), windowFlags, textFlags), + text(std::move(text)) +{ + if (lvobj != nullptr) { + lv_obj_del(lvobj); + } + lvobj = lv_btn_create(parent->getLvObj()); + lv_obj_set_pos(lvobj, rect.x, rect.y); /*Set its position*/ + lv_obj_set_size(lvobj, rect.w, rect.h); /*Set its size*/ + setTextFlags(textFlags | COLOR_THEME_PRIMARY1); + +#if 1 + auto color = COLOR_VAL(COLOR_THEME_PRIMARY2); + auto r = GET_RED(color), g = GET_GREEN(color), b = GET_BLUE(color); + + lv_style_init(&style_btn); + lv_style_set_radius(&style_btn, 0); + lv_obj_set_style_bg_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); + + lv_obj_add_style(lvobj, &style_btn,0); + + label = lv_label_create(lvobj); + lv_label_set_text(label, this->text.c_str()); + lv_obj_center(label); + color = COLOR_VAL(this->getTextFlags()); + lv_obj_set_style_text_color(label, lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)), LV_PART_MAIN); +#endif +} + +TextButton::~TextButton() +{ +} + void TextButton::paint(BitmapBuffer * dc) { - auto textColor = getTextFlags(); - auto bgColor = COLOR_THEME_SECONDARY2; + // auto textColor = getTextFlags(); + // auto bgColor = COLOR_THEME_SECONDARY2; - if (bgColorHandler) { - bgColor = bgColorHandler(); - } else if (checked()) { - bgColor = COLOR_THEME_ACTIVE; - } else if (hasFocus()) { - bgColor = COLOR_THEME_FOCUS; - textColor = COLOR_THEME_PRIMARY2; - } + // if (bgColorHandler) { + // bgColor = bgColorHandler(); + // } else if (checked()) { + // bgColor = COLOR_THEME_ACTIVE; + // } else if (hasFocus()) { + // bgColor = COLOR_THEME_FOCUS; + // textColor = COLOR_THEME_PRIMARY2; + // } - if (checked()) { - if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); - dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, bgColor); - } else { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); - } - } - else { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); - if (windowFlags & BUTTON_BACKGROUND) { - if (hasFocus()) { - textColor = COLOR_THEME_PRIMARY2; - } - } else if (hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); - } - else { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); - } - } + // if (checked()) { + // if (hasFocus()) { + // dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); + // dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, bgColor); + // } else { + // dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); + // } + // } + // else { + // dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); + // if (windowFlags & BUTTON_BACKGROUND) { + // if (hasFocus()) { + // textColor = COLOR_THEME_PRIMARY2; + // } + // } else if (hasFocus()) { + // dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); + // } + // else { + // dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); + // } + // } - dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, - text.c_str(), CENTERED | textColor); + // dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, + // text.c_str(), CENTERED | textColor); } void IconButton::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 725cd3ce7..fa8078329 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -87,19 +87,18 @@ class Button: public FormField void onPress(); }; + +extern void my_btn_event_cb(lv_event_t * e); + class TextButton: public Button { public: TextButton(FormGroup* parent, const rect_t& rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE, - LcdFlags textFlags = 0) : - Button(parent, rect, std::move(pressHandler), windowFlags, textFlags), - text(std::move(text)) - { - setTextFlags(textFlags | COLOR_THEME_PRIMARY1); - } - + LcdFlags textFlags = 0); + ~TextButton(); + #if defined(DEBUG_WINDOWS) std::string getName() const override { @@ -111,6 +110,7 @@ class TextButton: public Button { if (value != text) { text = std::move(value); + lv_label_set_text(label, text.c_str()); invalidate(); } } @@ -123,8 +123,11 @@ class TextButton: public Button void paint(BitmapBuffer* dc) override; protected: - std::string text; - std::function bgColorHandler = nullptr; + lv_obj_t * label = nullptr; + lv_style_t style_btn; + + std::string text; + std::function bgColorHandler = nullptr; }; class IconButton: public Button diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 8d88327fc..6bfa3b07f 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -21,8 +21,8 @@ #include "bitmapbuffer.h" #include "libopenui_config.h" -FormField::FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags) : - Window(parent, rect, windowFlags, textFlags) +FormField::FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags, bool isScreen) : + Window(parent, rect, windowFlags, textFlags, isScreen) { if (!(windowFlags & NO_FOCUS)) { auto * form = dynamic_cast(parent); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index f04483c46..871189a5e 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -30,7 +30,7 @@ constexpr WindowFlags FORM_FLAGS_LAST = FORM_NO_BORDER; class FormField: public Window { public: - FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); + FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, bool isScreen = false); virtual void changeEnd(bool forceChanged = false) { @@ -128,7 +128,7 @@ class FormField: public Window class FormGroup: public FormField { public: - FormGroup(Window * parent, const rect_t & rect, WindowFlags windowflags = 0) : + FormGroup(Window * parent, const rect_t & rect, WindowFlags windowflags = 0, bool isScreen = false) : FormField(parent, rect, windowflags) { } @@ -212,8 +212,8 @@ class FormGroup: public FormField class FormWindow: public FormGroup { public: - FormWindow(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0) : - FormGroup(parent, rect, windowFlags) + FormWindow(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, bool isScreen = false) : + FormGroup(parent, rect, windowFlags, isScreen) { } diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index 970b5e742..691ce3258 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -20,6 +20,7 @@ #pragma once #include "window.h" +extern lv_obj_t * canvas; class Layer { @@ -36,10 +37,20 @@ class Layer static void push(Window * window) { + // if (window->getLvObj()->parent != nullptr) { + // TRACE("Layers must have lvgl parent to be null"); + // return; + // } + if (!stack.empty()) { Layer & parentLayer = stack.back(); parentLayer.focus = Window::getFocus(); } + + // layers are screens in lvgl + lv_scr_load(window->getLvObj()); + lv_obj_set_parent(canvas, window->getLvObj()); + stack.emplace_back(Layer(window)); #if defined(DEBUG_WINDOWS) @@ -55,6 +66,8 @@ class Layer if (stack.back().main == window) { stack.pop_back(); const auto & back = stack.back(); + lv_obj_set_parent(canvas, back.main->getLvObj()); + lv_scr_load(back.main->getLvObj()); // load the screen. if (back.focus) { back.focus->setFocus(SET_FOCUS_DEFAULT); } @@ -63,6 +76,8 @@ class Layer // TODO it would be better to do it in reverse order for (auto it = stack.begin(); it != stack.end(); it++) { if (it->main == window) { + lv_obj_set_parent(canvas, it->main->getLvObj()); + lv_scr_load(it->main->getLvObj()); stack.erase(it); break; } diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index d8bf8f62b..d02f9889c 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -28,7 +28,7 @@ class MainWindow: public Window protected: // singleton MainWindow(): - Window(nullptr, {0, 0, LCD_W, LCD_H}), + Window(nullptr, {0, 0, LCD_W, LCD_H}, 0, 0, true), invalidatedRect(rect) { Layer::push(this); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index b33983164..ee8c67fe4 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -23,7 +23,7 @@ #include "layer.h" ModalWindow::ModalWindow(Window * parent, bool closeWhenClickOutside): - Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}), + Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}, 0, 0, true), closeWhenClickOutside(closeWhenClickOutside) { Layer::push(this); diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 64d5f62c6..3d1b09bfd 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -22,35 +22,35 @@ void StaticText::paint(BitmapBuffer * dc) { - if (bgColor) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); - } + // if (bgColor) { + // dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); + // } - coord_t x; - if (textFlags & CENTERED) - x = rect.w / 2; - else if (textFlags & RIGHT) - x = rect.w; - else - x = 0; + // coord_t x; + // if (textFlags & CENTERED) + // x = rect.w / 2; + // else if (textFlags & RIGHT) + // x = rect.w; + // else + // x = 0; - coord_t y = (textFlags & VCENTERED) ? (rect.h - getFontHeight(textFlags)) / 2 - : FIELD_PADDING_TOP; + // coord_t y = (textFlags & VCENTERED) ? (rect.h - getFontHeight(textFlags)) / 2 + // : FIELD_PADDING_TOP; - auto start = text.c_str(); - auto nextline = findNextLine(start); - if (nextline) { - auto current = start; - do { - dc->drawText(x, y, - text.substr(current - start, nextline - current).c_str(), - textFlags); - current = nextline + 1; - nextline = findNextLine(current); - y += getFontHeight(textFlags) + 2; - } while (nextline); - dc->drawText(x, y, current, textFlags); - } else { - dc->drawText(x, y, start, textFlags); - } + // auto start = text.c_str(); + // auto nextline = findNextLine(start); + // if (nextline) { + // auto current = start; + // do { + // dc->drawText(x, y, + // text.substr(current - start, nextline - current).c_str(), + // textFlags); + // current = nextline + 1; + // nextline = findNextLine(current); + // y += getFontHeight(textFlags) + 2; + // } while (nextline); + // dc->drawText(x, y, current, textFlags); + // } else { + // dc->drawText(x, y, start, textFlags); + // } } diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index f7beee158..7c8c18545 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -29,7 +29,18 @@ class StaticText: public Window Window(parent, rect, windowFlags, textFlags), text(std::move(text)) { + label = lv_label_create(lvobj); + lv_label_set_text(label, this->text.c_str()); + + auto color = COLOR_VAL(textFlags); + lv_obj_set_style_text_color(label, lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)), LV_PART_MAIN); + if (textFlags & CENTERED) + lv_obj_center(label); + if (textFlags & FONT(BOLD)) { + } + if (windowFlags & BUTTON_BACKGROUND) { + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); setBackgroundColor(COLOR_THEME_SECONDARY2); } } @@ -47,16 +58,20 @@ class StaticText: public Window { if (text != value) { text = std::move(value); - invalidate(); + lv_label_set_text(label, this->text.c_str()); + // invalidate(); } } void setBackgroundColor(LcdFlags color) { bgColor = color; + auto actualColor = COLOR_VAL(bgColor); + lv_obj_set_style_bg_color(lvobj, lv_color_make(GET_RED(actualColor), GET_GREEN(actualColor), GET_BLUE(actualColor)), LV_PART_MAIN); } protected: + lv_obj_t *label = nullptr; std::string text; LcdFlags bgColor = 0; }; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index c82a56605..74a087724 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -42,44 +42,111 @@ #include "clipboard.h" #endif + +static void ta_event_cb(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * ta = lv_event_get_target(e); + if(code == LV_EVENT_CLICKED || code == LV_EVENT_FOCUSED) { + /*Focus on the clicked text area*/ + if(kb != NULL) + lv_keyboard_set_textarea(kb, ta); + else { + /*Create a keyboard*/ + kb = lv_keyboard_create(lv_scr_act()); + lv_obj_set_size(kb, LV_HOR_RES, LV_VER_RES / 2); + lv_keyboard_set_textarea(kb, ta); + } + } + else if (code == LV_EVENT_DEFOCUSED) { + if (kb != nullptr) { + lv_obj_del(kb); + kb = nullptr; + } + } + else if(code == LV_EVENT_READY) { + LV_LOG_USER("Ready, current text: %s", lv_textarea_get_text(ta)); + } +} + +TextEdit::TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, + LcdFlags windowFlags, const char * _extra_chars) : + FormField(parent, rect, windowFlags), + value(value), + length(length) +{ + if (getLvObj() != nullptr) { + lv_obj_del(lvobj); + } + + lvobj = lv_textarea_create(parent->getLvObj()); + lv_obj_add_event_cb(lvobj, ta_event_cb, LV_EVENT_ALL, NULL); + lv_obj_set_pos(lvobj, rect.x, rect.y); + lv_obj_set_size(lvobj, rect.w, rect.h); + lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); + lv_obj_set_style_border_width(lvobj, 0, LV_PART_MAIN); + lv_obj_set_style_radius(lvobj, 0, LV_PART_MAIN); + lv_obj_set_style_pad_top(lvobj, 2, LV_PART_MAIN); + lv_obj_set_style_pad_bottom(lvobj, 2, LV_PART_MAIN); + lv_obj_set_style_pad_left(lvobj, 4, LV_PART_MAIN); + lv_obj_set_style_pad_right(lvobj, 4, LV_PART_MAIN); + + + lv_textarea_set_password_mode(lvobj, false); + lv_textarea_set_one_line(lvobj, true); + lv_textarea_set_text(lvobj, value); + lv_textarea_set_placeholder_text(lvobj, "---"); + lv_textarea_set_max_length(lvobj, length); + + lv_obj_set_style_text_font(lvobj, &lv_font_montserrat_12, LV_PART_MAIN); + lv_obj_set_style_text_line_space(lvobj, 0, LV_PART_MAIN); + + auto color = COLOR_VAL(COLOR_THEME_SECONDARY1); + auto label = lv_textarea_get_label(lvobj); + lv_obj_set_style_text_color(label, lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)), LV_PART_MAIN); + + extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; +} + + void TextEdit::paint(BitmapBuffer * dc) { - FormField::paint(dc); +// FormField::paint(dc); - if (editMode) { - dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, - COLOR_THEME_PRIMARY2); - coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); -#if defined(SOFTWARE_KEYBOARD) - dc->drawSolidFilledRect(left + 2, 2, 2, height() - 4, COLOR_THEME_PRIMARY2); -#else - char s[] = {value[cursorPos], '\0'}; - dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, - FIELD_PADDING_TOP - 1, getTextWidth(s, 1) + 1, - height() - 2, COLOR_THEME_PRIMARY2); - dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, - COLOR_THEME_SECONDARY1); -#endif - } else { - const char * displayedValue = value; - LcdFlags textColor = COLOR_THEME_PRIMARY2; - if (hasFocus()) { - if (strlen(value) == 0) { - displayedValue = "---"; - } - textColor = COLOR_THEME_PRIMARY2; - } - else { - if (strlen(value) == 0) { - displayedValue = "---"; - textColor = COLOR_THEME_DISABLED; - } - else { - textColor = COLOR_THEME_SECONDARY1; - } - } - dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); - } +// if (editMode) { +// dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, +// COLOR_THEME_PRIMARY2); +// coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); +// #if defined(SOFTWARE_KEYBOARD) +// dc->drawSolidFilledRect(left + 2, 2, 2, height() - 4, COLOR_THEME_PRIMARY2); +// #else +// char s[] = {value[cursorPos], '\0'}; +// dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, +// FIELD_PADDING_TOP - 1, getTextWidth(s, 1) + 1, +// height() - 2, COLOR_THEME_PRIMARY2); +// dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, +// COLOR_THEME_SECONDARY1); +// #endif +// } else { +// const char * displayedValue = value; +// LcdFlags textColor = COLOR_THEME_PRIMARY2; +// if (hasFocus()) { +// if (strlen(value) == 0) { +// displayedValue = "---"; +// } +// textColor = COLOR_THEME_PRIMARY2; +// } +// else { +// if (strlen(value) == 0) { +// displayedValue = "---"; +// textColor = COLOR_THEME_DISABLED; +// } +// else { +// textColor = COLOR_THEME_SECONDARY1; +// } +// } +// dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); +// } } void TextEdit::trim() @@ -254,9 +321,9 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) setFocus(SET_FOCUS_DEFAULT); } -#if defined(SOFTWARE_KEYBOARD) - TextKeyboard::show(this); -#endif +// #if defined(SOFTWARE_KEYBOARD) +// TextKeyboard::show(this); +// #endif coord_t rest = x; for (cursorPos = 0; cursorPos < length; cursorPos++) { @@ -276,9 +343,9 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) void TextEdit::onFocusLost() { -#if defined(SOFTWARE_KEYBOARD) - TextKeyboard::hide(); -#endif +// #if defined(SOFTWARE_KEYBOARD) +// TextKeyboard::hide(); +// #endif changeEnd(); FormField::onFocusLost(); diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 975f40e1a..e5e8d0539 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -23,17 +23,13 @@ const char extra_chars_default[] = ":;<=>"; +static lv_obj_t *kb = nullptr; + class TextEdit : public FormField { friend class TextKeyboard; public: - TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0, const char * _extra_chars = nullptr) : - FormField(parent, rect, windowFlags), - value(value), - length(length) - { - extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; - } + TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0, const char * _extra_chars = nullptr); #if defined(DEBUG_WINDOWS) std::string getName() const override diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 28ba8c027..04971ba3a 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -19,13 +19,29 @@ #include "window.h" #include "touch.h" +#include "mainwindow.h" Window * Window::focusWindow = nullptr; Window * Window::slidingWindow = nullptr; Window * Window::capturedWindow = nullptr; std::list Window::trash; -Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags): + +static void window_event_cb(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + Window *window = (Window *) lv_event_get_user_data(e); + if (code == LV_EVENT_GET_SELF_SIZE) { + lv_point_t * p = (lv_point_t *)lv_event_get_param(e); + if (p->x >= 0) { + p->x = window->getInnerWidth(); + } + if (p->y >= 0) + p->y = window->getInnerHeight(); + } +} + +Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags, bool isScreen) : parent(parent), rect(rect), innerWidth(rect.w), @@ -33,6 +49,18 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc windowFlags(windowFlags), textFlags(textFlags) { + lvobj = lv_obj_create(isScreen ? nullptr : parent != nullptr ? parent->lvobj : nullptr); + lv_obj_set_pos(lvobj, rect.x, rect.y); + lv_obj_set_size(lvobj, rect.w, rect.h); + + lv_obj_set_style_pad_all(lvobj, 0, LV_PART_MAIN); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); + lv_obj_set_style_border_width(lvobj, 0, LV_PART_MAIN); + lv_obj_set_style_radius(lvobj, 0, LV_PART_MAIN); + lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC); + lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); + if (parent) { parent->addChild(this, windowFlags & PUSH_FRONT); if (!(windowFlags & TRANSPARENT)) { @@ -44,6 +72,11 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc Window::~Window() { TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); + if (lvobj != nullptr) + { + lv_obj_del(lvobj); + lvobj = nullptr; + } if (focusWindow == this) { focusWindow = nullptr; @@ -153,6 +186,7 @@ void Window::setScrollPositionX(coord_t value) auto newScrollPosition = max(0, min(innerWidth - width(), value)); if (newScrollPosition != scrollPositionX) { scrollPositionX = newScrollPosition; + lv_obj_scroll_to_x(lvobj, scrollPositionX, LV_ANIM_OFF); invalidate(); } } @@ -167,6 +201,7 @@ void Window::setScrollPositionY(coord_t value) if (newScrollPosition != scrollPositionY) { scrollPositionY = newScrollPosition; + lv_obj_scroll_to_y(lvobj, value, LV_ANIM_OFF); invalidate(); } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 8f731010f..d8fcc5361 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -30,6 +30,7 @@ #include "libopenui_defines.h" #include "libopenui_helpers.h" #include "libopenui_config.h" +#include "../../lvgl/src/lvgl.h" typedef uint32_t WindowFlags; @@ -63,7 +64,7 @@ class Window friend class GridLayout; public: - Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); + Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, bool isScreen = false); virtual ~Window(); @@ -384,7 +385,10 @@ class Window return _deleted; } + inline lv_obj_t *getLvObj() { return lvobj; } + protected: + lv_obj_t *lvobj = nullptr; Window * parent; std::list children; rect_t rect; From bc961b7b8fd208b470de1395bde48f0f319cae8d Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 12 Jan 2022 07:51:52 -0500 Subject: [PATCH 470/714] fix changing height of a window --- radio/src/thirdparty/libopenui/src/window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 04971ba3a..1ee864219 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -529,6 +529,7 @@ coord_t Window::adjustHeight() coord_t old = rect.h; adjustInnerHeight(); rect.h = innerHeight; + lv_obj_set_style_height(getLvObj(), rect.h, LV_PART_MAIN); return rect.h - old; } From 3d2b5bf4604023329c1a245fcaa55d9e385cfacc Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Fri, 14 Jan 2022 08:44:00 +0100 Subject: [PATCH 471/714] move libopenui draw canvas to the back when switching screens --- radio/src/thirdparty/libopenui/src/layer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index 691ce3258..3e57672d5 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -50,6 +50,7 @@ class Layer // layers are screens in lvgl lv_scr_load(window->getLvObj()); lv_obj_set_parent(canvas, window->getLvObj()); + lv_obj_move_background(canvas); stack.emplace_back(Layer(window)); @@ -67,6 +68,7 @@ class Layer stack.pop_back(); const auto & back = stack.back(); lv_obj_set_parent(canvas, back.main->getLvObj()); + lv_obj_move_background(canvas); lv_scr_load(back.main->getLvObj()); // load the screen. if (back.focus) { back.focus->setFocus(SET_FOCUS_DEFAULT); From 2a85442708ef1c0607c66ca74fad84040b5bdcff Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Sat, 15 Jan 2022 07:22:46 -0500 Subject: [PATCH 472/714] added factory code, and the beginnings of styling --- radio/src/thirdparty/libopenui/src/button.cpp | 19 +++--- radio/src/thirdparty/libopenui/src/button.h | 4 +- radio/src/thirdparty/libopenui/src/form.cpp | 4 +- radio/src/thirdparty/libopenui/src/form.h | 8 +-- radio/src/thirdparty/libopenui/src/layer.h | 7 +-- .../thirdparty/libopenui/src/mainwindow.cpp | 5 ++ .../src/thirdparty/libopenui/src/mainwindow.h | 4 +- .../thirdparty/libopenui/src/modal_window.cpp | 6 +- .../src/thirdparty/libopenui/src/textedit.cpp | 61 +++++++++---------- radio/src/thirdparty/libopenui/src/window.cpp | 25 ++++++-- radio/src/thirdparty/libopenui/src/window.h | 9 ++- 11 files changed, 89 insertions(+), 63 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index f5f61f1e0..5e5efa4ea 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -77,22 +77,22 @@ void Button::checkEvents() } +LvglWidgetFactory textButtonFactory = LvglWidgetFactory( + [](lv_obj_t *parent) { + return lv_btn_create(parent); +}); + TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, std::function pressHandler, WindowFlags windowFlags, LcdFlags textFlags) : - Button(parent, rect, std::move(pressHandler), windowFlags, textFlags), + Button(parent, rect, std::move(pressHandler), windowFlags, textFlags, &textButtonFactory), text(std::move(text)) { - if (lvobj != nullptr) { - lv_obj_del(lvobj); - } - lvobj = lv_btn_create(parent->getLvObj()); - lv_obj_set_pos(lvobj, rect.x, rect.y); /*Set its position*/ - lv_obj_set_size(lvobj, rect.w, rect.h); /*Set its size*/ + lv_obj_set_pos(lvobj, rect.x, rect.y); + lv_obj_set_size(lvobj, rect.w, rect.h); setTextFlags(textFlags | COLOR_THEME_PRIMARY1); -#if 1 auto color = COLOR_VAL(COLOR_THEME_PRIMARY2); auto r = GET_RED(color), g = GET_GREEN(color), b = GET_BLUE(color); @@ -106,8 +106,7 @@ TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, lv_label_set_text(label, this->text.c_str()); lv_obj_center(label); color = COLOR_VAL(this->getTextFlags()); - lv_obj_set_style_text_color(label, lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)), LV_PART_MAIN); -#endif + // lv_obj_set_style_text_color(label, lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)), LV_PART_MAIN); } TextButton::~TextButton() diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index fa8078329..0671359fd 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -30,8 +30,8 @@ class Button: public FormField public: Button(FormGroup* parent, const rect_t& rect, std::function pressHandler = nullptr, - WindowFlags windowFlags = 0, LcdFlags textFlags = 0) : - FormField(parent, rect, windowFlags, textFlags), + WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr) : + FormField(parent, rect, windowFlags, textFlags, factory), pressHandler(std::move(pressHandler)) { } diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 6bfa3b07f..995238d31 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -21,8 +21,8 @@ #include "bitmapbuffer.h" #include "libopenui_config.h" -FormField::FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags, bool isScreen) : - Window(parent, rect, windowFlags, textFlags, isScreen) +FormField::FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags, LvglWidgetFactory *factory) : + Window(parent, rect, windowFlags, textFlags, factory) { if (!(windowFlags & NO_FOCUS)) { auto * form = dynamic_cast(parent); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 871189a5e..4fe22449a 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -30,7 +30,7 @@ constexpr WindowFlags FORM_FLAGS_LAST = FORM_NO_BORDER; class FormField: public Window { public: - FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, bool isScreen = false); + FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr); virtual void changeEnd(bool forceChanged = false) { @@ -128,7 +128,7 @@ class FormField: public Window class FormGroup: public FormField { public: - FormGroup(Window * parent, const rect_t & rect, WindowFlags windowflags = 0, bool isScreen = false) : + FormGroup(Window * parent, const rect_t & rect, WindowFlags windowflags = 0, LvglWidgetFactory *factory = nullptr) : FormField(parent, rect, windowflags) { } @@ -212,8 +212,8 @@ class FormGroup: public FormField class FormWindow: public FormGroup { public: - FormWindow(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, bool isScreen = false) : - FormGroup(parent, rect, windowFlags, isScreen) + FormWindow(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LvglWidgetFactory *factory = nullptr) : + FormGroup(parent, rect, windowFlags, factory) { } diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index 3e57672d5..e3e9caf02 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -37,10 +37,9 @@ class Layer static void push(Window * window) { - // if (window->getLvObj()->parent != nullptr) { - // TRACE("Layers must have lvgl parent to be null"); - // return; - // } + if (window->getLvObj()->parent != nullptr) { + TRACE("Layers must have lvgl parent to be null"); + } if (!stack.empty()) { Layer & parentLayer = stack.back(); diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 7d2de6336..ac717ea25 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -26,6 +26,11 @@ MainWindow * MainWindow::_instance = nullptr; +LvglWidgetFactory mainWindowFactory = LvglWidgetFactory([] (lv_obj_t *parent) { + return lv_obj_create(nullptr); +}); + + #if defined(HARDWARE_TOUCH) TouchState touchState; Keyboard * Keyboard::activeKeyboard = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index d02f9889c..2b0cdb427 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -23,12 +23,14 @@ #include "layer.h" #include "bitmapbuffer.h" +extern LvglWidgetFactory mainWindowFactory; + class MainWindow: public Window { protected: // singleton MainWindow(): - Window(nullptr, {0, 0, LCD_W, LCD_H}, 0, 0, true), + Window(nullptr, {0, 0, LCD_W, LCD_H}, 0, 0, &mainWindowFactory), invalidatedRect(rect) { Layer::push(this); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index ee8c67fe4..37c9ec9b6 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -22,8 +22,12 @@ #include "font.h" #include "layer.h" +LvglWidgetFactory modalFactory = LvglWidgetFactory([] (lv_obj_t *parent) { + return lv_obj_create(nullptr); +}); + ModalWindow::ModalWindow(Window * parent, bool closeWhenClickOutside): - Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}, 0, 0, true), + Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}, 0, 0, &modalFactory), closeWhenClickOutside(closeWhenClickOutside) { Layer::push(this); diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 74a087724..8dcf2055b 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -48,15 +48,13 @@ static void ta_event_cb(lv_event_t * e) lv_event_code_t code = lv_event_get_code(e); lv_obj_t * ta = lv_event_get_target(e); if(code == LV_EVENT_CLICKED || code == LV_EVENT_FOCUSED) { - /*Focus on the clicked text area*/ - if(kb != NULL) - lv_keyboard_set_textarea(kb, ta); - else { - /*Create a keyboard*/ + if (kb == nullptr) { kb = lv_keyboard_create(lv_scr_act()); lv_obj_set_size(kb, LV_HOR_RES, LV_VER_RES / 2); - lv_keyboard_set_textarea(kb, ta); } + + if(kb != NULL) + lv_keyboard_set_textarea(kb, ta); } else if (code == LV_EVENT_DEFOCUSED) { if (kb != nullptr) { @@ -64,47 +62,48 @@ static void ta_event_cb(lv_event_t * e) kb = nullptr; } } - else if(code == LV_EVENT_READY) { - LV_LOG_USER("Ready, current text: %s", lv_textarea_get_text(ta)); - } } + +LvglWidgetFactory textEditFactory = LvglWidgetFactory( + [] (lv_obj_t *parent) { + return lv_textarea_create(parent); + }, + [] (LvglWidgetFactory *factory) { + lv_style_set_border_width(&factory->style, 1); + lv_style_set_radius(&factory->style, 0); + lv_style_set_pad_top(&factory->style, 2); + lv_style_set_pad_bottom(&factory->style, 2); + lv_style_set_pad_left(&factory->style, 2); + lv_style_set_pad_right(&factory->style, 2); + lv_style_set_text_font(&factory->style, &lv_font_montserrat_14); + lv_style_set_text_line_space(&factory->style, 0); + + lv_style_set_bg_color(&factory->style, lv_color_make(10,10,10)); + } +); + + TextEdit::TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags, const char * _extra_chars) : - FormField(parent, rect, windowFlags), + FormField(parent, rect, windowFlags, 0, &textEditFactory), value(value), length(length) { - if (getLvObj() != nullptr) { - lv_obj_del(lvobj); - } - - lvobj = lv_textarea_create(parent->getLvObj()); lv_obj_add_event_cb(lvobj, ta_event_cb, LV_EVENT_ALL, NULL); - lv_obj_set_pos(lvobj, rect.x, rect.y); - lv_obj_set_size(lvobj, rect.w, rect.h); + lv_obj_add_style(lvobj, &textEditFactory.style, LV_PART_MAIN); + + lv_obj_set_style_bg_color(lvobj, lv_color_make(255,10,10), LV_PART_MAIN); + lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); - lv_obj_set_style_border_width(lvobj, 0, LV_PART_MAIN); - lv_obj_set_style_radius(lvobj, 0, LV_PART_MAIN); - lv_obj_set_style_pad_top(lvobj, 2, LV_PART_MAIN); - lv_obj_set_style_pad_bottom(lvobj, 2, LV_PART_MAIN); - lv_obj_set_style_pad_left(lvobj, 4, LV_PART_MAIN); - lv_obj_set_style_pad_right(lvobj, 4, LV_PART_MAIN); - - lv_textarea_set_password_mode(lvobj, false); lv_textarea_set_one_line(lvobj, true); lv_textarea_set_text(lvobj, value); lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); - lv_obj_set_style_text_font(lvobj, &lv_font_montserrat_12, LV_PART_MAIN); - lv_obj_set_style_text_line_space(lvobj, 0, LV_PART_MAIN); - - auto color = COLOR_VAL(COLOR_THEME_SECONDARY1); auto label = lv_textarea_get_label(lvobj); - lv_obj_set_style_text_color(label, lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)), LV_PART_MAIN); - + lv_obj_set_style_text_color(label, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 1ee864219..ea4cfdcb8 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -41,7 +41,18 @@ static void window_event_cb(lv_event_t * e) } } -Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags, bool isScreen) : +LvglWidgetFactory windowFactory = LvglWidgetFactory( + [] (lv_obj_t *parent) { + return lv_obj_create(parent); + }, + [] (LvglWidgetFactory *factory) { + lv_style_set_pad_all(&factory->style, 0); + lv_style_set_bg_opa(&factory->style, LV_OPA_TRANSP); + lv_style_set_border_width(&factory->style, 0); + lv_style_set_radius(&factory->style, 0); + }); + +Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags, LvglWidgetFactory *factory) : parent(parent), rect(rect), innerWidth(rect.w), @@ -49,14 +60,15 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc windowFlags(windowFlags), textFlags(textFlags) { - lvobj = lv_obj_create(isScreen ? nullptr : parent != nullptr ? parent->lvobj : nullptr); + lv_obj_t *lvParent = parent != nullptr ? parent->lvobj : nullptr; + lvobj = (factory == nullptr) ? + windowFactory.construct(lvParent) : + factory->construct(lvParent); + + lv_obj_add_style(lvobj, &windowFactory.style, LV_PART_MAIN); lv_obj_set_pos(lvobj, rect.x, rect.y); lv_obj_set_size(lvobj, rect.w, rect.h); - lv_obj_set_style_pad_all(lvobj, 0, LV_PART_MAIN); - lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); - lv_obj_set_style_border_width(lvobj, 0, LV_PART_MAIN); - lv_obj_set_style_radius(lvobj, 0, LV_PART_MAIN); lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC); lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); @@ -163,6 +175,7 @@ void Window::setFocus(uint8_t flag, Window * from) TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); if (focusWindow != this) { + // lv_obj_add_state(lvobj, LV_STATE_FOCUSED); // scroll before calling focusHandler so that the window can adjust the scroll position if needed Window * parent = this->parent; while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index d8fcc5361..fe5747bc3 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -30,7 +30,7 @@ #include "libopenui_defines.h" #include "libopenui_helpers.h" #include "libopenui_config.h" -#include "../../lvgl/src/lvgl.h" +#include "LvglWrapper.h" typedef uint32_t WindowFlags; @@ -64,7 +64,7 @@ class Window friend class GridLayout; public: - Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, bool isScreen = false); + Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr); virtual ~Window(); @@ -133,6 +133,11 @@ class Window void setTextFlags(LcdFlags flags) { textFlags = flags; + + // lv integration for colors + auto textColor = COLOR_VAL(flags); + auto r = GET_RED(textColor), g = GET_GREEN(textColor), b = GET_BLUE(textColor); + lv_obj_set_style_text_color(getLvObj(), lv_color_make(r, g, b), LV_PART_MAIN); } void setCloseHandler(std::function handler) From 56968f42fa830e67596324f3df6dd32c3ada2ad8 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Tue, 18 Jan 2022 08:40:34 -0500 Subject: [PATCH 473/714] more changes for lvgl sync. TextEdit is much better on coloring and eventing --- radio/src/thirdparty/libopenui/src/window.cpp | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index ea4cfdcb8..7cab154db 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -39,6 +39,20 @@ static void window_event_cb(lv_event_t * e) if (p->y >= 0) p->y = window->getInnerHeight(); } + + lv_obj_t *currentTarget = lv_event_get_current_target(e); + + // if the event bubbled to the topmost window then hand it to libopenui MainWindow. + if (MainWindow::isMainWindowCreated()) { + if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED || code == LV_EVENT_KEY || + code == LV_EVENT_SCROLL_BEGIN || code == LV_EVENT_SCROLL_END || code == LV_EVENT_SCROLL) { + + // if we have bubbled to the top then do legacy processing + if (lv_obj_get_parent(currentTarget) == nullptr) { + MainWindow::instance()->checkEvents(); + } + } + } } LvglWidgetFactory windowFactory = LvglWidgetFactory( @@ -65,11 +79,14 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc windowFactory.construct(lvParent) : factory->construct(lvParent); + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_EVENT_BUBBLE); + lv_obj_add_style(lvobj, &windowFactory.style, LV_PART_MAIN); lv_obj_set_pos(lvobj, rect.x, rect.y); lv_obj_set_size(lvobj, rect.w, rect.h); lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_MOMENTUM); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC); lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); @@ -175,7 +192,12 @@ void Window::setFocus(uint8_t flag, Window * from) TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); if (focusWindow != this) { - // lv_obj_add_state(lvobj, LV_STATE_FOCUSED); + // synchronize lvgl focused state with libopenui + lv_obj_add_state(lvobj, LV_STATE_FOCUSED); + if (focusWindow != nullptr) { + lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED); + } + // scroll before calling focusHandler so that the window can adjust the scroll position if needed Window * parent = this->parent; while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { From 78f6adb6ea36fa5b2c201335b078b888625a07fc Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Tue, 18 Jan 2022 08:40:52 -0500 Subject: [PATCH 474/714] more files that were left out --- .../thirdparty/libopenui/src/mainwindow.cpp | 5 +++- .../src/thirdparty/libopenui/src/mainwindow.h | 5 ++++ .../src/thirdparty/libopenui/src/textedit.cpp | 25 +++++++++++-------- radio/src/thirdparty/libopenui/src/textedit.h | 2 -- radio/src/thirdparty/libopenui/src/window.h | 6 ++++- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index ac717ea25..8300bc18f 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -229,7 +229,10 @@ void MainWindow::run(bool trash) { auto start = ticksNow(); - checkEvents(); + // KLK: removed for now. This is now + // called from lvgl event processing when + // necessary + // checkEvents(); if (trash) { emptyTrash(); diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 2b0cdb427..0cd3ec1b7 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -42,6 +42,11 @@ class MainWindow: public Window children.clear(); } + static bool isMainWindowCreated() + { + return _instance != nullptr; + } + static MainWindow * instance() { if (!_instance) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 8dcf2055b..b69f6619b 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -43,6 +43,8 @@ #endif +static lv_obj_t *kb = nullptr; + static void ta_event_cb(lv_event_t * e) { lv_event_code_t code = lv_event_get_code(e); @@ -71,15 +73,14 @@ LvglWidgetFactory textEditFactory = LvglWidgetFactory( }, [] (LvglWidgetFactory *factory) { lv_style_set_border_width(&factory->style, 1); + lv_style_set_border_color(&factory->style, makeLvColor(COLOR_THEME_SECONDARY2)); lv_style_set_radius(&factory->style, 0); - lv_style_set_pad_top(&factory->style, 2); - lv_style_set_pad_bottom(&factory->style, 2); - lv_style_set_pad_left(&factory->style, 2); - lv_style_set_pad_right(&factory->style, 2); - lv_style_set_text_font(&factory->style, &lv_font_montserrat_14); - lv_style_set_text_line_space(&factory->style, 0); - - lv_style_set_bg_color(&factory->style, lv_color_make(10,10,10)); + lv_style_set_pad_top(&factory->style, 0); + lv_style_set_pad_bottom(&factory->style, 0); + lv_style_set_pad_left(&factory->style, 0); + lv_style_set_pad_right(&factory->style, 0); + lv_style_set_bg_color(&factory->style, makeLvColor(COLOR_THEME_PRIMARY2)); + lv_style_set_bg_opa(&factory->style, LV_OPA_100); } ); @@ -93,8 +94,11 @@ TextEdit::TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t l lv_obj_add_event_cb(lvobj, ta_event_cb, LV_EVENT_ALL, NULL); lv_obj_add_style(lvobj, &textEditFactory.style, LV_PART_MAIN); - lv_obj_set_style_bg_color(lvobj, lv_color_make(255,10,10), LV_PART_MAIN); - + // this is for the focus state. the master style takes care of the normal background + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), + LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_textarea_set_password_mode(lvobj, false); lv_textarea_set_one_line(lvobj, true); @@ -104,6 +108,7 @@ TextEdit::TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t l auto label = lv_textarea_get_label(lvobj); lv_obj_set_style_text_color(label, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); + lv_obj_set_style_text_font(label, &lv_font_montserrat_12, LV_PART_MAIN); extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index e5e8d0539..779c811f1 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -23,8 +23,6 @@ const char extra_chars_default[] = ":;<=>"; -static lv_obj_t *kb = nullptr; - class TextEdit : public FormField { friend class TextKeyboard; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index fe5747bc3..da8e3f6d5 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -137,7 +137,11 @@ class Window // lv integration for colors auto textColor = COLOR_VAL(flags); auto r = GET_RED(textColor), g = GET_GREEN(textColor), b = GET_BLUE(textColor); - lv_obj_set_style_text_color(getLvObj(), lv_color_make(r, g, b), LV_PART_MAIN); + lv_obj_set_style_text_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); + for (auto i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { + auto child = lv_obj_get_child(lvobj, i); + lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN); + } } void setCloseHandler(std::function handler) From 79a2c3e28a5c121be336e254dbcba3e262b8b3f0 Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Wed, 19 Jan 2022 21:01:38 +0100 Subject: [PATCH 475/714] fix warning --- radio/src/thirdparty/libopenui/src/window.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index da8e3f6d5..1a6868aa1 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -138,7 +138,7 @@ class Window auto textColor = COLOR_VAL(flags); auto r = GET_RED(textColor), g = GET_GREEN(textColor), b = GET_BLUE(textColor); lv_obj_set_style_text_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); - for (auto i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { + for (uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { auto child = lv_obj_get_child(lvobj, i); lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN); } From e575d122a16cb6ab2da76bcb769ddccc87486a33 Mon Sep 17 00:00:00 2001 From: Malte Langermann Date: Wed, 19 Jan 2022 21:11:51 +0100 Subject: [PATCH 476/714] use the lvgl canvas buffer to directly draw on --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 8300bc18f..f8e211c15 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -193,15 +193,13 @@ void MainWindow::invalidate(const rect_t & rect) } #include "lvgl/lvgl.h" extern lv_obj_t * canvas; -extern BitmapBuffer lcdBackup; +extern BitmapBuffer canBuf; bool MainWindow::refresh() { if (invalidatedRect.w) { - lcdBackup.setOffset(0, 0); - lcdBackup.setClippingRect(0,LCD_W,0, LCD_H); - fullPaint(&lcdBackup); - lv_canvas_copy_buf(canvas, lcdBackup.getData(), 0, 0, LCD_W, LCD_H); - lv_img_cache_invalidate_src(lcdBackup.getData()); + canBuf.setOffset(0, 0); + canBuf.setClippingRect(0,LCD_W,0, LCD_H); + fullPaint(&canBuf); lv_obj_invalidate(canvas); } return false; From ee1cfdf9872a4959ebdb5309441f847af6ebd253 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Thu, 20 Jan 2022 19:34:07 +0100 Subject: [PATCH 477/714] fix some touch issues update min screen on input changes --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index f8e211c15..3647a403f 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -77,11 +77,15 @@ void MainWindow::setTouchEnabled(bool enable) } #endif +#if defined(HARDWARE_TOUCH) +bool getTouchOccured(); +#endif void MainWindow::checkEvents() { #if defined(HARDWARE_TOUCH) - if (touchPanelEventOccured()) { +// if (touchPanelEventOccured()) { + if (getTouchOccured()) { short lastDeltaX = touchState.lastDeltaX; short lastDeltaY = touchState.lastDeltaY;; TouchState newTouchState = touchPanelRead(); @@ -230,7 +234,7 @@ void MainWindow::run(bool trash) // KLK: removed for now. This is now // called from lvgl event processing when // necessary - // checkEvents(); + checkEvents(); if (trash) { emptyTrash(); From af575f20f1a849388b4f8bd6ec052add7b6fde61 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 23 Jan 2022 09:25:06 +0100 Subject: [PATCH 478/714] fix some lvgl lifetime management issues --- radio/src/thirdparty/libopenui/src/button.cpp | 2 + radio/src/thirdparty/libopenui/src/button.h | 1 - radio/src/thirdparty/libopenui/src/window.cpp | 46 ++++++++++++++----- radio/src/thirdparty/libopenui/src/window.h | 1 + 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 5e5efa4ea..245189be3 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -76,6 +76,8 @@ void Button::checkEvents() checkHandler(); } +lv_style_t style_btn; + LvglWidgetFactory textButtonFactory = LvglWidgetFactory( [](lv_obj_t *parent) { diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 0671359fd..286068959 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -124,7 +124,6 @@ class TextButton: public Button protected: lv_obj_t * label = nullptr; - lv_style_t style_btn; std::string text; std::function bgColorHandler = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7cab154db..f60bf072d 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -84,6 +84,7 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc lv_obj_add_style(lvobj, &windowFactory.style, LV_PART_MAIN); lv_obj_set_pos(lvobj, rect.x, rect.y); lv_obj_set_size(lvobj, rect.w, rect.h); + lv_obj_set_user_data(lvobj, this); lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_MOMENTUM); @@ -97,28 +98,34 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc } } } - +extern lv_obj_t * canvas; Window::~Window() { TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); - if (lvobj != nullptr) + if (focusWindow == this) { + focusWindow = nullptr; + } + + if(children.size() > 0) + deleteChildren(); + if (lvobj != nullptr && lvobj != canvas) { lv_obj_del(lvobj); lvobj = nullptr; } - if (focusWindow == this) { - focusWindow = nullptr; - } - - deleteChildren(); } void Window::attach(Window * newParent) { if (parent) detach(); parent = newParent; - if (newParent) newParent->addChild(this); + if (newParent) + { + newParent->addChild(this); + if(lvobj != nullptr) + lv_obj_set_parent(lvobj, newParent->lvobj); + } } void Window::detach() @@ -126,6 +133,8 @@ void Window::detach() if (parent) { parent->removeChild(this); parent = nullptr; + if(lvobj != nullptr) + lv_obj_set_parent(lvobj, nullptr); } } @@ -147,15 +156,15 @@ void Window::deleteLater(bool detach, bool trash) else parent = nullptr; + if (trash) { + Window::trash.push_back(this); + } + deleteChildren(); if (closeHandler) { closeHandler(); } - - if (trash) { - Window::trash.push_back(this); - } } void Window::clear() @@ -168,9 +177,20 @@ void Window::clear() invalidate(); } +void Window::clearLvgl() +{ + for (auto window: children) + { + if(lvobj != nullptr && window->lvobj != nullptr && window->lvobj->parent == lvobj) + window->clearLvgl(); + } + lvobj = nullptr; +} + void Window::deleteChildren() { for (auto window: children) { + window->clearLvgl(); window->deleteLater(false); } children.clear(); @@ -300,6 +320,8 @@ bool Window::hasOpaqueRect(const rect_t & testRect) const void Window::fullPaint(BitmapBuffer * dc) { + if(lvobj != nullptr) + lv_obj_invalidate(lvobj); bool paintNeeded = true; std::list::iterator firstChild; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 1a6868aa1..bbeeb9b25 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -162,6 +162,7 @@ class Window virtual void deleteLater(bool detach = true, bool trash = true); void clear(); + void clearLvgl(); void deleteChildren(); From b71cfcd8ebe823aadc2a3cdcfba4ec99b7e9373e Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Sun, 23 Jan 2022 06:42:41 -0500 Subject: [PATCH 479/714] - fix window position functions to call lvgl --- radio/src/thirdparty/libopenui/src/window.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index bbeeb9b25..925632393 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -187,12 +187,16 @@ class Window void setRect(rect_t value) { rect = value; + lv_obj_set_pos(lvobj, rect.x, rect.y); + lv_obj_set_width(lvobj, rect.w); + lv_obj_set_height(lvobj, rect.h); invalidate(); } void setWidth(coord_t value) { rect.w = value; + lv_obj_set_width(lvobj, rect.w); invalidate(); } @@ -200,11 +204,14 @@ class Window { rect.x = (parent->width() - width()) / 2; rect.y = (parent->height() - height()) / 2; + lv_obj_set_pos(lvobj, rect.x, rect.y); } void setHeight(coord_t value) { rect.h = value; + lv_obj_set_height(lvobj, rect.h); + if (windowFlags & FORWARD_SCROLL) innerHeight = value; else if (innerHeight <= value) { @@ -216,12 +223,14 @@ class Window void setLeft(coord_t x) { rect.x = x; + lv_obj_set_pos(lvobj, rect.x, rect.y); invalidate(); } void setTop(coord_t y) { rect.y = y; + lv_obj_set_pos(lvobj, rect.x, rect.y); invalidate(); } From 0328ab4a5a143e87cdbcd65bb16d8dff1476f47c Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 23 Jan 2022 12:50:47 +0100 Subject: [PATCH 480/714] clean LVGL objects on Window::clear() --- radio/src/thirdparty/libopenui/src/window.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index f60bf072d..5fc905d31 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -174,6 +174,8 @@ void Window::clear() innerWidth = rect.w; innerHeight = rect.h; deleteChildren(); + if(lvobj != nullptr) + lv_obj_clean(lvobj); invalidate(); } From 014f7026afa16cc514ddd4ea7d30f9015f25086e Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Sun, 23 Jan 2022 08:42:16 -0500 Subject: [PATCH 481/714] make buttons do the styling better. I still cant get text color right on focused items. --- radio/src/thirdparty/libopenui/src/button.cpp | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 245189be3..a48989ca1 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -27,7 +27,13 @@ void Button::onPress() bool check = (pressHandler && pressHandler()); if (check != bool(windowFlags & BUTTON_CHECKED)) { windowFlags ^= BUTTON_CHECKED; - invalidate(); + + if (! (windowFlags & BUTTON_CHECKED)) + lv_obj_clear_state(lvobj, LV_STATE_CHECKED); + else + lv_obj_add_state(lvobj, LV_STATE_CHECKED); + + // invalidate(); } } @@ -82,7 +88,9 @@ lv_style_t style_btn; LvglWidgetFactory textButtonFactory = LvglWidgetFactory( [](lv_obj_t *parent) { return lv_btn_create(parent); -}); + }, + [] (LvglWidgetFactory *factory) { + }); TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, std::function pressHandler, @@ -91,24 +99,32 @@ TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, Button(parent, rect, std::move(pressHandler), windowFlags, textFlags, &textButtonFactory), text(std::move(text)) { - lv_obj_set_pos(lvobj, rect.x, rect.y); - lv_obj_set_size(lvobj, rect.w, rect.h); setTextFlags(textFlags | COLOR_THEME_PRIMARY1); - - auto color = COLOR_VAL(COLOR_THEME_PRIMARY2); - auto r = GET_RED(color), g = GET_GREEN(color), b = GET_BLUE(color); + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CHECKABLE); + if (windowFlags & BUTTON_CHECKED) + lv_obj_add_state(lvobj, LV_STATE_CHECKED); lv_style_init(&style_btn); lv_style_set_radius(&style_btn, 0); - lv_obj_set_style_bg_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_ACTIVE), LV_PART_MAIN | LV_STATE_CHECKED); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_ACTIVE), LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); + lv_obj_set_style_border_width(lvobj, 0, LV_PART_MAIN); + + lv_obj_set_style_border_width(lvobj, 1, LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); + lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); + + lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); lv_obj_add_style(lvobj, &style_btn,0); label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); lv_obj_center(label); - color = COLOR_VAL(this->getTextFlags()); - // lv_obj_set_style_text_color(label, lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)), LV_PART_MAIN); + lv_obj_set_style_text_color(label, makeLvColor(this->getTextFlags()), LV_PART_MAIN); + lv_obj_set_style_text_color(label, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); } TextButton::~TextButton() From ba6f3dd9a23ae2f6ae7f44fa235f27394451f704 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 23 Jan 2022 15:54:50 +0100 Subject: [PATCH 482/714] Use LVGL scrolling events --- .../thirdparty/libopenui/src/mainwindow.cpp | 38 --------- radio/src/thirdparty/libopenui/src/window.cpp | 78 ++++++++++--------- 2 files changed, 43 insertions(+), 73 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 3647a403f..e5d5b4d92 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -139,44 +139,6 @@ void MainWindow::checkEvents() touchState.event = TE_NONE; onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); } - else if (touchState.event == TE_SLIDE) { - if (touchState.deltaX || touchState.deltaY) { - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.deltaX, touchState.deltaY); - touchState.lastDeltaX = touchState.deltaX; - touchState.lastDeltaY = touchState.deltaY; - touchState.deltaX = 0; - touchState.deltaY = 0; - } - } - else if (touchState.event == TE_SLIDE_END && slidingWindow) { - if (touchState.lastDeltaX > SLIDE_SPEED_REDUCTION) - touchState.lastDeltaX -= SLIDE_SPEED_REDUCTION; - else if (touchState.lastDeltaX < -SLIDE_SPEED_REDUCTION) - touchState.lastDeltaX += SLIDE_SPEED_REDUCTION; - else - touchState.lastDeltaX = 0; - if (touchState.lastDeltaY > SLIDE_SPEED_REDUCTION) - touchState.lastDeltaY -= SLIDE_SPEED_REDUCTION; - else if (touchState.lastDeltaY < -SLIDE_SPEED_REDUCTION) - touchState.lastDeltaY += SLIDE_SPEED_REDUCTION; - else - touchState.lastDeltaY = 0; - - Window::capturedWindow = nullptr; - - onTouchSlide(touchState.x, touchState.y, touchState.startX, touchState.startY, touchState.lastDeltaX, touchState.lastDeltaY); - if(touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) - { - touchState.event = TE_NONE; - slidingWindow = nullptr; - } - } else if (touchState.event == TE_SLIDE_END && slidingWindow == nullptr) { - onTouchEnd(touchState.x + scrollPositionX, touchState.y + scrollPositionY); - touchState.event = TE_NONE; - } - if(touchState.event != TE_SLIDE_END) { - touchState.event = TE_NONE; - } #endif Window::checkEvents(); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 5fc905d31..d44912588 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -26,7 +26,6 @@ Window * Window::slidingWindow = nullptr; Window * Window::capturedWindow = nullptr; std::list Window::trash; - static void window_event_cb(lv_event_t * e) { lv_event_code_t code = lv_event_get_code(e); @@ -38,19 +37,43 @@ static void window_event_cb(lv_event_t * e) } if (p->y >= 0) p->y = window->getInnerHeight(); + + return; } lv_obj_t *currentTarget = lv_event_get_current_target(e); // if the event bubbled to the topmost window then hand it to libopenui MainWindow. - if (MainWindow::isMainWindowCreated()) { - if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED || code == LV_EVENT_KEY || - code == LV_EVENT_SCROLL_BEGIN || code == LV_EVENT_SCROLL_END || code == LV_EVENT_SCROLL) { - - // if we have bubbled to the top then do legacy processing - if (lv_obj_get_parent(currentTarget) == nullptr) { - MainWindow::instance()->checkEvents(); + if (MainWindow::isMainWindowCreated() + && (lv_obj_get_parent(currentTarget) == nullptr)) { + + switch(code) { + case LV_EVENT_PRESSED: + case LV_EVENT_RELEASED: + case LV_EVENT_KEY: + // do legacy processing + MainWindow::instance()->checkEvents(); + break; + case LV_EVENT_SCROLL: { + lv_obj_t* target = lv_event_get_target(e); + lv_coord_t scroll_y = lv_obj_get_scroll_y(target); + lv_coord_t scroll_x = lv_obj_get_scroll_x(target); + TRACE("SCROLL[x=%d;y=%d]", scroll_x, scroll_y); + Window* w = (Window*)lv_obj_get_user_data(target); + if (w) { +#if defined(DEBUG_WINDOWS) + TRACE("%s", w->getWindowDebugString().c_str()); +#endif + w->setScrollPositionY(scroll_y); + w->setScrollPositionX(scroll_x); } + } break; + case LV_EVENT_SCROLL_BEGIN: + TRACE("SCROLL_BEGIN"); + break; + case LV_EVENT_SCROLL_END: + TRACE("SCROLL_END"); + break; } } } @@ -87,7 +110,6 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc lv_obj_set_user_data(lvobj, this); lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); - lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_MOMENTUM); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC); lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); @@ -243,7 +265,6 @@ void Window::setScrollPositionX(coord_t value) auto newScrollPosition = max(0, min(innerWidth - width(), value)); if (newScrollPosition != scrollPositionX) { scrollPositionX = newScrollPosition; - lv_obj_scroll_to_x(lvobj, scrollPositionX, LV_ANIM_OFF); invalidate(); } } @@ -258,7 +279,6 @@ void Window::setScrollPositionY(coord_t value) if (newScrollPosition != scrollPositionY) { scrollPositionY = newScrollPosition; - lv_obj_scroll_to_y(lvobj, value, LV_ANIM_OFF); invalidate(); } } @@ -288,17 +308,22 @@ void Window::scrollTo(Window * child) void Window::scrollTo(const rect_t & rect) { if (rect.top() < scrollPositionY) { - setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.top() - 5); - } - else if (rect.bottom() > scrollPositionY + height() - 5) { - setScrollPositionY(pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.bottom() - height() + 5); + auto y = + pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.top(); + lv_obj_scroll_to_y(lvobj, y, LV_ANIM_OFF); + } else if (rect.bottom() > scrollPositionY + height()) { + auto y = pageHeight ? rect.top() - (rect.top() % pageHeight) + : rect.bottom() - height(); + lv_obj_scroll_to_y(lvobj, y, LV_ANIM_OFF); } if (rect.left() < scrollPositionX) { - setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.left() - 5); + auto x = pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.left(); + lv_obj_scroll_to_x(lvobj, x, LV_ANIM_OFF); } - else if (rect.right() > scrollPositionX + width() - 5) { - setScrollPositionX(pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.right() - width() + 5); + else if (rect.right() > scrollPositionX + width()) { + auto x = pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.right() - width(); + lv_obj_scroll_to_x(lvobj, x, LV_ANIM_OFF); } } @@ -472,23 +497,6 @@ void Window::checkEvents() if (windowFlags & REFRESH_ALWAYS) { invalidate(); } - -#if defined(HARDWARE_TOUCH) - if (touchState.event != TE_SLIDE && touchState.lastDeltaX == 0 && touchState.lastDeltaY == 0) { - if (pageWidth) { - coord_t relativeScrollPosition = getScrollPositionX() % pageWidth; - if (relativeScrollPosition) { - setScrollPositionX(getScrollPositionX() + getSnapStep(relativeScrollPosition, pageWidth)); - } - } - if (pageHeight) { - coord_t relativeScrollPosition = getScrollPositionY() % pageHeight; - if (relativeScrollPosition) { - setScrollPositionY(getScrollPositionY() + getSnapStep(relativeScrollPosition, pageHeight)); - } - } - } -#endif } void Window::onEvent(event_t event) From 49ba2e46b2c2ff26b38d3a32b5ea74af0368e8be Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 Jan 2022 07:09:19 +0100 Subject: [PATCH 483/714] Some more fixes related to scrolling --- radio/src/thirdparty/libopenui/src/window.cpp | 16 ---------------- radio/src/thirdparty/libopenui/src/window.h | 6 +++--- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index d44912588..0176c427c 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -562,22 +562,6 @@ bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, } } - if (slidingWindow && slidingWindow != this) { - return false; - } - - if (slideY && innerHeight > rect.h) { - setScrollPositionY(scrollPositionY - slideY); - slidingWindow = this; - return true; - } - - if (slideX && innerWidth > rect.w) { - setScrollPositionX(scrollPositionX - slideX); - slidingWindow = this; - return true; - } - return false; } #endif diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 925632393..a63158bb4 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -215,7 +215,7 @@ class Window if (windowFlags & FORWARD_SCROLL) innerHeight = value; else if (innerHeight <= value) { - setScrollPositionY(0); + lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); } invalidate(); } @@ -325,12 +325,12 @@ class Window parent->adjustInnerHeight(); } else if (height() >= h) { - setScrollPositionY(0); + lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); } else { coord_t maxScrollPosition = h - height(); if (scrollPositionY > maxScrollPosition) { - setScrollPositionY(maxScrollPosition); + lv_obj_scroll_to_y(lvobj, maxScrollPosition, LV_ANIM_OFF); } } invalidate(); From a079d3987cad73093420f9960508540d4061f8a2 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 Jan 2022 07:44:09 +0100 Subject: [PATCH 484/714] LVGL sliders --- radio/src/thirdparty/libopenui/src/slider.cpp | 108 +++++++++++------- radio/src/thirdparty/libopenui/src/slider.h | 57 ++++----- 2 files changed, 85 insertions(+), 80 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index 610af4639..e3c60d196 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -20,9 +20,71 @@ #include "slider.h" #include "theme.h" +#include "LvglWrapper.h" + +static void slider_changed_cb(lv_event_t * e) +{ + lv_obj_t * target = lv_event_get_target(e); + if (!lv_slider_is_dragged(target)) { + Slider* sl = (Slider*)lv_obj_get_user_data(target); + sl->setValue(lv_slider_get_value(target)); + } +} + +static lv_style_t style_main; +static lv_style_t style_ind; +static lv_style_t style_knob; +static lv_style_t style_focused; + +static LvglWidgetFactory sliderFactory = { lv_slider_create, nullptr }; + +constexpr coord_t SLIDER_PADDING = 6; + +Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, + std::function getValue, + std::function setValue) : + FormField(parent, + {rect.x + SLIDER_PADDING, rect.y + SLIDER_PADDING, + rect.w - 2 * SLIDER_PADDING, rect.h - 2 * SLIDER_PADDING}, + 0, 0, &sliderFactory), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + _setValue(std::move(setValue)) +{ + lv_obj_add_event_cb(lvobj, slider_changed_cb, LV_EVENT_VALUE_CHANGED, NULL); + lv_slider_set_range(lvobj, vmin, vmax); + lv_slider_set_value(lvobj, getValue(), LV_ANIM_OFF); + + // LV_PART_MAIN + lv_style_init(&style_main); + lv_style_set_bg_opa(&style_main, LV_OPA_100); + lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1)); + lv_style_set_radius(&style_main, LV_RADIUS_CIRCLE); + lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN); + + // LV_PART_INDICATOR + lv_style_init(&style_ind); + lv_style_set_bg_opa(&style_ind, LV_OPA_100); + lv_style_set_bg_color(&style_ind, makeLvColor(COLOR_THEME_SECONDARY1)); + lv_obj_add_style(lvobj, &style_ind, LV_PART_INDICATOR); + + // Knob + lv_style_init(&style_knob); + lv_style_set_bg_color(&style_knob, makeLvColor(COLOR_THEME_PRIMARY2)); + lv_style_set_border_color(&style_knob, makeLvColor(COLOR_THEME_SECONDARY1)); + lv_style_set_border_width(&style_knob, 2); + lv_obj_add_style(lvobj, &style_knob, LV_PART_KNOB); + + // LV_STATE_FOCUSED + lv_style_init(&style_focused); + lv_style_set_bg_color(&style_focused, makeLvColor(COLOR_THEME_FOCUS)); + lv_obj_add_style(lvobj, &style_focused, LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_add_style(lvobj, &style_focused, LV_PART_INDICATOR | LV_STATE_FOCUSED); +} + void Slider::paint(BitmapBuffer * dc) { - theme->drawSlider(dc, vmin, vmax, getValue(), rect, editMode, hasFocus()); } int Slider::value(coord_t x) const @@ -38,11 +100,13 @@ void Slider::onEvent(event_t event) if (editMode) { if (event == EVT_ROTARY_RIGHT) { setValue(getValue() + ROTARY_ENCODER_SPEED()); + lv_slider_set_value(lvobj, getValue(), LV_ANIM_OFF); onKeyPress(); return; } else if (event == EVT_ROTARY_LEFT) { setValue(getValue() - ROTARY_ENCODER_SPEED()); + lv_slider_set_value(lvobj, getValue(), LV_ANIM_OFF); onKeyPress(); return; } else if (event == EVT_KEY_FIRST(KEY_EXIT)) @@ -52,45 +116,3 @@ void Slider::onEvent(event_t event) FormField::onEvent(event); } #endif - -#if defined(HARDWARE_TOUCH) -bool Slider::onTouchStart(coord_t x, coord_t y) -{ - if(!enabled) - return true; - - if (!hasFocus()) { - onKeyPress(); - setFocus(SET_FOCUS_DEFAULT); - } - sliding = (value(x) == getValue()); - return true; -} - -bool Slider::onTouchEnd(coord_t x, coord_t y) -{ - if(!enabled) - return true; - - setValue(value(x)); - invalidate(); - onKeyPress(); - return true; -} - -bool Slider::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) -{ - if(!enabled) - return true; - - if (hasFocus() && sliding) { - int newValue = value(x); - if (getValue() != newValue) { - setValue(newValue); - invalidate(); - onKeyPress(); - } - } - return true; -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index c144ec4bc..fb26e551e 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -21,50 +21,33 @@ #include "form.h" -class Slider: public FormField { - public: - Slider(Window * parent, const rect_t & rect, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue): - FormField(parent, rect), - vmin(vmin), - vmax(vmax), - getValue(std::move(getValue)), - _setValue(std::move(setValue)) - { - } +class Slider : public FormField +{ + public: + Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, + std::function getValue, std::function setValue); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "Slider"; - } + std::string getName() const override { return "Slider"; } #endif - void setValue(int value) - { - _setValue(limit(vmin, value, vmax)); - invalidate(); - } + void setValue(int value) + { + _setValue(limit(vmin, value, vmax)); + invalidate(); + } - void paint(BitmapBuffer * dc) override; + void paint(BitmapBuffer* dc) override; #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; + void onEvent(event_t event) override; #endif -#if defined(HARDWARE_TOUCH) - bool onTouchStart(coord_t x, coord_t y) override; - - bool onTouchEnd(coord_t x, coord_t y) override; - - bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; -#endif - - protected: - int value(coord_t x) const; - int vmin; - int vmax; - bool sliding = false; - std::function getValue; - std::function _setValue; + protected: + int value(coord_t x) const; + int vmin; + int vmax; + bool sliding = false; + std::function getValue; + std::function _setValue; }; - From 56a36968b79c9b7c9d39dd3a3b0159043b28208b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 Jan 2022 08:18:05 +0100 Subject: [PATCH 485/714] Fix propagating focus from LVGL to libopenui --- radio/src/thirdparty/libopenui/src/window.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0176c427c..e65863097 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -74,6 +74,15 @@ static void window_event_cb(lv_event_t * e) case LV_EVENT_SCROLL_END: TRACE("SCROLL_END"); break; + case LV_EVENT_FOCUSED: { + lv_obj_t* target = lv_event_get_target(e); + bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); + bool loiu_focused = ((Window*)target->user_data)->hasFocus(); + TRACE("FOCUSED[%d|%d]", lvgl_focused, loiu_focused); + if (!loiu_focused) { + ((Window*)target->user_data)->setFocus(); + } + } break; } } } @@ -237,9 +246,11 @@ void Window::setFocus(uint8_t flag, Window * from) if (focusWindow != this) { // synchronize lvgl focused state with libopenui - lv_obj_add_state(lvobj, LV_STATE_FOCUSED); - if (focusWindow != nullptr) { - lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED); + if (!lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) { + lv_obj_add_state(lvobj, LV_STATE_FOCUSED); + if (focusWindow != nullptr) { + lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED); + } } // scroll before calling focusHandler so that the window can adjust the scroll position if needed From 197d177a6c6b32e9d577b57b134d1f1ee17817c5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 Jan 2022 09:53:31 +0100 Subject: [PATCH 486/714] Add slider edit style --- radio/src/thirdparty/libopenui/src/form.h | 5 +++++ radio/src/thirdparty/libopenui/src/slider.cpp | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 4fe22449a..0ece8f5e2 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -77,6 +77,11 @@ class FormField: public Window virtual void setEditMode(bool newEditMode) { editMode = newEditMode; + if (editMode) { + lv_obj_add_state(lvobj, LV_STATE_EDITED | LV_STATE_FOCUSED); + } else { + lv_obj_clear_state(lvobj, LV_STATE_EDITED); + } invalidate(); } diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index e3c60d196..403537cca 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -35,6 +35,7 @@ static lv_style_t style_main; static lv_style_t style_ind; static lv_style_t style_knob; static lv_style_t style_focused; +static lv_style_t style_edit; static LvglWidgetFactory sliderFactory = { lv_slider_create, nullptr }; @@ -79,8 +80,16 @@ Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, // LV_STATE_FOCUSED lv_style_init(&style_focused); lv_style_set_bg_color(&style_focused, makeLvColor(COLOR_THEME_FOCUS)); + lv_style_set_outline_width(&style_focused, 0); + lv_obj_add_style(lvobj, &style_focused, LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_add_style(lvobj, &style_focused, LV_PART_MAIN | LV_STATE_FOCUSED | LV_STATE_EDITED); lv_obj_add_style(lvobj, &style_focused, LV_PART_INDICATOR | LV_STATE_FOCUSED); + lv_obj_add_style(lvobj, &style_focused, LV_PART_INDICATOR | LV_STATE_FOCUSED | LV_STATE_EDITED); + + lv_style_init(&style_edit); + lv_style_set_bg_color(&style_edit, makeLvColor(COLOR_THEME_EDIT)); + lv_obj_add_style(lvobj, &style_edit, LV_PART_KNOB | LV_STATE_FOCUSED | LV_STATE_EDITED); } void Slider::paint(BitmapBuffer * dc) From b788bdb84221ab9079f83cc4292c38f19f9940ae Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 Jan 2022 13:47:03 +0100 Subject: [PATCH 487/714] Use touch events only from LVGL --- .../thirdparty/libopenui/src/mainwindow.cpp | 67 --------- .../src/thirdparty/libopenui/src/mainwindow.h | 2 - radio/src/thirdparty/libopenui/src/window.cpp | 132 ++++++++++++------ radio/src/thirdparty/libopenui/src/window.h | 15 +- 4 files changed, 102 insertions(+), 114 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index e5d5b4d92..7ebb47f14 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -77,73 +77,6 @@ void MainWindow::setTouchEnabled(bool enable) } #endif -#if defined(HARDWARE_TOUCH) -bool getTouchOccured(); -#endif -void MainWindow::checkEvents() -{ -#if defined(HARDWARE_TOUCH) - -// if (touchPanelEventOccured()) { - if (getTouchOccured()) { - short lastDeltaX = touchState.lastDeltaX; - short lastDeltaY = touchState.lastDeltaY;; - TouchState newTouchState = touchPanelRead(); - if(touchState.event != TE_SLIDE_END || newTouchState.event != TE_NONE) - touchState = newTouchState; - touchState.lastDeltaX = lastDeltaX; - touchState.lastDeltaY = lastDeltaY; - } - - TouchEnableState currentState = touchEnableState; - - switch(touchState.event) - { - case TE_DOWN: - case TE_SLIDE: - if(touchEnableState == TouchOn) - touchEnableState = TouchOn_Touching; - else if (touchEnableState == TouchOff) - touchEnableState = TouchOff_Touching; - break; - case TE_UP: - case TE_SLIDE_END: - if(touchEnableState == TouchOn_Touching) - touchEnableState = TouchOn; - else if (touchEnableState == TouchOff_Touching) - touchEnableState = TouchOff; - else if(touchEnableState == TouchOnToOff_Touching) - touchEnableState = TouchOff; - else if(touchEnableState == TouchOffToOn_Touching) - touchEnableState = TouchOn; - break; - default: - break; - } - - if(touchState.event != TE_NONE) - _touchEventOccured = true; - - if ( currentState == TouchOff - || currentState == TouchOff_Touching - || currentState == TouchOffToOn_Touching) - { - touchState.event = TE_NONE; - } - - if (touchState.event == TE_DOWN) { - onTouchStart(touchState.x + scrollPositionX, touchState.y + scrollPositionY); - slidingWindow = nullptr; - } - else if (touchState.event == TE_UP) { - touchState.event = TE_NONE; - onTouchEnd(touchState.startX + scrollPositionX, touchState.startY + scrollPositionY); - } -#endif - - Window::checkEvents(); -} - void MainWindow::invalidate(const rect_t & rect) { if (invalidatedRect.w) { diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 0cd3ec1b7..ec140811d 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -69,8 +69,6 @@ class MainWindow: public Window } #endif - void checkEvents() override; - #if defined(HARDWARE_TOUCH) void setTouchEnabled(bool enable); bool touchEventOccured() { diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index e65863097..65c1bb94e 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -26,6 +26,10 @@ Window * Window::slidingWindow = nullptr; Window * Window::capturedWindow = nullptr; std::list Window::trash; +extern lv_obj_t *virtual_kb; + +static bool is_scrolling = false; + static void window_event_cb(lv_event_t * e) { lv_event_code_t code = lv_event_get_code(e); @@ -41,48 +45,98 @@ static void window_event_cb(lv_event_t * e) return; } - lv_obj_t *currentTarget = lv_event_get_current_target(e); - - // if the event bubbled to the topmost window then hand it to libopenui MainWindow. - if (MainWindow::isMainWindowCreated() - && (lv_obj_get_parent(currentTarget) == nullptr)) { + // If the event bubbled to the topmost window then + // hand it to libopenui MainWindow. + lv_obj_t *cur_target = lv_event_get_current_target(e); + if (MainWindow::isMainWindowCreated() && + (lv_obj_get_parent(cur_target) == nullptr)) { + lv_obj_t *target = lv_event_get_target(e); + switch (code) { + case LV_EVENT_PRESSED: + case LV_EVENT_RELEASED: { + lv_indev_t* click_source = (lv_indev_t*)lv_event_get_param(e); + if(click_source == NULL || is_scrolling) return; - switch(code) { - case LV_EVENT_PRESSED: - case LV_EVENT_RELEASED: - case LV_EVENT_KEY: - // do legacy processing - MainWindow::instance()->checkEvents(); - break; - case LV_EVENT_SCROLL: { - lv_obj_t* target = lv_event_get_target(e); - lv_coord_t scroll_y = lv_obj_get_scroll_y(target); - lv_coord_t scroll_x = lv_obj_get_scroll_x(target); - TRACE("SCROLL[x=%d;y=%d]", scroll_x, scroll_y); - Window* w = (Window*)lv_obj_get_user_data(target); - if (w) { + // Exclude keyboard ? + // if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD || + // lv_indev_get_type(click_source) == LV_INDEV_TYPE_ENCODER) { + // return; + // } + lv_area_t obj_coords; + lv_obj_get_coords(target, &obj_coords); + + lv_point_t point_act; + lv_indev_get_point(click_source, &point_act); + + // Ignore event from keypad + if(point_act.x < 0 || point_act.y < 0) return; + + lv_point_t rel_pos; + rel_pos.x = point_act.x - obj_coords.x1; + rel_pos.y = point_act.y - obj_coords.y1; + + auto w = (Window*)lv_obj_get_user_data(target); + if (!w) return; + + rel_pos.x += w->getScrollPositionX(); + rel_pos.y += w->getScrollPositionY(); + + if (code == LV_EVENT_PRESSED) { + TRACE("PRESSED[%d|%d]", rel_pos.x, rel_pos.y); + w->onTouchStart(rel_pos.x, rel_pos.y); + } else { + + lv_point_t vect_act; + lv_indev_get_vect(click_source, &vect_act); + + TRACE("RELEASED[%d|%d] vect[%d|%d]", + rel_pos.x, rel_pos.y, + vect_act.x, vect_act.y, + lv_indev_get_scroll_dir(click_source)); + + if (vect_act.x != 0 || vect_act.y != 0) return; + w->onTouchEnd(rel_pos.x, rel_pos.y); + } + } break; + + case LV_EVENT_CLICKED: + TRACE("CLICKED"); + break; + + case LV_EVENT_KEY: + // do legacy processing + MainWindow::instance()->checkEvents(); + break; + + case LV_EVENT_SCROLL: { + lv_coord_t scroll_y = lv_obj_get_scroll_y(target); + lv_coord_t scroll_x = lv_obj_get_scroll_x(target); + TRACE("SCROLL[x=%d;y=%d]", scroll_x, scroll_y); + Window *w = (Window *)lv_obj_get_user_data(target); + if (w) { #if defined(DEBUG_WINDOWS) - TRACE("%s", w->getWindowDebugString().c_str()); + TRACE("%s", w->getWindowDebugString().c_str()); #endif - w->setScrollPositionY(scroll_y); - w->setScrollPositionX(scroll_x); - } - } break; - case LV_EVENT_SCROLL_BEGIN: - TRACE("SCROLL_BEGIN"); - break; - case LV_EVENT_SCROLL_END: - TRACE("SCROLL_END"); - break; - case LV_EVENT_FOCUSED: { - lv_obj_t* target = lv_event_get_target(e); - bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); - bool loiu_focused = ((Window*)target->user_data)->hasFocus(); - TRACE("FOCUSED[%d|%d]", lvgl_focused, loiu_focused); - if (!loiu_focused) { - ((Window*)target->user_data)->setFocus(); - } - } break; + w->setScrollPositionY(scroll_y); + w->setScrollPositionX(scroll_x); + } + } break; + case LV_EVENT_SCROLL_BEGIN: + TRACE("SCROLL_BEGIN"); + is_scrolling = true; + break; + case LV_EVENT_SCROLL_END: + TRACE("SCROLL_END"); + is_scrolling = false; + break; + case LV_EVENT_FOCUSED: { + bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); + bool loiu_focused = ((Window *)target->user_data)->hasFocus(); + TRACE("FOCUSED[%d|%d]", lvgl_focused, loiu_focused); + if (!loiu_focused) { + ((Window *)target->user_data)->setFocus(); + } + } break; } } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index a63158bb4..617ddf2e3 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -404,6 +404,11 @@ class Window return _deleted; } +#if defined(HARDWARE_TOUCH) + virtual bool onTouchStart(coord_t x, coord_t y); + virtual bool onTouchEnd(coord_t x, coord_t y); +#endif + inline lv_obj_t *getLvObj() { return lvobj; } protected: @@ -463,13 +468,11 @@ class Window } #if defined(HARDWARE_TOUCH) - static coord_t getSnapStep(coord_t relativeScrollPosition, coord_t pageSize); + static coord_t getSnapStep(coord_t relativeScrollPosition, + coord_t pageSize); - virtual bool onTouchStart(coord_t x, coord_t y); - - virtual bool onTouchEnd(coord_t x, coord_t y); - - virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); + virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, + coord_t startY, coord_t slideX, coord_t slideY); #endif bool forwardTouchEnd(coord_t x, coord_t y); From f112e4228227683553bc1b9e824d38adb1574f75 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 Jan 2022 15:22:31 +0100 Subject: [PATCH 488/714] Style fixes for TextEdit --- .../src/thirdparty/libopenui/src/textedit.cpp | 114 ++++++++++-------- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 2 files changed, 65 insertions(+), 51 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index b69f6619b..fd4b459c8 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -43,75 +43,89 @@ #endif -static lv_obj_t *kb = nullptr; +static lv_obj_t *virtual_kb = nullptr; -static void ta_event_cb(lv_event_t * e) +static void ta_event_cb(lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); - lv_obj_t * ta = lv_event_get_target(e); - if(code == LV_EVENT_CLICKED || code == LV_EVENT_FOCUSED) { - if (kb == nullptr) { - kb = lv_keyboard_create(lv_scr_act()); - lv_obj_set_size(kb, LV_HOR_RES, LV_VER_RES / 2); - } + lv_obj_t *ta = lv_event_get_target(e); + if (code == LV_EVENT_CLICKED) { + if (virtual_kb == nullptr) { + virtual_kb = lv_keyboard_create(lv_scr_act()); + lv_obj_set_size(virtual_kb, LV_HOR_RES, LV_VER_RES / 2); + } - if(kb != NULL) - lv_keyboard_set_textarea(kb, ta); - } - else if (code == LV_EVENT_DEFOCUSED) { - if (kb != nullptr) { - lv_obj_del(kb); - kb = nullptr; + if (virtual_kb != NULL) { + lv_keyboard_set_textarea(virtual_kb, ta); + lv_obj_add_state(ta, LV_STATE_EDITED); + } + } else if (code == LV_EVENT_DEFOCUSED || code == LV_EVENT_DELETE) { + if (virtual_kb != nullptr) { + lv_obj_del(virtual_kb); + virtual_kb = nullptr; } } } +static lv_style_t style_main; +static lv_style_t style_edit; +static LvglWidgetFactory textEditFactory = { lv_textarea_create, nullptr }; -LvglWidgetFactory textEditFactory = LvglWidgetFactory( - [] (lv_obj_t *parent) { - return lv_textarea_create(parent); - }, - [] (LvglWidgetFactory *factory) { - lv_style_set_border_width(&factory->style, 1); - lv_style_set_border_color(&factory->style, makeLvColor(COLOR_THEME_SECONDARY2)); - lv_style_set_radius(&factory->style, 0); - lv_style_set_pad_top(&factory->style, 0); - lv_style_set_pad_bottom(&factory->style, 0); - lv_style_set_pad_left(&factory->style, 0); - lv_style_set_pad_right(&factory->style, 0); - lv_style_set_bg_color(&factory->style, makeLvColor(COLOR_THEME_PRIMARY2)); - lv_style_set_bg_opa(&factory->style, LV_OPA_100); - } -); - - -TextEdit::TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, - LcdFlags windowFlags, const char * _extra_chars) : - FormField(parent, rect, windowFlags, 0, &textEditFactory), - value(value), - length(length) +TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, + uint8_t length, LcdFlags windowFlags, + const char *_extra_chars) : + FormField(parent, {rect.x, rect.y, rect.w, rect.h - 2}, windowFlags, 0, + &textEditFactory), + value(value), + length(length) { + extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; + + // callbacks lv_obj_add_event_cb(lvobj, ta_event_cb, LV_EVENT_ALL, NULL); - lv_obj_add_style(lvobj, &textEditFactory.style, LV_PART_MAIN); - - // this is for the focus state. the master style takes care of the normal background - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), - LV_PART_MAIN | LV_STATE_FOCUSED); - lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN | LV_STATE_FOCUSED); + // properties lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_textarea_set_password_mode(lvobj, false); lv_textarea_set_one_line(lvobj, true); lv_textarea_set_text(lvobj, value); lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); - - auto label = lv_textarea_get_label(lvobj); - lv_obj_set_style_text_color(label, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); - lv_obj_set_style_text_font(label, &lv_font_montserrat_12, LV_PART_MAIN); - extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; -} + // LV_PART_MAIN + lv_style_init(&style_main); + lv_style_set_border_width(&style_main, 1); + lv_style_set_border_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY2)); + lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_PRIMARY2)); + lv_style_set_bg_opa(&style_main, LV_OPA_COVER); + //lv_style_set_radius(&style_main, 0); + lv_style_set_text_font(&style_main, &lv_font_montserrat_12); + lv_style_set_text_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1)); + lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN); + + // LV_STATE_FOCUSED + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), + LV_PART_MAIN | LV_STATE_FOCUSED); + + lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), + LV_PART_MAIN | LV_STATE_FOCUSED); + + // Hide cursor + lv_obj_set_style_opa(lvobj, 0, LV_PART_CURSOR); + + // Show Cursor in "Edit" mode + lv_style_init(&style_edit); + lv_style_set_opa(&style_edit, LV_OPA_COVER); + lv_style_set_bg_opa(&style_edit, LV_OPA_COVER); + lv_style_set_pad_left(&style_edit, (lv_coord_t)-(FIELD_PADDING_LEFT+2)); + lv_style_set_pad_top(&style_edit, (lv_coord_t)-(FIELD_PADDING_TOP+2)); + lv_obj_add_style(lvobj, &style_edit, LV_PART_CURSOR | LV_STATE_EDITED); + + // Text padding + auto label = lv_textarea_get_label(lvobj); + lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); + lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); +} void TextEdit::paint(BitmapBuffer * dc) { diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 65c1bb94e..7a84e6455 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -303,7 +303,7 @@ void Window::setFocus(uint8_t flag, Window * from) if (!lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) { lv_obj_add_state(lvobj, LV_STATE_FOCUSED); if (focusWindow != nullptr) { - lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED); + lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | LV_STATE_EDITED); } } From d438525f724fb1a8e6bb4f3712799c4e6f8ff67e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 Jan 2022 15:29:23 +0100 Subject: [PATCH 489/714] Fix sliders with "getValue" begin null --- radio/src/thirdparty/libopenui/src/slider.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index 403537cca..7f4494eb3 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -55,7 +55,9 @@ Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, { lv_obj_add_event_cb(lvobj, slider_changed_cb, LV_EVENT_VALUE_CHANGED, NULL); lv_slider_set_range(lvobj, vmin, vmax); - lv_slider_set_value(lvobj, getValue(), LV_ANIM_OFF); + + if (getValue) + lv_slider_set_value(lvobj, getValue(), LV_ANIM_OFF); // LV_PART_MAIN lv_style_init(&style_main); From 2baf771c30d55dbdfeee038ec03d8214efcf84bf Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Mon, 24 Jan 2022 14:06:09 -0500 Subject: [PATCH 490/714] 1. fix slider setValue and getValue. 2. add editMode processing so focus gets set on pressing slider. 3. fix right align of Static. Clean up color processing --- radio/src/thirdparty/libopenui/src/slider.cpp | 30 ++++++++++++------- radio/src/thirdparty/libopenui/src/slider.h | 6 ++-- radio/src/thirdparty/libopenui/src/static.h | 6 ++-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index 7f4494eb3..26a5feac9 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -25,9 +25,16 @@ static void slider_changed_cb(lv_event_t * e) { lv_obj_t * target = lv_event_get_target(e); - if (!lv_slider_is_dragged(target)) { - Slider* sl = (Slider*)lv_obj_get_user_data(target); - sl->setValue(lv_slider_get_value(target)); + auto code = lv_event_get_code(e); + + Slider* sl = (Slider*)lv_obj_get_user_data(target); + if (code == LV_EVENT_VALUE_CHANGED) { + // if (!lv_slider_is_dragged(target)) { + if (sl != nullptr) + sl->setValue(lv_slider_get_value(target)); + // } + } else if (code == LV_EVENT_PRESSED) { + sl->setEditMode(true); } } @@ -50,14 +57,15 @@ Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, 0, 0, &sliderFactory), vmin(vmin), vmax(vmax), - getValue(std::move(getValue)), + _getValue(std::move(getValue)), _setValue(std::move(setValue)) { - lv_obj_add_event_cb(lvobj, slider_changed_cb, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(lvobj, slider_changed_cb, LV_EVENT_VALUE_CHANGED, this); + lv_obj_add_event_cb(lvobj, slider_changed_cb, LV_EVENT_PRESSED, this); lv_slider_set_range(lvobj, vmin, vmax); - if (getValue) - lv_slider_set_value(lvobj, getValue(), LV_ANIM_OFF); + if (_getValue != nullptr) + lv_slider_set_value(lvobj, _getValue(), LV_ANIM_OFF); // LV_PART_MAIN lv_style_init(&style_main); @@ -110,14 +118,14 @@ void Slider::onEvent(event_t event) if (editMode) { if (event == EVT_ROTARY_RIGHT) { - setValue(getValue() + ROTARY_ENCODER_SPEED()); - lv_slider_set_value(lvobj, getValue(), LV_ANIM_OFF); + setValue(_getValue() + ROTARY_ENCODER_SPEED()); + lv_slider_set_value(lvobj, _getValue(), LV_ANIM_OFF); onKeyPress(); return; } else if (event == EVT_ROTARY_LEFT) { - setValue(getValue() - ROTARY_ENCODER_SPEED()); - lv_slider_set_value(lvobj, getValue(), LV_ANIM_OFF); + setValue(_getValue() - ROTARY_ENCODER_SPEED()); + lv_slider_set_value(lvobj, _getValue(), LV_ANIM_OFF); onKeyPress(); return; } else if (event == EVT_KEY_FIRST(KEY_EXIT)) diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index fb26e551e..d289676d7 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -33,8 +33,8 @@ class Slider : public FormField void setValue(int value) { - _setValue(limit(vmin, value, vmax)); - invalidate(); + if (_setValue != nullptr) + _setValue(limit(vmin, value, vmax)); } void paint(BitmapBuffer* dc) override; @@ -48,6 +48,6 @@ class Slider : public FormField int vmin; int vmax; bool sliding = false; - std::function getValue; + std::function _getValue; std::function _setValue; }; diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 7c8c18545..0d4c9d680 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -32,10 +32,12 @@ class StaticText: public Window label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); - auto color = COLOR_VAL(textFlags); - lv_obj_set_style_text_color(label, lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)), LV_PART_MAIN); + lv_obj_set_style_text_color(label, makeLvColor(textFlags), LV_PART_MAIN); if (textFlags & CENTERED) lv_obj_center(label); + else if (textFlags & RIGHT) + lv_obj_align(label, LV_ALIGN_TOP_RIGHT, 0, 0); + if (textFlags & FONT(BOLD)) { } From 7f724c187d1cbf67080a5ae7e5fcb0911550f1f9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 24 Jan 2022 16:08:13 +0100 Subject: [PATCH 491/714] Fixed scrolling --- radio/src/thirdparty/libopenui/src/keyboard_base.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- radio/src/thirdparty/libopenui/src/menutoolbar.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 50bb07051..6475e66ed 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -99,12 +99,12 @@ void Keyboard::setField(FormField* newField) #endif // scroll the header of the window out of view to get more space to // see the field being edited - fieldContainer->setScrollPositionY(fields->top()); + lv_obj_scroll_to_y(fieldContainer->getLvObj(), fields->top(), LV_ANIM_OFF); oldHeight = fields->height(); fields->setHeight(newHeight); coord_t offsetY = calcScrollOffsetForField(newField, fields); - fields->setScrollPositionY(offsetY); + lv_obj_scroll_to_y(fields->getLvObj(), offsetY, LV_ANIM_OFF); } else { // TODO: what happens if field container is null. // we will need to interrogate the fieldContainer for its diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 9d25a6d51..952c7e614 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -42,7 +42,7 @@ void MenuBody::setIndex(int index) scrollY += lines[i].lineHeight(); if (innerHeight > height()) { - setScrollPositionY(scrollY - 3 * MENUS_LINE_HEIGHT); + lv_obj_scroll_to_y(lvobj, scrollY - 3 * MENUS_LINE_HEIGHT, LV_ANIM_OFF); } invalidate(); diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index a4f57834e..49e37817c 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -90,7 +90,7 @@ class MenuToolbar: public FormGroup { scrollTo(button); } else { - setScrollPositionY(0); + lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); } } } From a6a387a87c47635229af9689b755698006cc02c2 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Mon, 24 Jan 2022 21:15:27 +0100 Subject: [PATCH 492/714] reduce redraw issues on screen change fix crash when lvobj is already deleted, but events are still in the queue --- radio/src/thirdparty/libopenui/src/button.cpp | 3 +++ radio/src/thirdparty/libopenui/src/layer.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index a48989ca1..b527751b9 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -28,6 +28,9 @@ void Button::onPress() if (check != bool(windowFlags & BUTTON_CHECKED)) { windowFlags ^= BUTTON_CHECKED; + if(lvobj == nullptr) + return; + if (! (windowFlags & BUTTON_CHECKED)) lv_obj_clear_state(lvobj, LV_STATE_CHECKED); else diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index e3e9caf02..64da2542b 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -47,9 +47,10 @@ class Layer } // layers are screens in lvgl - lv_scr_load(window->getLvObj()); lv_obj_set_parent(canvas, window->getLvObj()); lv_obj_move_background(canvas); + lv_scr_load(window->getLvObj()); + lv_obj_invalidate(canvas); stack.emplace_back(Layer(window)); From 684a004f9e54716e464742f8a747a704efa78510 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Tue, 25 Jan 2022 15:53:58 -0500 Subject: [PATCH 493/714] changes for keyboard processing. It is pretty good now. The next thing will be to do the numeric keyboard. Stay tuned --- radio/src/thirdparty/libopenui/src/form.h | 4 +- .../libopenui/src/keyboard_base.cpp | 12 +- .../thirdparty/libopenui/src/keyboard_base.h | 12 +- .../libopenui/src/keyboard_text.cpp | 425 +++++++++--------- .../thirdparty/libopenui/src/keyboard_text.h | 2 + .../thirdparty/libopenui/src/mainwindow.cpp | 1 - .../src/thirdparty/libopenui/src/textedit.cpp | 46 +- radio/src/thirdparty/libopenui/src/window.cpp | 2 - radio/src/thirdparty/libopenui/src/window.h | 13 +- 9 files changed, 274 insertions(+), 243 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 0ece8f5e2..b0b4ba954 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -77,9 +77,9 @@ class FormField: public Window virtual void setEditMode(bool newEditMode) { editMode = newEditMode; - if (editMode) { + if (editMode && lvobj != nullptr) { lv_obj_add_state(lvobj, LV_STATE_EDITED | LV_STATE_FOCUSED); - } else { + } else if (lvobj != nullptr) { lv_obj_clear_state(lvobj, LV_STATE_EDITED); } invalidate(); diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 6475e66ed..1295fec1c 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -18,6 +18,9 @@ */ #include "keyboard_base.h" +Keyboard * Keyboard::activeKeyboard = nullptr; +lv_obj_t *Keyboard::keyboard = nullptr; + coord_t calcScrollOffsetForField(FormField *newField, Window *topWindow) { // now we need to calculate the offset of the field in the fields scroll container @@ -32,7 +35,7 @@ coord_t calcScrollOffsetForField(FormField *newField, Window *topWindow) // try and place it in the middle of the screen. The containing window MUST have // already been resized - return offsetY - topWindow->height() / 2; + return offsetY - topWindow->height() * 2 / 7; } bool Keyboard::attachKeyboard() @@ -99,7 +102,10 @@ void Keyboard::setField(FormField* newField) #endif // scroll the header of the window out of view to get more space to // see the field being edited - lv_obj_scroll_to_y(fieldContainer->getLvObj(), fields->top(), LV_ANIM_OFF); + + // TODO: this no longer works. we need to figure out something else + //lv_obj_scroll_to_y(fieldContainer->getLvObj(), fields->top(), LV_ANIM_OFF); + oldHeight = fields->height(); fields->setHeight(newHeight); @@ -113,6 +119,8 @@ void Keyboard::setField(FormField* newField) invalidate(); newField->setEditMode(true); + lv_keyboard_set_textarea(keyboard, newField->getLvObj()); + field = newField; } else { clearField(); diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index cf0e42df4..bb717ff36 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -25,13 +25,21 @@ class Keyboard: public FormWindow { public: - explicit Keyboard(coord_t height) : FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) + explicit Keyboard(coord_t height) : + FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) { + // set the background of the window and opacity to 100% + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); } void clearField() { TRACE("CLEAR FIELD"); + if (keyboard != nullptr) { + lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); + } + detach(); if (fields) { fields->setHeight(oldHeight); @@ -59,6 +67,8 @@ class Keyboard: public FormWindow protected: static Keyboard * activeKeyboard; + static lv_obj_t * keyboard; + FormField *field = nullptr; Window *fieldContainer = nullptr; FormWindow *fields = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 19a0ca44b..75a8b52bc 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -22,283 +22,288 @@ #include "textedit.h" #include "font.h" -constexpr coord_t KEYBOARD_HEIGHT = 160; +constexpr coord_t KEYBOARD_HEIGHT = LCD_H * 2 / 5; TextKeyboard * TextKeyboard::_instance = nullptr; -const uint8_t LBM_KEY_UPPERCASE[] = { -#include "mask_key_uppercase.lbm" -}; +// const uint8_t LBM_KEY_UPPERCASE[] = { +// #include "mask_key_uppercase.lbm" +// }; -const uint8_t LBM_KEY_LOWERCASE[] = { -#include "mask_key_lowercase.lbm" -}; +// const uint8_t LBM_KEY_LOWERCASE[] = { +// #include "mask_key_lowercase.lbm" +// }; -const uint8_t LBM_KEY_BACKSPACE[] = { -#include "mask_key_backspace.lbm" -}; +// const uint8_t LBM_KEY_BACKSPACE[] = { +// #include "mask_key_backspace.lbm" +// }; -const uint8_t LBM_KEY_LETTERS[] = { -#include "mask_key_letters.lbm" -}; +// const uint8_t LBM_KEY_LETTERS[] = { +// #include "mask_key_letters.lbm" +// }; -const uint8_t LBM_KEY_NUMBERS[] = { -#include "mask_key_numbers.lbm" -}; +// const uint8_t LBM_KEY_NUMBERS[] = { +// #include "mask_key_numbers.lbm" +// }; -const uint8_t LBM_KEY_SPACEBAR[] = { -#include "mask_key_spacebar.lbm" -}; +// const uint8_t LBM_KEY_SPACEBAR[] = { +// #include "mask_key_spacebar.lbm" +// }; -const uint8_t * const LBM_SPECIAL_KEYS[] = { - LBM_KEY_BACKSPACE, - LBM_KEY_UPPERCASE, - LBM_KEY_LOWERCASE, - LBM_KEY_LETTERS, - LBM_KEY_NUMBERS, -}; +// const uint8_t * const LBM_SPECIAL_KEYS[] = { +// LBM_KEY_BACKSPACE, +// LBM_KEY_UPPERCASE, +// LBM_KEY_LOWERCASE, +// LBM_KEY_LETTERS, +// LBM_KEY_NUMBERS, +// }; -const char * const KEYBOARD_LOWERCASE[] = { - "qwertyuiop", - " asdfghjkl", - KEYBOARD_SET_UPPERCASE "zxcvbnm" KEYBOARD_BACKSPACE, - KEYBOARD_SET_NUMBERS KEYBOARD_SPACE KEYBOARD_ENTER -}; +// const char * const KEYBOARD_LOWERCASE[] = { +// "qwertyuiop", +// " asdfghjkl", +// KEYBOARD_SET_UPPERCASE "zxcvbnm" KEYBOARD_BACKSPACE, +// KEYBOARD_SET_NUMBERS KEYBOARD_SPACE KEYBOARD_ENTER +// }; -const char * const KEYBOARD_UPPERCASE[] = { - "QWERTYUIOP", - " ASDFGHJKL", - KEYBOARD_SET_LOWERCASE "ZXCVBNM" KEYBOARD_BACKSPACE, - KEYBOARD_SET_NUMBERS KEYBOARD_SPACE KEYBOARD_ENTER -}; +// const char * const KEYBOARD_UPPERCASE[] = { +// "QWERTYUIOP", +// " ASDFGHJKL", +// KEYBOARD_SET_LOWERCASE "ZXCVBNM" KEYBOARD_BACKSPACE, +// KEYBOARD_SET_NUMBERS KEYBOARD_SPACE KEYBOARD_ENTER +// }; -const char * const KEYBOARD_NUMBERS[] = { - "1234567890", - "_-", - " " KEYBOARD_BACKSPACE, - KEYBOARD_SET_LETTERS KEYBOARD_SPACE KEYBOARD_ENTER -}; +// const char * const KEYBOARD_NUMBERS[] = { +// "1234567890", +// "_-", +// " " KEYBOARD_BACKSPACE, +// KEYBOARD_SET_LETTERS KEYBOARD_SPACE KEYBOARD_ENTER +// }; -#define NUMBER_OF_KEYBOARD_LAYOUTS 4 -const char * const * const KEYBOARD_LAYOUTS[NUMBER_OF_KEYBOARD_LAYOUTS] = { - KEYBOARD_UPPERCASE, - KEYBOARD_LOWERCASE, - KEYBOARD_LOWERCASE, - KEYBOARD_NUMBERS, -}; +// #define NUMBER_OF_KEYBOARD_LAYOUTS 4 +// const char * const * const KEYBOARD_LAYOUTS[NUMBER_OF_KEYBOARD_LAYOUTS] = { +// KEYBOARD_UPPERCASE, +// KEYBOARD_LOWERCASE, +// KEYBOARD_LOWERCASE, +// KEYBOARD_NUMBERS, +// }; TextKeyboard::TextKeyboard(): - Keyboard(KEYBOARD_HEIGHT), - layout(KEYBOARD_LOWERCASE) + Keyboard(KEYBOARD_HEIGHT) + // layout(KEYBOARD_LOWERCASE) { + if (keyboard == nullptr) { + keyboard = lv_keyboard_create(lv_scr_act()); + lv_obj_set_size(keyboard, LCD_W, LCD_H * 2 / 5); + lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN); + } } TextKeyboard::~TextKeyboard() { _instance = nullptr; + } int TextKeyboard::getCharWidth(const char c) { - if (int8_t(c) < 0) return KEYBOARD_BITMAP_WIDTH; + // if (int8_t(c) < 0) return KEYBOARD_BITMAP_WIDTH; - switch (c) - { - case ' ': return 15; - case KEYBOARD_SPACE[0]: return KEYBOARD_SPACE_WIDTH; - case KEYBOARD_ENTER[0]: return KEYBOARD_ENTER_WIDTH; - default: return KEYBOARD_CHAR_WIDTH; - } + // switch (c) + // { + // case ' ': return 15; + // case KEYBOARD_SPACE[0]: return KEYBOARD_SPACE_WIDTH; + // case KEYBOARD_ENTER[0]: return KEYBOARD_ENTER_WIDTH; + // default: return KEYBOARD_CHAR_WIDTH; + // } } int TextKeyboard::calculateMaxWidth() { - int maxWidth = 0; - for (uint8_t i = 0; i < NUMBER_OF_KEYBOARD_LAYOUTS; i++) { - int lineWidth = 0; - const char * c = layout[i]; - while (*c) { - lineWidth += getCharWidth(*c); - maxWidth = max(lineWidth, maxWidth); - c++; - } - } + // int maxWidth = 0; + // for (uint8_t i = 0; i < NUMBER_OF_KEYBOARD_LAYOUTS; i++) { + // int lineWidth = 0; + // const char * c = layout[i]; + // while (*c) { + // lineWidth += getCharWidth(*c); + // maxWidth = max(lineWidth, maxWidth); + // c++; + // } + // } - return maxWidth; + // return maxWidth; } void TextKeyboard::paint(BitmapBuffer * dc) { - lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); - dc->clear(CUSTOM_COLOR); + // lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); + // dc->clear(CUSTOM_COLOR); - // center the keyboard - coord_t start_x = (dc->width() - calculateMaxWidth()) / 2; + // // center the keyboard + // coord_t start_x = (dc->width() - calculateMaxWidth()) / 2; - for (uint8_t i = 0; i < NUMBER_OF_KEYBOARD_LAYOUTS; i++) { - coord_t y = KEYBOARD_OFFSET_Y + i * KEYBOARD_ROW_HEIGHT; - coord_t x = start_x; - const char * c = layout[i]; + // for (uint8_t i = 0; i < NUMBER_OF_KEYBOARD_LAYOUTS; i++) { + // coord_t y = KEYBOARD_OFFSET_Y + i * KEYBOARD_ROW_HEIGHT; + // coord_t x = start_x; + // const char * c = layout[i]; - while (*c) { - if (*c == KEYBOARD_SPACE[0]) { - // spacebar - dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, COLOR_THEME_SECONDARY1); - } - else if (*c == KEYBOARD_ENTER[0]) { - // enter - dc->drawSolidFilledRect(x, y - 2, KEYBOARD_ENTER_WIDTH, 25, COLOR_THEME_DISABLED); - dc->drawText(x + KEYBOARD_ENTER_WIDTH / 2, y + 2, "ENTER", CENTERED); - } - else if (int8_t(*c) < 0) { - const uint8_t *pBitmap = LBM_SPECIAL_KEYS[uint8_t(*c - 128)]; - uint8_t bitmapWidth = *pBitmap, bitmapHeight = *(pBitmap + 2); - if (touched && *c == touch_key) { - dc->drawSolidFilledRect(x - 2, y + 3, bitmapWidth + 4, bitmapHeight + 4, COLOR_THEME_SECONDARY2); - } + // while (*c) { + // if (*c == KEYBOARD_SPACE[0]) { + // // spacebar + // dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, COLOR_THEME_SECONDARY1); + // } + // else if (*c == KEYBOARD_ENTER[0]) { + // // enter + // dc->drawSolidFilledRect(x, y - 2, KEYBOARD_ENTER_WIDTH, 25, COLOR_THEME_DISABLED); + // dc->drawText(x + KEYBOARD_ENTER_WIDTH / 2, y + 2, "ENTER", CENTERED); + // } + // else if (int8_t(*c) < 0) { + // const uint8_t *pBitmap = LBM_SPECIAL_KEYS[uint8_t(*c - 128)]; + // uint8_t bitmapWidth = *pBitmap, bitmapHeight = *(pBitmap + 2); + // if (touched && *c == touch_key) { + // dc->drawSolidFilledRect(x - 2, y + 3, bitmapWidth + 4, bitmapHeight + 4, COLOR_THEME_SECONDARY2); + // } - dc->drawBitmapPattern(x, y + 5, pBitmap, COLOR_THEME_SECONDARY1); - } - else { - if (touched && *c == touch_key) { - dc->drawSolidFilledRect(x - KEYBOARD_CHAR_WIDTH / 2, y - ((KEYBOARD_ROW_HEIGHT - 16) / 2), KEYBOARD_CHAR_WIDTH, KEYBOARD_ROW_HEIGHT, COLOR_THEME_FOCUS); - } - dc->drawSizedText(x, y, c, 1, CENTERED); - } - x += getCharWidth(*c); - c++; - } - } + // dc->drawBitmapPattern(x, y + 5, pBitmap, COLOR_THEME_SECONDARY1); + // } + // else { + // if (touched && *c == touch_key) { + // dc->drawSolidFilledRect(x - KEYBOARD_CHAR_WIDTH / 2, y - ((KEYBOARD_ROW_HEIGHT - 16) / 2), KEYBOARD_CHAR_WIDTH, KEYBOARD_ROW_HEIGHT, COLOR_THEME_FOCUS); + // } + // dc->drawSizedText(x, y, c, 1, CENTERED); + // } + // x += getCharWidth(*c); + // c++; + // } + // } } bool TextKeyboard::onTouchStart(coord_t x, coord_t y) { - // remove the centering. If x < 0 then this is an invalid touch event - x -= (this->width() - calculateMaxWidth()) / 2; - if (x < 0) { - if (touched) { - touched = false; - invalidate(); - } - return true; - } + // // remove the centering. If x < 0 then this is an invalid touch event + // x -= (this->width() - calculateMaxWidth()) / 2; + // if (x < 0) { + // if (touched) { + // touched = false; + // invalidate(); + // } + // return true; + // } - touched = false; + // touched = false; - uint8_t row = max(0, y - 5) / KEYBOARD_ROW_HEIGHT; - const char * key = layout[row]; + // uint8_t row = max(0, y - 5) / KEYBOARD_ROW_HEIGHT; + // const char * key = layout[row]; - while(*key) { - if (*key == KEYBOARD_SPACE[0] && x <= KEYBOARD_SPACE_WIDTH) { - touch_key = KEYBOARD_SPACE[0]; - touched = true; - break; - } - else if (*key == KEYBOARD_ENTER[0] && x <= KEYBOARD_ENTER_WIDTH) { - touch_key = KEYBOARD_ENTER[0]; - touched = true; - break; - } - else if (int8_t(*key) < 0 && x <= KEYBOARD_BITMAP_WIDTH) { - if (uint8_t(*key) == 128) { - touch_key = KEYBOARD_BACKSPACE[0]; - touched = true; - break; - } else { - touched = false; - break; - } - } - else if (*key != ' ' && (x >= -(KEYBOARD_CHAR_WIDTH / 2) && x <= KEYBOARD_CHAR_WIDTH / 2)) { - touch_key = *key; - touched = true; - break; - } + // while(*key) { + // if (*key == KEYBOARD_SPACE[0] && x <= KEYBOARD_SPACE_WIDTH) { + // touch_key = KEYBOARD_SPACE[0]; + // touched = true; + // break; + // } + // else if (*key == KEYBOARD_ENTER[0] && x <= KEYBOARD_ENTER_WIDTH) { + // touch_key = KEYBOARD_ENTER[0]; + // touched = true; + // break; + // } + // else if (int8_t(*key) < 0 && x <= KEYBOARD_BITMAP_WIDTH) { + // if (uint8_t(*key) == 128) { + // touch_key = KEYBOARD_BACKSPACE[0]; + // touched = true; + // break; + // } else { + // touched = false; + // break; + // } + // } + // else if (*key != ' ' && (x >= -(KEYBOARD_CHAR_WIDTH / 2) && x <= KEYBOARD_CHAR_WIDTH / 2)) { + // touch_key = *key; + // touched = true; + // break; + // } - x -= getCharWidth(*key); - key++; + // x -= getCharWidth(*key); + // key++; - if (touched) { - invalidate(); - break; - } - } - return true; + // if (touched) { + // invalidate(); + // break; + // } + // } + // return true; } bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) { - // remove the centering width, if x < 0 then this is an invalid touch on a centered keyboard. - x -= (this->width() - calculateMaxWidth()) / 2; - if (x < 0) { - touched = false; - return true; - } + // // remove the centering width, if x < 0 then this is an invalid touch on a centered keyboard. + // x -= (this->width() - calculateMaxWidth()) / 2; + // if (x < 0) { + // touched = false; + // return true; + // } - onKeyPress(); + // onKeyPress(); - if (touched) - invalidate(); - touched = false; + // if (touched) + // invalidate(); + // touched = false; - uint8_t row = max(0, y - 5) / KEYBOARD_ROW_HEIGHT; + // uint8_t row = max(0, y - 5) / KEYBOARD_ROW_HEIGHT; - const char * key = layout[row]; - while (*key) { - if (*key == KEYBOARD_SPACE[0]) { - if (x <= KEYBOARD_SPACE_WIDTH) { - pushEvent(EVT_VIRTUAL_KEY(' ')); - return true; - } - } - else if (*key == KEYBOARD_ENTER[0]) { - if (x <= KEYBOARD_ENTER_WIDTH) { - pushEvent(EVT_VIRTUAL_KEY(KEYBOARD_ENTER[0])); - // enter - hide(); - return true; - } - } - else if (int8_t(*key) < 0) { - if (x <= KEYBOARD_BITMAP_WIDTH) { - uint8_t specialKey = *key; - if (specialKey == 128) { - // backspace - pushEvent(EVT_VIRTUAL_KEY(KEYBOARD_BACKSPACE[0])); - } - else { - layout = KEYBOARD_LAYOUTS[specialKey - 129]; - invalidate(); - } - break; - } - } - else { - if (*key != ' ' && (x >= -(KEYBOARD_CHAR_WIDTH / 2) && x <= KEYBOARD_CHAR_WIDTH / 2)) { - pushEvent(EVT_VIRTUAL_KEY(*key)); - return true; - } - } - x -= getCharWidth(*key); - key++; - } + // const char * key = layout[row]; + // while (*key) { + // if (*key == KEYBOARD_SPACE[0]) { + // if (x <= KEYBOARD_SPACE_WIDTH) { + // pushEvent(EVT_VIRTUAL_KEY(' ')); + // return true; + // } + // } + // else if (*key == KEYBOARD_ENTER[0]) { + // if (x <= KEYBOARD_ENTER_WIDTH) { + // pushEvent(EVT_VIRTUAL_KEY(KEYBOARD_ENTER[0])); + // // enter + // hide(); + // return true; + // } + // } + // else if (int8_t(*key) < 0) { + // if (x <= KEYBOARD_BITMAP_WIDTH) { + // uint8_t specialKey = *key; + // if (specialKey == 128) { + // // backspace + // pushEvent(EVT_VIRTUAL_KEY(KEYBOARD_BACKSPACE[0])); + // } + // else { + // layout = KEYBOARD_LAYOUTS[specialKey - 129]; + // invalidate(); + // } + // break; + // } + // } + // else { + // if (*key != ' ' && (x >= -(KEYBOARD_CHAR_WIDTH / 2) && x <= KEYBOARD_CHAR_WIDTH / 2)) { + // pushEvent(EVT_VIRTUAL_KEY(*key)); + // return true; + // } + // } + // x -= getCharWidth(*key); + // key++; + // } - return true; + // return true; } bool TextKeyboard::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) { - if (touched) - invalidate(); + // if (touched) + // invalidate(); - touched = false; - return false; + // touched = false; + // return false; } #if defined(HARDWARE_KEYS) void TextKeyboard::onEvent(event_t event) { - } #endif diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index 732e6e2c3..462016fc5 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -53,6 +53,8 @@ class TextKeyboard : public Keyboard { { if (!_instance) _instance = new TextKeyboard(); + else + lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); _instance->setField(field); } diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 7ebb47f14..d9d7dd51b 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -33,7 +33,6 @@ LvglWidgetFactory mainWindowFactory = LvglWidgetFactory([] (lv_obj_t *parent) { #if defined(HARDWARE_TOUCH) TouchState touchState; -Keyboard * Keyboard::activeKeyboard = nullptr; #endif void MainWindow::emptyTrash() diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index fd4b459c8..09cd291ab 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -47,24 +47,24 @@ static lv_obj_t *virtual_kb = nullptr; static void ta_event_cb(lv_event_t *e) { - lv_event_code_t code = lv_event_get_code(e); - lv_obj_t *ta = lv_event_get_target(e); - if (code == LV_EVENT_CLICKED) { - if (virtual_kb == nullptr) { - virtual_kb = lv_keyboard_create(lv_scr_act()); - lv_obj_set_size(virtual_kb, LV_HOR_RES, LV_VER_RES / 2); - } + // lv_event_code_t code = lv_event_get_code(e); + // lv_obj_t *ta = lv_event_get_target(e); + // if (code == LV_EVENT_CLICKED) { + // if (virtual_kb == nullptr) { + // virtual_kb = lv_keyboard_create(lv_scr_act()); + // lv_obj_set_size(virtual_kb, LV_HOR_RES, LV_VER_RES / 2); + // } - if (virtual_kb != NULL) { - lv_keyboard_set_textarea(virtual_kb, ta); - lv_obj_add_state(ta, LV_STATE_EDITED); - } - } else if (code == LV_EVENT_DEFOCUSED || code == LV_EVENT_DELETE) { - if (virtual_kb != nullptr) { - lv_obj_del(virtual_kb); - virtual_kb = nullptr; - } - } + // if (virtual_kb != NULL) { + // lv_keyboard_set_textarea(virtual_kb, ta); + // lv_obj_add_state(ta, LV_STATE_EDITED); + // } + // } else if (code == LV_EVENT_DEFOCUSED || code == LV_EVENT_DELETE) { + // if (virtual_kb != nullptr) { + // lv_obj_del(virtual_kb); + // virtual_kb = nullptr; + // } + // } } static lv_style_t style_main; @@ -339,9 +339,9 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) setFocus(SET_FOCUS_DEFAULT); } -// #if defined(SOFTWARE_KEYBOARD) -// TextKeyboard::show(this); -// #endif +#if defined(SOFTWARE_KEYBOARD) + TextKeyboard::show(this); +#endif coord_t rest = x; for (cursorPos = 0; cursorPos < length; cursorPos++) { @@ -361,9 +361,9 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) void TextEdit::onFocusLost() { -// #if defined(SOFTWARE_KEYBOARD) -// TextKeyboard::hide(); -// #endif +#if defined(SOFTWARE_KEYBOARD) + TextKeyboard::hide(); +#endif changeEnd(); FormField::onFocusLost(); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7a84e6455..0ff9571c5 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -208,8 +208,6 @@ void Window::attach(Window * newParent) if (newParent) { newParent->addChild(this); - if(lvobj != nullptr) - lv_obj_set_parent(lvobj, newParent->lvobj); } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 617ddf2e3..0bfc0eaa1 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -210,11 +210,12 @@ class Window void setHeight(coord_t value) { rect.h = value; - lv_obj_set_height(lvobj, rect.h); + if (lvobj != nullptr) + lv_obj_set_height(lvobj, rect.h); if (windowFlags & FORWARD_SCROLL) innerHeight = value; - else if (innerHeight <= value) { + else if (lvobj != nullptr && innerHeight <= value) { lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); } invalidate(); @@ -436,6 +437,12 @@ class Window void addChild(Window * window, bool front = false) { + if (lv_obj_get_parent(window->lvobj) != nullptr) { + lv_obj_set_parent(window->lvobj, this->lvobj); + if (front) + lv_obj_move_to_index(window->lvobj, 0); + } + if (front) children.push_front(window); else @@ -445,6 +452,8 @@ class Window void removeChild(Window * window) { children.remove(window); + if (window->lvobj != nullptr) + lv_obj_set_parent(window->lvobj, nullptr); invalidate(); } From 4fc2fc8ae6d8a5cfe870a4c113bdf3adca894879 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 25 Jan 2022 18:51:52 +0100 Subject: [PATCH 494/714] Layers are only meant to backup the focus window --- radio/src/thirdparty/libopenui/src/layer.h | 13 ------------- .../thirdparty/libopenui/src/mainwindow.cpp | 18 +++++++++++------- .../src/thirdparty/libopenui/src/mainwindow.h | 9 +-------- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index 64da2542b..94c0677a6 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -37,20 +37,12 @@ class Layer static void push(Window * window) { - if (window->getLvObj()->parent != nullptr) { - TRACE("Layers must have lvgl parent to be null"); - } if (!stack.empty()) { Layer & parentLayer = stack.back(); parentLayer.focus = Window::getFocus(); } - // layers are screens in lvgl - lv_obj_set_parent(canvas, window->getLvObj()); - lv_obj_move_background(canvas); - lv_scr_load(window->getLvObj()); - lv_obj_invalidate(canvas); stack.emplace_back(Layer(window)); @@ -67,9 +59,6 @@ class Layer if (stack.back().main == window) { stack.pop_back(); const auto & back = stack.back(); - lv_obj_set_parent(canvas, back.main->getLvObj()); - lv_obj_move_background(canvas); - lv_scr_load(back.main->getLvObj()); // load the screen. if (back.focus) { back.focus->setFocus(SET_FOCUS_DEFAULT); } @@ -78,8 +67,6 @@ class Layer // TODO it would be better to do it in reverse order for (auto it = stack.begin(); it != stack.end(); it++) { if (it->main == window) { - lv_obj_set_parent(canvas, it->main->getLvObj()); - lv_scr_load(it->main->getLvObj()); stack.erase(it); break; } diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index d9d7dd51b..aa9a28e37 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -24,17 +24,21 @@ #include "touch.h" #endif -MainWindow * MainWindow::_instance = nullptr; - -LvglWidgetFactory mainWindowFactory = LvglWidgetFactory([] (lv_obj_t *parent) { - return lv_obj_create(nullptr); -}); - - #if defined(HARDWARE_TOUCH) TouchState touchState; #endif +static LvglWidgetFactory mainWindowFactory = { lv_obj_create, nullptr }; + +MainWindow * MainWindow::_instance = nullptr; + +MainWindow::MainWindow() : + Window(nullptr, {0, 0, LCD_W, LCD_H}, 0, 0, &mainWindowFactory), + invalidatedRect(rect) +{ + Layer::push(this); +} + void MainWindow::emptyTrash() { for (auto window: trash) { diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index ec140811d..9e1d5951d 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -23,18 +23,11 @@ #include "layer.h" #include "bitmapbuffer.h" -extern LvglWidgetFactory mainWindowFactory; - class MainWindow: public Window { protected: // singleton - MainWindow(): - Window(nullptr, {0, 0, LCD_W, LCD_H}, 0, 0, &mainWindowFactory), - invalidatedRect(rect) - { - Layer::push(this); - } + MainWindow(); public: ~MainWindow() override From 0f06fad706024a9db31826a2ee06035f256c1edd Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 25 Jan 2022 19:19:34 +0100 Subject: [PATCH 495/714] Fix touch events --- radio/src/thirdparty/libopenui/src/window.cpp | 38 +++---------------- radio/src/thirdparty/libopenui/src/window.h | 1 - 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0ff9571c5..9309cf3d5 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -573,42 +573,16 @@ void Window::onEvent(event_t event) #if defined(HARDWARE_TOUCH) bool Window::onTouchStart(coord_t x, coord_t y) { - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(x, y)) { - if (child->onTouchStart(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { - return true; - } - } - } - - return false; -} - -bool Window::forwardTouchEnd(coord_t x, coord_t y) -{ - if (capturedWindow != nullptr) - { - capturedWindow->onTouchEnd(x, y); - capturedWindow = nullptr; - return true; - } - - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(x, y)) { - if (child->onTouchEnd(x - child->rect.x + child->scrollPositionX, y - child->rect.y + child->scrollPositionY)) { - return true; - } - } - } - - return false; + if (parent) + return parent->onTouchStart(x, y); + return true; } bool Window::onTouchEnd(coord_t x, coord_t y) { - return forwardTouchEnd(x, y) ? true : (windowFlags & OPAQUE); + if (parent && !(windowFlags & OPAQUE)) + return parent->onTouchEnd(x, y); + return true; } bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 0bfc0eaa1..5a3d9339e 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -484,7 +484,6 @@ class Window coord_t startY, coord_t slideX, coord_t slideY); #endif - bool forwardTouchEnd(coord_t x, coord_t y); bool hasOpaqueRect(const rect_t & testRect) const; }; From 648d60948bbc5d863f30366c58ab16efbcd54a05 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 26 Jan 2022 08:36:26 +0100 Subject: [PATCH 496/714] Remove event bubbling --- radio/src/thirdparty/libopenui/src/window.cpp | 160 ++++++++---------- 1 file changed, 66 insertions(+), 94 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 9309cf3d5..fff38f17b 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -32,112 +32,85 @@ static bool is_scrolling = false; static void window_event_cb(lv_event_t * e) { + lv_obj_t *target = lv_event_get_target(e); lv_event_code_t code = lv_event_get_code(e); - Window *window = (Window *) lv_event_get_user_data(e); + + Window* window = (Window *)lv_obj_get_user_data(target); + if (!window) return; + if (code == LV_EVENT_GET_SELF_SIZE) { - lv_point_t * p = (lv_point_t *)lv_event_get_param(e); - if (p->x >= 0) { - p->x = window->getInnerWidth(); - } - if (p->y >= 0) - p->y = window->getInnerHeight(); - + lv_point_t *p = (lv_point_t *)lv_event_get_param(e); + if (p->x >= 0) { p->x = window->getInnerWidth(); } + if (p->y >= 0) { p->y = window->getInnerHeight(); } return; - } + } else if (code == LV_EVENT_DRAW_MAIN) { + TRACE_WINDOWS("DRAW_MAIN %s", window->getWindowDebugString().c_str()); + return; + } else if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) { + TRACE_WINDOWS("PRESSED: %s", window->getWindowDebugString().c_str()); - // If the event bubbled to the topmost window then - // hand it to libopenui MainWindow. - lv_obj_t *cur_target = lv_event_get_current_target(e); - if (MainWindow::isMainWindowCreated() && - (lv_obj_get_parent(cur_target) == nullptr)) { - lv_obj_t *target = lv_event_get_target(e); - switch (code) { - case LV_EVENT_PRESSED: - case LV_EVENT_RELEASED: { - lv_indev_t* click_source = (lv_indev_t*)lv_event_get_param(e); - if(click_source == NULL || is_scrolling) return; + lv_indev_t *click_source = (lv_indev_t *)lv_event_get_param(e); + if (click_source == NULL || is_scrolling) return; - // Exclude keyboard ? - // if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD || - // lv_indev_get_type(click_source) == LV_INDEV_TYPE_ENCODER) { - // return; - // } - lv_area_t obj_coords; - lv_obj_get_coords(target, &obj_coords); + // Exclude keyboard ? + // if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD || + // lv_indev_get_type(click_source) == LV_INDEV_TYPE_ENCODER) { + // return; + // } + lv_area_t obj_coords; + lv_obj_get_coords(target, &obj_coords); - lv_point_t point_act; - lv_indev_get_point(click_source, &point_act); + lv_point_t point_act; + lv_indev_get_point(click_source, &point_act); - // Ignore event from keypad - if(point_act.x < 0 || point_act.y < 0) return; + // Ignore event from keypad + if (point_act.x < 0 || point_act.y < 0) return; - lv_point_t rel_pos; - rel_pos.x = point_act.x - obj_coords.x1; - rel_pos.y = point_act.y - obj_coords.y1; + lv_point_t rel_pos; + rel_pos.x = point_act.x - obj_coords.x1; + rel_pos.y = point_act.y - obj_coords.y1; - auto w = (Window*)lv_obj_get_user_data(target); - if (!w) return; + rel_pos.x += window->getScrollPositionX(); + rel_pos.y += window->getScrollPositionY(); - rel_pos.x += w->getScrollPositionX(); - rel_pos.y += w->getScrollPositionY(); - - if (code == LV_EVENT_PRESSED) { - TRACE("PRESSED[%d|%d]", rel_pos.x, rel_pos.y); - w->onTouchStart(rel_pos.x, rel_pos.y); - } else { + if (code == LV_EVENT_PRESSED) { + TRACE("PRESSED[%d|%d]", rel_pos.x, rel_pos.y); + window->onTouchStart(rel_pos.x, rel_pos.y); + } else { + lv_point_t vect_act; + lv_indev_get_vect(click_source, &vect_act); - lv_point_t vect_act; - lv_indev_get_vect(click_source, &vect_act); - - TRACE("RELEASED[%d|%d] vect[%d|%d]", - rel_pos.x, rel_pos.y, - vect_act.x, vect_act.y, - lv_indev_get_scroll_dir(click_source)); + TRACE("RELEASED[%d|%d] vect[%d|%d]", rel_pos.x, rel_pos.y, vect_act.x, + vect_act.y, lv_indev_get_scroll_dir(click_source)); - if (vect_act.x != 0 || vect_act.y != 0) return; - w->onTouchEnd(rel_pos.x, rel_pos.y); - } - } break; - - case LV_EVENT_CLICKED: - TRACE("CLICKED"); - break; - - case LV_EVENT_KEY: - // do legacy processing - MainWindow::instance()->checkEvents(); - break; - - case LV_EVENT_SCROLL: { - lv_coord_t scroll_y = lv_obj_get_scroll_y(target); - lv_coord_t scroll_x = lv_obj_get_scroll_x(target); - TRACE("SCROLL[x=%d;y=%d]", scroll_x, scroll_y); - Window *w = (Window *)lv_obj_get_user_data(target); - if (w) { -#if defined(DEBUG_WINDOWS) - TRACE("%s", w->getWindowDebugString().c_str()); -#endif - w->setScrollPositionY(scroll_y); - w->setScrollPositionX(scroll_x); - } - } break; - case LV_EVENT_SCROLL_BEGIN: - TRACE("SCROLL_BEGIN"); - is_scrolling = true; - break; - case LV_EVENT_SCROLL_END: - TRACE("SCROLL_END"); - is_scrolling = false; - break; - case LV_EVENT_FOCUSED: { - bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); - bool loiu_focused = ((Window *)target->user_data)->hasFocus(); - TRACE("FOCUSED[%d|%d]", lvgl_focused, loiu_focused); - if (!loiu_focused) { - ((Window *)target->user_data)->setFocus(); - } - } break; + if (vect_act.x != 0 || vect_act.y != 0) return; + window->onTouchEnd(rel_pos.x, rel_pos.y); } + return; + } else if (code == LV_EVENT_SCROLL) { + lv_coord_t scroll_y = lv_obj_get_scroll_y(target); + lv_coord_t scroll_x = lv_obj_get_scroll_x(target); + TRACE_WINDOWS("SCROLL[x=%d;y=%d] %s", scroll_x, scroll_y, + window->getWindowDebugString().c_str()); + window->setScrollPositionY(scroll_y); + window->setScrollPositionX(scroll_x); + return; + } else if (code == LV_EVENT_SCROLL_BEGIN) { + TRACE("SCROLL_BEGIN"); + is_scrolling = true; + return; + } else if (code == LV_EVENT_SCROLL_END) { + TRACE("SCROLL_END"); + is_scrolling = false; + return; + } else if (code == LV_EVENT_FOCUSED) { + bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); + bool loiu_focused = ((Window *)target->user_data)->hasFocus(); + TRACE_WINDOWS("FOCUSED[%d|%d] %s", + lvgl_focused, loiu_focused, + window->getWindowDebugString().c_str()); + if (!loiu_focused) { window->setFocus(); } + return; } } @@ -165,7 +138,6 @@ Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, Lc windowFactory.construct(lvParent) : factory->construct(lvParent); - lv_obj_add_flag(lvobj, LV_OBJ_FLAG_EVENT_BUBBLE); lv_obj_add_style(lvobj, &windowFactory.style, LV_PART_MAIN); lv_obj_set_pos(lvobj, rect.x, rect.y); From 64a834d5846f7b68c34a70ac9e6602287c851ce4 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 26 Jan 2022 08:40:43 +0100 Subject: [PATCH 497/714] Small cleanup --- radio/src/thirdparty/libopenui/src/window.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index fff38f17b..234ac60cc 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -42,10 +42,8 @@ static void window_event_cb(lv_event_t * e) lv_point_t *p = (lv_point_t *)lv_event_get_param(e); if (p->x >= 0) { p->x = window->getInnerWidth(); } if (p->y >= 0) { p->y = window->getInnerHeight(); } - return; } else if (code == LV_EVENT_DRAW_MAIN) { TRACE_WINDOWS("DRAW_MAIN %s", window->getWindowDebugString().c_str()); - return; } else if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) { TRACE_WINDOWS("PRESSED: %s", window->getWindowDebugString().c_str()); @@ -86,7 +84,6 @@ static void window_event_cb(lv_event_t * e) if (vect_act.x != 0 || vect_act.y != 0) return; window->onTouchEnd(rel_pos.x, rel_pos.y); } - return; } else if (code == LV_EVENT_SCROLL) { lv_coord_t scroll_y = lv_obj_get_scroll_y(target); lv_coord_t scroll_x = lv_obj_get_scroll_x(target); @@ -94,15 +91,12 @@ static void window_event_cb(lv_event_t * e) window->getWindowDebugString().c_str()); window->setScrollPositionY(scroll_y); window->setScrollPositionX(scroll_x); - return; } else if (code == LV_EVENT_SCROLL_BEGIN) { TRACE("SCROLL_BEGIN"); is_scrolling = true; - return; } else if (code == LV_EVENT_SCROLL_END) { TRACE("SCROLL_END"); is_scrolling = false; - return; } else if (code == LV_EVENT_FOCUSED) { bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); bool loiu_focused = ((Window *)target->user_data)->hasFocus(); @@ -110,7 +104,6 @@ static void window_event_cb(lv_event_t * e) lvgl_focused, loiu_focused, window->getWindowDebugString().c_str()); if (!loiu_focused) { window->setFocus(); } - return; } } From ca5b6a121c35aae2c5a110504f3d1534b7562adb Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 26 Jan 2022 12:22:00 +0100 Subject: [PATCH 498/714] Do not use lv_obj_create(nullptr) --- .../thirdparty/libopenui/src/modal_window.cpp | 6 +----- radio/src/thirdparty/libopenui/src/window.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 37c9ec9b6..b33983164 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -22,12 +22,8 @@ #include "font.h" #include "layer.h" -LvglWidgetFactory modalFactory = LvglWidgetFactory([] (lv_obj_t *parent) { - return lv_obj_create(nullptr); -}); - ModalWindow::ModalWindow(Window * parent, bool closeWhenClickOutside): - Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}, 0, 0, &modalFactory), + Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}), closeWhenClickOutside(closeWhenClickOutside) { Layer::push(this); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 234ac60cc..c5b585c53 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -118,20 +118,20 @@ LvglWidgetFactory windowFactory = LvglWidgetFactory( lv_style_set_radius(&factory->style, 0); }); -Window::Window(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags, LvglWidgetFactory *factory) : - parent(parent), - rect(rect), - innerWidth(rect.w), - innerHeight(rect.h), - windowFlags(windowFlags), - textFlags(textFlags) +Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, + LcdFlags textFlags, LvglWidgetFactory *factory) : + parent(parent), + rect(rect), + innerWidth(rect.w), + innerHeight(rect.h), + windowFlags(windowFlags), + textFlags(textFlags) { lv_obj_t *lvParent = parent != nullptr ? parent->lvobj : nullptr; lvobj = (factory == nullptr) ? windowFactory.construct(lvParent) : factory->construct(lvParent); - lv_obj_add_style(lvobj, &windowFactory.style, LV_PART_MAIN); lv_obj_set_pos(lvobj, rect.x, rect.y); lv_obj_set_size(lvobj, rect.w, rect.h); From 551d7ae7667eeb251689587df3f6ef24faa4dd70 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 26 Jan 2022 06:40:17 -0500 Subject: [PATCH 499/714] keyboard changes --- .../thirdparty/libopenui/src/basenumberedit.h | 4 +- .../libopenui/src/keyboard_base.cpp | 60 ++++ .../thirdparty/libopenui/src/keyboard_base.h | 42 +-- .../libopenui/src/keyboard_number.cpp | 86 +++--- .../libopenui/src/keyboard_number.h | 6 +- .../libopenui/src/keyboard_text.cpp | 269 +----------------- .../thirdparty/libopenui/src/keyboard_text.h | 10 +- .../thirdparty/libopenui/src/numberedit.cpp | 79 ++--- .../src/thirdparty/libopenui/src/numberedit.h | 2 - .../src/thirdparty/libopenui/src/textedit.cpp | 84 +----- radio/src/thirdparty/libopenui/src/textedit.h | 3 +- 11 files changed, 159 insertions(+), 486 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index cefe8c081..525618634 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -25,8 +25,8 @@ class BaseNumberEdit: public FormField { public: BaseNumberEdit(Window * parent, const rect_t &rect, int vmin, int vmax, - std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0): - FormField(parent, rect, windowFlags, textFlags), + std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr): + FormField(parent, rect, windowFlags, textFlags, factory), vmin(vmin), vmax(vmax), _getValue(std::move(getValue)), diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 1295fec1c..a02e5897e 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -21,6 +21,66 @@ Keyboard * Keyboard::activeKeyboard = nullptr; lv_obj_t *Keyboard::keyboard = nullptr; +static void keyboard_event_cb(lv_event_t * e) +{ + auto code = lv_event_get_code(e); + Keyboard *kb = (Keyboard *) lv_event_get_user_data(e); + if (code == LV_EVENT_READY) { + pushEvent(EVT_VIRTUAL_KEY('\n')); + kb->hide(); + } +} + + +Keyboard::Keyboard(coord_t height) : + FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) +{ + // set the background of the window and opacity to 100% + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); + + if (keyboard == nullptr) { + keyboard = lv_keyboard_create(lv_scr_act()); + lv_obj_add_event_cb(keyboard, keyboard_event_cb, LV_EVENT_ALL, this); + + + lv_obj_set_size(keyboard, LCD_W, LCD_H * 2 / 5); + lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN); + } +} + +void Keyboard::clearField() +{ + TRACE("CLEAR FIELD"); + if (keyboard != nullptr) { + lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); + } + + detach(); + if (fields) { + fields->setHeight(oldHeight); + fields = nullptr; + } + if (fieldContainer) { + fieldContainer->setHeight(LCD_H - 0 - fieldContainer->top()); + fieldContainer = nullptr; + } + if (field) { + field->setEditMode(false); + field->changeEnd(); + field = nullptr; + } +} + +void Keyboard::hide() +{ + if (activeKeyboard) { + activeKeyboard->clearField(); + activeKeyboard = nullptr; + } +} + + coord_t calcScrollOffsetForField(FormField *newField, Window *topWindow) { // now we need to calculate the offset of the field in the fields scroll container diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index bb717ff36..d0c729254 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -25,45 +25,9 @@ class Keyboard: public FormWindow { public: - explicit Keyboard(coord_t height) : - FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) - { - // set the background of the window and opacity to 100% - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); - lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); - } - - void clearField() - { - TRACE("CLEAR FIELD"); - if (keyboard != nullptr) { - lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); - } - - detach(); - if (fields) { - fields->setHeight(oldHeight); - fields = nullptr; - } - if (fieldContainer) { - fieldContainer->setHeight(LCD_H - 0 - fieldContainer->top()); - fieldContainer = nullptr; - } - if (field) { - field->setEditMode(false); - field->changeEnd(); - field = nullptr; - } - } - - static void hide() - { - if (activeKeyboard) { - activeKeyboard->clearField(); - activeKeyboard = nullptr; - } - } - + explicit Keyboard(coord_t height); + void clearField(); + static void hide(); protected: static Keyboard * activeKeyboard; diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index daf04c1f4..7308663b9 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -22,59 +22,58 @@ #include "libopenui_globals.h" constexpr coord_t KEYBOARD_HEIGHT = 90; - NumberKeyboard * NumberKeyboard::_instance = nullptr; NumberKeyboard::NumberKeyboard() : Keyboard(KEYBOARD_HEIGHT) { - new TextButton(this, {LCD_W / 2 - 115, 10, 50, 30}, "<<", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_BACKWARD); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + // new TextButton(this, {LCD_W / 2 - 115, 10, 50, 30}, "<<", + // [=]() -> uint8_t { + // pushEvent(EVT_VIRTUAL_KEY_BACKWARD); + // return 0; + // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_MINUS); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + // new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", + // [=]() -> uint8_t { + // pushEvent(EVT_VIRTUAL_KEY_MINUS); + // return 0; + // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_PLUS); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + // new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", + // [=]() -> uint8_t { + // pushEvent(EVT_VIRTUAL_KEY_PLUS); + // return 0; + // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_FORWARD); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + // new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", + // [=]() -> uint8_t { + // pushEvent(EVT_VIRTUAL_KEY_FORWARD); + // return 0; + // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_MIN); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + // new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", + // [=]() -> uint8_t { + // pushEvent(EVT_VIRTUAL_KEY_MIN); + // return 0; + // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_MAX); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + // new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", + // [=]() -> uint8_t { + // pushEvent(EVT_VIRTUAL_KEY_MAX); + // return 0; + // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - new TextButton(this, { LCD_W/2 - 55, 50, 50, 30 }, "DEF", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_DEFAULT); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + // new TextButton(this, { LCD_W/2 - 55, 50, 50, 30 }, "DEF", + // [=]() -> uint8_t { + // pushEvent(EVT_VIRTUAL_KEY_DEFAULT); + // return 0; + // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - new TextButton(this, { LCD_W/2 + 5, 50, 50, 30 }, "+/-", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_SIGN); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + // new TextButton(this, { LCD_W/2 + 5, 50, 50, 30 }, "+/-", + // [=]() -> uint8_t { + // pushEvent(EVT_VIRTUAL_KEY_SIGN); + // return 0; + // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); } NumberKeyboard::~NumberKeyboard() @@ -82,8 +81,3 @@ NumberKeyboard::~NumberKeyboard() _instance = nullptr; } -void NumberKeyboard::paint(BitmapBuffer * dc) -{ - lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); - dc->clear(CUSTOM_COLOR); -} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h index 4f8fca93c..67794a1f0 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -38,11 +38,13 @@ class NumberKeyboard : public Keyboard { { if (!_instance) _instance = new NumberKeyboard(); + else + lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + + lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_NUMBER); _instance->setField(field); } - void paint(BitmapBuffer * dc) override; - protected: static NumberKeyboard * _instance; }; diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 75a8b52bc..109d0c4a1 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -23,283 +23,16 @@ #include "font.h" constexpr coord_t KEYBOARD_HEIGHT = LCD_H * 2 / 5; - TextKeyboard * TextKeyboard::_instance = nullptr; -// const uint8_t LBM_KEY_UPPERCASE[] = { -// #include "mask_key_uppercase.lbm" -// }; - -// const uint8_t LBM_KEY_LOWERCASE[] = { -// #include "mask_key_lowercase.lbm" -// }; - -// const uint8_t LBM_KEY_BACKSPACE[] = { -// #include "mask_key_backspace.lbm" -// }; - -// const uint8_t LBM_KEY_LETTERS[] = { -// #include "mask_key_letters.lbm" -// }; - -// const uint8_t LBM_KEY_NUMBERS[] = { -// #include "mask_key_numbers.lbm" -// }; - -// const uint8_t LBM_KEY_SPACEBAR[] = { -// #include "mask_key_spacebar.lbm" -// }; - -// const uint8_t * const LBM_SPECIAL_KEYS[] = { -// LBM_KEY_BACKSPACE, -// LBM_KEY_UPPERCASE, -// LBM_KEY_LOWERCASE, -// LBM_KEY_LETTERS, -// LBM_KEY_NUMBERS, -// }; - -// const char * const KEYBOARD_LOWERCASE[] = { -// "qwertyuiop", -// " asdfghjkl", -// KEYBOARD_SET_UPPERCASE "zxcvbnm" KEYBOARD_BACKSPACE, -// KEYBOARD_SET_NUMBERS KEYBOARD_SPACE KEYBOARD_ENTER -// }; - -// const char * const KEYBOARD_UPPERCASE[] = { -// "QWERTYUIOP", -// " ASDFGHJKL", -// KEYBOARD_SET_LOWERCASE "ZXCVBNM" KEYBOARD_BACKSPACE, -// KEYBOARD_SET_NUMBERS KEYBOARD_SPACE KEYBOARD_ENTER -// }; - -// const char * const KEYBOARD_NUMBERS[] = { -// "1234567890", -// "_-", -// " " KEYBOARD_BACKSPACE, -// KEYBOARD_SET_LETTERS KEYBOARD_SPACE KEYBOARD_ENTER -// }; - -// #define NUMBER_OF_KEYBOARD_LAYOUTS 4 -// const char * const * const KEYBOARD_LAYOUTS[NUMBER_OF_KEYBOARD_LAYOUTS] = { -// KEYBOARD_UPPERCASE, -// KEYBOARD_LOWERCASE, -// KEYBOARD_LOWERCASE, -// KEYBOARD_NUMBERS, -// }; - -TextKeyboard::TextKeyboard(): +TextKeyboard::TextKeyboard() : Keyboard(KEYBOARD_HEIGHT) - // layout(KEYBOARD_LOWERCASE) { - if (keyboard == nullptr) { - keyboard = lv_keyboard_create(lv_scr_act()); - lv_obj_set_size(keyboard, LCD_W, LCD_H * 2 / 5); - lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN); - } } TextKeyboard::~TextKeyboard() { _instance = nullptr; - -} - -int TextKeyboard::getCharWidth(const char c) -{ - // if (int8_t(c) < 0) return KEYBOARD_BITMAP_WIDTH; - - // switch (c) - // { - // case ' ': return 15; - // case KEYBOARD_SPACE[0]: return KEYBOARD_SPACE_WIDTH; - // case KEYBOARD_ENTER[0]: return KEYBOARD_ENTER_WIDTH; - // default: return KEYBOARD_CHAR_WIDTH; - // } -} - - -int TextKeyboard::calculateMaxWidth() -{ - // int maxWidth = 0; - // for (uint8_t i = 0; i < NUMBER_OF_KEYBOARD_LAYOUTS; i++) { - // int lineWidth = 0; - // const char * c = layout[i]; - // while (*c) { - // lineWidth += getCharWidth(*c); - // maxWidth = max(lineWidth, maxWidth); - // c++; - // } - // } - - // return maxWidth; -} - -void TextKeyboard::paint(BitmapBuffer * dc) -{ - // lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); - // dc->clear(CUSTOM_COLOR); - - // // center the keyboard - // coord_t start_x = (dc->width() - calculateMaxWidth()) / 2; - - // for (uint8_t i = 0; i < NUMBER_OF_KEYBOARD_LAYOUTS; i++) { - // coord_t y = KEYBOARD_OFFSET_Y + i * KEYBOARD_ROW_HEIGHT; - // coord_t x = start_x; - // const char * c = layout[i]; - - // while (*c) { - // if (*c == KEYBOARD_SPACE[0]) { - // // spacebar - // dc->drawBitmapPattern(x, y, LBM_KEY_SPACEBAR, COLOR_THEME_SECONDARY1); - // } - // else if (*c == KEYBOARD_ENTER[0]) { - // // enter - // dc->drawSolidFilledRect(x, y - 2, KEYBOARD_ENTER_WIDTH, 25, COLOR_THEME_DISABLED); - // dc->drawText(x + KEYBOARD_ENTER_WIDTH / 2, y + 2, "ENTER", CENTERED); - // } - // else if (int8_t(*c) < 0) { - // const uint8_t *pBitmap = LBM_SPECIAL_KEYS[uint8_t(*c - 128)]; - // uint8_t bitmapWidth = *pBitmap, bitmapHeight = *(pBitmap + 2); - // if (touched && *c == touch_key) { - // dc->drawSolidFilledRect(x - 2, y + 3, bitmapWidth + 4, bitmapHeight + 4, COLOR_THEME_SECONDARY2); - // } - - // dc->drawBitmapPattern(x, y + 5, pBitmap, COLOR_THEME_SECONDARY1); - // } - // else { - // if (touched && *c == touch_key) { - // dc->drawSolidFilledRect(x - KEYBOARD_CHAR_WIDTH / 2, y - ((KEYBOARD_ROW_HEIGHT - 16) / 2), KEYBOARD_CHAR_WIDTH, KEYBOARD_ROW_HEIGHT, COLOR_THEME_FOCUS); - // } - // dc->drawSizedText(x, y, c, 1, CENTERED); - // } - // x += getCharWidth(*c); - // c++; - // } - // } -} - -bool TextKeyboard::onTouchStart(coord_t x, coord_t y) -{ - // // remove the centering. If x < 0 then this is an invalid touch event - // x -= (this->width() - calculateMaxWidth()) / 2; - // if (x < 0) { - // if (touched) { - // touched = false; - // invalidate(); - // } - // return true; - // } - - // touched = false; - - // uint8_t row = max(0, y - 5) / KEYBOARD_ROW_HEIGHT; - // const char * key = layout[row]; - - // while(*key) { - // if (*key == KEYBOARD_SPACE[0] && x <= KEYBOARD_SPACE_WIDTH) { - // touch_key = KEYBOARD_SPACE[0]; - // touched = true; - // break; - // } - // else if (*key == KEYBOARD_ENTER[0] && x <= KEYBOARD_ENTER_WIDTH) { - // touch_key = KEYBOARD_ENTER[0]; - // touched = true; - // break; - // } - // else if (int8_t(*key) < 0 && x <= KEYBOARD_BITMAP_WIDTH) { - // if (uint8_t(*key) == 128) { - // touch_key = KEYBOARD_BACKSPACE[0]; - // touched = true; - // break; - // } else { - // touched = false; - // break; - // } - // } - // else if (*key != ' ' && (x >= -(KEYBOARD_CHAR_WIDTH / 2) && x <= KEYBOARD_CHAR_WIDTH / 2)) { - // touch_key = *key; - // touched = true; - // break; - // } - - // x -= getCharWidth(*key); - // key++; - - // if (touched) { - // invalidate(); - // break; - // } - // } - // return true; -} - -bool TextKeyboard::onTouchEnd(coord_t x, coord_t y) -{ - // // remove the centering width, if x < 0 then this is an invalid touch on a centered keyboard. - // x -= (this->width() - calculateMaxWidth()) / 2; - // if (x < 0) { - // touched = false; - // return true; - // } - - // onKeyPress(); - - // if (touched) - // invalidate(); - // touched = false; - - // uint8_t row = max(0, y - 5) / KEYBOARD_ROW_HEIGHT; - - // const char * key = layout[row]; - // while (*key) { - // if (*key == KEYBOARD_SPACE[0]) { - // if (x <= KEYBOARD_SPACE_WIDTH) { - // pushEvent(EVT_VIRTUAL_KEY(' ')); - // return true; - // } - // } - // else if (*key == KEYBOARD_ENTER[0]) { - // if (x <= KEYBOARD_ENTER_WIDTH) { - // pushEvent(EVT_VIRTUAL_KEY(KEYBOARD_ENTER[0])); - // // enter - // hide(); - // return true; - // } - // } - // else if (int8_t(*key) < 0) { - // if (x <= KEYBOARD_BITMAP_WIDTH) { - // uint8_t specialKey = *key; - // if (specialKey == 128) { - // // backspace - // pushEvent(EVT_VIRTUAL_KEY(KEYBOARD_BACKSPACE[0])); - // } - // else { - // layout = KEYBOARD_LAYOUTS[specialKey - 129]; - // invalidate(); - // } - // break; - // } - // } - // else { - // if (*key != ' ' && (x >= -(KEYBOARD_CHAR_WIDTH / 2) && x <= KEYBOARD_CHAR_WIDTH / 2)) { - // pushEvent(EVT_VIRTUAL_KEY(*key)); - // return true; - // } - // } - // x -= getCharWidth(*key); - // key++; - // } - - // return true; -} - -bool TextKeyboard::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) -{ - // if (touched) - // invalidate(); - - // touched = false; - // return false; } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index 462016fc5..11547f50d 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -55,17 +55,11 @@ class TextKeyboard : public Keyboard { _instance = new TextKeyboard(); else lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + + lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_TEXT_LOWER); _instance->setField(field); } - void paint(BitmapBuffer * dc) override; - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; - bool onTouchStart(coord_t x, coord_t y) override; - bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; -#endif - protected: #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 70e6d6889..6c2a29a63 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -23,46 +23,57 @@ #include "keyboard_number.h" #endif + +static LvglWidgetFactory numberEditFactory = { lv_textarea_create, nullptr }; +static lv_style_t style_main; +static lv_style_t style_edit; + NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags, LcdFlags textFlags): - BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags) + BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags, &numberEditFactory) { -} + // properties + lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); + lv_textarea_set_password_mode(lvobj, false); + lv_textarea_set_one_line(lvobj, true); + lv_textarea_set_accepted_chars(lvobj, "0123456789"); -void NumberEdit::paint(BitmapBuffer * dc) -{ - FormField::paint(dc); + auto value = _getValue(); + auto strValue = std::to_string(value); + lv_textarea_set_text(lvobj, strValue.c_str()); - auto value = getValue(); + // LV_PART_MAIN + lv_style_init(&style_main); + lv_style_set_border_width(&style_main, 1); + lv_style_set_border_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY2)); + lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_PRIMARY2)); + lv_style_set_bg_opa(&style_main, LV_OPA_COVER); + //lv_style_set_radius(&style_main, 0); + lv_style_set_text_font(&style_main, &lv_font_montserrat_12); + lv_style_set_text_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1)); + lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN); - LcdFlags textColor; - if (editMode) - textColor = COLOR_THEME_PRIMARY2; - else if (hasFocus()) - textColor = COLOR_THEME_PRIMARY2; - // else if (enabled && (value != 0 || zeroText.empty())) - // textColor = COLOR_THEME_SECONDARY1; - else - textColor = COLOR_THEME_SECONDARY1; + // LV_STATE_FOCUSED + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), + LV_PART_MAIN | LV_STATE_FOCUSED); - if (displayFunction) { - displayFunction(dc, textColor, value); - } else if (value == 0 && !zeroText.empty()) { - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), - textColor | textFlags); - if (textFlags & RIGHT) - dc->drawText(rect.w - FIELD_PADDING_LEFT, FIELD_PADDING_TOP, - zeroText.c_str(), textColor | textFlags); - else - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, zeroText.c_str(), - textColor | textFlags); - } else { - if (textFlags & RIGHT) - dc->drawNumber(rect.w - FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, - textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); - else - dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, - textColor | textFlags, 0, prefix.c_str(), suffix.c_str()); - } + lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), + LV_PART_MAIN | LV_STATE_FOCUSED); + + // Hide cursor + lv_obj_set_style_opa(lvobj, 0, LV_PART_CURSOR); + + // Show Cursor in "Edit" mode + lv_style_init(&style_edit); + lv_style_set_opa(&style_edit, LV_OPA_COVER); + lv_style_set_bg_opa(&style_edit, LV_OPA_COVER); + lv_style_set_pad_left(&style_edit, (lv_coord_t)-(FIELD_PADDING_LEFT+2)); + lv_style_set_pad_top(&style_edit, (lv_coord_t)-(FIELD_PADDING_TOP+2)); + lv_obj_add_style(lvobj, &style_edit, LV_PART_CURSOR | LV_STATE_EDITED); + + // Text padding + auto label = lv_textarea_get_label(lvobj); + lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); + lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); } void NumberEdit::onEvent(event_t event) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index a44d35fbd..edcd5a5ca 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -34,8 +34,6 @@ class NumberEdit : public BaseNumberEdit } #endif - void paint(BitmapBuffer * dc) override; - void setAvailableHandler(std::function handler) { isValueAvailable = std::move(handler); diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 09cd291ab..38a795193 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -42,31 +42,6 @@ #include "clipboard.h" #endif - -static lv_obj_t *virtual_kb = nullptr; - -static void ta_event_cb(lv_event_t *e) -{ - // lv_event_code_t code = lv_event_get_code(e); - // lv_obj_t *ta = lv_event_get_target(e); - // if (code == LV_EVENT_CLICKED) { - // if (virtual_kb == nullptr) { - // virtual_kb = lv_keyboard_create(lv_scr_act()); - // lv_obj_set_size(virtual_kb, LV_HOR_RES, LV_VER_RES / 2); - // } - - // if (virtual_kb != NULL) { - // lv_keyboard_set_textarea(virtual_kb, ta); - // lv_obj_add_state(ta, LV_STATE_EDITED); - // } - // } else if (code == LV_EVENT_DEFOCUSED || code == LV_EVENT_DELETE) { - // if (virtual_kb != nullptr) { - // lv_obj_del(virtual_kb); - // virtual_kb = nullptr; - // } - // } -} - static lv_style_t style_main; static lv_style_t style_edit; static LvglWidgetFactory textEditFactory = { lv_textarea_create, nullptr }; @@ -81,9 +56,6 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, { extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; - // callbacks - lv_obj_add_event_cb(lvobj, ta_event_cb, LV_EVENT_ALL, NULL); - // properties lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_textarea_set_password_mode(lvobj, false); @@ -127,45 +99,6 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); } -void TextEdit::paint(BitmapBuffer * dc) -{ -// FormField::paint(dc); - -// if (editMode) { -// dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, length, -// COLOR_THEME_PRIMARY2); -// coord_t left = (cursorPos == 0 ? 0 : getTextWidth(value, cursorPos)); -// #if defined(SOFTWARE_KEYBOARD) -// dc->drawSolidFilledRect(left + 2, 2, 2, height() - 4, COLOR_THEME_PRIMARY2); -// #else -// char s[] = {value[cursorPos], '\0'}; -// dc->drawSolidFilledRect(FIELD_PADDING_LEFT + left - 1, -// FIELD_PADDING_TOP - 1, getTextWidth(s, 1) + 1, -// height() - 2, COLOR_THEME_PRIMARY2); -// dc->drawText(FIELD_PADDING_LEFT + left, FIELD_PADDING_TOP, s, -// COLOR_THEME_SECONDARY1); -// #endif -// } else { -// const char * displayedValue = value; -// LcdFlags textColor = COLOR_THEME_PRIMARY2; -// if (hasFocus()) { -// if (strlen(value) == 0) { -// displayedValue = "---"; -// } -// textColor = COLOR_THEME_PRIMARY2; -// } -// else { -// if (strlen(value) == 0) { -// displayedValue = "---"; -// textColor = COLOR_THEME_DISABLED; -// } -// else { -// textColor = COLOR_THEME_SECONDARY1; -// } -// } -// dc->drawSizedText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, displayedValue, length, textColor); -// } -} void TextEdit::trim() { @@ -184,24 +117,9 @@ void TextEdit::onEvent(event_t event) #if defined(HARDWARE_TOUCH) if (IS_VIRTUAL_KEY_EVENT(event)) { uint8_t c = event & 0xFF; - if (c == (uint8_t)KEYBOARD_BACKSPACE[0]) { - if (cursorPos > 0) { - memmove(value + cursorPos - 1, value + cursorPos, length - cursorPos); - value[length - 1] = '\0'; - --cursorPos; - invalidate(); - changed = true; - } - } - else if (c == (uint8_t)KEYBOARD_ENTER[0]) { + if (c == (uint8_t)KEYBOARD_ENTER[0]) { changeEnd(); } - else if (cursorPos < length) { - memmove(value + cursorPos + 1, value + cursorPos, length - cursorPos - 1); - value[cursorPos++] = c; - invalidate(); - changed = true; - } } #endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 779c811f1..bf7b65b19 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -46,8 +46,6 @@ class TextEdit : public FormField { return value; } - void paint(BitmapBuffer * dc) override; - void onEvent(event_t event) override; #if defined(HARDWARE_TOUCH) @@ -67,6 +65,7 @@ class TextEdit : public FormField { void changeEnd(bool forceChanged = false) override { + strncpy(value, lv_textarea_get_text(lvobj), length); cursorPos = 0; if (changed || forceChanged) { changed = false; From 0284a520fa14a94c6405c50a643c9aa7c3fde566 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 27 Jan 2022 10:44:42 -0500 Subject: [PATCH 500/714] keyboard changes make window honor no focus. --- .../thirdparty/libopenui/src/basenumberedit.h | 48 ++++++++++- radio/src/thirdparty/libopenui/src/button.cpp | 19 ++--- .../libopenui/src/keyboard_base.cpp | 22 +++-- .../thirdparty/libopenui/src/keyboard_base.h | 3 +- .../libopenui/src/keyboard_number.cpp | 82 ++++++++++--------- .../libopenui/src/keyboard_number.h | 12 ++- .../thirdparty/libopenui/src/keyboard_text.h | 6 +- .../thirdparty/libopenui/src/numberedit.cpp | 4 +- .../src/thirdparty/libopenui/src/numberedit.h | 24 ------ radio/src/thirdparty/libopenui/src/window.cpp | 7 +- 10 files changed, 122 insertions(+), 105 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 525618634..3cf6387e6 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -20,6 +20,7 @@ #pragma once #include "form.h" +#include "bitmapbuffer.h" class BaseNumberEdit: public FormField { @@ -76,11 +77,40 @@ class BaseNumberEdit: public FormField void setValue(int value) { - currentValue = limit(vmin, value, vmax); - if (instantChange) { - _setValue(currentValue); + auto newValue = limit(vmin, value, vmax); + if (newValue != currentValue) { + currentValue = newValue; + if (instantChange) { + _setValue(currentValue); + } } - invalidate(); + if (lvobj != nullptr) { + if (displayFunction != nullptr) { + lv_textarea_set_text(lvobj, displayFunction(currentValue).c_str()); + } else { + char s[50]; + BitmapBuffer::formatNumberAsString(s, 49, currentValue, textFlags, 0, prefix.c_str(), suffix.c_str()); + lv_textarea_set_text(lvobj, s); + } + } + } + + void setPrefix(std::string value) + { + prefix = std::move(value); + setValue(currentValue); + } + + void setSuffix(std::string value) + { + suffix = std::move(value); + setValue(currentValue); + } + + void setZeroText(std::string value) + { + zeroText = std::move(value); + setValue(currentValue); } void enableInstantChange(bool value) @@ -117,6 +147,12 @@ class BaseNumberEdit: public FormField } } + void setDisplayHandler(std::function function) + { + displayFunction = std::move(function); + setValue(currentValue); + } + protected: int vdefault = 0; int vmin; @@ -124,6 +160,10 @@ class BaseNumberEdit: public FormField int step = 1; int currentValue; bool instantChange = true; + std::string prefix; + std::string suffix; + std::string zeroText; std::function _getValue; std::function _setValue; + std::function displayFunction; }; diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index b527751b9..d59a3c5a0 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -25,19 +25,14 @@ void Button::onPress() { bool check = (pressHandler && pressHandler()); - if (check != bool(windowFlags & BUTTON_CHECKED)) { - windowFlags ^= BUTTON_CHECKED; + windowFlags = check ? windowFlags | BUTTON_CHECKED : windowFlags & ~BUTTON_CHECKED; + if(lvobj == nullptr) + return; - if(lvobj == nullptr) - return; - - if (! (windowFlags & BUTTON_CHECKED)) - lv_obj_clear_state(lvobj, LV_STATE_CHECKED); - else - lv_obj_add_state(lvobj, LV_STATE_CHECKED); - - // invalidate(); - } + if (! (windowFlags & BUTTON_CHECKED)) + lv_obj_clear_state(lvobj, LV_STATE_CHECKED); + else + lv_obj_add_state(lvobj, LV_STATE_CHECKED); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index a02e5897e..7cd47f006 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -17,9 +17,9 @@ * Lesser General Public License for more details. */ #include "keyboard_base.h" +#include "mainwindow.h" Keyboard * Keyboard::activeKeyboard = nullptr; -lv_obj_t *Keyboard::keyboard = nullptr; static void keyboard_event_cb(lv_event_t * e) { @@ -33,20 +33,18 @@ static void keyboard_event_cb(lv_event_t * e) Keyboard::Keyboard(coord_t height) : - FormWindow(nullptr, {0, LCD_H - height, LCD_W, height}, OPAQUE) + FormWindow(MainWindow::instance(), {0, LCD_H - height, LCD_W, height}, OPAQUE) { // set the background of the window and opacity to 100% - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); + lv_obj_set_parent(lvobj, lv_layer_top()); // the keyboard is always on top + lv_obj_set_style_bg_color(lvobj, lv_color_make(0xE0, 0xE0, 0xE0), LV_PART_MAIN); lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); - if (keyboard == nullptr) { - keyboard = lv_keyboard_create(lv_scr_act()); - lv_obj_add_event_cb(keyboard, keyboard_event_cb, LV_EVENT_ALL, this); - - - lv_obj_set_size(keyboard, LCD_W, LCD_H * 2 / 5); - lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN); - } + keyboard = lv_keyboard_create(this->getLvObj()); + lv_obj_add_event_cb(keyboard, keyboard_event_cb, LV_EVENT_ALL, this); + lv_obj_set_pos(keyboard, 0, 0); + lv_obj_set_size(keyboard, LCD_W, height); + lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN); } void Keyboard::clearField() @@ -76,6 +74,7 @@ void Keyboard::hide() { if (activeKeyboard) { activeKeyboard->clearField(); + lv_obj_add_flag(activeKeyboard->lvobj, LV_OBJ_FLAG_HIDDEN); activeKeyboard = nullptr; } } @@ -106,7 +105,6 @@ bool Keyboard::attachKeyboard() } activeKeyboard = this; - attach(MainWindow::instance()); return true; } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index d0c729254..9f41d0bb7 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -19,7 +19,6 @@ #pragma once -#include "mainwindow.h" #include "form.h" class Keyboard: public FormWindow @@ -31,7 +30,7 @@ class Keyboard: public FormWindow protected: static Keyboard * activeKeyboard; - static lv_obj_t * keyboard; + lv_obj_t * keyboard = nullptr; FormField *field = nullptr; Window *fieldContainer = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index 7308663b9..93b1b0d2e 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -27,53 +27,55 @@ NumberKeyboard * NumberKeyboard::_instance = nullptr; NumberKeyboard::NumberKeyboard() : Keyboard(KEYBOARD_HEIGHT) { - // new TextButton(this, {LCD_W / 2 - 115, 10, 50, 30}, "<<", - // [=]() -> uint8_t { - // pushEvent(EVT_VIRTUAL_KEY_BACKWARD); - // return 0; - // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); // ToDo: for now we are still using the old keyboard - // new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", - // [=]() -> uint8_t { - // pushEvent(EVT_VIRTUAL_KEY_MINUS); - // return 0; - // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + new TextButton(this, {LCD_W / 2 - 115, 10, 50, 30}, "<<", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_BACKWARD); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - // new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", - // [=]() -> uint8_t { - // pushEvent(EVT_VIRTUAL_KEY_PLUS); - // return 0; - // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_MINUS); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - // new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", - // [=]() -> uint8_t { - // pushEvent(EVT_VIRTUAL_KEY_FORWARD); - // return 0; - // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_PLUS); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - // new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", - // [=]() -> uint8_t { - // pushEvent(EVT_VIRTUAL_KEY_MIN); - // return 0; - // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_FORWARD); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - // new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", - // [=]() -> uint8_t { - // pushEvent(EVT_VIRTUAL_KEY_MAX); - // return 0; - // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_MIN); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - // new TextButton(this, { LCD_W/2 - 55, 50, 50, 30 }, "DEF", - // [=]() -> uint8_t { - // pushEvent(EVT_VIRTUAL_KEY_DEFAULT); - // return 0; - // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_MAX); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + + new TextButton(this, { LCD_W/2 - 55, 50, 50, 30 }, "DEF", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_DEFAULT); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - // new TextButton(this, { LCD_W/2 + 5, 50, 50, 30 }, "+/-", - // [=]() -> uint8_t { - // pushEvent(EVT_VIRTUAL_KEY_SIGN); - // return 0; - // }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + new TextButton(this, { LCD_W/2 + 5, 50, 50, 30 }, "+/-", + [=]() -> uint8_t { + pushEvent(EVT_VIRTUAL_KEY_SIGN); + return 0; + }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); } NumberKeyboard::~NumberKeyboard() diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h index 67794a1f0..bd1f764f5 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -38,10 +38,16 @@ class NumberKeyboard : public Keyboard { { if (!_instance) _instance = new NumberKeyboard(); - else - lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + // else + // lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + + // lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_NUMBER); + + lv_obj_clear_flag(_instance->lvobj, LV_OBJ_FLAG_HIDDEN); + // lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + lv_keyboard_set_mode(_instance->keyboard, LV_KEYBOARD_MODE_NUMBER); + lv_obj_move_foreground(_instance->getLvObj()); - lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_NUMBER); _instance->setField(field); } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index 11547f50d..0e873de86 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -53,10 +53,10 @@ class TextKeyboard : public Keyboard { { if (!_instance) _instance = new TextKeyboard(); - else - lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); - lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_TEXT_LOWER); + lv_obj_clear_flag(_instance->lvobj, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + lv_keyboard_set_mode(_instance->keyboard, LV_KEYBOARD_MODE_TEXT_LOWER); _instance->setField(field); } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 6c2a29a63..0fdc360ff 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -35,11 +35,9 @@ NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_textarea_set_password_mode(lvobj, false); lv_textarea_set_one_line(lvobj, true); - lv_textarea_set_accepted_chars(lvobj, "0123456789"); auto value = _getValue(); - auto strValue = std::to_string(value); - lv_textarea_set_text(lvobj, strValue.c_str()); + this->setValue(value); // LV_PART_MAIN lv_style_init(&style_main); diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index edcd5a5ca..246b65f28 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -39,26 +39,6 @@ class NumberEdit : public BaseNumberEdit isValueAvailable = std::move(handler); } - void setPrefix(std::string value) - { - prefix = std::move(value); - } - - void setSuffix(std::string value) - { - suffix = std::move(value); - } - - void setZeroText(std::string value) - { - zeroText = std::move(value); - } - - void setDisplayHandler(std::function function) - { - displayFunction = std::move(function); - } - void onEvent(event_t event) override; void onFocusLost() override; @@ -67,10 +47,6 @@ class NumberEdit : public BaseNumberEdit #endif protected: - std::function displayFunction; - std::string prefix; - std::string suffix; - std::string zeroText; std::function isValueAvailable; }; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index c5b585c53..7881d3112 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -147,6 +147,11 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, invalidate(); } } + + // honor the no focus flag of libopenui + if (this->windowFlags & NO_FOCUS) { + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); + } } extern lv_obj_t * canvas; Window::~Window() @@ -181,8 +186,6 @@ void Window::detach() if (parent) { parent->removeChild(this); parent = nullptr; - if(lvobj != nullptr) - lv_obj_set_parent(lvobj, nullptr); } } From a363e89fba125cb81e46b231f9f8b8c9b9d37b1b Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sat, 29 Jan 2022 12:27:41 +0100 Subject: [PATCH 501/714] prepared using the roller via lvgl --- radio/src/thirdparty/libopenui/src/choice.cpp | 8 ++++++ radio/src/thirdparty/libopenui/src/choice.h | 6 +---- radio/src/thirdparty/libopenui/src/form.cpp | 8 +++++- radio/src/thirdparty/libopenui/src/form.h | 8 ++++++ .../thirdparty/libopenui/src/numberedit.cpp | 26 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/window.cpp | 10 ++++++- 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index d7fbf825e..ffc1e2871 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -21,6 +21,14 @@ #include "menu.h" #include "theme.h" +ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, WindowFlags windowFlags): + FormField(parent, rect, windowFlags), + type(type) +{ + +} + + Choice::Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index bea30052a..d6bfc2af3 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -32,11 +32,7 @@ enum ChoiceType { class ChoiceBase : public FormField { public: - ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0): - FormField(parent, rect, windowFlags), - type(type) - { - } + ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0); inline ChoiceType getType() const { diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 995238d31..e04b9271a 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -64,11 +64,17 @@ void FormField::onEvent(event_t event) } } #endif - +extern lv_group_t* inputGroup; void FormField::setFocus(uint8_t flag, Window * from) { if (enabled) { Window::setFocus(flag, from); + if(lvobj != nullptr) + { + lv_group_focus_freeze(inputGroup, false); + lv_group_focus_obj(lvobj); + lv_group_focus_freeze(inputGroup, true); + } } else { if (flag == SET_FOCUS_BACKWARD) { diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index b0b4ba954..c645454f4 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -77,6 +77,14 @@ class FormField: public Window virtual void setEditMode(bool newEditMode) { editMode = newEditMode; + + if(lvobj != nullptr) + { + lv_group_t* grp = (lv_group_t*)lv_obj_get_group(lvobj); + if(grp != nullptr) + lv_group_set_editing(grp, editMode); + } + if (editMode && lvobj != nullptr) { lv_obj_add_state(lvobj, LV_STATE_EDITED | LV_STATE_FOCUSED); } else if (lvobj != nullptr) { diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 0fdc360ff..9b967d4f4 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -28,6 +28,30 @@ static LvglWidgetFactory numberEditFactory = { lv_textarea_create, nullptr }; static lv_style_t style_main; static lv_style_t style_edit; +extern "C" void lvglEventCb(lv_event_t* event) +{ + if(!event) + return; + uint32_t key = lv_event_get_key(event); + NumberEdit* numEdit = (NumberEdit*)event->user_data; + if(!numEdit) + return; + + lv_group_t* grp = (lv_group_t*)lv_obj_get_group(numEdit->getLvObj()); + if(grp && lv_group_get_focused(grp) == numEdit->getLvObj()) + numEdit->setEditMode(lv_group_get_editing(grp)); + switch(key) + { + case LV_KEY_LEFT: +// numEdit->onEvent(EVT_ROTARY_LEFT); + break; + case LV_KEY_RIGHT: +// numEdit->onEvent(EVT_ROTARY_RIGHT); + break; + } + event->code = LV_EVENT_REFRESH; +} + NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags, LcdFlags textFlags): BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags, &numberEditFactory) { @@ -72,6 +96,8 @@ NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, auto label = lv_textarea_get_label(lvobj); lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); + + lv_obj_add_event_cb(lvobj, lvglEventCb, LV_EVENT_ALL, this); } void NumberEdit::onEvent(event_t event) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7881d3112..282a7d97e 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -37,6 +37,7 @@ static void window_event_cb(lv_event_t * e) Window* window = (Window *)lv_obj_get_user_data(target); if (!window) return; + if(window->deleted()) return; if (code == LV_EVENT_GET_SELF_SIZE) { lv_point_t *p = (lv_point_t *)lv_event_get_param(e); @@ -232,11 +233,16 @@ void Window::clear() void Window::clearLvgl() { + if(lvobj == nullptr) + return; + for (auto window: children) { - if(lvobj != nullptr && window->lvobj != nullptr && window->lvobj->parent == lvobj) + if(window->lvobj != nullptr && window->lvobj->parent == lvobj) window->clearLvgl(); } +// lv_obj_set_user_data(lvobj, nullptr); + lvobj = nullptr; } @@ -509,6 +515,7 @@ coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) } #endif +void lvglPushEncoderEvent(event_t evt); void Window::checkEvents() { auto copy = children; @@ -520,6 +527,7 @@ void Window::checkEvents() if (this == Window::focusWindow) { event_t event = getWindowEvent(); + lvglPushEncoderEvent(event); if (event) { this->onEvent(event); } From ce8e7b67d094846ea0a8572e44eb64f622a18e59 Mon Sep 17 00:00:00 2001 From: kevinkoenig Date: Sun, 30 Jan 2022 03:58:59 -0500 Subject: [PATCH 502/714] checkbox implementation. almost right. it still needs some styling of the checkmark color. cant figure out how to make it happen --- .../src/thirdparty/libopenui/src/checkbox.cpp | 61 ++++++++++++++----- radio/src/thirdparty/libopenui/src/checkbox.h | 21 ++----- 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 9dd22bcfd..6e91050c3 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -20,9 +20,46 @@ #include "checkbox.h" #include "theme.h" -void CheckBox::paint(BitmapBuffer * dc) +LvglWidgetFactory checkBoxFactory = { lv_checkbox_create, nullptr }; +lv_style_t style_indicator; +lv_style_t style_edit; + + +static void checkbox_event_handler(lv_event_t * e) { - theme->drawCheckBox(dc, this); + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * target = lv_event_get_target(e); + CheckBox* cb = (CheckBox *)lv_obj_get_user_data(target); + + if(code == LV_EVENT_VALUE_CHANGED) { + onKeyPress(); + cb->setValue(lv_obj_get_state(target) & LV_STATE_CHECKED); + } +} + +CheckBox::CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, WindowFlags flags) : + FormField(parent, rect, flags, 0, &checkBoxFactory), + _getValue(std::move(getValue)), + _setValue(std::move(setValue)) +{ + coord_t size = min(rect.w, rect.h); + setWidth(size); + setHeight(size); + + // BORDER + lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_INDICATOR); + lv_obj_set_style_border_width(lvobj, 1, LV_PART_INDICATOR); + + lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_INDICATOR | LV_STATE_FOCUSED); + lv_obj_set_style_border_width(lvobj, 2, LV_PART_INDICATOR | LV_STATE_FOCUSED); + + // UNCHECKED BACKGROUND + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_INDICATOR); + + if (_getValue()) + lv_obj_add_state(lvobj, LV_STATE_CHECKED); + + lv_obj_add_event_cb(lvobj, checkbox_event_handler, LV_EVENT_ALL, this); } #if defined(HARDWARE_KEYS) @@ -32,8 +69,12 @@ void CheckBox::onEvent(event_t event) if (enabled && event == EVT_KEY_BREAK(KEY_ENTER)) { onKeyPress(); - _setValue(!getValue()); - invalidate(); + if (lv_obj_get_state(lvobj) & LV_STATE_CHECKED) + lv_obj_clear_state(lvobj, LV_STATE_CHECKED); + else + lv_obj_add_state(lvobj, LV_STATE_CHECKED); + + _setValue(lv_obj_get_state(lvobj) & LV_STATE_CHECKED); } else { FormField::onEvent(event); @@ -41,15 +82,3 @@ void CheckBox::onEvent(event_t event) } #endif -#if defined(HARDWARE_TOUCH) -bool CheckBox::onTouchEnd(coord_t x, coord_t y) -{ - if (enabled) { - onKeyPress(); - _setValue(!getValue()); - setFocus(SET_FOCUS_DEFAULT); - invalidate(); - } - return true; -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 8bdd51756..e85ceb13c 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -24,15 +24,7 @@ class CheckBox : public FormField { public: - CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, WindowFlags flags = 0) : - FormField(parent, rect, flags), - _getValue(std::move(getValue)), - _setValue(std::move(setValue)) - { - coord_t size = min(rect.w, rect.h); - setWidth(size); - setHeight(size); - } + CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, WindowFlags flags = 0); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -51,21 +43,20 @@ class CheckBox : public FormField { label = std::move(newLabel); } - void paint(BitmapBuffer * dc) override; - #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; #endif -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif - uint8_t getValue() const { return _getValue(); } + void setValue(uint8_t value) + { + _setValue(value); + } + void setSetValueHandler(std::function handler) { _setValue = std::move(handler); From 4206bf9e94be95047667df20a264c6f4e7805141 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 30 Jan 2022 10:41:09 +0100 Subject: [PATCH 503/714] fix crash when exiting page with number edit disable setting lvgl group focus --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- .../thirdparty/libopenui/src/numberedit.cpp | 28 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index e04b9271a..bb9632d3c 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -72,7 +72,7 @@ void FormField::setFocus(uint8_t flag, Window * from) if(lvobj != nullptr) { lv_group_focus_freeze(inputGroup, false); - lv_group_focus_obj(lvobj); +// lv_group_focus_obj(lvobj); lv_group_focus_freeze(inputGroup, true); } } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 9b967d4f4..f58287686 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -36,18 +36,26 @@ extern "C" void lvglEventCb(lv_event_t* event) NumberEdit* numEdit = (NumberEdit*)event->user_data; if(!numEdit) return; + if(numEdit->deleted()) + return; - lv_group_t* grp = (lv_group_t*)lv_obj_get_group(numEdit->getLvObj()); - if(grp && lv_group_get_focused(grp) == numEdit->getLvObj()) - numEdit->setEditMode(lv_group_get_editing(grp)); - switch(key) + lv_obj_t* obj = numEdit->getLvObj(); + + if(obj != nullptr && lv_obj_is_editable(obj)) { - case LV_KEY_LEFT: -// numEdit->onEvent(EVT_ROTARY_LEFT); - break; - case LV_KEY_RIGHT: -// numEdit->onEvent(EVT_ROTARY_RIGHT); - break; + lv_group_t* grp = (lv_group_t*)lv_obj_get_group(obj); + if(grp && lv_group_get_focused(grp) == obj) + numEdit->setEditMode(lv_group_get_editing(grp)); + + switch(key) + { + case LV_KEY_LEFT: + // numEdit->onEvent(EVT_ROTARY_LEFT); + break; + case LV_KEY_RIGHT: + // numEdit->onEvent(EVT_ROTARY_RIGHT); + break; + } } event->code = LV_EVENT_REFRESH; } From a4bb55adc2fc999a9946f8c5f1740cbe4d0b5ffd Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 30 Jan 2022 11:23:15 +0100 Subject: [PATCH 504/714] remove outline from numberedit --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index f58287686..886b664ae 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -89,6 +89,11 @@ NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_set_style_outline_width(lvobj, 0, LV_PART_MAIN|LV_STATE_EDITED); + lv_obj_set_style_outline_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN|LV_STATE_EDITED); + lv_obj_set_style_outline_width(lvobj, 0, LV_PART_MAIN|LV_STATE_FOCUS_KEY); + lv_obj_set_style_outline_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN|LV_STATE_FOCUS_KEY); + // Hide cursor lv_obj_set_style_opa(lvobj, 0, LV_PART_CURSOR); From ff4cde159ec4a1404b20cf7db155a1500c51af68 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 30 Jan 2022 13:02:12 +0100 Subject: [PATCH 505/714] remove libopenui paint implementation for lvgl-elements --- radio/src/thirdparty/libopenui/src/basenumberedit.h | 2 ++ radio/src/thirdparty/libopenui/src/textedit.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 3cf6387e6..4b9b4939b 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -153,6 +153,8 @@ class BaseNumberEdit: public FormField setValue(currentValue); } + virtual void paint(BitmapBuffer* dc) override {}; + protected: int vdefault = 0; int vmin; diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index bf7b65b19..6770141f8 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -54,6 +54,8 @@ class TextEdit : public FormField { void onFocusLost() override; + virtual void paint(BitmapBuffer* dc) override {}; + protected: char * value; bool changed = false; From 04293415285783c1d5606e41bd14ea19e5975417 Mon Sep 17 00:00:00 2001 From: kevinkoenig Date: Sun, 30 Jan 2022 10:57:50 -0500 Subject: [PATCH 506/714] fix checkbox check color to match theme --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 6e91050c3..f29a38e4c 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -55,6 +55,9 @@ CheckBox::CheckBox(Window * parent, const rect_t & rect, std::function Date: Sun, 30 Jan 2022 11:17:15 -0500 Subject: [PATCH 507/714] 1. fixed checkbox focus on touch 2. fixed crash if you edit a textbox and close the simu. TextEdit changeEnd() wasnt checking lvgl for validity 3. removed dead code in new TextEdit onTouchEnd() --- .../src/thirdparty/libopenui/src/checkbox.cpp | 12 ++++++++++ radio/src/thirdparty/libopenui/src/checkbox.h | 4 ++++ .../src/thirdparty/libopenui/src/textedit.cpp | 23 ++++++++++--------- radio/src/thirdparty/libopenui/src/textedit.h | 4 +++- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index f29a38e4c..d62c6a939 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -85,3 +85,15 @@ void CheckBox::onEvent(event_t event) } #endif +#if defined(HARDWARE_TOUCH) +bool CheckBox::onTouchEnd(coord_t x, coord_t y) +{ + if (!hasFocus()) { + setFocus(SET_FOCUS_DEFAULT); + } + + return true; +} +#endif + + diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index e85ceb13c..32f8fc94a 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -47,6 +47,10 @@ class CheckBox : public FormField { void onEvent(event_t event) override; #endif +#if defined(HARDWARE_TOUCH) + bool onTouchEnd(coord_t x, coord_t y) override; +#endif + uint8_t getValue() const { return _getValue(); diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 38a795193..871f2cbf8 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -261,18 +261,19 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) TextKeyboard::show(this); #endif - coord_t rest = x; - for (cursorPos = 0; cursorPos < length; cursorPos++) { - char c = value[cursorPos]; - if (c == '\0') - break; - uint8_t w = getCharWidth(c, fontspecsTable[0]) + 1; - if (rest < w) - break; - rest -= w; - } + // coord_t rest = x; + // for (cursorPos = 0; cursorPos < length; cursorPos++) { + // char c = value[cursorPos]; + // if (c == '\0') + // break; + // uint8_t w = getCharWidth(c, fontspecsTable[0]) + 1; + // if (rest < w) + // break; + // rest -= w; + // } + + // invalidate(); - invalidate(); return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 6770141f8..a9965b0e9 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -67,7 +67,9 @@ class TextEdit : public FormField { void changeEnd(bool forceChanged = false) override { - strncpy(value, lv_textarea_get_text(lvobj), length); + if (lvobj != nullptr) + strncpy(value, lv_textarea_get_text(lvobj), length); + cursorPos = 0; if (changed || forceChanged) { changed = false; From cab656b5d22459082e3a11ed72a2d834bf680a1b Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 2 Feb 2022 07:23:05 -0500 Subject: [PATCH 508/714] redo checkbox styling. --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 3 +-- radio/src/thirdparty/libopenui/src/checkbox.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index d62c6a939..de696d4ac 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -91,9 +91,8 @@ bool CheckBox::onTouchEnd(coord_t x, coord_t y) if (!hasFocus()) { setFocus(SET_FOCUS_DEFAULT); } - + return true; } #endif - diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 32f8fc94a..c94c4f2ff 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -48,7 +48,7 @@ class CheckBox : public FormField { #endif #if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; + bool onTouchEnd(coord_t x, coord_t y) override; #endif uint8_t getValue() const From 66c500799cc99cbd22eb9a309d162c287d1fce70 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Sun, 6 Feb 2022 08:54:57 -0500 Subject: [PATCH 509/714] menu and modal almost working --- radio/src/thirdparty/libopenui/src/menu.cpp | 153 ++++++++++++------ radio/src/thirdparty/libopenui/src/menu.h | 21 +-- .../thirdparty/libopenui/src/modal_window.cpp | 27 +++- .../thirdparty/libopenui/src/modal_window.h | 20 +-- 4 files changed, 132 insertions(+), 89 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 952c7e614..950e411ee 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,6 +21,62 @@ #include "font.h" #include "theme.h" +LvglWidgetFactory menuBodyWidgetFactory = { [] (lv_obj_t *parent) { + return lv_list_create(parent); +}, nullptr}; + +void menuBodyEventCallback(lv_event_t *e) +{ + auto code = lv_event_get_code(e); + if (code == LV_EVENT_CLICKED) { + lv_obj_t *target = lv_event_get_target(e); + if (target != nullptr) { + long index = (long)lv_event_get_user_data(e); + MenuBody *mb = (MenuBody *) lv_obj_get_user_data(lv_obj_get_parent(target)); + Menu *menu = mb->getParentMenu(); + if (index < (int)mb->lines.size()) { + onKeyPress(); + if (menu->multiple) { + if (mb->selectedIndex == index) + mb->lines[index].onPress(); + else + mb->setIndex(index); + } + else { + mb->setIndex(index); + menu->deleteLater(); + mb->lines[index].onPress(); + } + } + } + } +} + +MenuBody::MenuBody(Window * parent, const rect_t & rect): + Window(parent, rect, OPAQUE, 0, &menuBodyWidgetFactory) +{ + setPageHeight(MENUS_LINE_HEIGHT); +} + +void MenuBody::addLine(const std::string & text, std::function onPress, std::function isChecked) +{ + lines.emplace_back(text, std::move(onPress), std::move(isChecked)); + lv_obj_t *btn = lv_list_add_btn(lvobj, nullptr, text.c_str()); + lv_obj_set_style_bg_color(btn, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN | LV_STATE_CHECKED); + lv_obj_set_style_bg_color(btn, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); + lv_obj_set_style_bg_opa(btn, LV_OPA_100, LV_PART_MAIN); + lv_obj_set_style_bg_opa(btn, LV_OPA_100, LV_PART_MAIN | LV_STATE_CHECKED); + lv_obj_set_style_bg_opa(btn, LV_OPA_100, LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); + lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); + lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, (void *) (lines.size() - 1)); + lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); +} + +void MenuBody::removeLines() +{ + lines.clear(); + lv_obj_clean(lvobj); +} // ensure index is in range and also handle wrapping index int MenuBody::rangeCheck(int index) @@ -35,17 +91,14 @@ int MenuBody::rangeCheck(int index) void MenuBody::setIndex(int index) { - selectedIndex = index; - // calculate the correct scroll postion based on there being separators - int scrollY = 0; - for (int i = 0; i < selectedIndex; i++) - scrollY += lines[i].lineHeight(); + if (index < (int)lines.size()) { + selectedIndex = index; - if (innerHeight > height()) { - lv_obj_scroll_to_y(lvobj, scrollY - 3 * MENUS_LINE_HEIGHT, LV_ANIM_OFF); + lv_obj_t *btn = lv_obj_get_child(lvobj, selectedIndex); + if (btn != nullptr) { + lv_obj_add_state(btn, LV_STATE_CHECKED); + } } - - invalidate(); } void MenuBody::select(int index) @@ -166,42 +219,42 @@ bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) void MenuBody::paint(BitmapBuffer * dc) { - dc->clear(COLOR_THEME_PRIMARY2); + // dc->clear(COLOR_THEME_PRIMARY2); - int y = 0; - for (int i = 0; i < (signed)lines.size(); i++) { - auto& line = lines[i]; - LcdFlags flags = COLOR_THEME_PRIMARY3 | MENU_FONT; + // int y = 0; + // for (int i = 0; i < (signed)lines.size(); i++) { + // auto& line = lines[i]; + // LcdFlags flags = COLOR_THEME_PRIMARY3 | MENU_FONT; - // draw selection if appropriate - if (selectedIndex == i && ! line.isSeparator) { - flags = COLOR_THEME_PRIMARY2 | MENU_FONT; - if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { - dc->drawSolidFilledRect(0, y, width(), - MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); - } - } + // // draw selection if appropriate + // if (selectedIndex == i && ! line.isSeparator) { + // flags = COLOR_THEME_PRIMARY2 | MENU_FONT; + // if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { + // dc->drawSolidFilledRect(0, y, width(), + // MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); + // } + // } - if (line.isSeparator) { - dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY1); - } else if (line.drawLine) { - line.drawLine(dc, 0, y, COLOR_THEME_PRIMARY3); - } else { - const char* text = line.text.data(); - dc->drawText(10, - y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, - text[0] == '\0' ? "---" : text, flags); - } + // if (line.isSeparator) { + // dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY1); + // } else if (line.drawLine) { + // line.drawLine(dc, 0, y, COLOR_THEME_PRIMARY3); + // } else { + // const char* text = line.text.data(); + // dc->drawText(10, + // y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, + // text[0] == '\0' ? "---" : text, flags); + // } - Menu* menu = getParentMenu(); - if (menu->multiple && line.isChecked) { - theme->drawCheckBox(dc, line.isChecked(), width() - 35, - y + (MENUS_LINE_HEIGHT - 20) / 2, - 0); - } + // Menu* menu = getParentMenu(); + // if (menu->multiple && line.isChecked) { + // theme->drawCheckBox(dc, line.isChecked(), width() - 35, + // y + (MENUS_LINE_HEIGHT - 20) / 2, + // 0); + // } - y += line.lineHeight(); - } + // y += line.lineHeight(); + // } } MenuWindowContent::MenuWindowContent(Menu* parent) : @@ -214,17 +267,17 @@ MenuWindowContent::MenuWindowContent(Menu* parent) : void MenuWindowContent::paint(BitmapBuffer * dc) { - // the background - dc->clear(COLOR_THEME_SECONDARY1); + // // the background + // dc->clear(COLOR_THEME_SECONDARY1); - // the title - if (!title.empty()) { - dc->drawText(MENUS_WIDTH / 2, - (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, - title.c_str(), CENTERED | MENU_HEADER_FONT | COLOR_THEME_PRIMARY2); - dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, - COLOR_THEME_SECONDARY2); - } + // // the title + // if (!title.empty()) { + // dc->drawText(MENUS_WIDTH / 2, + // (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, + // title.c_str(), CENTERED | MENU_HEADER_FONT | COLOR_THEME_PRIMARY2); + // dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, + // COLOR_THEME_SECONDARY2); + // } } Menu::Menu(Window * parent, bool multiple): diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index cf16aff9e..1285f8f96 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -69,6 +69,7 @@ class MenuBody: public Window MenuLine(MenuLine &&) = default; protected: + friend void menuBodyEventCallback(lv_event_t *e); bool isSeparator = false; coord_t height = MENUS_LINE_HEIGHT; std::string text; @@ -78,11 +79,7 @@ class MenuBody: public Window }; public: - MenuBody(Window * parent, const rect_t & rect): - Window(parent, rect, OPAQUE) - { - setPageHeight(MENUS_LINE_HEIGHT); - } + MenuBody(Window * parent, const rect_t & rect); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -116,11 +113,7 @@ class MenuBody: public Window bool onTouchEnd(coord_t x, coord_t y) override; #endif - void addLine(const std::string & text, std::function onPress, std::function isChecked) - { - lines.emplace_back(text, std::move(onPress), std::move(isChecked)); - invalidate(); - } + void addLine(const std::string & text, std::function onPress, std::function isChecked); void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) { @@ -133,11 +126,7 @@ class MenuBody: public Window lines.emplace_back(true); } - void removeLines() - { - lines.clear(); - invalidate(); - } + void removeLines(); void setCancelHandler(std::function handler) { @@ -147,6 +136,7 @@ class MenuBody: public Window void paint(BitmapBuffer * dc) override; protected: + friend void menuBodyEventCallback(lv_event_t *); void selectNext(MENU_DIRECTION direction); int rangeCheck(int); void setIndex(int index); @@ -264,6 +254,7 @@ class Menu: public ModalWindow void paint(BitmapBuffer * dc) override; protected: + friend void menuBodyEventCallback(lv_event_t *); MenuWindowContent * content; bool multiple; Window * toolbar = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index b33983164..1ca084510 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -26,6 +26,10 @@ ModalWindow::ModalWindow(Window * parent, bool closeWhenClickOutside): Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}), closeWhenClickOutside(closeWhenClickOutside) { + lv_obj_set_parent(lvobj, lv_layer_top()); + lv_obj_set_style_bg_color(lvobj, lv_color_black(), LV_PART_MAIN); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_50, LV_PART_MAIN); + lv_obj_add_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE); Layer::push(this); } @@ -33,20 +37,33 @@ void ModalWindow::deleteLater(bool detach, bool trash) { if (_deleted) return; - + + lv_obj_clear_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE); Layer::pop(this); Window::deleteLater(detach, trash); } +#if defined(HARDWARE_TOUCH) +bool ModalWindow::onTouchEnd(coord_t x, coord_t y) +{ + if (closeWhenClickOutside) { + onKeyPress(); + deleteLater(); + } + return true; +} +#endif + + void ModalWindow::paint(BitmapBuffer * dc) { - dc->drawFilledRect(0, 0, width(), height(), SOLID, BLACK, OPACITY(6)); + // dc->drawFilledRect(0, 0, width(), height(), SOLID, BLACK, OPACITY(6)); } void ModalWindowContent::paint(BitmapBuffer * dc) { - dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY1); - dc->drawText(FIELD_PADDING_LEFT, (POPUP_HEADER_HEIGHT - getFontHeight(FONT(STD))) / 2, title.c_str(), COLOR_THEME_PRIMARY2); - dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, width(), height() - POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY3); + // dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY1); + // dc->drawText(FIELD_PADDING_LEFT, (POPUP_HEADER_HEIGHT - getFontHeight(FONT(STD))) / 2, title.c_str(), COLOR_THEME_PRIMARY2); + // dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, width(), height() - POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY3); } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index e4580e689..fedaeea77 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -38,25 +38,7 @@ class ModalWindow: public Window void paint(BitmapBuffer * dc) override; #if defined(HARDWARE_TOUCH) - bool onTouchStart(coord_t /*x*/, coord_t /*y*/) override - { - return true; - } - - bool onTouchEnd(coord_t x, coord_t y) override - { - if (!Window::onTouchEnd(x, y) && closeWhenClickOutside) { - onKeyPress(); - deleteLater(); - } - return true; - } - - bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override - { - Window::onTouchSlide(x, y, startX, startY, slideX, slideY); - return true; - } + bool onTouchEnd(coord_t x, coord_t y) override; #endif void setCloseWhenClickOutside(bool value = true) From f7c0d58281ee44f3764de94e846d1740e588ab32 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 6 Feb 2022 15:07:39 +0100 Subject: [PATCH 510/714] use roboto font --- radio/src/thirdparty/libopenui/src/static.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 0d4c9d680..89464643b 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -32,6 +32,7 @@ class StaticText: public Window label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); + lv_obj_set_style_text_font(lvobj, &lv_font_roboto_13, LV_PART_MAIN); lv_obj_set_style_text_color(label, makeLvColor(textFlags), LV_PART_MAIN); if (textFlags & CENTERED) lv_obj_center(label); From ac5a7bef0d694668ec192566260a291cfb46ce46 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 6 Feb 2022 15:08:10 +0100 Subject: [PATCH 511/714] use roboto font --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 2 +- radio/src/thirdparty/libopenui/src/textedit.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 886b664ae..d085db5ff 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -78,7 +78,7 @@ NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_PRIMARY2)); lv_style_set_bg_opa(&style_main, LV_OPA_COVER); //lv_style_set_radius(&style_main, 0); - lv_style_set_text_font(&style_main, &lv_font_montserrat_12); + lv_style_set_text_font(&style_main, &lv_font_roboto_13); lv_style_set_text_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1)); lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN); diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 871f2cbf8..58c73aa84 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -71,7 +71,7 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_PRIMARY2)); lv_style_set_bg_opa(&style_main, LV_OPA_COVER); //lv_style_set_radius(&style_main, 0); - lv_style_set_text_font(&style_main, &lv_font_montserrat_12); + lv_style_set_text_font(&style_main, &lv_font_roboto_13); lv_style_set_text_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1)); lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN); From fe7d425cede621d7b2234df40fdfa3cd3ff0de97 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 26 Jan 2022 13:39:31 +0100 Subject: [PATCH 512/714] LVGL drawing --- .../thirdparty/libopenui/src/mainwindow.cpp | 41 +------------- radio/src/thirdparty/libopenui/src/window.cpp | 55 ++++++++++++++----- radio/src/thirdparty/libopenui/src/window.h | 8 +-- 3 files changed, 49 insertions(+), 55 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index aa9a28e37..51f4f913b 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -20,6 +20,8 @@ #include "mainwindow.h" #include "keyboard_base.h" +#include "lvgl/lvgl.h" + #if defined(HARDWARE_TOUCH) #include "touch.h" #endif @@ -28,12 +30,10 @@ TouchState touchState; #endif -static LvglWidgetFactory mainWindowFactory = { lv_obj_create, nullptr }; - MainWindow * MainWindow::_instance = nullptr; MainWindow::MainWindow() : - Window(nullptr, {0, 0, LCD_W, LCD_H}, 0, 0, &mainWindowFactory), + Window(nullptr, {0, 0, LCD_W, LCD_H}), invalidatedRect(rect) { Layer::push(this); @@ -93,37 +93,6 @@ void MainWindow::invalidate(const rect_t & rect) invalidatedRect = rect; } } -#include "lvgl/lvgl.h" -extern lv_obj_t * canvas; -extern BitmapBuffer canBuf; -bool MainWindow::refresh() -{ - if (invalidatedRect.w) { - canBuf.setOffset(0, 0); - canBuf.setClippingRect(0,LCD_W,0, LCD_H); - fullPaint(&canBuf); - lv_obj_invalidate(canvas); - } - return false; - - if (invalidatedRect.w) { - if (invalidatedRect.x > 0 || invalidatedRect.y > 0 || invalidatedRect.w < LCD_W || invalidatedRect.h < LCD_H) { - TRACE_WINDOWS("Refresh rect: left=%d top=%d width=%d height=%d", invalidatedRect.left(), invalidatedRect.top(), invalidatedRect.w, invalidatedRect.h); - lcdCopy(lcd->getData(), lcdFront->getData()); - } - else { - TRACE_WINDOWS("Refresh full screen"); - } - lcd->setOffset(0, 0); - lcd->setClippingRect(invalidatedRect.left(), invalidatedRect.right(), invalidatedRect.top(), invalidatedRect.bottom()); - fullPaint(lcd); - invalidatedRect.w = 0; - return true; - } - else { - return false; - } -} void MainWindow::run(bool trash) { @@ -138,10 +107,6 @@ void MainWindow::run(bool trash) emptyTrash(); } - if (refresh()) { - lcdRefresh(); - } - auto delta = ticksNow() - start; if (delta > 10 * SYSTEM_TICKS_1MS) { TRACE_WINDOWS("MainWindow::run took %dms", (ticksNow() - start) / SYSTEM_TICKS_1MS); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 282a7d97e..260a38448 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -37,14 +37,39 @@ static void window_event_cb(lv_event_t * e) Window* window = (Window *)lv_obj_get_user_data(target); if (!window) return; - if(window->deleted()) return; + + if(code == LV_EVENT_DELETE || + window->deleted()) return; if (code == LV_EVENT_GET_SELF_SIZE) { + lv_point_t *p = (lv_point_t *)lv_event_get_param(e); if (p->x >= 0) { p->x = window->getInnerWidth(); } if (p->y >= 0) { p->y = window->getInnerHeight(); } + } else if (code == LV_EVENT_DRAW_MAIN) { + + lv_draw_ctx_t* draw_ctx = lv_event_get_draw_ctx(e); + + lv_area_t a, clipping, obj_coords; + lv_area_copy(&a, draw_ctx->buf_area); + lv_area_copy(&clipping, draw_ctx->clip_area); + lv_obj_get_coords(target, &obj_coords); + + auto w = a.x2 - a.x1 + 1; + auto h = a.y2 - a.y1 + 1; + TRACE_WINDOWS("DRAW_MAIN %s", window->getWindowDebugString().c_str()); + + BitmapBuffer buf = {BMP_RGB565, (uint16_t)w, (uint16_t)h, + (uint16_t *)draw_ctx->buf}; + + buf.setOffset(obj_coords.x1 - a.x1, obj_coords.y1 - a.y1); + buf.setClippingRect(clipping.x1 - a.x1, clipping.x2 + 1 - a.x1, + clipping.y1 - a.y1, clipping.y2 + 1 - a.y1); + + window->paint(&buf); + } else if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) { TRACE_WINDOWS("PRESSED: %s", window->getWindowDebugString().c_str()); @@ -86,12 +111,16 @@ static void window_event_cb(lv_event_t * e) window->onTouchEnd(rel_pos.x, rel_pos.y); } } else if (code == LV_EVENT_SCROLL) { + lv_coord_t scroll_y = lv_obj_get_scroll_y(target); lv_coord_t scroll_x = lv_obj_get_scroll_x(target); + TRACE_WINDOWS("SCROLL[x=%d;y=%d] %s", scroll_x, scroll_y, window->getWindowDebugString().c_str()); + window->setScrollPositionY(scroll_y); window->setScrollPositionX(scroll_x); + } else if (code == LV_EVENT_SCROLL_BEGIN) { TRACE("SCROLL_BEGIN"); is_scrolling = true; @@ -138,6 +167,10 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, lv_obj_set_size(lvobj, rect.w, rect.h); lv_obj_set_user_data(lvobj, this); + if (windowFlags & OPAQUE) { + lv_obj_set_style_bg_opa(lvobj, LV_OPA_MAX, LV_PART_MAIN); + } + lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC); lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); @@ -154,7 +187,7 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); } } -extern lv_obj_t * canvas; + Window::~Window() { TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); @@ -162,22 +195,19 @@ Window::~Window() focusWindow = nullptr; } - if(children.size() > 0) - deleteChildren(); - if (lvobj != nullptr && lvobj != canvas) - { + if (children.size() > 0) deleteChildren(); + + if (lvobj != nullptr) { lv_obj_del(lvobj); lvobj = nullptr; } - } -void Window::attach(Window * newParent) +void Window::attach(Window *newParent) { if (parent) detach(); parent = newParent; - if (newParent) - { + if (newParent) { newParent->addChild(this); } } @@ -187,6 +217,7 @@ void Window::detach() if (parent) { parent->removeChild(this); parent = nullptr; + // if (lvobj != nullptr) lv_obj_set_parent(lvobj, nullptr); } } @@ -613,9 +644,7 @@ void Window::moveWindowsTop(coord_t y, coord_t delta) void Window::invalidate(const rect_t & rect) { - if (isVisible()) { - parent->invalidate({this->rect.x + rect.x - parent->scrollPositionX, this->rect.y + rect.y - parent->scrollPositionY, rect.w, rect.h}); - } + if (lvobj) lv_obj_invalidate(lvobj); } void Window::drawVerticalScrollbar(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 5a3d9339e..78e5c148b 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -405,6 +405,10 @@ class Window return _deleted; } + virtual void paint(BitmapBuffer *) + { + } + #if defined(HARDWARE_TOUCH) virtual bool onTouchStart(coord_t x, coord_t y); virtual bool onTouchEnd(coord_t x, coord_t y); @@ -463,10 +467,6 @@ class Window void fullPaint(BitmapBuffer * dc); - virtual void paint(BitmapBuffer *) - { - } - virtual void onFocusLost() { TRACE_WINDOWS("%s onFocusLost()", getWindowDebugString().c_str()); From c5175f7061229d8131b7e4345cbf37fab2e56895 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Mon, 7 Feb 2022 04:53:03 -0500 Subject: [PATCH 513/714] more menu changes --- radio/src/thirdparty/libopenui/src/button.cpp | 53 +------------------ radio/src/thirdparty/libopenui/src/button.h | 6 --- radio/src/thirdparty/libopenui/src/menu.cpp | 11 +++- .../thirdparty/libopenui/src/menutoolbar.h | 3 +- 4 files changed, 12 insertions(+), 61 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index d59a3c5a0..8d8bf6239 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -81,14 +81,7 @@ void Button::checkEvents() } lv_style_t style_btn; - - -LvglWidgetFactory textButtonFactory = LvglWidgetFactory( - [](lv_obj_t *parent) { - return lv_btn_create(parent); - }, - [] (LvglWidgetFactory *factory) { - }); +LvglWidgetFactory textButtonFactory = {lv_btn_create, nullptr}; TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, std::function pressHandler, @@ -125,50 +118,6 @@ TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, lv_obj_set_style_text_color(label, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); } -TextButton::~TextButton() -{ -} - -void TextButton::paint(BitmapBuffer * dc) -{ - // auto textColor = getTextFlags(); - // auto bgColor = COLOR_THEME_SECONDARY2; - - // if (bgColorHandler) { - // bgColor = bgColorHandler(); - // } else if (checked()) { - // bgColor = COLOR_THEME_ACTIVE; - // } else if (hasFocus()) { - // bgColor = COLOR_THEME_FOCUS; - // textColor = COLOR_THEME_PRIMARY2; - // } - - // if (checked()) { - // if (hasFocus()) { - // dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); - // dc->drawSolidFilledRect(3, 3, rect.w - 6, rect.h - 6, bgColor); - // } else { - // dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); - // } - // } - // else { - // dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); - // if (windowFlags & BUTTON_BACKGROUND) { - // if (hasFocus()) { - // textColor = COLOR_THEME_PRIMARY2; - // } - // } else if (hasFocus()) { - // dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); - // } - // else { - // dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); - // } - // } - - // dc->drawText(rect.w / 2, 1 + (rect.h - getFontHeight(textFlags)) / 2, - // text.c_str(), CENTERED | textColor); -} - void IconButton::paint(BitmapBuffer * dc) { dc->drawBitmap(0, 0, theme->getIcon(icon, checked() ? STATE_PRESSED : STATE_DEFAULT)); diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 286068959..1f11a9a4c 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -88,8 +88,6 @@ class Button: public FormField }; -extern void my_btn_event_cb(lv_event_t * e); - class TextButton: public Button { public: @@ -97,7 +95,6 @@ class TextButton: public Button std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE, LcdFlags textFlags = 0); - ~TextButton(); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -111,7 +108,6 @@ class TextButton: public Button if (value != text) { text = std::move(value); lv_label_set_text(label, text.c_str()); - invalidate(); } } @@ -120,8 +116,6 @@ class TextButton: public Button bgColorHandler = std::move(handler); } - void paint(BitmapBuffer* dc) override; - protected: lv_obj_t * label = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 950e411ee..3ec5a384d 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -92,11 +92,17 @@ int MenuBody::rangeCheck(int index) void MenuBody::setIndex(int index) { if (index < (int)lines.size()) { - selectedIndex = index; - lv_obj_t *btn = lv_obj_get_child(lvobj, selectedIndex); + if (btn != nullptr) { + lv_obj_clear_state(btn, LV_STATE_CHECKED); + } + + selectedIndex = index; + + btn = lv_obj_get_child(lvobj, selectedIndex); if (btn != nullptr) { lv_obj_add_state(btn, LV_STATE_CHECKED); + lv_obj_scroll_to_view(btn, LV_ANIM_ON); } } } @@ -263,6 +269,7 @@ MenuWindowContent::MenuWindowContent(Menu* parent) : body(this, {0, 0, width(), height()}) { body.setFocus(SET_FOCUS_DEFAULT); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); } void MenuWindowContent::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 49e37817c..37c6b8a01 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -53,11 +53,12 @@ class MenuToolbar: public FormGroup { choice(choice), menu(menu) { + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); } void paint(BitmapBuffer * dc) override { - dc->clear(COLOR_THEME_SECONDARY3); +// dc->clear(COLOR_THEME_SECONDARY3); } #if defined(HARDWARE_KEYS) From a0624cc17a895403fc23dd6a67c06fc174dd1eed Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 7 Feb 2022 11:37:02 +0100 Subject: [PATCH 514/714] Fix some use-after-free issue This is basically about LV_EVENT_DELETE. --- .../thirdparty/libopenui/src/numberedit.cpp | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index d085db5ff..419bccd39 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -28,40 +28,43 @@ static LvglWidgetFactory numberEditFactory = { lv_textarea_create, nullptr }; static lv_style_t style_main; static lv_style_t style_edit; -extern "C" void lvglEventCb(lv_event_t* event) +static void text_area_cb(lv_event_t* e) { - if(!event) - return; - uint32_t key = lv_event_get_key(event); - NumberEdit* numEdit = (NumberEdit*)event->user_data; - if(!numEdit) - return; - if(numEdit->deleted()) - return; + lv_event_code_t code = lv_event_get_code(e); + if (code == LV_EVENT_DELETE) return; + NumberEdit* numEdit = (NumberEdit*)lv_event_get_user_data(e); + if (!numEdit || numEdit->deleted()) return; + + uint32_t key = lv_event_get_key(e); lv_obj_t* obj = numEdit->getLvObj(); - if(obj != nullptr && lv_obj_is_editable(obj)) - { + if (obj != nullptr && lv_obj_is_editable(obj)) { lv_group_t* grp = (lv_group_t*)lv_obj_get_group(obj); - if(grp && lv_group_get_focused(grp) == obj) + if (grp && lv_group_get_focused(grp) == obj) numEdit->setEditMode(lv_group_get_editing(grp)); - switch(key) - { - case LV_KEY_LEFT: - // numEdit->onEvent(EVT_ROTARY_LEFT); - break; - case LV_KEY_RIGHT: - // numEdit->onEvent(EVT_ROTARY_RIGHT); - break; + switch (key) { + case LV_KEY_LEFT: + // numEdit->onEvent(EVT_ROTARY_LEFT); + break; + case LV_KEY_RIGHT: + // numEdit->onEvent(EVT_ROTARY_RIGHT); + break; } } - event->code = LV_EVENT_REFRESH; + + // TODO: this looks highly forbidden!!! + e->code = LV_EVENT_REFRESH; } -NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags, LcdFlags textFlags): - BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags, &numberEditFactory) +NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, + std::function getValue, + std::function setValue, + WindowFlags windowFlags, LcdFlags textFlags) : + BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), + std::move(setValue), windowFlags, textFlags, + &numberEditFactory) { // properties lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); @@ -110,7 +113,7 @@ NumberEdit::NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); - lv_obj_add_event_cb(lvobj, lvglEventCb, LV_EVENT_ALL, this); + lv_obj_add_event_cb(lvobj, text_area_cb, LV_EVENT_ALL, this); } void NumberEdit::onEvent(event_t event) From 5597524b7a0e6cb4aa88dd290a0e58a932fe96d9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 7 Feb 2022 15:32:39 +0100 Subject: [PATCH 515/714] Add empty paint() method to Checkbox & Button --- radio/src/thirdparty/libopenui/src/button.h | 2 ++ radio/src/thirdparty/libopenui/src/checkbox.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 1f11a9a4c..448ddc6b3 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -116,6 +116,8 @@ class TextButton: public Button bgColorHandler = std::move(handler); } + void paint(BitmapBuffer* dc) override {} + protected: lv_obj_t * label = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index c94c4f2ff..2d772968f 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -66,6 +66,8 @@ class CheckBox : public FormField { _setValue = std::move(handler); } + void paint(BitmapBuffer* dc) override {} + protected: std::string label; std::function _getValue; From b823150158e2420a2f595617da8da48faa516f39 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 7 Feb 2022 15:33:05 +0100 Subject: [PATCH 516/714] Small checkbox opti --- .../src/thirdparty/libopenui/src/checkbox.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index de696d4ac..43ed81df0 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -27,20 +27,20 @@ lv_style_t style_edit; static void checkbox_event_handler(lv_event_t * e) { - lv_event_code_t code = lv_event_get_code(e); lv_obj_t * target = lv_event_get_target(e); CheckBox* cb = (CheckBox *)lv_obj_get_user_data(target); - if(code == LV_EVENT_VALUE_CHANGED) { - onKeyPress(); + onKeyPress(); + if (cb) cb->setValue(lv_obj_get_state(target) & LV_STATE_CHECKED); - } } -CheckBox::CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, WindowFlags flags) : - FormField(parent, rect, flags, 0, &checkBoxFactory), - _getValue(std::move(getValue)), - _setValue(std::move(setValue)) +CheckBox::CheckBox(Window* parent, const rect_t& rect, + std::function getValue, + std::function setValue, WindowFlags flags) : + FormField(parent, rect, flags, 0, &checkBoxFactory), + _getValue(std::move(getValue)), + _setValue(std::move(setValue)) { coord_t size = min(rect.w, rect.h); setWidth(size); @@ -62,7 +62,7 @@ CheckBox::CheckBox(Window * parent, const rect_t & rect, std::function Date: Mon, 7 Feb 2022 13:20:37 -0500 Subject: [PATCH 517/714] implementation of choice and filechoice using lvgl --- radio/src/thirdparty/libopenui/src/choice.cpp | 72 +++++++++++++++---- radio/src/thirdparty/libopenui/src/choice.h | 4 ++ .../thirdparty/libopenui/src/filechoice.cpp | 10 ++- .../src/thirdparty/libopenui/src/filechoice.h | 1 + radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- 5 files changed, 74 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index ffc1e2871..0c1ab0826 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -21,14 +21,62 @@ #include "menu.h" #include "theme.h" +void choicePaintCallback(lv_event_t *e) +{ + auto code = lv_event_get_code(e); + + if (code == LV_EVENT_DRAW_MAIN_BEGIN) { + lv_obj_t *target = lv_event_get_target(e); + if (target != nullptr) { + ChoiceBase *cb = (ChoiceBase*)lv_obj_get_user_data(target); + std::string text = cb->getLabelText(); + lv_label_set_text(cb->label, text.c_str()); + } + } +} + ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, WindowFlags windowFlags): FormField(parent, rect, windowFlags), type(type) { + lv_obj_add_event_cb(lvobj, choicePaintCallback, LV_EVENT_DRAW_MAIN_BEGIN, lvobj); + label = lv_label_create(lvobj); + // Normal font and background color + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); + lv_obj_set_style_pad_right(lvobj, 5, LV_PART_MAIN); + lv_obj_set_style_border_width(lvobj, 1, LV_PART_MAIN); + lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN); + lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); + lv_obj_set_style_text_font(lvobj, &lv_font_roboto_13, LV_PART_MAIN); + // focused + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); + + lv_obj_set_width(label, lv_pct(80)); + lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); + lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); + + + // add the image + lv_obj_t *img = lv_img_create(lvobj); + lv_img_set_src(img, type == CHOICE_TYPE_DROPOWN ? LV_SYMBOL_DOWN : LV_SYMBOL_DIRECTORY); + lv_obj_set_align(img, LV_ALIGN_RIGHT_MID); } +std::string Choice::getLabelText() +{ + std::string text = ""; + if (textHandler != nullptr) + text = textHandler(getValue()); + else if (getValue() < values.size()) + text = values[getValue()]; + + return text; +} + Choice::Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), @@ -111,20 +159,20 @@ void Choice::setValues(const char * const values[]) void Choice::paint(BitmapBuffer * dc) { - FormField::paint(dc); - auto val = getValue(); + // FormField::paint(dc); + // auto val = getValue(); - std::string str = ""; - if (textHandler) { - str = textHandler(val); - } else { - val -= vmin; - if (val >= 0 && val < (int)values.size()) { - str = values[val]; - } - } + // std::string str = ""; + // if (textHandler) { + // str = textHandler(val); + // } else { + // val -= vmin; + // if (val >= 0 && val < (int)values.size()) { + // str = values[val]; + // } + // } - theme->drawChoice(dc, this, str.c_str()); + // theme->drawChoice(dc, this, str.c_str()); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index d6bfc2af3..1406747b4 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -40,7 +40,10 @@ class ChoiceBase : public FormField } protected: + friend void choicePaintCallback(lv_event_t *e); ChoiceType type; + lv_obj_t *label; + virtual std::string getLabelText() { return ""; }; }; class Choice: public ChoiceBase { @@ -150,6 +153,7 @@ class Choice: public ChoiceBase { } protected: + std::string getLabelText() override; std::vector values; int vmin = 0; int vmax = 0; diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 0e289b9be..36443b80a 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -48,10 +48,16 @@ FileChoice::FileChoice(FormGroup *parent, const rect_t &rect, { } +std::string FileChoice::getLabelText() +{ + return getValue(); +} + + void FileChoice::paint(BitmapBuffer * dc) { - FormField::paint(dc); - theme->drawChoice(dc, this, getValue().c_str()); + // FormField::paint(dc); + // theme->drawChoice(dc, this, getValue().c_str()); } bool FileChoice::openMenu() diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index 5e5f57c51..dd7befa59 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -46,6 +46,7 @@ public: #endif protected: + std::string getLabelText() override; std::string folder; const char* extension; int maxlen; diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 3ec5a384d..420ea53ea 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -102,7 +102,7 @@ void MenuBody::setIndex(int index) btn = lv_obj_get_child(lvobj, selectedIndex); if (btn != nullptr) { lv_obj_add_state(btn, LV_STATE_CHECKED); - lv_obj_scroll_to_view(btn, LV_ANIM_ON); + lv_obj_scroll_to_view(btn, LV_ANIM_OFF); } } } From e53c334aed3ccc6dc5ed6329826c41a08c078779 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Mon, 7 Feb 2022 13:52:53 -0500 Subject: [PATCH 518/714] some additional menu fixes --- radio/src/thirdparty/libopenui/src/menu.cpp | 107 -------------------- radio/src/thirdparty/libopenui/src/menu.h | 12 +-- 2 files changed, 1 insertion(+), 118 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 420ea53ea..5f0f75d2d 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -182,87 +182,6 @@ void MenuBody::onEvent(event_t event) } #endif -#if defined(HARDWARE_TOUCH) -bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) -{ - Menu * menu = getParentMenu(); - - int index = 0; - - // dynamically find the selected element based on variable line heights - coord_t yAccumulator = 0; - for (int i = 0; i < (signed)lines.size(); i++) { - if (y >= yAccumulator && y <= yAccumulator + lines[i].lineHeight()) { - index = i; - break; - } - yAccumulator += lines[i].lineHeight(); - } - - // dont allow selecting separators - if (lines[index].isSeparator) - return false; - - if (index < (int)lines.size()) { - onKeyPress(); - if (menu->multiple) { - if (selectedIndex == index) - lines[index].onPress(); - else - setIndex(index); - menu->invalidate(); - } - else { - setIndex(index); - menu->deleteLater(); - lines[index].onPress(); - } - } - - return true; -} -#endif - -void MenuBody::paint(BitmapBuffer * dc) -{ - // dc->clear(COLOR_THEME_PRIMARY2); - - // int y = 0; - // for (int i = 0; i < (signed)lines.size(); i++) { - // auto& line = lines[i]; - // LcdFlags flags = COLOR_THEME_PRIMARY3 | MENU_FONT; - - // // draw selection if appropriate - // if (selectedIndex == i && ! line.isSeparator) { - // flags = COLOR_THEME_PRIMARY2 | MENU_FONT; - // if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { - // dc->drawSolidFilledRect(0, y, width(), - // MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); - // } - // } - - // if (line.isSeparator) { - // dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY1); - // } else if (line.drawLine) { - // line.drawLine(dc, 0, y, COLOR_THEME_PRIMARY3); - // } else { - // const char* text = line.text.data(); - // dc->drawText(10, - // y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, - // text[0] == '\0' ? "---" : text, flags); - // } - - // Menu* menu = getParentMenu(); - // if (menu->multiple && line.isChecked) { - // theme->drawCheckBox(dc, line.isChecked(), width() - 35, - // y + (MENUS_LINE_HEIGHT - 20) / 2, - // 0); - // } - - // y += line.lineHeight(); - // } -} - MenuWindowContent::MenuWindowContent(Menu* parent) : ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), @@ -272,21 +191,6 @@ MenuWindowContent::MenuWindowContent(Menu* parent) : lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); } -void MenuWindowContent::paint(BitmapBuffer * dc) -{ - // // the background - // dc->clear(COLOR_THEME_SECONDARY1); - - // // the title - // if (!title.empty()) { - // dc->drawText(MENUS_WIDTH / 2, - // (POPUP_HEADER_HEIGHT - getFontHeight(MENU_HEADER_FONT)) / 2, - // title.c_str(), CENTERED | MENU_HEADER_FONT | COLOR_THEME_PRIMARY2); - // dc->drawSolidHorizontalLine(0, POPUP_HEADER_HEIGHT - 1, MENUS_WIDTH, - // COLOR_THEME_SECONDARY2); - // } -} - Menu::Menu(Window * parent, bool multiple): ModalWindow(parent, true), content(createMenuWindow(this)), @@ -360,14 +264,3 @@ void Menu::onEvent(event_t event) } #endif -void Menu::paint(BitmapBuffer * dc) -{ - ModalWindow::paint(dc); - - rect_t r(content->getRect()); - if (toolbar) { - r.x = toolbar->left(); - r.w += toolbar->width(); - } - dc->drawSolidRect(r.x - 1, r.y - 1, r.w + 2, r.h + 2, 1, COLOR_THEME_SECONDARY1); -} diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 1285f8f96..c539719b1 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -71,7 +71,7 @@ class MenuBody: public Window protected: friend void menuBodyEventCallback(lv_event_t *e); bool isSeparator = false; - coord_t height = MENUS_LINE_HEIGHT; + coord_t height = MENUS_LINE_HEIGHT + 10; std::string text; std::function drawLine; std::function onPress; @@ -109,10 +109,6 @@ class MenuBody: public Window void onEvent(event_t event) override; #endif -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif - void addLine(const std::string & text, std::function onPress, std::function isChecked); void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) @@ -133,8 +129,6 @@ class MenuBody: public Window onCancel = std::move(handler); } - void paint(BitmapBuffer * dc) override; - protected: friend void menuBodyEventCallback(lv_event_t *); void selectNext(MENU_DIRECTION direction); @@ -171,8 +165,6 @@ class MenuWindowContent: public ModalWindowContent } #endif - void paint(BitmapBuffer * dc) override; - protected: MenuBody body; }; @@ -251,8 +243,6 @@ class Menu: public ModalWindow } } - void paint(BitmapBuffer * dc) override; - protected: friend void menuBodyEventCallback(lv_event_t *); MenuWindowContent * content; From 9804ff9f2122d131195372cca4936e1bed694c41 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 8 Feb 2022 13:34:59 +0100 Subject: [PATCH 519/714] Implement form drawing via styles rather than paint() method --- radio/src/thirdparty/libopenui/src/button.h | 3 +- radio/src/thirdparty/libopenui/src/checkbox.h | 2 - radio/src/thirdparty/libopenui/src/form.cpp | 76 +++-- radio/src/thirdparty/libopenui/src/form.h | 292 ++++++++---------- 4 files changed, 165 insertions(+), 208 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 448ddc6b3..cc2ab19cf 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -34,6 +34,7 @@ class Button: public FormField FormField(parent, rect, windowFlags, textFlags, factory), pressHandler(std::move(pressHandler)) { + lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); } #if defined(DEBUG_WINDOWS) @@ -116,8 +117,6 @@ class TextButton: public Button bgColorHandler = std::move(handler); } - void paint(BitmapBuffer* dc) override {} - protected: lv_obj_t * label = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 2d772968f..235d08b9c 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -65,8 +65,6 @@ class CheckBox : public FormField { { _setValue = std::move(handler); } - - void paint(BitmapBuffer* dc) override {} protected: std::string label; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index bb9632d3c..a09a1005d 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -21,15 +21,34 @@ #include "bitmapbuffer.h" #include "libopenui_config.h" -FormField::FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags, LcdFlags textFlags, LvglWidgetFactory *factory) : - Window(parent, rect, windowFlags, textFlags, factory) +static lv_style_t bg_style; +static lv_style_t focus_style; +static lv_style_t edit_style; + +FormField::FormField(Window* parent, const rect_t& rect, + WindowFlags windowFlags, LcdFlags textFlags, + LvglWidgetFactory* factory) : + Window(parent, rect, windowFlags, textFlags, factory) { if (!(windowFlags & NO_FOCUS)) { - auto * form = dynamic_cast(parent); + auto* form = dynamic_cast(parent); if (form) { form->addField(this, windowFlags & PUSH_FRONT); } } + + lv_style_init(&bg_style); + lv_style_set_bg_color(&bg_style, makeLvColor(COLOR_THEME_PRIMARY2)); + lv_style_set_bg_opa(&bg_style, LV_OPA_COVER); + lv_obj_add_style(lvobj, &bg_style, LV_PART_MAIN); + + lv_style_init(&focus_style); + lv_style_set_bg_color(&focus_style, makeLvColor(COLOR_THEME_FOCUS)); + lv_obj_add_style(lvobj, &focus_style, LV_PART_MAIN | LV_STATE_FOCUSED); + + lv_style_init(&edit_style); + lv_style_set_bg_color(&edit_style, makeLvColor(COLOR_THEME_EDIT)); + lv_obj_add_style(lvobj, &edit_style, LV_PART_MAIN | LV_STATE_EDITED); } #if defined(HARDWARE_KEYS) @@ -90,25 +109,28 @@ void FormField::setFocus(uint8_t flag, Window * from) } } -void FormField::paint(BitmapBuffer * dc) -{ - uint32_t bgColor = 0; - if (backgroundHandler) { - bgColor = backgroundHandler(this); - } else { - if (editMode) { - bgColor = COLOR_THEME_EDIT; - } else if (hasFocus()) { - bgColor = COLOR_THEME_FOCUS; - } else { - bgColor = COLOR_THEME_PRIMARY2; - } - } +static lv_style_t border_style; +static lv_style_t focus_border_style; - if (editMode || ! (windowFlags & FORM_NO_BORDER)) { - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); - if (!editMode) - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); +FormGroup::FormGroup(Window* parent, const rect_t& rect, + WindowFlags windowflags, LvglWidgetFactory* factory) : + FormField(parent, rect, windowflags) +{ + lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); + + + if (!(windowFlags & (FORM_NO_BORDER | FORM_FORWARD_FOCUS))) { + lv_style_init(&border_style); + lv_style_set_border_width(&border_style, 2); + lv_style_set_border_color(&border_style, makeLvColor(COLOR_THEME_FOCUS)); + lv_obj_add_style(lvobj, &border_style, LV_PART_MAIN | LV_STATE_FOCUSED); + + if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { + lv_style_init(&focus_border_style); + lv_style_set_border_width(&focus_border_style, 1); + lv_style_set_border_color(&focus_border_style, makeLvColor(COLOR_THEME_SECONDARY2)); + lv_obj_add_style(lvobj, &focus_border_style, LV_PART_MAIN); + } } } @@ -276,18 +298,6 @@ void FormGroup::onEvent(event_t event) } #endif -void FormGroup::paint(BitmapBuffer * dc) -{ - if (!(windowFlags & (FORM_NO_BORDER | FORM_FORWARD_FOCUS))) { - if (!editMode && hasFocus()) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); - } - else if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); - } - } -} - #if defined(HARDWARE_KEYS) void FormWindow::onEvent(event_t event) { diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index c645454f4..10aab8d6e 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -27,222 +27,172 @@ constexpr WindowFlags FORM_BORDER_FOCUS_ONLY = WINDOW_FLAGS_LAST << 3; constexpr WindowFlags FORM_NO_BORDER = WINDOW_FLAGS_LAST << 4; constexpr WindowFlags FORM_FLAGS_LAST = FORM_NO_BORDER; -class FormField: public Window +class FormField : public Window { - public: - FormField(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr); + public: + FormField(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0, + LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr); - virtual void changeEnd(bool forceChanged = false) - { - if (changeHandler) { - changeHandler(); - } + virtual void changeEnd(bool forceChanged = false) + { + if (changeHandler) { + changeHandler(); } - - void setChangeHandler(std::function handler) - { - changeHandler = std::move(handler); + } + + void setChangeHandler(std::function handler) + { + changeHandler = std::move(handler); + } + + void setBackgroundHandler(std::function handler) + { + backgroundHandler = std::move(handler); + } + + inline void setNextField(FormField *field) { next = field; } + + inline void setPreviousField(FormField *field) { previous = field; } + + inline FormField *getPreviousField() { return previous; } + + inline FormField *getNextField() { return next; } + + inline bool isEditMode() const { return editMode; } + + virtual void setEditMode(bool newEditMode) + { + editMode = newEditMode; + + if (lvobj != nullptr) { + lv_group_t *grp = (lv_group_t *)lv_obj_get_group(lvobj); + if (grp != nullptr) lv_group_set_editing(grp, editMode); } - void setBackgroundHandler(std::function handler) - { - backgroundHandler = std::move(handler); + if (editMode && lvobj != nullptr) { + lv_obj_add_state(lvobj, LV_STATE_EDITED | LV_STATE_FOCUSED); + } else if (lvobj != nullptr) { + lv_obj_clear_state(lvobj, LV_STATE_EDITED); } + invalidate(); + } - inline void setNextField(FormField *field) - { - next = field; - } + void enable(bool value = true) + { + enabled = value; + invalidate(); + } - inline void setPreviousField(FormField * field) - { - previous = field; - } + bool isEnabled() const { return enabled; } + void disable() { enable(false); } - inline FormField * getPreviousField() - { - return previous; - } + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, + Window *from = nullptr) override; - inline FormField * getNextField() - { - return next; - } + void onFocusLost() override + { + setEditMode(false); + Window::onFocusLost(); + } - inline bool isEditMode() const - { - return editMode; - } - - virtual void setEditMode(bool newEditMode) - { - editMode = newEditMode; - - if(lvobj != nullptr) - { - lv_group_t* grp = (lv_group_t*)lv_obj_get_group(lvobj); - if(grp != nullptr) - lv_group_set_editing(grp, editMode); - } - - if (editMode && lvobj != nullptr) { - lv_obj_add_state(lvobj, LV_STATE_EDITED | LV_STATE_FOCUSED); - } else if (lvobj != nullptr) { - lv_obj_clear_state(lvobj, LV_STATE_EDITED); - } - invalidate(); - } - - void enable(bool value = true) - { - enabled = value; - invalidate(); - } - - bool isEnabled() const - { - return enabled; - } - - void disable() - { - enable(false); - } - - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override; - - void onFocusLost() override - { - setEditMode(false); - Window::onFocusLost(); - } - - inline static void link(FormField * previous, FormField * next) - { - previous->setNextField(next); - next->setPreviousField(previous); - } - - void paint(BitmapBuffer * dc) override; + inline static void link(FormField *previous, FormField *next) + { + previous->setNextField(next); + next->setPreviousField(previous); + } #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; + void onEvent(event_t event) override; #endif - protected: - FormField * next = nullptr; - FormField * previous = nullptr; - bool editMode = false; - bool enabled = true; - std::function changeHandler = nullptr; - std::function backgroundHandler = nullptr; + protected: + FormField *next = nullptr; + FormField *previous = nullptr; + bool editMode = false; + bool enabled = true; + std::function changeHandler = nullptr; + std::function backgroundHandler = nullptr; }; -class FormGroup: public FormField +class FormGroup : public FormField { - public: - FormGroup(Window * parent, const rect_t & rect, WindowFlags windowflags = 0, LvglWidgetFactory *factory = nullptr) : - FormField(parent, rect, windowflags) - { - } + public: + FormGroup(Window *parent, const rect_t &rect, WindowFlags windowflags = 0, + LvglWidgetFactory *factory = nullptr); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "FormGroup"; - } + std::string getName() const override { return "FormGroup"; } #endif - void clear() - { - Window::clear(); - first = nullptr; - last = nullptr; - if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { - previous->setNextField(this); - } + void clear() + { + Window::clear(); + first = nullptr; + last = nullptr; + if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { + previous->setNextField(this); } + } - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override; + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, + Window *from = nullptr) override; - virtual void addField(FormField * field, bool front = false); + virtual void addField(FormField *field, bool front = false); + virtual void removeField(FormField *field); - virtual void removeField(FormField * field); + void setFirstField(FormField *field) { first = field; } + void setLastField(FormField *field) { last = field; } + FormField *getFirstField() const { return first; } + FormField *getLastField() const { return last; } - void setFirstField(FormField * field) - { - first = field; + void setFocusOnFirstVisibleField(uint8_t flag = SET_FOCUS_DEFAULT) const + { + auto field = getFirstField(); + while (field && !field->isInsideParentScrollingArea()) { + field = field->getNextField(); } - - void setLastField(FormField * field) - { - last = field; + if (field) { + field->setFocus(flag); } + } - FormField * getFirstField() const - { - return first; + void setFocusOnLastVisibleField(uint8_t flag = SET_FOCUS_DEFAULT) const + { + auto field = getLastField(); + while (field && !field->isInsideParentScrollingArea()) { + field = field->getPreviousField(); } - - FormField * getLastField() const - { - return last; + if (field) { + field->setFocus(flag); } + } - void setFocusOnFirstVisibleField(uint8_t flag = SET_FOCUS_DEFAULT) const - { - auto field = getFirstField(); - while (field && !field->isInsideParentScrollingArea()) { - field = field->getNextField(); - } - if (field) { - field->setFocus(flag); - } - } - - void setFocusOnLastVisibleField(uint8_t flag = SET_FOCUS_DEFAULT) const - { - auto field = getLastField(); - while (field && !field->isInsideParentScrollingArea()) { - field = field->getPreviousField(); - } - if (field) { - field->setFocus(flag); - } - } - - protected: - FormField * first = nullptr; - FormField * last = nullptr; + protected: + FormField *first = nullptr; + FormField *last = nullptr; #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; + void onEvent(event_t event) override; #endif - - void paint(BitmapBuffer * dc) override; }; -class FormWindow: public FormGroup +class FormWindow : public FormGroup { - public: - FormWindow(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LvglWidgetFactory *factory = nullptr) : + public: + FormWindow(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0, + LvglWidgetFactory *factory = nullptr) : FormGroup(parent, rect, windowFlags, factory) - { - } + { + } #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "FormWindow"; - } + std::string getName() const override { return "FormWindow"; } #endif - protected: - void paint(BitmapBuffer *) override - { - } + protected: + void paint(BitmapBuffer *) override {} #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; + void onEvent(event_t event) override; #endif }; From 83a5fd913f33cea6dd783c32b32e0325d3cfdfd4 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 9 Feb 2022 02:51:40 -0500 Subject: [PATCH 520/714] added choice to default group --- radio/src/thirdparty/libopenui/src/choice.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 0c1ab0826..f1d83e1c7 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -41,6 +41,11 @@ ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, { lv_obj_add_event_cb(lvobj, choicePaintCallback, LV_EVENT_DRAW_MAIN_BEGIN, lvobj); label = lv_label_create(lvobj); + lv_group_t * def_group = lv_group_get_default(); + if(def_group) { + lv_group_add_obj(def_group, lvobj); + } + // Normal font and background color lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN); lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); @@ -71,8 +76,13 @@ std::string Choice::getLabelText() std::string text = ""; if (textHandler != nullptr) text = textHandler(getValue()); - else if (getValue() < values.size()) - text = values[getValue()]; + else { + int val = getValue(); + val -= vmin; + if (val >= 0 && val < (int)values.size()) { + text = values[val]; + } + } return text; } From 2a0bb66dafe4c58339895c950b3c8c3a4268c55d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Feb 2022 12:31:22 +0100 Subject: [PATCH 521/714] WIP: default theme --- .../thirdparty/libopenui/src/basenumberedit.h | 21 +++++---- radio/src/thirdparty/libopenui/src/button.cpp | 43 ++++++++++++------- radio/src/thirdparty/libopenui/src/button.h | 17 ++++---- .../src/thirdparty/libopenui/src/checkbox.cpp | 38 ++++++++-------- radio/src/thirdparty/libopenui/src/form.cpp | 8 ++-- radio/src/thirdparty/libopenui/src/form.h | 8 ++-- radio/src/thirdparty/libopenui/src/menu.cpp | 6 +-- .../thirdparty/libopenui/src/numberedit.cpp | 3 +- radio/src/thirdparty/libopenui/src/slider.cpp | 4 +- .../src/thirdparty/libopenui/src/textedit.cpp | 3 +- radio/src/thirdparty/libopenui/src/window.cpp | 19 ++------ radio/src/thirdparty/libopenui/src/window.h | 7 ++- 12 files changed, 89 insertions(+), 88 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 4b9b4939b..e0911ccb1 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -25,15 +25,18 @@ class BaseNumberEdit: public FormField { public: - BaseNumberEdit(Window * parent, const rect_t &rect, int vmin, int vmax, - std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr): - FormField(parent, rect, windowFlags, textFlags, factory), - vmin(vmin), - vmax(vmax), - _getValue(std::move(getValue)), - _setValue(std::move(setValue)) - { - } + BaseNumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, + std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0, LcdFlags textFlags = 0, + LvglCreate objConstruct = nullptr) : + FormField(parent, rect, windowFlags, textFlags, objConstruct), + vmin(vmin), + vmax(vmax), + _getValue(std::move(getValue)), + _setValue(std::move(setValue)) + { + } void setMin(int value) { diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 8d8bf6239..e40fbabd2 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -81,13 +81,12 @@ void Button::checkEvents() } lv_style_t style_btn; -LvglWidgetFactory textButtonFactory = {lv_btn_create, nullptr}; TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, - std::function pressHandler, - WindowFlags windowFlags, - LcdFlags textFlags) : - Button(parent, rect, std::move(pressHandler), windowFlags, textFlags, &textButtonFactory), + std::function pressHandler, + WindowFlags windowFlags, LcdFlags textFlags) : + Button(parent, rect, std::move(pressHandler), windowFlags, textFlags, + lv_btn_create), text(std::move(text)) { setTextFlags(textFlags | COLOR_THEME_PRIMARY1); @@ -97,25 +96,37 @@ TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, lv_style_init(&style_btn); lv_style_set_radius(&style_btn, 0); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_ACTIVE), LV_PART_MAIN | LV_STATE_CHECKED); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_ACTIVE), LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_FOCUSED); + + //TODO: use 'style_btn' instead of local style + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), + LV_PART_MAIN); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_ACTIVE), + LV_PART_MAIN | LV_STATE_CHECKED); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_ACTIVE), + LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), + LV_PART_MAIN | LV_STATE_FOCUSED); lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); lv_obj_set_style_border_width(lvobj, 0, LV_PART_MAIN); - - lv_obj_set_style_border_width(lvobj, 1, LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); - lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); - lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_set_style_border_width( + lvobj, 1, LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); + lv_obj_set_style_border_color( + lvobj, makeLvColor(COLOR_THEME_FOCUS), + LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); - lv_obj_add_style(lvobj, &style_btn,0); + lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), + LV_PART_MAIN | LV_STATE_FOCUSED); + + lv_obj_add_style(lvobj, &style_btn, 0); label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); lv_obj_center(label); - lv_obj_set_style_text_color(label, makeLvColor(this->getTextFlags()), LV_PART_MAIN); - lv_obj_set_style_text_color(label, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_set_style_text_color(label, makeLvColor(this->getTextFlags()), + LV_PART_MAIN); + lv_obj_set_style_text_color(label, makeLvColor(COLOR_THEME_PRIMARY2), + LV_PART_MAIN | LV_STATE_FOCUSED); } void IconButton::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index cc2ab19cf..695ccf30e 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -28,14 +28,15 @@ constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3u; class Button: public FormField { public: - Button(FormGroup* parent, const rect_t& rect, - std::function pressHandler = nullptr, - WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr) : - FormField(parent, rect, windowFlags, textFlags, factory), - pressHandler(std::move(pressHandler)) - { - lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); - } + Button(FormGroup* parent, const rect_t& rect, + std::function pressHandler = nullptr, + WindowFlags windowFlags = 0, LcdFlags textFlags = 0, + LvglCreate objConstruct = nullptr) : + FormField(parent, rect, windowFlags, textFlags, objConstruct), + pressHandler(std::move(pressHandler)) + { + lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); + } #if defined(DEBUG_WINDOWS) std::string getName() const override diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 43ed81df0..2e914fe79 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -20,25 +20,22 @@ #include "checkbox.h" #include "theme.h" -LvglWidgetFactory checkBoxFactory = { lv_checkbox_create, nullptr }; lv_style_t style_indicator; lv_style_t style_edit; - -static void checkbox_event_handler(lv_event_t * e) +static void checkbox_event_handler(lv_event_t* e) { - lv_obj_t * target = lv_event_get_target(e); - CheckBox* cb = (CheckBox *)lv_obj_get_user_data(target); - + lv_obj_t* target = lv_event_get_target(e); + CheckBox* cb = (CheckBox*)lv_obj_get_user_data(target); + onKeyPress(); - if (cb) - cb->setValue(lv_obj_get_state(target) & LV_STATE_CHECKED); + if (cb) cb->setValue(lv_obj_get_state(target) & LV_STATE_CHECKED); } CheckBox::CheckBox(Window* parent, const rect_t& rect, std::function getValue, std::function setValue, WindowFlags flags) : - FormField(parent, rect, flags, 0, &checkBoxFactory), + FormField(parent, rect, flags, 0, lv_checkbox_create), _getValue(std::move(getValue)), _setValue(std::move(setValue)) { @@ -46,23 +43,30 @@ CheckBox::CheckBox(Window* parent, const rect_t& rect, setWidth(size); setHeight(size); + //TODO: migrate to default theme + // BORDER - lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_INDICATOR); + lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), + LV_PART_INDICATOR); lv_obj_set_style_border_width(lvobj, 1, LV_PART_INDICATOR); - lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_INDICATOR | LV_STATE_FOCUSED); + lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), + LV_PART_INDICATOR | LV_STATE_FOCUSED); lv_obj_set_style_border_width(lvobj, 2, LV_PART_INDICATOR | LV_STATE_FOCUSED); // UNCHECKED BACKGROUND - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_INDICATOR); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_INDICATOR | LV_STATE_CHECKED); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), + LV_PART_INDICATOR); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), + LV_PART_INDICATOR | LV_STATE_CHECKED); - lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_INDICATOR | LV_STATE_CHECKED); + lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), + LV_PART_INDICATOR | LV_STATE_CHECKED); - if (_getValue()) - lv_obj_add_state(lvobj, LV_STATE_CHECKED); + if (_getValue()) lv_obj_add_state(lvobj, LV_STATE_CHECKED); - lv_obj_add_event_cb(lvobj, checkbox_event_handler, LV_EVENT_VALUE_CHANGED, this); + lv_obj_add_event_cb(lvobj, checkbox_event_handler, LV_EVENT_VALUE_CHANGED, + this); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index a09a1005d..f26f3a608 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -27,8 +27,8 @@ static lv_style_t edit_style; FormField::FormField(Window* parent, const rect_t& rect, WindowFlags windowFlags, LcdFlags textFlags, - LvglWidgetFactory* factory) : - Window(parent, rect, windowFlags, textFlags, factory) + LvglCreate objConstruct) : + Window(parent, rect, windowFlags, textFlags, objConstruct) { if (!(windowFlags & NO_FOCUS)) { auto* form = dynamic_cast(parent); @@ -113,8 +113,8 @@ static lv_style_t border_style; static lv_style_t focus_border_style; FormGroup::FormGroup(Window* parent, const rect_t& rect, - WindowFlags windowflags, LvglWidgetFactory* factory) : - FormField(parent, rect, windowflags) + WindowFlags windowflags, LvglCreate objConstruct) : + FormField(parent, rect, windowflags, 0, objConstruct) { lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 10aab8d6e..0bdb95855 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -31,7 +31,7 @@ class FormField : public Window { public: FormField(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0, - LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr); + LcdFlags textFlags = 0, LvglCreate objConstruct = nullptr); virtual void changeEnd(bool forceChanged = false) { @@ -118,7 +118,7 @@ class FormGroup : public FormField { public: FormGroup(Window *parent, const rect_t &rect, WindowFlags windowflags = 0, - LvglWidgetFactory *factory = nullptr); + LvglCreate objConstruct = nullptr); #if defined(DEBUG_WINDOWS) std::string getName() const override { return "FormGroup"; } @@ -180,8 +180,8 @@ class FormWindow : public FormGroup { public: FormWindow(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0, - LvglWidgetFactory *factory = nullptr) : - FormGroup(parent, rect, windowFlags, factory) + LvglCreate objConstruct = nullptr) : + FormGroup(parent, rect, windowFlags, objConstruct) { } diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 5f0f75d2d..0e17c8a62 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,10 +21,6 @@ #include "font.h" #include "theme.h" -LvglWidgetFactory menuBodyWidgetFactory = { [] (lv_obj_t *parent) { - return lv_list_create(parent); -}, nullptr}; - void menuBodyEventCallback(lv_event_t *e) { auto code = lv_event_get_code(e); @@ -53,7 +49,7 @@ void menuBodyEventCallback(lv_event_t *e) } MenuBody::MenuBody(Window * parent, const rect_t & rect): - Window(parent, rect, OPAQUE, 0, &menuBodyWidgetFactory) + Window(parent, rect, OPAQUE, 0, lv_list_create) { setPageHeight(MENUS_LINE_HEIGHT); } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 419bccd39..8ac861805 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -24,7 +24,6 @@ #endif -static LvglWidgetFactory numberEditFactory = { lv_textarea_create, nullptr }; static lv_style_t style_main; static lv_style_t style_edit; @@ -64,7 +63,7 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, WindowFlags windowFlags, LcdFlags textFlags) : BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags, - &numberEditFactory) + lv_textarea_create) { // properties lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index 26a5feac9..34807c0a4 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -44,8 +44,6 @@ static lv_style_t style_knob; static lv_style_t style_focused; static lv_style_t style_edit; -static LvglWidgetFactory sliderFactory = { lv_slider_create, nullptr }; - constexpr coord_t SLIDER_PADDING = 6; Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, @@ -54,7 +52,7 @@ Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, FormField(parent, {rect.x + SLIDER_PADDING, rect.y + SLIDER_PADDING, rect.w - 2 * SLIDER_PADDING, rect.h - 2 * SLIDER_PADDING}, - 0, 0, &sliderFactory), + 0, 0, lv_slider_create), vmin(vmin), vmax(vmax), _getValue(std::move(getValue)), diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 58c73aa84..9b4f67aaf 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -44,13 +44,12 @@ static lv_style_t style_main; static lv_style_t style_edit; -static LvglWidgetFactory textEditFactory = { lv_textarea_create, nullptr }; TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, uint8_t length, LcdFlags windowFlags, const char *_extra_chars) : FormField(parent, {rect.x, rect.y, rect.w, rect.h - 2}, windowFlags, 0, - &textEditFactory), + lv_textarea_create), value(value), length(length) { diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 260a38448..0849526b4 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -137,19 +137,8 @@ static void window_event_cb(lv_event_t * e) } } -LvglWidgetFactory windowFactory = LvglWidgetFactory( - [] (lv_obj_t *parent) { - return lv_obj_create(parent); - }, - [] (LvglWidgetFactory *factory) { - lv_style_set_pad_all(&factory->style, 0); - lv_style_set_bg_opa(&factory->style, LV_OPA_TRANSP); - lv_style_set_border_width(&factory->style, 0); - lv_style_set_radius(&factory->style, 0); - }); - Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, - LcdFlags textFlags, LvglWidgetFactory *factory) : + LcdFlags textFlags, LvglCreate objConstruct) : parent(parent), rect(rect), innerWidth(rect.w), @@ -158,11 +147,9 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, textFlags(textFlags) { lv_obj_t *lvParent = parent != nullptr ? parent->lvobj : nullptr; - lvobj = (factory == nullptr) ? - windowFactory.construct(lvParent) : - factory->construct(lvParent); + lvobj = (objConstruct == nullptr) ? lv_obj_create(lvParent) + : objConstruct(lvParent); - lv_obj_add_style(lvobj, &windowFactory.style, LV_PART_MAIN); lv_obj_set_pos(lvobj, rect.x, rect.y); lv_obj_set_size(lvobj, rect.w, rect.h); lv_obj_set_user_data(lvobj, this); diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 78e5c148b..3d66933a9 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -59,14 +59,17 @@ enum SetFocusFlag SET_FOCUS_FIRST }; +typedef lv_obj_t *(*LvglCreate)(lv_obj_t *); + class Window { friend class GridLayout; public: - Window(Window * parent, const rect_t & rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglWidgetFactory *factory = nullptr); + Window(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0, + LcdFlags textFlags = 0, LvglCreate objConstruct = nullptr); - virtual ~Window(); + virtual ~Window(); #if defined(DEBUG_WINDOWS) virtual std::string getName() const From 885ebe69f699a638eea50153a7b6fac9116b8a8d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Feb 2022 13:14:26 +0100 Subject: [PATCH 522/714] Moved button styles to theme --- radio/src/thirdparty/libopenui/src/button.cpp | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index e40fbabd2..c857c3694 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -94,32 +94,6 @@ TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, if (windowFlags & BUTTON_CHECKED) lv_obj_add_state(lvobj, LV_STATE_CHECKED); - lv_style_init(&style_btn); - lv_style_set_radius(&style_btn, 0); - - //TODO: use 'style_btn' instead of local style - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), - LV_PART_MAIN); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_ACTIVE), - LV_PART_MAIN | LV_STATE_CHECKED); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_ACTIVE), - LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), - LV_PART_MAIN | LV_STATE_FOCUSED); - lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); - lv_obj_set_style_border_width(lvobj, 0, LV_PART_MAIN); - - lv_obj_set_style_border_width( - lvobj, 1, LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); - lv_obj_set_style_border_color( - lvobj, makeLvColor(COLOR_THEME_FOCUS), - LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); - - lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), - LV_PART_MAIN | LV_STATE_FOCUSED); - - lv_obj_add_style(lvobj, &style_btn, 0); - label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); lv_obj_center(label); From 3486480f3677fcfbf108aa9bb9c6da33acd6abb0 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Wed, 9 Feb 2022 13:34:04 +0100 Subject: [PATCH 523/714] LVGL rotarty encoder handling updated LVGL rotary encoder handling disabled, to reenable un-comment windowcpp Window::checkEvents the call to lvglPushEncoderEvent --- radio/src/thirdparty/libopenui/src/form.cpp | 10 +++++----- radio/src/thirdparty/libopenui/src/window.cpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index f26f3a608..52c36af1c 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -88,12 +88,12 @@ void FormField::setFocus(uint8_t flag, Window * from) { if (enabled) { Window::setFocus(flag, from); - if(lvobj != nullptr) - { - lv_group_focus_freeze(inputGroup, false); +// if(lvobj != nullptr) +// { +// lv_group_focus_freeze(inputGroup, false); // lv_group_focus_obj(lvobj); - lv_group_focus_freeze(inputGroup, true); - } +// lv_group_focus_freeze(inputGroup, true); +// } } else { if (flag == SET_FOCUS_BACKWARD) { diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0849526b4..d077dd1f5 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -533,7 +533,7 @@ coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) } #endif -void lvglPushEncoderEvent(event_t evt); +void lvglPushEncoderEvent(event_t& evt); void Window::checkEvents() { auto copy = children; @@ -545,7 +545,7 @@ void Window::checkEvents() if (this == Window::focusWindow) { event_t event = getWindowEvent(); - lvglPushEncoderEvent(event); +// lvglPushEncoderEvent(event); if (event) { this->onEvent(event); } From 157b42324324cefb0454fb7f65db3ab50d5ed386 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Wed, 9 Feb 2022 08:06:04 -0500 Subject: [PATCH 524/714] cleanup of choice --- radio/src/thirdparty/libopenui/src/choice.cpp | 18 ------------------ radio/src/thirdparty/libopenui/src/choice.h | 2 -- 2 files changed, 20 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index f1d83e1c7..fdcdaac93 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -167,24 +167,6 @@ void Choice::setValues(const char * const values[]) } } -void Choice::paint(BitmapBuffer * dc) -{ - // FormField::paint(dc); - // auto val = getValue(); - - // std::string str = ""; - // if (textHandler) { - // str = textHandler(val); - // } else { - // val -= vmin; - // if (val >= 0 && val < (int)values.size()) { - // str = values[val]; - // } - // } - - // theme->drawChoice(dc, this, str.c_str()); -} - #if defined(HARDWARE_KEYS) void Choice::onEvent(event_t event) { diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 1406747b4..710990572 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -68,8 +68,6 @@ class Choice: public ChoiceBase { } #endif - void paint(BitmapBuffer * dc) override; - #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; #endif From f99c16c7aa7bc3ada03606c830e8c43fd0cd8aee Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Feb 2022 15:20:22 +0100 Subject: [PATCH 525/714] Moved menu styles to theme --- radio/src/thirdparty/libopenui/src/menu.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 0e17c8a62..66b9136c2 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -54,18 +54,14 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect): setPageHeight(MENUS_LINE_HEIGHT); } -void MenuBody::addLine(const std::string & text, std::function onPress, std::function isChecked) +void MenuBody::addLine(const std::string &text, std::function onPress, + std::function isChecked) { lines.emplace_back(text, std::move(onPress), std::move(isChecked)); lv_obj_t *btn = lv_list_add_btn(lvobj, nullptr, text.c_str()); - lv_obj_set_style_bg_color(btn, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN | LV_STATE_CHECKED); - lv_obj_set_style_bg_color(btn, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); - lv_obj_set_style_bg_opa(btn, LV_OPA_100, LV_PART_MAIN); - lv_obj_set_style_bg_opa(btn, LV_OPA_100, LV_PART_MAIN | LV_STATE_CHECKED); - lv_obj_set_style_bg_opa(btn, LV_OPA_100, LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_FOCUSED); lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); - lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, (void *) (lines.size() - 1)); - lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); + lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, + (void *)(lines.size() - 1)); } void MenuBody::removeLines() From 9d453035314c279779aa2c0d054f2c2b2bbea005 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Thu, 10 Feb 2022 14:12:09 +0100 Subject: [PATCH 526/714] make button lvgl-Event aware --- radio/src/thirdparty/libopenui/src/button.cpp | 35 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/button.h | 8 ++--- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index c857c3694..07e165a86 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -22,6 +22,26 @@ #include "font.h" #include "theme.h" +static void lvglEvent(lv_event_t* e) +{ + Button* btn = (Button*)lv_event_get_user_data(e); + if(btn->deleted()) + return; + + btn->onEvent(e); +} + +Button::Button(FormGroup* parent, const rect_t& rect, + std::function pressHandler, + WindowFlags windowFlag, LcdFlags textFlags, + LvglCreate objConstruct) : + FormField(parent, rect, windowFlag, textFlags, objConstruct), + pressHandler(std::move(pressHandler)) +{ + lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); + lv_obj_add_event_cb(lvobj, lvglEvent, LV_EVENT_CLICKED, this); +} + void Button::onPress() { bool check = (pressHandler && pressHandler()); @@ -51,6 +71,21 @@ void Button::onEvent(event_t event) } #endif +void Button::onEvent(lv_event_t* event) +{ + lv_event_code_t code = lv_event_get_code(event); + + TRACE_WINDOWS("%s received lvgl event code 0x%X", getWindowDebugString("Button").c_str(), code); + + if (enabled && code == LV_EVENT_CLICKED) { + onKeyPress(); + onPress(); + } + else { +// FormField::onEvent(event); + } +} + #if defined(HARDWARE_TOUCH) bool Button::onTouchStart(coord_t x, coord_t y) { diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 695ccf30e..7d2394cc7 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -31,12 +31,7 @@ class Button: public FormField Button(FormGroup* parent, const rect_t& rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, - LvglCreate objConstruct = nullptr) : - FormField(parent, rect, windowFlags, textFlags, objConstruct), - pressHandler(std::move(pressHandler)) - { - lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); - } + LvglCreate objConstruct = nullptr); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -74,6 +69,7 @@ class Button: public FormField #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; #endif + void onEvent(lv_event_t* e);// override; #if defined(HARDWARE_TOUCH) bool onTouchStart(coord_t x, coord_t y) override; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index d077dd1f5..19c154972 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -290,7 +290,7 @@ void Window::setFocus(uint8_t flag, Window * from) if (focusWindow != this) { // synchronize lvgl focused state with libopenui - if (!lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) { + if (lvobj != nullptr && !lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) { lv_obj_add_state(lvobj, LV_STATE_FOCUSED); if (focusWindow != nullptr) { lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | LV_STATE_EDITED); From 5260f61530615b3d10ba7874e39ac5d415a74b01 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Sat, 12 Feb 2022 05:23:21 -0500 Subject: [PATCH 527/714] fix focus for menus. Focus is still broken. --- radio/src/thirdparty/libopenui/src/choice.cpp | 6 ++---- radio/src/thirdparty/libopenui/src/menu.cpp | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index fdcdaac93..26a93f53a 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -184,6 +184,8 @@ void Choice::onEvent(event_t event) void Choice::openMenu() { + setEditMode(true); // this needs to be done first before menu is created. + auto menu = new Menu(this); if (!menuTitle.empty()) menu->setTitle(menuTitle); @@ -224,13 +226,9 @@ void Choice::openMenu() beforeDisplayMenuHandler(menu); } - menu->setCloseHandler([=]() { setEditMode(false); }); - - setEditMode(true); - invalidate(); } #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 66b9136c2..3b4440649 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -52,6 +52,7 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect): Window(parent, rect, OPAQUE, 0, lv_list_create) { setPageHeight(MENUS_LINE_HEIGHT); + setFocus(); } void MenuBody::addLine(const std::string &text, std::function onPress, From 6376f8295ab1888328c1975a63bf8c408b0f4920 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Feb 2022 08:57:02 +0100 Subject: [PATCH 528/714] Add LVGL submodule and move submodules --- radio/src/thirdparty/libopenui/.gitmodules | 8 +- .../thirdparty/libopenui/src/CMakeLists.txt | 14 ++ .../thirdparty/libopenui/src/bitmapbuffer.cpp | 2 +- .../libopenui/thirdparty/CMakeLists.txt | 177 ++++++++++++++++++ .../src/thirdparty/libopenui/thirdparty/lvgl | 1 + .../libopenui/{src => }/thirdparty/stb | 0 6 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt create mode 160000 radio/src/thirdparty/libopenui/thirdparty/lvgl rename radio/src/thirdparty/libopenui/{src => }/thirdparty/stb (100%) diff --git a/radio/src/thirdparty/libopenui/.gitmodules b/radio/src/thirdparty/libopenui/.gitmodules index 07807ffb0..d7500db7a 100644 --- a/radio/src/thirdparty/libopenui/.gitmodules +++ b/radio/src/thirdparty/libopenui/.gitmodules @@ -1,3 +1,7 @@ -[submodule "src/thirdparty/stb"] - path = src/thirdparty/stb +[submodule "thirdparty/stb"] + path = thirdparty/stb url = https://github.com/nothings/stb.git +[submodule "thirdparty/lvgl"] + path = thirdparty/lvgl + url = https://github.com/lvgl/lvgl.git + branch = release/v8.2 diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 661dee69e..0c812042b 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -46,3 +46,17 @@ if(SOFTWARE_KEYBOARD) keyboard_base.cpp ) endif() + +foreach(LIBOPENUI_FILE ${LIBOPENUI_SRC}) + set(LIBOPENUI_SRC_FILES ${LIBOPENUI_SRC_FILES} ${LIBOPENUI_SRC_DIR}/src/${LIBOPENUI_FILE}) +endforeach() + +set(SRC ${SRC} ${LIBOPENUI_SRC_FILES}) + +# add_library(libopenui_native OBJECT ${LIBOPENUI_SRC_FILES}) +# add_library(libopenui_stm32 OBJECT ${LIBOPENUI_SRC_FILES}) + +# Dirty hack to include FatFs into libopenui +# TODO: this must go away +#target_include_directories(libopenui PRIVATE +# ${CMAKE_CURRENT_SOURCE_DIR}/../../FatFs) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index b7484e10e..bda7e7f54 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1551,7 +1551,7 @@ void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz) #define STBI_NO_TGA #define STBI_NO_BMP #define STB_IMAGE_IMPLEMENTATION -#include "thirdparty/stb/stb_image.h" +#include "stb/stb_image.h" // fill 'data' with 'size' bytes. return number of bytes actually read int stbc_read(void *user, char * data, int size) diff --git a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt new file mode 100644 index 000000000..32400cd8e --- /dev/null +++ b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt @@ -0,0 +1,177 @@ +set(LVGL_SOURCES + core/lv_group.c + core/lv_obj.c + core/lv_event.c + core/lv_indev.c + core/lv_obj_scroll.c + core/lv_obj_draw.c + core/lv_disp.c + core/lv_obj_style.c + core/lv_theme.c + core/lv_obj_style_gen.c + core/lv_refr.c + core/lv_indev_scroll.c + core/lv_obj_class.c + core/lv_obj_tree.c + core/lv_obj_pos.c + font/lv_font_montserrat_20.c + font/lv_font_unscii_8.c + font/lv_font_montserrat_48.c + font/lv_font_montserrat_28.c + font/lv_font_montserrat_26.c + font/lv_font_montserrat_38.c + font/lv_font_montserrat_18.c + font/lv_font_montserrat_8.c + font/lv_font_montserrat_44.c + font/lv_font_montserrat_40.c + font/lv_font_dejavu_16_persian_hebrew.c + font/lv_font_montserrat_32.c + font/lv_font_montserrat_46.c + font/lv_font_montserrat_14.c + font/lv_font_montserrat_12_subpx.c + font/lv_font_montserrat_34.c + font/lv_font_montserrat_16.c + font/lv_font_montserrat_42.c + font/lv_font_montserrat_12.c + font/lv_font_simsun_16_cjk.c + font/lv_font_montserrat_30.c + font/lv_font_montserrat_22.c + font/lv_font_montserrat_10.c + font/lv_font_fmt_txt.c + font/lv_font_montserrat_24.c + font/lv_font_loader.c + font/lv_font_montserrat_28_compressed.c + font/lv_font.c + font/lv_font_montserrat_36.c + font/lv_font_unscii_16.c + misc/lv_timer.c + misc/lv_txt.c + misc/lv_gc.c + misc/lv_style_gen.c + misc/lv_templ.c + misc/lv_color.c + misc/lv_fs.c + misc/lv_printf.c + misc/lv_style.c + misc/lv_log.c + misc/lv_area.c + misc/lv_math.c + misc/lv_lru.c + misc/lv_tlsf.c + misc/lv_anim_timeline.c + misc/lv_ll.c + misc/lv_bidi.c + misc/lv_mem.c + misc/lv_utils.c + misc/lv_async.c + misc/lv_anim.c + misc/lv_txt_ap.c + draw/lv_draw_mask.c + draw/lv_img_cache.c + draw/sw/lv_draw_sw_gradient.c + draw/sw/lv_draw_sw.c + draw/sw/lv_draw_sw_blend.c + draw/sw/lv_draw_sw_letter.c + draw/sw/lv_draw_sw_arc.c + draw/sw/lv_draw_sw_polygon.c + draw/sw/lv_draw_sw_img.c + draw/sw/lv_draw_sw_line.c + draw/sw/lv_draw_sw_dither.c + draw/sw/lv_draw_sw_rect.c + draw/lv_draw_line.c + draw/lv_draw.c + draw/lv_img_buf.c + draw/lv_draw_rect.c + draw/lv_draw_label.c + draw/lv_draw_img.c + draw/lv_draw_arc.c + draw/lv_draw_triangle.c + draw/lv_img_decoder.c + + # STM32 specific + draw/stm32_dma2d/lv_gpu_stm32_dma2d.c + # SDL specific + draw/sdl/lv_draw_sdl_rect.c + draw/sdl/lv_draw_sdl_arc.c + draw/sdl/lv_draw_sdl.c + draw/sdl/lv_draw_sdl_mask.c + draw/sdl/lv_draw_sdl_polygon.c + draw/sdl/lv_draw_sdl_stack_blur.c + draw/sdl/lv_draw_sdl_utils.c + draw/sdl/lv_draw_sdl_img.c + draw/sdl/lv_draw_sdl_bg.c + draw/sdl/lv_draw_sdl_label.c + draw/sdl/lv_draw_sdl_texture_cache.c + draw/sdl/lv_draw_sdl_line.c + draw/sdl/lv_draw_sdl_composite.c + + hal/lv_hal_indev.c + hal/lv_hal_disp.c + hal/lv_hal_tick.c + extra/others/monkey/lv_monkey.c + extra/others/gridnav/lv_gridnav.c + extra/others/snapshot/lv_snapshot.c + extra/layouts/grid/lv_grid.c + extra/layouts/flex/lv_flex.c + extra/libs/bmp/lv_bmp.c + extra/libs/ffmpeg/lv_ffmpeg.c + extra/libs/gif/lv_gif.c + extra/libs/gif/gifdec.c + extra/libs/freetype/lv_freetype.c + extra/libs/qrcode/lv_qrcode.c + extra/libs/qrcode/qrcodegen.c + extra/libs/sjpg/tjpgd.c + extra/libs/sjpg/lv_sjpg.c + extra/libs/fsdrv/lv_fs_win32.c + extra/libs/fsdrv/lv_fs_stdio.c + extra/libs/fsdrv/lv_fs_posix.c + extra/libs/fsdrv/lv_fs_fatfs.c + extra/libs/rlottie/lv_rlottie.c + extra/libs/png/lv_png.c + extra/libs/png/lodepng.c + extra/lv_extra.c + extra/widgets/imgbtn/lv_imgbtn.c + extra/widgets/menu/lv_menu.c + extra/widgets/win/lv_win.c + extra/widgets/colorwheel/lv_colorwheel.c + extra/widgets/list/lv_list.c + extra/widgets/chart/lv_chart.c + extra/widgets/calendar/lv_calendar_header_arrow.c + extra/widgets/calendar/lv_calendar.c + extra/widgets/calendar/lv_calendar_header_dropdown.c + extra/widgets/tileview/lv_tileview.c + extra/widgets/led/lv_led.c + extra/widgets/spinner/lv_spinner.c + extra/widgets/tabview/lv_tabview.c + extra/widgets/meter/lv_meter.c + extra/widgets/spinbox/lv_spinbox.c + extra/widgets/animimg/lv_animimg.c + extra/widgets/span/lv_span.c + extra/widgets/keyboard/lv_keyboard.c + extra/widgets/msgbox/lv_msgbox.c + extra/themes/basic/lv_theme_basic.c + extra/themes/default/lv_theme_default.c + extra/themes/mono/lv_theme_mono.c + widgets/lv_line.c + widgets/lv_dropdown.c + widgets/lv_objx_templ.c + widgets/lv_canvas.c + widgets/lv_switch.c + widgets/lv_btnmatrix.c + widgets/lv_bar.c + widgets/lv_roller.c + widgets/lv_img.c + widgets/lv_btn.c + widgets/lv_checkbox.c + widgets/lv_label.c + widgets/lv_table.c + widgets/lv_slider.c + widgets/lv_textarea.c + widgets/lv_arc.c +) + +foreach(LVGL_FILE ${LVGL_SOURCES}) + set(LVGL_SRC_FILES ${LVGL_SRC_FILES} ${LVGL_SRC_DIR}/${LVGL_FILE}) +endforeach() + +set(SRC ${SRC} ${LVGL_SRC_FILES}) diff --git a/radio/src/thirdparty/libopenui/thirdparty/lvgl b/radio/src/thirdparty/libopenui/thirdparty/lvgl new file mode 160000 index 000000000..0b5a1d4b2 --- /dev/null +++ b/radio/src/thirdparty/libopenui/thirdparty/lvgl @@ -0,0 +1 @@ +Subproject commit 0b5a1d4b23975b920ff841ea9cd038802f51711b diff --git a/radio/src/thirdparty/libopenui/src/thirdparty/stb b/radio/src/thirdparty/libopenui/thirdparty/stb similarity index 100% rename from radio/src/thirdparty/libopenui/src/thirdparty/stb rename to radio/src/thirdparty/libopenui/thirdparty/stb From 77382ba406077712a78d8d110c71e8fa82888474 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Feb 2022 22:39:42 +0100 Subject: [PATCH 529/714] WiP: remove legacy fonts --- radio/src/thirdparty/libopenui/.gitignore | 1 + .../thirdparty/libopenui/src/CMakeLists.txt | 1 - .../thirdparty/libopenui/src/bitmapbuffer.cpp | 98 +++++++------------ .../thirdparty/libopenui/src/bitmapbuffer.h | 12 ++- .../thirdparty/libopenui/src/coloredit.cpp | 1 - radio/src/thirdparty/libopenui/src/font.h | 4 +- .../libopenui/src/keyboard_number.cpp | 1 - .../libopenui/src/keyboard_text.cpp | 1 - .../thirdparty/libopenui/src/keyboard_text.h | 8 +- .../libopenui/src/libopenui_defines.h | 1 - .../libopenui/src/libopenui_globals.cpp | 21 ---- .../libopenui/src/libopenui_globals.h | 28 ------ radio/src/thirdparty/libopenui/src/roller.h | 1 - .../src/thirdparty/libopenui/src/textedit.cpp | 1 - radio/src/thirdparty/libopenui/src/window.cpp | 2 + 15 files changed, 51 insertions(+), 130 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/.gitignore delete mode 100644 radio/src/thirdparty/libopenui/src/libopenui_globals.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/libopenui_globals.h diff --git a/radio/src/thirdparty/libopenui/.gitignore b/radio/src/thirdparty/libopenui/.gitignore new file mode 100644 index 000000000..e5d594669 --- /dev/null +++ b/radio/src/thirdparty/libopenui/.gitignore @@ -0,0 +1 @@ +src/thirdparty/stb/ diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 0c812042b..9e9b31f6a 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -11,7 +11,6 @@ if(SOFTWARE_KEYBOARD) endif() set(LIBOPENUI_SRC - libopenui_globals.cpp libopenui_file.cpp bitmapbuffer.cpp window.cpp diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index bda7e7f54..a5b0e8810 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -20,7 +20,6 @@ #include #include "bitmapbuffer.h" #include "libopenui_depends.h" -#include "libopenui_globals.h" #include "libopenui_helpers.h" #include "libopenui_file.h" #include "font.h" @@ -1008,21 +1007,13 @@ void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, srcx, srcy, srcw, srch, COLOR_VAL(flags)); } -uint8_t BitmapBuffer::drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags) -{ - coord_t offset = spec[index + 1]; - coord_t width = spec[index + 2] - offset; - if (width > 0) { - drawBitmapPattern(x, y, font, flags, offset, width); - } - return width; -} +// TODO: find a better place to define this +extern lv_color_t makeLvColor(uint32_t colorFlags); -#define INCREMENT_POS(delta) do { if (flags & VERTICAL) y -= delta; else x += delta; } while(0) - -coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags) +coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, + uint8_t len, LcdFlags flags) { - if (!s) return (flags & VERTICAL) ? y : x; + if (!s) return x; MOVE_OFFSET(); int height = getFontHeight(flags); @@ -1032,61 +1023,46 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_ return x; } - uint32_t fontindex = FONT_INDEX(flags); - const unsigned char * font = fontsTable[fontindex]; - const uint16_t * fontspecs = fontspecsTable[fontindex]; - - if (flags & (RIGHT | CENTERED)) { - int width = getTextWidth(s, len, flags); - if (flags & RIGHT) { - INCREMENT_POS(-width); - } - else if (flags & CENTERED) { - INCREMENT_POS(-width / 2); - } - } - - coord_t & pos = (flags & VERTICAL) ? y : x; + coord_t pos = x; const coord_t orig_pos = pos; - while (len--) { - unsigned c = uint8_t(*s); - // TRACE("c = %d %o 0x%X '%c'", c, c, c, c); + lv_draw_label_dsc_t label_draw_dsc; + lv_draw_label_dsc_init(&label_draw_dsc); - if (!c) { - break; - } - else if (c >= 0xFE) { - // CJK char - s++; - c = uint8_t(*s) + ((c & 0x01u) << 8u) - 1; - // TRACE("CJK = %d", c); - if (c >= 0x100) - c -= 1; - c += CJK_FIRST_LETTER_INDEX; - uint8_t width = drawChar(x, y, font, fontspecs, c, flags); - INCREMENT_POS(width + CHAR_SPACING); - } - else if ((c >= 0x20u) && (c < fontCharactersTable[FONT_INDEX(flags)] + 0x20u)) { - uint8_t width = drawChar(x, y, font, fontspecs, getMappedChar(c), flags); - if ((flags & SPACING_NUMBERS_CONST) && c >= '0' && c <= '9') - INCREMENT_POS(getCharWidth('9', fontspecs) + CHAR_SPACING); - else - INCREMENT_POS(width + CHAR_SPACING); - } - else if (c == '\n') { - pos = orig_pos; - if (flags & VERTICAL) - x += height; - else - y += height; - } + const lv_font_t* font = getFont(flags); + label_draw_dsc.font = font; + label_draw_dsc.color = makeLvColor(flags); - s++; + x += draw_ctx->buf_area->x1; + y += draw_ctx->buf_area->y1; + + lv_point_t p; + lv_txt_get_size(&p, s, font, label_draw_dsc.letter_space, + label_draw_dsc.line_space, LV_COORD_MAX, 0); + + lv_coord_t lv_x = (lv_coord_t)x; + lv_coord_t lv_y = (lv_coord_t)y; + + lv_area_t coords = { + lv_x, lv_y, lv_x, lv_y, + }; + + coords.x2 += p.x - 1; + coords.y2 += p.y - 1; + + if (flags & RIGHT) { + label_draw_dsc.align = LV_TEXT_ALIGN_RIGHT; + } else if (flags & CENTERED) { + label_draw_dsc.align = LV_TEXT_ALIGN_CENTER; + coords.x1 -= p.x / 2; + coords.x2 -= p.x / 2; } + lv_draw_label(draw_ctx, &label_draw_dsc, &coords, s, nullptr); + RESTORE_OFFSET(); + pos += p.x; return ((flags & RIGHT) ? orig_pos : pos) - offsetX; } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index f5ce24548..ee10d5b8a 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -52,6 +52,9 @@ enum BitmapFormats BMP_ARGB4444 }; +struct _lv_draw_ctx_t; +typedef _lv_draw_ctx_t lv_draw_ctx_t; + template class BitmapBufferBase { @@ -196,6 +199,7 @@ class BitmapBuffer: public BitmapBufferBase #if defined(DEBUG) bool leakReported; #endif + lv_draw_ctx_t* draw_ctx = nullptr; public: BitmapBuffer(uint8_t format, uint16_t width, uint16_t height); @@ -203,6 +207,8 @@ class BitmapBuffer: public BitmapBufferBase ~BitmapBuffer(); + void setDrawCtx(lv_draw_ctx_t* ctx) { draw_ctx = ctx; } + inline void setFormat(uint8_t format) { this->format = format; @@ -313,8 +319,8 @@ class BitmapBuffer: public BitmapBufferBase coord_t drawText(coord_t x, coord_t y, const char * s, LcdFlags flags = 0) { - if (!s) return (flags & VERTICAL) ? y : x; - return drawSizedText(x, y, s, 255, flags); + if (!s) return x; + return drawSizedText(x, y, s, strlen(s), flags); } coord_t drawTextAtIndex(coord_t x, coord_t y, const char * s, uint8_t idx, LcdFlags flags = 0) @@ -384,8 +390,6 @@ class BitmapBuffer: public BitmapBufferBase coord_t srcy = 0, coord_t srcw = 0, coord_t srch = 0, float scale = 0); - uint8_t drawChar(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, unsigned int index, LcdFlags flags); - inline void drawPixel(pixel_t * p, pixel_t value) { if (data && (data <= p || p < data_end)) { diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index 35b7d6c0e..3c97cfb10 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -18,7 +18,6 @@ */ #include "coloredit.h" -#include "libopenui_globals.h" #include "numberedit.h" #include "bitfield.h" diff --git a/radio/src/thirdparty/libopenui/src/font.h b/radio/src/thirdparty/libopenui/src/font.h index b45bf8150..97e99346e 100644 --- a/radio/src/thirdparty/libopenui/src/font.h +++ b/radio/src/thirdparty/libopenui/src/font.h @@ -19,10 +19,10 @@ #pragma once +#include #include "libopenui_types.h" -uint8_t getMappedChar(uint8_t c); +const lv_font_t* getFont(LcdFlags flags); uint8_t getFontHeight(LcdFlags flags); -int getCharWidth(uint8_t c, const uint16_t * spec); int getTextWidth(const char * s, int len = 0, LcdFlags flags = 0); diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index 93b1b0d2e..2a4b5fad9 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -19,7 +19,6 @@ #include "keyboard_number.h" #include "button.h" -#include "libopenui_globals.h" constexpr coord_t KEYBOARD_HEIGHT = 90; NumberKeyboard * NumberKeyboard::_instance = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index 109d0c4a1..a449a6cad 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -18,7 +18,6 @@ */ #include "keyboard_text.h" -#include "libopenui_globals.h" #include "textedit.h" #include "font.h" diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index 0e873de86..c4e4e8e50 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -66,12 +66,6 @@ class TextKeyboard : public Keyboard { #endif static TextKeyboard * _instance; - const char * const * layout; - private: - int calculateMaxWidth(); - int getCharWidth(const char c); - - char touch_key = -128; - bool touched = false; + // const char * const * layout; }; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index b766f035e..94ddacdd5 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -35,7 +35,6 @@ #define SHADOWED 0x80u /* black copy at +1 +1 */ // 0x1000u used by Lua in api_colorlcd.h #define SPACING_NUMBERS_CONST 0x2000u -#define VERTICAL 0x4000u // 0x8000u used by Lua in api_colorlcd.h /* drawNumber flags */ diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp b/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp deleted file mode 100644 index 3bee77f51..000000000 --- a/radio/src/thirdparty/libopenui/src/libopenui_globals.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "libopenui_globals.h" - diff --git a/radio/src/thirdparty/libopenui/src/libopenui_globals.h b/radio/src/thirdparty/libopenui/src/libopenui_globals.h deleted file mode 100644 index c1352e747..000000000 --- a/radio/src/thirdparty/libopenui/src/libopenui_globals.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#pragma once - -#include -#include "libopenui_config.h" - -extern const uint16_t fontCharactersTable[FONTS_COUNT]; -extern const uint16_t * const fontspecsTable[FONTS_COUNT]; -extern const uint8_t * fontsTable[FONTS_COUNT]; - diff --git a/radio/src/thirdparty/libopenui/src/roller.h b/radio/src/thirdparty/libopenui/src/roller.h index 7135e509a..c9ea66850 100644 --- a/radio/src/thirdparty/libopenui/src/roller.h +++ b/radio/src/thirdparty/libopenui/src/roller.h @@ -24,7 +24,6 @@ #include #include #include -#include "libopenui_globals.h" #include "libopenui_config.h" constexpr WindowFlags ROLLER_SEPARATION_LINES = (FORM_FLAGS_LAST << 1u); diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 9b4f67aaf..50d37ced3 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -19,7 +19,6 @@ #include "textedit.h" #include "font.h" -#include "libopenui_globals.h" #include "libopenui_config.h" #if !defined(STR_EDIT) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 19c154972..5d9dd5a62 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -64,6 +64,8 @@ static void window_event_cb(lv_event_t * e) BitmapBuffer buf = {BMP_RGB565, (uint16_t)w, (uint16_t)h, (uint16_t *)draw_ctx->buf}; + buf.setDrawCtx(draw_ctx); + buf.setOffset(obj_coords.x1 - a.x1, obj_coords.y1 - a.y1); buf.setClippingRect(clipping.x1 - a.x1, clipping.x2 + 1 - a.x1, clipping.y1 - a.y1, clipping.y2 + 1 - a.y1); From ba7564c62aa81304a8de542a175fc073fa9e0bf7 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Feb 2022 11:53:47 +0100 Subject: [PATCH 530/714] Remove usage of scratch buffer --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 106 +++++++++++++----- .../thirdparty/libopenui/src/bitmapbuffer.h | 4 + .../src/thirdparty/libopenui/src/mainwindow.h | 4 + 3 files changed, 85 insertions(+), 29 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index a5b0e8810..518f1f43c 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -24,6 +24,10 @@ #include "libopenui_file.h" #include "font.h" +#include "lvgl/src/draw/sw/lv_draw_sw.h" + + + RLEBitmap::RLEBitmap(uint8_t format, const uint8_t* rle_data) : BitmapBufferBase(format, 0, 0, nullptr) { @@ -81,6 +85,10 @@ BitmapBuffer::BitmapBuffer(uint8_t format, uint16_t width, uint16_t height) : { data = (uint16_t *)malloc(align32(width * height * sizeof(uint16_t))); data_end = data + (width * height); + + // Assume we need a canvas here + canvas = lv_canvas_create(nullptr); + lv_canvas_set_buffer(canvas, data, width, height, LV_IMG_CF_TRUE_COLOR); } BitmapBuffer::BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, @@ -96,6 +104,7 @@ BitmapBuffer::BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, BitmapBuffer::~BitmapBuffer() { if (dataAllocated) { + lv_obj_del(canvas); free(data); } } @@ -516,46 +525,75 @@ void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t } } -void BitmapBuffer::drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags) +void BitmapBuffer::drawSolidFilledRect(coord_t x, coord_t y, coord_t w, + coord_t h, LcdFlags flags) { - APPLY_OFFSET(); - - if (!applyClippingRect(x, y, w, h)) - return; - - // No 'opacity' here, only 'color' - DMAFillRect(data, _width, _height, x, y, w, h, COLOR_VAL(flags)); + drawFilledRect(x, y, w, h, SOLID, flags, 0); } -void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags flags, uint8_t opacity) +void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, + uint8_t pat, LcdFlags flags, uint8_t opacity) { APPLY_OFFSET(); + if (!applyClippingRect(x, y, w, h)) return; - if (!applyClippingRect(x, y, w, h)) - return; - - // Use DMA instead... if (SOLID != pat) { + // If we have a pattern, draw line by line for (coord_t i = y; i < y + h; i++) { drawHorizontalLineAbs(x, i, w, pat, flags, opacity); } + return; } - else { - // SOLID - // TODO: can this be done in one step? (see drawBitmapPattern()) - - // Use the DMA2D to blend a scratch buffer filled with overlay color - BitmapBuffer scratch(BMP_ARGB4444, LCD_W, LCD_H, lcdGetScratchBuffer()); + lv_draw_sw_blend_dsc_t blend_dsc = {0}; + blend_dsc.blend_mode = LV_BLEND_MODE_NORMAL; - RGB_SPLIT(COLOR_VAL(flags), r, g, b); - pixel_t color_argb = ARGB((OPACITY_MAX - opacity) << 4, r << 3, g << 2, b << 3); + if (opacity == OPACITY_MAX) { + // we don't draw fully transparent things + return; + } else if (!opacity) { + blend_dsc.opa = LV_OPA_COVER; + } else { + blend_dsc.opa = ((OPACITY_MAX - opacity) * LV_OPA_COVER) / OPACITY_MAX; + } - // Fill the buffer - scratch.drawSolidFilledRect(0, 0, w, h, color_argb << 16); + auto color = COLOR_VAL(flags); + blend_dsc.color = + lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)); - // And blend - drawBitmapAbs(x, y, &scratch, 0, 0, w, h); + if (draw_ctx) { + x += draw_ctx->buf_area->x1; + y += draw_ctx->buf_area->y1; + } + + lv_coord_t lv_x = (lv_coord_t)x; + lv_coord_t lv_y = (lv_coord_t)y; + + lv_area_t coords = { + lv_x, + lv_y, + lv_x, + lv_y, + }; + + coords.x2 += w - 1; + coords.y2 += h - 1; + + if (draw_ctx) { + lv_area_t clipped_coords; + if (!_lv_area_intersect(&clipped_coords, &coords, draw_ctx->clip_area)) + return; + blend_dsc.blend_area = &clipped_coords; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } else if (canvas) { + lv_draw_rect_dsc_t rect_dsc; + lv_draw_rect_dsc_init(&rect_dsc); + + rect_dsc.blend_mode = blend_dsc.blend_mode; + rect_dsc.bg_color = blend_dsc.color; + rect_dsc.bg_opa = blend_dsc.opa; + lv_canvas_draw_rect(canvas, coords.x1, coords.y1, coords.x2 - coords.x1 + 1, + coords.y2 - coords.y1 + 1, &rect_dsc); } } @@ -1031,10 +1069,15 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, const lv_font_t* font = getFont(flags); label_draw_dsc.font = font; - label_draw_dsc.color = makeLvColor(flags); - x += draw_ctx->buf_area->x1; - y += draw_ctx->buf_area->y1; + auto color = COLOR_VAL(flags); + label_draw_dsc.color = + lv_color_make(GET_RED(color), GET_GREEN(color), GET_BLUE(color)); + + if (draw_ctx) { + x += draw_ctx->buf_area->x1; + y += draw_ctx->buf_area->y1; + } lv_point_t p; lv_txt_get_size(&p, s, font, label_draw_dsc.letter_space, @@ -1058,7 +1101,12 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, coords.x2 -= p.x / 2; } - lv_draw_label(draw_ctx, &label_draw_dsc, &coords, s, nullptr); + if (draw_ctx) { + lv_draw_label(draw_ctx, &label_draw_dsc, &coords, s, nullptr); + } else if (canvas) { + lv_canvas_draw_text(canvas, coords.x1, coords.y1, coords.x2 - coords.x1 + 1, + &label_draw_dsc, s); + } RESTORE_OFFSET(); diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index ee10d5b8a..9122ca9a2 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -55,6 +55,9 @@ enum BitmapFormats struct _lv_draw_ctx_t; typedef _lv_draw_ctx_t lv_draw_ctx_t; +struct _lv_obj_t; +typedef _lv_obj_t lv_obj_t; + template class BitmapBufferBase { @@ -199,6 +202,7 @@ class BitmapBuffer: public BitmapBufferBase #if defined(DEBUG) bool leakReported; #endif + lv_obj_t* canvas = nullptr; lv_draw_ctx_t* draw_ctx = nullptr; public: diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 9e1d5951d..045614379 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -74,6 +74,10 @@ class MainWindow: public Window } #endif + void setActiveScreen() { + lv_scr_load(lvobj); + } + void invalidate() { invalidate({0, 0, rect.w, rect.h}); From c62208261e909038db5bee189983b15ef285cb16 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Feb 2022 14:07:14 +0100 Subject: [PATCH 531/714] Allows for multi-byte pictograms in MenuToolbarButton --- radio/src/thirdparty/libopenui/src/menutoolbar.cpp | 8 ++++---- radio/src/thirdparty/libopenui/src/menutoolbar.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 1c5181c63..aff6fcb9e 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -28,10 +28,10 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, COLOR_THEME_FOCUS); - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, - &picto, 1, CENTERED | COLOR_THEME_PRIMARY2); + dc->drawText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, + picto, CENTERED | COLOR_THEME_PRIMARY2); } else { - dc->drawSizedText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, - &picto, 1, CENTERED | COLOR_THEME_PRIMARY1); + dc->drawText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, + picto, CENTERED | COLOR_THEME_PRIMARY1); } } diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 37c6b8a01..dd858af3a 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -27,7 +27,7 @@ int getFirstAvailable(int min, int max, std::functionisValueAvailable class MenuToolbarButton: public Button { public: - MenuToolbarButton(FormGroup * window, const rect_t & rect, char picto): + MenuToolbarButton(FormGroup * window, const rect_t & rect, const char* picto): #if defined(HARDWARE_TOUCH) Button(window, rect, nullptr), #else @@ -40,7 +40,7 @@ class MenuToolbarButton: public Button { void paint(BitmapBuffer * dc) override; protected: - char picto; + const char* picto; }; template @@ -112,7 +112,7 @@ class MenuToolbar: public FormGroup { coord_t y = 0; std::vector menuButtons; - void addButton(char picto, int16_t filtermin, int16_t filtermax) + void addButton(const char* picto, int16_t filtermin, int16_t filtermax) { int vmin = choice->vmin; int vmax = choice->vmax; From 3ffa80ca14f56ad3713aff7a680ecde8502446f2 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Feb 2022 18:35:47 +0100 Subject: [PATCH 532/714] Remove useless code to avoid the bootloader to overflow --- .../src/thirdparty/libopenui/src/bitmapbuffer.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 518f1f43c..fbf66077b 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -86,9 +86,11 @@ BitmapBuffer::BitmapBuffer(uint8_t format, uint16_t width, uint16_t height) : data = (uint16_t *)malloc(align32(width * height * sizeof(uint16_t))); data_end = data + (width * height); +#if !defined(BOOT) // Assume we need a canvas here canvas = lv_canvas_create(nullptr); lv_canvas_set_buffer(canvas, data, width, height, LV_IMG_CF_TRUE_COLOR); +#endif } BitmapBuffer::BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, @@ -104,7 +106,9 @@ BitmapBuffer::BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, BitmapBuffer::~BitmapBuffer() { if (dataAllocated) { +#if !defined(BOOT) lv_obj_del(canvas); +#endif free(data); } } @@ -585,7 +589,9 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, return; blend_dsc.blend_area = &clipped_coords; lv_draw_sw_blend(draw_ctx, &blend_dsc); - } else if (canvas) { + } +#if !defined(BOOT) + else if (canvas) { lv_draw_rect_dsc_t rect_dsc; lv_draw_rect_dsc_init(&rect_dsc); @@ -595,6 +601,7 @@ void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, lv_canvas_draw_rect(canvas, coords.x1, coords.y1, coords.x2 - coords.x1 + 1, coords.y2 - coords.y1 + 1, &rect_dsc); } +#endif } void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags) @@ -1103,10 +1110,13 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, if (draw_ctx) { lv_draw_label(draw_ctx, &label_draw_dsc, &coords, s, nullptr); - } else if (canvas) { + } +#if !defined(BOOT) + else if (canvas) { lv_canvas_draw_text(canvas, coords.x1, coords.y1, coords.x2 - coords.x1 + 1, &label_draw_dsc, s); } +#endif RESTORE_OFFSET(); From 45be56469030c7037d01e91079a414282041049a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 18 Feb 2022 11:35:06 +0100 Subject: [PATCH 533/714] LVGL minimal subset for bootloader --- .../libopenui/thirdparty/CMakeLists.txt | 150 ++++++++++-------- 1 file changed, 81 insertions(+), 69 deletions(-) diff --git a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt index 32400cd8e..48a518726 100644 --- a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LVGL_SOURCES +set(LVGL_SOURCES_MINIMAL core/lv_group.c core/lv_obj.c core/lv_event.c @@ -14,58 +14,7 @@ set(LVGL_SOURCES core/lv_obj_class.c core/lv_obj_tree.c core/lv_obj_pos.c - font/lv_font_montserrat_20.c - font/lv_font_unscii_8.c - font/lv_font_montserrat_48.c - font/lv_font_montserrat_28.c - font/lv_font_montserrat_26.c - font/lv_font_montserrat_38.c - font/lv_font_montserrat_18.c - font/lv_font_montserrat_8.c - font/lv_font_montserrat_44.c - font/lv_font_montserrat_40.c - font/lv_font_dejavu_16_persian_hebrew.c - font/lv_font_montserrat_32.c - font/lv_font_montserrat_46.c - font/lv_font_montserrat_14.c - font/lv_font_montserrat_12_subpx.c - font/lv_font_montserrat_34.c - font/lv_font_montserrat_16.c - font/lv_font_montserrat_42.c - font/lv_font_montserrat_12.c - font/lv_font_simsun_16_cjk.c - font/lv_font_montserrat_30.c - font/lv_font_montserrat_22.c - font/lv_font_montserrat_10.c - font/lv_font_fmt_txt.c - font/lv_font_montserrat_24.c - font/lv_font_loader.c - font/lv_font_montserrat_28_compressed.c - font/lv_font.c - font/lv_font_montserrat_36.c - font/lv_font_unscii_16.c - misc/lv_timer.c - misc/lv_txt.c - misc/lv_gc.c - misc/lv_style_gen.c - misc/lv_templ.c - misc/lv_color.c - misc/lv_fs.c - misc/lv_printf.c - misc/lv_style.c - misc/lv_log.c - misc/lv_area.c - misc/lv_math.c - misc/lv_lru.c - misc/lv_tlsf.c - misc/lv_anim_timeline.c - misc/lv_ll.c - misc/lv_bidi.c - misc/lv_mem.c - misc/lv_utils.c - misc/lv_async.c - misc/lv_anim.c - misc/lv_txt_ap.c + draw/lv_draw_mask.c draw/lv_img_cache.c draw/sw/lv_draw_sw_gradient.c @@ -90,6 +39,7 @@ set(LVGL_SOURCES # STM32 specific draw/stm32_dma2d/lv_gpu_stm32_dma2d.c + # SDL specific draw/sdl/lv_draw_sdl_rect.c draw/sdl/lv_draw_sdl_arc.c @@ -108,6 +58,80 @@ set(LVGL_SOURCES hal/lv_hal_indev.c hal/lv_hal_disp.c hal/lv_hal_tick.c + + font/lv_font.c + font/lv_font_loader.c + font/lv_font_fmt_txt.c + + misc/lv_timer.c + misc/lv_txt.c + misc/lv_gc.c + misc/lv_style_gen.c + misc/lv_templ.c + misc/lv_color.c + misc/lv_fs.c + misc/lv_printf.c + misc/lv_style.c + misc/lv_log.c + misc/lv_area.c + misc/lv_math.c + misc/lv_lru.c + misc/lv_tlsf.c + misc/lv_anim_timeline.c + misc/lv_ll.c + misc/lv_bidi.c + misc/lv_mem.c + misc/lv_utils.c + misc/lv_async.c + misc/lv_anim.c + misc/lv_txt_ap.c +) + +set(LVGL_SOURCES + ${LVGL_SOURCES_MINIMAL} + # font/lv_font_unscii_8.c + # font/lv_font_unscii_16.c + # font/lv_font_simsun_16_cjk.c + # font/lv_font_dejavu_16_persian_hebrew.c + # font/lv_font_montserrat_8.c + # font/lv_font_montserrat_10.c + # font/lv_font_montserrat_12.c + # font/lv_font_montserrat_12_subpx.c + # font/lv_font_montserrat_14.c + # font/lv_font_montserrat_16.c + # font/lv_font_montserrat_18.c + # font/lv_font_montserrat_20.c + # font/lv_font_montserrat_22.c + # font/lv_font_montserrat_24.c + # font/lv_font_montserrat_26.c + # font/lv_font_montserrat_28.c + # font/lv_font_montserrat_28_compressed.c + # font/lv_font_montserrat_30.c + # font/lv_font_montserrat_32.c + # font/lv_font_montserrat_34.c + # font/lv_font_montserrat_36.c + # font/lv_font_montserrat_38.c + # font/lv_font_montserrat_40.c + # font/lv_font_montserrat_42.c + # font/lv_font_montserrat_44.c + # font/lv_font_montserrat_46.c + # font/lv_font_montserrat_48.c + widgets/lv_line.c + widgets/lv_dropdown.c + widgets/lv_objx_templ.c + widgets/lv_canvas.c + widgets/lv_switch.c + widgets/lv_btnmatrix.c + widgets/lv_bar.c + widgets/lv_roller.c + widgets/lv_img.c + widgets/lv_btn.c + widgets/lv_checkbox.c + widgets/lv_label.c + widgets/lv_table.c + widgets/lv_slider.c + widgets/lv_textarea.c + widgets/lv_arc.c extra/others/monkey/lv_monkey.c extra/others/gridnav/lv_gridnav.c extra/others/snapshot/lv_snapshot.c @@ -152,24 +176,12 @@ set(LVGL_SOURCES extra/themes/basic/lv_theme_basic.c extra/themes/default/lv_theme_default.c extra/themes/mono/lv_theme_mono.c - widgets/lv_line.c - widgets/lv_dropdown.c - widgets/lv_objx_templ.c - widgets/lv_canvas.c - widgets/lv_switch.c - widgets/lv_btnmatrix.c - widgets/lv_bar.c - widgets/lv_roller.c - widgets/lv_img.c - widgets/lv_btn.c - widgets/lv_checkbox.c - widgets/lv_label.c - widgets/lv_table.c - widgets/lv_slider.c - widgets/lv_textarea.c - widgets/lv_arc.c ) +foreach(LVGL_FILE ${LVGL_SOURCES_MINIMAL}) + set(LVGL_SRC_FILES_MINIMAL ${LVGL_SRC_FILES_MINIMAL} ${LVGL_SRC_DIR}/${LVGL_FILE}) +endforeach() + foreach(LVGL_FILE ${LVGL_SOURCES}) set(LVGL_SRC_FILES ${LVGL_SRC_FILES} ${LVGL_SRC_DIR}/${LVGL_FILE}) endforeach() From bb9a3c98e0ba432305ba1de84702fd4597d0aa24 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Feb 2022 11:13:06 +0100 Subject: [PATCH 534/714] Added extra init to minimal set --- radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt index 48a518726..67da934b1 100644 --- a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt @@ -85,6 +85,8 @@ set(LVGL_SOURCES_MINIMAL misc/lv_async.c misc/lv_anim.c misc/lv_txt_ap.c + + extra/lv_extra.c ) set(LVGL_SOURCES @@ -153,7 +155,6 @@ set(LVGL_SOURCES extra/libs/rlottie/lv_rlottie.c extra/libs/png/lv_png.c extra/libs/png/lodepng.c - extra/lv_extra.c extra/widgets/imgbtn/lv_imgbtn.c extra/widgets/menu/lv_menu.c extra/widgets/win/lv_win.c From 37a6409da07434755092de4284b42a12ac9e7535 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Feb 2022 11:13:24 +0100 Subject: [PATCH 535/714] Added BitmapBuffer::setData() method --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 9122ca9a2..526f31915 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -212,6 +212,12 @@ class BitmapBuffer: public BitmapBufferBase ~BitmapBuffer(); void setDrawCtx(lv_draw_ctx_t* ctx) { draw_ctx = ctx; } + void setData(uint16_t* d) { + if (!dataAllocated) { + data = d; + data_end = d + (_width * _height); + } + } inline void setFormat(uint8_t format) { From 2246c3cfbc6beb7503a467d5bbf14366a87986ef Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Feb 2022 17:39:22 +0100 Subject: [PATCH 536/714] Use string arrays --- .../thirdparty/libopenui/src/bitmapbuffer.h | 5 ++-- radio/src/thirdparty/libopenui/src/choice.h | 29 ++++++++++++++----- .../libopenui/src/libopenui_helpers.h | 5 ++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 526f31915..ce3f05fc0 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -333,10 +333,9 @@ class BitmapBuffer: public BitmapBufferBase return drawSizedText(x, y, s, strlen(s), flags); } - coord_t drawTextAtIndex(coord_t x, coord_t y, const char * s, uint8_t idx, LcdFlags flags = 0) + coord_t drawTextAtIndex(coord_t x, coord_t y, const char** s, uint8_t idx, LcdFlags flags = 0) { - char length = *s++; - return drawSizedText(x, y, s+length*idx, length, flags); + return drawText(x, y, s[idx], flags); } static void formatNumberAsString(char *buffer, const uint8_t buffer_size, int32_t val, LcdFlags flags = 0, uint8_t len = 0, const char * prefix = nullptr, const char * suffix = nullptr); diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 710990572..b54d57676 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -48,18 +48,31 @@ class ChoiceBase : public FormField class Choice: public ChoiceBase { public: - Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup * parent, const rect_t & rect, std::vector values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup * parent, const rect_t & rect, const char * values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); + Choice(FormGroup *parent, const rect_t &rect, int vmin, int vmax, + std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0); + Choice(FormGroup *parent, const rect_t &rect, + std::vector values, int vmin, int vmax, + std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0); + Choice(FormGroup *parent, const rect_t &rect, const char *const values[], + int vmin, int vmax, std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0); + Choice(FormGroup *parent, const rect_t &rect, const char *values, int vmin, + int vmax, std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0); - void addValue(const char * value); + void addValue(const char *value); - void addValues(const char * const values[], uint8_t count); + void addValues(const char *const values[], uint8_t count); - void setValues(std::vector values); + void setValues(std::vector values); - void setValues(const char * const values[]); + void setValues(const char *const values[]); #if defined(DEBUG_WINDOWS) std::string getName() const override diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index a5914a95a..be7a1ad96 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -79,10 +79,9 @@ inline T sgn(T a) } // TODO duplicated code -inline std::string TEXT_AT_INDEX(const char * val, uint8_t idx) +inline std::string TEXT_AT_INDEX(const char** val, uint8_t idx) { - const char * start = val + 1 + idx * val[0]; - return std::string(start, min(val[0], (uint8_t)strlen(start))); + return std::string(val[idx]); } inline const char * findNextLine(const char * stack) From 22619d7a0aa58bfe336c71490e5c754b9ba97baa Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Feb 2022 17:38:09 +0100 Subject: [PATCH 537/714] Wait for DMA operations to complete before proceeding --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index fbf66077b..39b27015a 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -26,7 +26,7 @@ #include "lvgl/src/draw/sw/lv_draw_sw.h" - +void DMAWait(); RLEBitmap::RLEBitmap(uint8_t format, const uint8_t* rle_data) : BitmapBufferBase(format, 0, 0, nullptr) @@ -163,9 +163,6 @@ void BitmapBuffer::drawBitmapAbs(coord_t x, coord_t y, const T *bmp, { coord_t bmpw = bmp->width(); coord_t bmph = bmp->height(); - //TRACE(" BitmapBuffer::drawBitmapAbs %dx%d %s->%s", bmpw, bmph, - // bmp->getFormat() == BMP_RGB565 ? "BMP_RGB565" : "BMP_ARGB4444", - // getFormat() == BMP_RGB565 ? "BMP_RGB565" : "BMP_ARGB4444"); if (srcw == 0) srcw = bmpw; if (srch == 0) srch = bmph; @@ -212,6 +209,7 @@ void BitmapBuffer::drawBitmapAbs(coord_t x, coord_t y, const T *bmp, return; } + DMAWait(); if (scale == 0) { if (bmp->getFormat() == BMP_ARGB4444) { DMACopyAlphaBitmap(data, _width, _height, x, y, bmp->getData(), bmpw, @@ -316,6 +314,7 @@ void BitmapBuffer::drawHorizontalLineAbs(coord_t x, coord_t y, coord_t w, uint8_ // opacity = 0x0F - opacity; + DMAWait(); if (pat == SOLID) { while (w--) { drawAlphaPixel(p, opacity, color); @@ -350,6 +349,7 @@ void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat // opacity = 0x0F - opacity; + DMAWait(); pixel_t color = COLOR_VAL(flags); if (pat == SOLID) { while (h--) { @@ -489,6 +489,7 @@ void BitmapBuffer::drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, int px = x1; int py = y1; + DMAWait(); if (dxabs >= dyabs) { /* the line is more horizontal than vertical */ for (int i = 0; i <= dxabs; i++) { @@ -615,6 +616,7 @@ void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFla pixel_t color = COLOR_VAL(flags); RGB_SPLIT(color, red, green, blue); + DMAWait(); for (int i = y; i < y + h; i++) { pixel_t * p = getPixelPtrAbs(x, i); for (int j = 0; j < w; j++) { @@ -668,6 +670,7 @@ void BitmapBuffer::drawFilledTriangle(coord_t x0, coord_t y0, coord_t x1, if (y1 > y2) { SWAP(y2, y1); SWAP(x2, x1); } if (y0 > y1) { SWAP(y0, y1); SWAP(x0, x1); } + DMAWait(); if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing a = b = x0; if (x1 < a) @@ -716,6 +719,7 @@ void BitmapBuffer::drawCircle(coord_t x, coord_t y, coord_t radius, LcdFlags fla int decisionOver2 = 1 - x1; pixel_t color = COLOR_VAL(flags); + DMAWait(); while (y1 <= x1) { drawPixel(x1 + x, y1 + y, color); drawPixel(y1 + x, x1 + y, color); @@ -741,6 +745,8 @@ void BitmapBuffer::drawFilledCircle(coord_t x, coord_t y, coord_t radius, LcdFla coord_t imax = ((coord_t)((coord_t)radius * 707)) / 1000 + 1; coord_t sqmax = (coord_t)radius * (coord_t)radius + (coord_t)radius / 2; coord_t x1 = radius; + + DMAWait(); drawSolidHorizontalLine(x - radius, y, radius * 2, flags); for (coord_t i = 1; i <= imax; i++) { if ((i * i + x1 * x1) > sqmax) { @@ -858,6 +864,7 @@ void BitmapBuffer::drawBitmapPatternPie(coord_t x, coord_t y, const uint8_t * im int w2 = width / 2; int h2 = height / 2; + DMAWait(); for (int y1 = h2 - 1; y1 >= 0; y1--) { for (int x1 = w2 - 1; x1 >= 0; x1--) { Slope slope(false, x1 == 0 ? 99000 : y1 * 100 / x1); @@ -892,6 +899,7 @@ void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadiu coord_t internalDist = internalRadius * internalRadius; coord_t externalDist = externalRadius * externalRadius; + DMAWait(); for (int y1 = 0; y1 <= externalRadius; y1++) { for (int x1 = 0; x1 <= externalRadius; x1++) { auto dist = x1 * x1 + y1 * y1; @@ -942,6 +950,7 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, Lcd // pixel_t color = COLOR_VAL(flags); + DMAWait(); for (coord_t row = 0; row < height; row++) { if (y + row < ymin || y + row >= ymax) continue; @@ -990,6 +999,7 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, con // TODO: This should be doable the same way as with // drawBitmapPattern() (just with ARGB as input). // + DMAWait(); for (coord_t row = 0; row < height; row++) { if (y + row < ymin || y + row >= ymax) continue; From f3f816d75bbd11848895ef68569763f8f6a8a9e5 Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Tue, 22 Feb 2022 07:29:15 -0500 Subject: [PATCH 538/714] fix encoder editing in textedit. also fixed cursor and text positioning --- .../thirdparty/libopenui/src/basenumberedit.h | 9 ++-- .../src/thirdparty/libopenui/src/textedit.cpp | 50 ++++++++----------- radio/src/thirdparty/libopenui/src/textedit.h | 4 +- radio/src/thirdparty/libopenui/src/window.cpp | 5 +- 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index e0911ccb1..d97bf5cee 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -20,7 +20,7 @@ #pragma once #include "form.h" -#include "bitmapbuffer.h" +#include "strhelpers.h" class BaseNumberEdit: public FormField { @@ -83,7 +83,7 @@ class BaseNumberEdit: public FormField auto newValue = limit(vmin, value, vmax); if (newValue != currentValue) { currentValue = newValue; - if (instantChange) { + if (instantChange && _setValue != nullptr) { _setValue(currentValue); } } @@ -91,9 +91,8 @@ class BaseNumberEdit: public FormField if (displayFunction != nullptr) { lv_textarea_set_text(lvobj, displayFunction(currentValue).c_str()); } else { - char s[50]; - BitmapBuffer::formatNumberAsString(s, 49, currentValue, textFlags, 0, prefix.c_str(), suffix.c_str()); - lv_textarea_set_text(lvobj, s); + auto dispString = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(), suffix.c_str()); + lv_textarea_set_text(lvobj, dispString.c_str()); } } } diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 50d37ced3..69dd2629c 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -61,6 +61,7 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_textarea_set_text(lvobj, value); lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); + lv_textarea_set_cursor_pos(lvobj, 0); // LV_PART_MAIN lv_style_init(&style_main); @@ -68,9 +69,10 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_style_set_border_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY2)); lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_PRIMARY2)); lv_style_set_bg_opa(&style_main, LV_OPA_COVER); - //lv_style_set_radius(&style_main, 0); lv_style_set_text_font(&style_main, &lv_font_roboto_13); lv_style_set_text_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1)); + lv_style_set_pad_left(&style_main, FIELD_PADDING_LEFT); + lv_style_set_pad_top(&style_main, FIELD_PADDING_TOP); lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN); // LV_STATE_FOCUSED @@ -86,18 +88,16 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, // Show Cursor in "Edit" mode lv_style_init(&style_edit); lv_style_set_opa(&style_edit, LV_OPA_COVER); - lv_style_set_bg_opa(&style_edit, LV_OPA_COVER); - lv_style_set_pad_left(&style_edit, (lv_coord_t)-(FIELD_PADDING_LEFT+2)); - lv_style_set_pad_top(&style_edit, (lv_coord_t)-(FIELD_PADDING_TOP+2)); + lv_style_set_bg_opa(&style_edit, LV_OPA_50); lv_obj_add_style(lvobj, &style_edit, LV_PART_CURSOR | LV_STATE_EDITED); - - // Text padding - auto label = lv_textarea_get_label(lvobj); - lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); - lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); } +void TextEdit::setCursorPos(int cursorPos) +{ + lv_textarea_set_cursor_pos(lvobj, cursorPos); +} + void TextEdit::trim() { for (int i = length - 1; i >= 0; i--) { @@ -141,15 +141,13 @@ void TextEdit::onEvent(event_t event) case EVT_KEY_BREAK(KEY_LEFT): if (cursorPos > 0) { - cursorPos--; - invalidate(); + setCursorPos(cursorPos - 1); } break; case EVT_KEY_BREAK(KEY_RIGHT): if (cursorPos < length - 1 && value[cursorPos + 1] != '\0') { - cursorPos++; - invalidate(); + setCursorPos(cursorPos + 1); } break; @@ -164,7 +162,8 @@ void TextEdit::onEvent(event_t event) value[cursorPos] = ' '; changed = true; } - invalidate(); + lv_textarea_set_text(lvobj, value); + setCursorPos(cursorPos); } else { changeEnd(); @@ -225,7 +224,8 @@ void TextEdit::onEvent(event_t event) if (cursorPos > 0 && value[cursorPos] == '\0') { cursorPos = cursorPos - 1; } - invalidate(); + lv_textarea_set_text(lvobj, value); + setCursorPos(cursorPos); } break; } @@ -233,12 +233,13 @@ void TextEdit::onEvent(event_t event) if (c != v) { // TRACE("value[%d] = %d", cursorPos, v); value[cursorPos] = v; - invalidate(); + lv_textarea_set_text(lvobj, value); + setCursorPos(cursorPos); changed = true; } } else { - cursorPos = 0; + setCursorPos(0); FormField::onEvent(event); } #endif @@ -255,23 +256,12 @@ bool TextEdit::onTouchEnd(coord_t x, coord_t y) setFocus(SET_FOCUS_DEFAULT); } + cursorPos = lv_textarea_get_cursor_pos(lvobj); + #if defined(SOFTWARE_KEYBOARD) TextKeyboard::show(this); #endif - // coord_t rest = x; - // for (cursorPos = 0; cursorPos < length; cursorPos++) { - // char c = value[cursorPos]; - // if (c == '\0') - // break; - // uint8_t w = getCharWidth(c, fontspecsTable[0]) + 1; - // if (rest < w) - // break; - // rest -= w; - // } - - // invalidate(); - return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index a9965b0e9..9f9d74307 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -56,6 +56,8 @@ class TextEdit : public FormField { virtual void paint(BitmapBuffer* dc) override {}; + void setCursorPos(int cursorPos); + protected: char * value; bool changed = false; @@ -70,7 +72,7 @@ class TextEdit : public FormField { if (lvobj != nullptr) strncpy(value, lv_textarea_get_text(lvobj), length); - cursorPos = 0; + setCursorPos(0); if (changed || forceChanged) { changed = false; trim(); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 5d9dd5a62..5cb524b4c 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -76,7 +76,10 @@ static void window_event_cb(lv_event_t * e) TRACE_WINDOWS("PRESSED: %s", window->getWindowDebugString().c_str()); lv_indev_t *click_source = (lv_indev_t *)lv_event_get_param(e); - if (click_source == NULL || is_scrolling) return; + if (click_source == NULL || is_scrolling) { + TRACE_WINDOWS("CLICK WHILE SCROLLING"); + return; + } // Exclude keyboard ? // if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD || From 9f4b2c11a77b29b704baf1ee12c03f3ddc7bcf75 Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Tue, 22 Feb 2022 07:47:10 -0500 Subject: [PATCH 539/714] move styles for textedit to theme --- .../src/thirdparty/libopenui/src/textedit.cpp | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 69dd2629c..543d8ce51 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -62,34 +62,6 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); lv_textarea_set_cursor_pos(lvobj, 0); - - // LV_PART_MAIN - lv_style_init(&style_main); - lv_style_set_border_width(&style_main, 1); - lv_style_set_border_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY2)); - lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_PRIMARY2)); - lv_style_set_bg_opa(&style_main, LV_OPA_COVER); - lv_style_set_text_font(&style_main, &lv_font_roboto_13); - lv_style_set_text_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1)); - lv_style_set_pad_left(&style_main, FIELD_PADDING_LEFT); - lv_style_set_pad_top(&style_main, FIELD_PADDING_TOP); - lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN); - - // LV_STATE_FOCUSED - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), - LV_PART_MAIN | LV_STATE_FOCUSED); - - lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), - LV_PART_MAIN | LV_STATE_FOCUSED); - - // Hide cursor - lv_obj_set_style_opa(lvobj, 0, LV_PART_CURSOR); - - // Show Cursor in "Edit" mode - lv_style_init(&style_edit); - lv_style_set_opa(&style_edit, LV_OPA_COVER); - lv_style_set_bg_opa(&style_edit, LV_OPA_50); - lv_obj_add_style(lvobj, &style_edit, LV_PART_CURSOR | LV_STATE_EDITED); } From fe0f5b849028385455b115a531227634d5565ee4 Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Tue, 22 Feb 2022 13:06:16 -0500 Subject: [PATCH 540/714] cleanup styles from text edit --- radio/src/thirdparty/libopenui/src/textedit.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 543d8ce51..9ed47f95a 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -41,9 +41,6 @@ #include "clipboard.h" #endif -static lv_style_t style_main; -static lv_style_t style_edit; - TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, uint8_t length, LcdFlags windowFlags, const char *_extra_chars) : From 52cab6fd7cc2366f3023a8e4547b0483f7ce60ec Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Tue, 22 Feb 2022 13:12:07 -0500 Subject: [PATCH 541/714] cleanup static paint routine --- radio/src/thirdparty/libopenui/src/static.cpp | 34 ------------------- radio/src/thirdparty/libopenui/src/static.h | 3 -- 2 files changed, 37 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 3d1b09bfd..4d1a9900f 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -20,37 +20,3 @@ #include "static.h" #include "font.h" -void StaticText::paint(BitmapBuffer * dc) -{ - // if (bgColor) { - // dc->drawSolidFilledRect(0, 0, rect.w, rect.h, bgColor); - // } - - // coord_t x; - // if (textFlags & CENTERED) - // x = rect.w / 2; - // else if (textFlags & RIGHT) - // x = rect.w; - // else - // x = 0; - - // coord_t y = (textFlags & VCENTERED) ? (rect.h - getFontHeight(textFlags)) / 2 - // : FIELD_PADDING_TOP; - - // auto start = text.c_str(); - // auto nextline = findNextLine(start); - // if (nextline) { - // auto current = start; - // do { - // dc->drawText(x, y, - // text.substr(current - start, nextline - current).c_str(), - // textFlags); - // current = nextline + 1; - // nextline = findNextLine(current); - // y += getFontHeight(textFlags) + 2; - // } while (nextline); - // dc->drawText(x, y, current, textFlags); - // } else { - // dc->drawText(x, y, start, textFlags); - // } -} diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 89464643b..032545006 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -55,14 +55,11 @@ class StaticText: public Window } #endif - void paint(BitmapBuffer * dc) override; - void setText(std::string value) { if (text != value) { text = std::move(value); lv_label_set_text(label, this->text.c_str()); - // invalidate(); } } From e97cb0a4dc207db0133d790224e5e9f81b5470b9 Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Tue, 22 Feb 2022 13:57:37 -0500 Subject: [PATCH 542/714] cleanup filechoice --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 6 ------ radio/src/thirdparty/libopenui/src/filechoice.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 36443b80a..9a9f3972c 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -54,12 +54,6 @@ std::string FileChoice::getLabelText() } -void FileChoice::paint(BitmapBuffer * dc) -{ - // FormField::paint(dc); - // theme->drawChoice(dc, this, getValue().c_str()); -} - bool FileChoice::openMenu() { FILINFO fno; diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index dd7befa59..94cafc3c3 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -35,8 +35,6 @@ public: std::string getName() const override { return "FileChoice"; } #endif - void paint(BitmapBuffer* dc) override; - #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; #endif From 92cb885018585e9a4f79ab49f8a1100ed04dd930 Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Wed, 23 Feb 2022 05:14:35 -0500 Subject: [PATCH 543/714] fix issue created when making button lvgl aware. First the lvgl event then the touchEnd event. Deleted touchEnd event code. --- radio/src/thirdparty/libopenui/src/button.cpp | 25 +++++-------------- radio/src/thirdparty/libopenui/src/button.h | 1 - 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 07e165a86..8a6ea8dc6 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -80,30 +80,17 @@ void Button::onEvent(lv_event_t* event) if (enabled && code == LV_EVENT_CLICKED) { onKeyPress(); onPress(); - } - else { -// FormField::onEvent(event); - } -} -#if defined(HARDWARE_TOUCH) -bool Button::onTouchStart(coord_t x, coord_t y) -{ - Window::onTouchStart(x, y); - return true; -} -#endif - -#if defined(HARDWARE_TOUCH) -bool Button::onTouchEnd(coord_t x, coord_t y) -{ - if (enabled) { - onKeyPress(); if (!(windowFlags & NO_FOCUS)) { setFocus(SET_FOCUS_DEFAULT); } - onPress(); } +} + +#if defined(HARDWARE_TOUCH) +// this needs to stay so that viewmain doesnt get the touch event +bool Button::onTouchEnd(coord_t x, coord_t y) +{ return true; } #endif diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 7d2394cc7..a74f86c8d 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -72,7 +72,6 @@ class Button: public FormField void onEvent(lv_event_t* e);// override; #if defined(HARDWARE_TOUCH) - bool onTouchStart(coord_t x, coord_t y) override; bool onTouchEnd(coord_t x, coord_t y) override; #endif From b4686686fcb7b3fb41987b8554bb68e5068f2a7a Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Wed, 23 Feb 2022 06:13:40 -0500 Subject: [PATCH 544/714] removing the call to lv_group_set_editing fixes keyboard focus. dont know what else it breaks. --- radio/src/thirdparty/libopenui/src/form.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 0bdb95855..6f75350d3 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -64,10 +64,10 @@ class FormField : public Window { editMode = newEditMode; - if (lvobj != nullptr) { - lv_group_t *grp = (lv_group_t *)lv_obj_get_group(lvobj); - if (grp != nullptr) lv_group_set_editing(grp, editMode); - } + // if (lvobj != nullptr) { + // lv_group_t *grp = (lv_group_t *)lv_obj_get_group(lvobj); + // if (grp != nullptr) lv_group_set_editing(grp, editMode); + // } if (editMode && lvobj != nullptr) { lv_obj_add_state(lvobj, LV_STATE_EDITED | LV_STATE_FOCUSED); From 17910bc48cbb6492f59a1f40a88d2f7819f51c74 Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Wed, 23 Feb 2022 06:17:47 -0500 Subject: [PATCH 545/714] fix cursor positioning on reedit using keyboard ENTER --- radio/src/thirdparty/libopenui/src/textedit.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 9ed47f95a..2175ed642 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -64,6 +64,7 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, void TextEdit::setCursorPos(int cursorPos) { + this->cursorPos = cursorPos; lv_textarea_set_cursor_pos(lvobj, cursorPos); } @@ -208,8 +209,8 @@ void TextEdit::onEvent(event_t event) } } else { - setCursorPos(0); FormField::onEvent(event); + setCursorPos(0); } #endif } From bf27d7d5120f6e202a4d55b4f0ede7997ba76a2e Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Wed, 23 Feb 2022 09:36:27 -0500 Subject: [PATCH 546/714] make static not focusable to fix most of the focus issues. change scroll handingine in event callback to make it more robust. see comments --- radio/src/thirdparty/libopenui/src/static.h | 2 ++ radio/src/thirdparty/libopenui/src/window.cpp | 25 ++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 032545006..1c8546215 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -31,6 +31,8 @@ class StaticText: public Window { label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); + lv_obj_clear_flag(label, LV_OBJ_FLAG_CLICK_FOCUSABLE); + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); lv_obj_set_style_text_font(lvobj, &lv_font_roboto_13, LV_PART_MAIN); lv_obj_set_style_text_color(label, makeLvColor(textFlags), LV_PART_MAIN); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 5cb524b4c..8297b339b 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -35,11 +35,22 @@ static void window_event_cb(lv_event_t * e) lv_obj_t *target = lv_event_get_target(e); lv_event_code_t code = lv_event_get_code(e); + // we do this before looking for user data (libopenui ptr) for this, and in + // fact during delete operations the window will be deleted and you need + // to reset scrolling if you get a pressing as when you are deleting controls + // you get scroll begin events and no scroll end + if (code == LV_EVENT_SCROLL_BEGIN) { + TRACE("SCROLL_BEGIN"); + is_scrolling = true; + } else if (code == LV_EVENT_SCROLL_END || code == LV_EVENT_PRESSING) { + TRACE("SCROLL_END"); + is_scrolling = false; + } + Window* window = (Window *)lv_obj_get_user_data(target); if (!window) return; - if(code == LV_EVENT_DELETE || - window->deleted()) return; + if(code == LV_EVENT_DELETE || window->deleted()) return; if (code == LV_EVENT_GET_SELF_SIZE) { @@ -126,12 +137,6 @@ static void window_event_cb(lv_event_t * e) window->setScrollPositionY(scroll_y); window->setScrollPositionX(scroll_x); - } else if (code == LV_EVENT_SCROLL_BEGIN) { - TRACE("SCROLL_BEGIN"); - is_scrolling = true; - } else if (code == LV_EVENT_SCROLL_END) { - TRACE("SCROLL_END"); - is_scrolling = false; } else if (code == LV_EVENT_FOCUSED) { bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); bool loiu_focused = ((Window *)target->user_data)->hasFocus(); @@ -298,7 +303,9 @@ void Window::setFocus(uint8_t flag, Window * from) if (lvobj != nullptr && !lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) { lv_obj_add_state(lvobj, LV_STATE_FOCUSED); if (focusWindow != nullptr) { - lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | LV_STATE_EDITED); + lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED); + lv_obj_clear_state(focusWindow->lvobj, LV_STATE_EDITED); + // lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | LV_STATE_EDITED); } } From 997515f0eee16b364369f0197bced7d411575ee3 Mon Sep 17 00:00:00 2001 From: kevinkoenig Date: Sun, 27 Feb 2022 06:08:03 -0500 Subject: [PATCH 547/714] cleanup styles in numberedit --- .../thirdparty/libopenui/src/numberedit.cpp | 59 ++++--------------- .../src/thirdparty/libopenui/src/textedit.cpp | 3 +- 2 files changed, 15 insertions(+), 47 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 8ac861805..6db3c3d20 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -24,10 +24,7 @@ #endif -static lv_style_t style_main; -static lv_style_t style_edit; - -static void text_area_cb(lv_event_t* e) +static void numberedit_cb(lv_event_t* e) { lv_event_code_t code = lv_event_get_code(e); if (code == LV_EVENT_DELETE) return; @@ -57,13 +54,23 @@ static void text_area_cb(lv_event_t* e) e->code = LV_EVENT_REFRESH; } +// create a new class for number edit so we can set the sytles differently if needed. +lv_obj_class_t lv_numberedit_class = lv_textarea_class; + +lv_obj_t *lv_numberedit_create(lv_obj_t *parent) +{ + lv_obj_t * obj = lv_obj_class_create_obj(&lv_numberedit_class, parent); + lv_obj_class_init_obj(obj); + return obj; +} + NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags, LcdFlags textFlags) : BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags, - lv_textarea_create) + lv_numberedit_create) { // properties lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); @@ -72,47 +79,7 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, auto value = _getValue(); this->setValue(value); - - // LV_PART_MAIN - lv_style_init(&style_main); - lv_style_set_border_width(&style_main, 1); - lv_style_set_border_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY2)); - lv_style_set_bg_color(&style_main, makeLvColor(COLOR_THEME_PRIMARY2)); - lv_style_set_bg_opa(&style_main, LV_OPA_COVER); - //lv_style_set_radius(&style_main, 0); - lv_style_set_text_font(&style_main, &lv_font_roboto_13); - lv_style_set_text_color(&style_main, makeLvColor(COLOR_THEME_SECONDARY1)); - lv_obj_add_style(lvobj, &style_main, LV_PART_MAIN); - - // LV_STATE_FOCUSED - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), - LV_PART_MAIN | LV_STATE_FOCUSED); - - lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), - LV_PART_MAIN | LV_STATE_FOCUSED); - - lv_obj_set_style_outline_width(lvobj, 0, LV_PART_MAIN|LV_STATE_EDITED); - lv_obj_set_style_outline_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN|LV_STATE_EDITED); - lv_obj_set_style_outline_width(lvobj, 0, LV_PART_MAIN|LV_STATE_FOCUS_KEY); - lv_obj_set_style_outline_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN|LV_STATE_FOCUS_KEY); - - // Hide cursor - lv_obj_set_style_opa(lvobj, 0, LV_PART_CURSOR); - - // Show Cursor in "Edit" mode - lv_style_init(&style_edit); - lv_style_set_opa(&style_edit, LV_OPA_COVER); - lv_style_set_bg_opa(&style_edit, LV_OPA_COVER); - lv_style_set_pad_left(&style_edit, (lv_coord_t)-(FIELD_PADDING_LEFT+2)); - lv_style_set_pad_top(&style_edit, (lv_coord_t)-(FIELD_PADDING_TOP+2)); - lv_obj_add_style(lvobj, &style_edit, LV_PART_CURSOR | LV_STATE_EDITED); - - // Text padding - auto label = lv_textarea_get_label(lvobj); - lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); - lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); - - lv_obj_add_event_cb(lvobj, text_area_cb, LV_EVENT_ALL, this); + lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_ALL, this); } void NumberEdit::onEvent(event_t event) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 2175ed642..704bd0fa1 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -65,7 +65,8 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, void TextEdit::setCursorPos(int cursorPos) { this->cursorPos = cursorPos; - lv_textarea_set_cursor_pos(lvobj, cursorPos); + if (lvobj != nullptr) + lv_textarea_set_cursor_pos(lvobj, cursorPos); } void TextEdit::trim() From 44b508ebc8aed16ac7c2f99e6dc5275181f3f9c0 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Mon, 14 Mar 2022 07:50:46 +0100 Subject: [PATCH 548/714] fix not updated dynamic text --- radio/src/thirdparty/libopenui/src/static.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 1c8546215..2c44da122 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -180,7 +180,7 @@ class DynamicText: public StaticText StaticText::checkEvents(); std::string newText = textHandler(); if (newText != text) { - text = newText; + setText(newText); invalidate(); } } From a20792a2113bd3e2bf6307a26c7f3be0054cef82 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 19 Apr 2022 10:42:50 +0200 Subject: [PATCH 549/714] menu: added support for icons rather than custom draw function --- radio/src/thirdparty/libopenui/src/menu.cpp | 65 +++++++++++++---- radio/src/thirdparty/libopenui/src/menu.h | 78 ++++++++++----------- 2 files changed, 87 insertions(+), 56 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 3b4440649..6019d573f 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,6 +21,8 @@ #include "font.h" #include "theme.h" +#include "lvgl/src/widgets/lv_canvas.h" + void menuBodyEventCallback(lv_event_t *e) { auto code = lv_event_get_code(e); @@ -40,8 +42,8 @@ void menuBodyEventCallback(lv_event_t *e) } else { mb->setIndex(index); - menu->deleteLater(); mb->lines[index].onPress(); + menu->deleteLater(); } } } @@ -58,19 +60,52 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect): void MenuBody::addLine(const std::string &text, std::function onPress, std::function isChecked) { - lines.emplace_back(text, std::move(onPress), std::move(isChecked)); + lines.emplace_back(std::move(onPress), std::move(isChecked), nullptr); lv_obj_t *btn = lv_list_add_btn(lvobj, nullptr, text.c_str()); lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, (void *)(lines.size() - 1)); } +void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked) +{ + lv_obj_t* canvas = lv_canvas_create(nullptr); + lines.emplace_back(std::move(onPress), std::move(isChecked), canvas); + + lv_coord_t w = *((uint16_t *)icon_mask); + lv_coord_t h = *(((uint16_t *)icon_mask)+1); + void* buf = (void*)(icon_mask + 4); + lv_canvas_set_buffer(canvas, buf, w, h, LV_IMG_CF_ALPHA_8BIT); + + lv_obj_t *btn = lv_list_add_btn( + lvobj, (const char *)lv_canvas_get_img(canvas), text.c_str()); + + lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); + lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, + (void *)(lines.size() - 1)); +} + void MenuBody::removeLines() { lines.clear(); lv_obj_clean(lvobj); } +coord_t MenuBody::getContentHeight() +{ + coord_t h = 0; + lv_obj_update_layout(lvobj); + for(int i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { + auto child = lv_obj_get_child(lvobj, i); + h += lv_obj_get_height(child); + } + + return h; +} + + // ensure index is in range and also handle wrapping index int MenuBody::rangeCheck(int index) { @@ -88,6 +123,7 @@ void MenuBody::setIndex(int index) lv_obj_t *btn = lv_obj_get_child(lvobj, selectedIndex); if (btn != nullptr) { lv_obj_clear_state(btn, LV_STATE_CHECKED); + lv_obj_clear_state(btn, LV_STATE_FOCUS_KEY); } selectedIndex = index; @@ -95,6 +131,7 @@ void MenuBody::setIndex(int index) btn = lv_obj_get_child(lvobj, selectedIndex); if (btn != nullptr) { lv_obj_add_state(btn, LV_STATE_CHECKED); + lv_obj_add_state(btn, LV_STATE_FOCUS_KEY); lv_obj_scroll_to_view(btn, LV_ANIM_OFF); } } @@ -156,8 +193,8 @@ void MenuBody::onEvent(event_t event) menu->invalidate(); } else { - menu->deleteLater(); lines[selectedIndex].onPress(); + menu->deleteLater(); } } } @@ -193,10 +230,7 @@ Menu::Menu(Window * parent, bool multiple): void Menu::updatePosition() { - // calcualte the correct menu height given that the line heights are variable - coord_t height = 0; - for (int i = 0; i < (signed)content->body.lines.size(); i++) - height += content->body.lines[i].lineHeight(); + coord_t height = content->body.getContentHeight(); if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen @@ -217,24 +251,27 @@ void Menu::setTitle(std::string text) updatePosition(); } -void Menu::addLine(const std::string & text, std::function onPress, std::function isChecked) +void Menu::addLine(const std::string &text, std::function onPress, + std::function isChecked) { content->body.addLine(text, std::move(onPress), std::move(isChecked)); updatePosition(); } +void Menu::addLine(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked) +{ + content->body.addLine(icon_mask, text, std::move(onPress), std::move(isChecked)); + updatePosition(); +} + void Menu::addSeparator() { content->body.addSeparator(); updatePosition(); } -void Menu::addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) -{ - content->body.addCustomLine(std::move(drawLine), std::move(onPress), std::move(isChecked)); - updatePosition(); -} - void Menu::removeLines() { content->body.removeLines(); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index c539719b1..0798ec8d5 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -27,6 +27,13 @@ class Menu; class MenuWindowContent; +struct lvobj_delete { + constexpr lvobj_delete() = default; + void operator()(lv_obj_t* obj) const { + lv_obj_del(obj); + } +}; + class MenuBody: public Window { friend class MenuWindowContent; @@ -38,44 +45,27 @@ class MenuBody: public Window DIRECTION_DOWN = -1 }; - class MenuLine { + class MenuLine + { friend class MenuBody; - public: - MenuLine(std::string text, std::function onPress, std::function isChecked): - text(std::move(text)), - onPress(std::move(onPress)), - isChecked(std::move(isChecked)) - { - } + public: + MenuLine(std::function onPress, std::function isChecked, + lv_obj_t *icon) : + onPress(std::move(onPress)), isChecked(std::move(isChecked)), icon(icon) + { + } - MenuLine(std::function drawLine, std::function onPress, std::function isChecked): - drawLine(std::move(drawLine)), - onPress(std::move(onPress)), - isChecked(std::move(isChecked)) - { - } + MenuLine(bool isSeparator) : isSeparator(true) {} + MenuLine(MenuLine &) = delete; + MenuLine(MenuLine &&) = default; - MenuLine(bool isSeparator) : - isSeparator(true), - height(MENUS_SEPARATOR_HEIGHT) - { - } - - inline coord_t lineHeight() { return height; } - - MenuLine(MenuLine &) = delete; - - MenuLine(MenuLine &&) = default; - - protected: - friend void menuBodyEventCallback(lv_event_t *e); - bool isSeparator = false; - coord_t height = MENUS_LINE_HEIGHT + 10; - std::string text; - std::function drawLine; - std::function onPress; - std::function isChecked; + protected: + friend void menuBodyEventCallback(lv_event_t *e); + bool isSeparator = false; + std::function onPress; + std::function isChecked; + std::unique_ptr icon; }; public: @@ -109,13 +99,12 @@ class MenuBody: public Window void onEvent(event_t event) override; #endif - void addLine(const std::string & text, std::function onPress, std::function isChecked); + void addLine(const std::string &text, std::function onPress, + std::function isChecked); - void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) - { - lines.emplace_back(std::move(drawLine), std::move(onPress), std::move(isChecked)); - invalidate(); - } + void addLine(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked); void addSeparator() { @@ -129,6 +118,8 @@ class MenuBody: public Window onCancel = std::move(handler); } + coord_t getContentHeight(); + protected: friend void menuBodyEventCallback(lv_event_t *); void selectNext(MENU_DIRECTION direction); @@ -208,9 +199,12 @@ class Menu: public ModalWindow void setTitle(std::string text); - void addLine(const std::string & text, std::function onPress, std::function isChecked = nullptr); + void addLine(const std::string &text, std::function onPress, + std::function isChecked = nullptr); - void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked = nullptr); + void addLine(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked = nullptr); void addSeparator(); From d64b02438161ee9cf7a98c01df724e9a88cd15c5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 19 Apr 2022 10:43:49 +0200 Subject: [PATCH 550/714] button: set focus before onPress() onPress() might create a new window or change the focus. --- radio/src/thirdparty/libopenui/src/button.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 8a6ea8dc6..cf69bb42f 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -78,12 +78,11 @@ void Button::onEvent(lv_event_t* event) TRACE_WINDOWS("%s received lvgl event code 0x%X", getWindowDebugString("Button").c_str(), code); if (enabled && code == LV_EVENT_CLICKED) { - onKeyPress(); - onPress(); - if (!(windowFlags & NO_FOCUS)) { setFocus(SET_FOCUS_DEFAULT); } + onKeyPress(); + onPress(); } } From 29fcc8d0fd7ccfc0e4e23550d0811b24d8e0a334 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 19 Apr 2022 11:23:53 +0200 Subject: [PATCH 551/714] menu: center on screen / no minimum size --- radio/src/thirdparty/libopenui/src/menu.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 6019d573f..6f638b686 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -53,7 +53,6 @@ void menuBodyEventCallback(lv_event_t *e) MenuBody::MenuBody(Window * parent, const rect_t & rect): Window(parent, rect, OPAQUE, 0, lv_list_create) { - setPageHeight(MENUS_LINE_HEIGHT); setFocus(); } @@ -235,8 +234,8 @@ void Menu::updatePosition() if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; - auto bodyHeight = limit(MENUS_MIN_HEIGHT, height, MENUS_MAX_HEIGHT); - content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); + auto bodyHeight = min(height, MENUS_MAX_HEIGHT); + content->setTop((LCD_H - headerHeight - bodyHeight) / 2); content->setHeight(headerHeight + bodyHeight); content->body.setTop(headerHeight); content->body.setHeight(bodyHeight); From 502b37a19aca8bde03d711259ad3fad6f3e88e95 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 19 Apr 2022 16:35:48 +0200 Subject: [PATCH 552/714] fix potential null pointer access --- radio/src/thirdparty/libopenui/src/choice.cpp | 6 +++-- radio/src/thirdparty/libopenui/src/menu.cpp | 26 ++++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 26a93f53a..cd1ea8cb0 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -29,8 +29,10 @@ void choicePaintCallback(lv_event_t *e) lv_obj_t *target = lv_event_get_target(e); if (target != nullptr) { ChoiceBase *cb = (ChoiceBase*)lv_obj_get_user_data(target); - std::string text = cb->getLabelText(); - lv_label_set_text(cb->label, text.c_str()); + if (cb) { + std::string text = cb->getLabelText(); + lv_label_set_text(cb->label, text.c_str()); + } } } } diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 6f638b686..87eaf91ba 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -31,19 +31,21 @@ void menuBodyEventCallback(lv_event_t *e) if (target != nullptr) { long index = (long)lv_event_get_user_data(e); MenuBody *mb = (MenuBody *) lv_obj_get_user_data(lv_obj_get_parent(target)); - Menu *menu = mb->getParentMenu(); - if (index < (int)mb->lines.size()) { - onKeyPress(); - if (menu->multiple) { - if (mb->selectedIndex == index) - mb->lines[index].onPress(); - else + if (mb) { + Menu *menu = mb->getParentMenu(); + if (index < (int)mb->lines.size()) { + onKeyPress(); + if (menu->multiple) { + if (mb->selectedIndex == index) + mb->lines[index].onPress(); + else + mb->setIndex(index); + } + else { mb->setIndex(index); - } - else { - mb->setIndex(index); - mb->lines[index].onPress(); - menu->deleteLater(); + mb->lines[index].onPress(); + menu->deleteLater(); + } } } } From 475d0685213632ced3be79efb6cc11950746aaa4 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 19 Apr 2022 16:36:41 +0200 Subject: [PATCH 553/714] static text: uses now LVGL label directly --- radio/src/thirdparty/libopenui/src/static.cpp | 50 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/static.h | 48 ++---------------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 4d1a9900f..19e320b1b 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -20,3 +20,53 @@ #include "static.h" #include "font.h" +StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, + WindowFlags windowFlags, LcdFlags textFlags) : + Window(parent, rect, windowFlags, textFlags, lv_label_create), + text(std::move(txt)) +{ + lv_label_set_text(lvobj, text.c_str()); + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); + + lv_obj_set_style_text_font(lvobj, LV_FONT_DEFAULT, LV_PART_MAIN); + lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN); + + if (textFlags & CENTERED) + lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_CENTER, 0); + else if (textFlags & RIGHT) + lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_RIGHT, 0); + + // if (textFlags & FONT(BOLD)) { + // } + + if (windowFlags & BUTTON_BACKGROUND) { + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); + setBackgroundColor(COLOR_THEME_SECONDARY2); + } +} + +#if defined(DEBUG_WINDOWS) +std::string StaticText::getName() const +{ + return "StaticText \"" + text + "\""; +} +#endif + +void StaticText::setText(std::string value) +{ + if (text != value) { + text = std::move(value); + lv_label_set_text(lvobj, text.c_str()); + } +} + +void StaticText::setBackgroundColor(LcdFlags color) +{ + bgColor = color; + auto actualColor = COLOR_VAL(bgColor); + lv_obj_set_style_bg_color( + lvobj, + lv_color_make(GET_RED(actualColor), GET_GREEN(actualColor), + GET_BLUE(actualColor)), + LV_PART_MAIN); +} diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 2c44da122..5269e93df 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -25,55 +25,17 @@ class StaticText: public Window { public: - StaticText(Window * parent, const rect_t & rect, std::string text = "", WindowFlags windowFlags = 0, LcdFlags textFlags = 0) : - Window(parent, rect, windowFlags, textFlags), - text(std::move(text)) - { - label = lv_label_create(lvobj); - lv_label_set_text(label, this->text.c_str()); - lv_obj_clear_flag(label, LV_OBJ_FLAG_CLICK_FOCUSABLE); - lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); - - lv_obj_set_style_text_font(lvobj, &lv_font_roboto_13, LV_PART_MAIN); - lv_obj_set_style_text_color(label, makeLvColor(textFlags), LV_PART_MAIN); - if (textFlags & CENTERED) - lv_obj_center(label); - else if (textFlags & RIGHT) - lv_obj_align(label, LV_ALIGN_TOP_RIGHT, 0, 0); - - if (textFlags & FONT(BOLD)) { - } - - if (windowFlags & BUTTON_BACKGROUND) { - lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); - setBackgroundColor(COLOR_THEME_SECONDARY2); - } - } + StaticText(Window *parent, const rect_t &rect, std::string text = "", + WindowFlags windowFlags = 0, LcdFlags textFlags = 0); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "StaticText \"" + text + "\""; - } + std::string getName() const override; #endif - void setText(std::string value) - { - if (text != value) { - text = std::move(value); - lv_label_set_text(label, this->text.c_str()); - } - } - - void setBackgroundColor(LcdFlags color) - { - bgColor = color; - auto actualColor = COLOR_VAL(bgColor); - lv_obj_set_style_bg_color(lvobj, lv_color_make(GET_RED(actualColor), GET_GREEN(actualColor), GET_BLUE(actualColor)), LV_PART_MAIN); - } + void setText(std::string value); + void setBackgroundColor(LcdFlags color); protected: - lv_obj_t *label = nullptr; std::string text; LcdFlags bgColor = 0; }; From 071c657a7f58fd3c5b58055d1c912272d5ef9859 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 19 Apr 2022 16:38:30 +0200 Subject: [PATCH 554/714] fix attempt at use-after-free When a control which is focused is destroyed out-of-order, it gets a notification while it's user data it destroyed but not NULL. --- radio/src/thirdparty/libopenui/src/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 8297b339b..1f0dd159b 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -195,6 +195,7 @@ Window::~Window() if (children.size() > 0) deleteChildren(); if (lvobj != nullptr) { + lv_obj_set_user_data(lvobj, nullptr); lv_obj_del(lvobj); lvobj = nullptr; } @@ -269,8 +270,8 @@ void Window::clearLvgl() if(window->lvobj != nullptr && window->lvobj->parent == lvobj) window->clearLvgl(); } -// lv_obj_set_user_data(lvobj, nullptr); + lv_obj_set_user_data(lvobj, nullptr); lvobj = nullptr; } From a8c449c0143fa7cad754030a8799a3d17234b706 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 13:26:32 +0200 Subject: [PATCH 555/714] window: create "raw" windows with provided LVGL object --- radio/src/thirdparty/libopenui/src/window.cpp | 46 +++++++++++++++---- radio/src/thirdparty/libopenui/src/window.h | 10 ++-- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 1f0dd159b..0c6eaf689 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -52,13 +52,7 @@ static void window_event_cb(lv_event_t * e) if(code == LV_EVENT_DELETE || window->deleted()) return; - if (code == LV_EVENT_GET_SELF_SIZE) { - - lv_point_t *p = (lv_point_t *)lv_event_get_param(e); - if (p->x >= 0) { p->x = window->getInnerWidth(); } - if (p->y >= 0) { p->y = window->getInnerHeight(); } - - } else if (code == LV_EVENT_DRAW_MAIN) { + if (code == LV_EVENT_DRAW_MAIN) { lv_draw_ctx_t* draw_ctx = lv_event_get_draw_ctx(e); @@ -160,8 +154,39 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, lvobj = (objConstruct == nullptr) ? lv_obj_create(lvParent) : objConstruct(lvParent); - lv_obj_set_pos(lvobj, rect.x, rect.y); - lv_obj_set_size(lvobj, rect.w, rect.h); + lv_obj_set_user_data(lvobj, this); + + if (rect.w || rect.h) { + lv_obj_set_pos(lvobj, rect.x, rect.y); + lv_obj_set_size(lvobj, rect.w, rect.h); + } + + if (windowFlags & OPAQUE) { + lv_obj_set_style_bg_opa(lvobj, LV_OPA_MAX, LV_PART_MAIN); + } + + lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC); + lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); + + if (parent) { + parent->addChild(this, windowFlags & PUSH_FRONT); + if (!(windowFlags & TRANSPARENT)) { + invalidate(); + } + } + + // honor the no focus flag of libopenui + if (this->windowFlags & NO_FOCUS) { + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); + } +} + +Window::Window(Window *parent, lv_obj_t* lvobj) + : lvobj(lvobj), + parent(parent), + rect(nullRect) +{ lv_obj_set_user_data(lvobj, this); if (windowFlags & OPAQUE) { @@ -255,8 +280,9 @@ void Window::clear() innerWidth = rect.w; innerHeight = rect.h; deleteChildren(); - if(lvobj != nullptr) + if(lvobj != nullptr) { lv_obj_clean(lvobj); + } invalidate(); } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 3d66933a9..67c4befd0 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -69,6 +69,8 @@ class Window Window(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglCreate objConstruct = nullptr); + Window(Window *parent, lv_obj_t* lvobj); + virtual ~Window(); #if defined(DEBUG_WINDOWS) @@ -424,14 +426,14 @@ class Window Window * parent; std::list children; rect_t rect; - coord_t innerWidth; - coord_t innerHeight; + coord_t innerWidth = 0; + coord_t innerHeight = 0; coord_t pageWidth = 0; coord_t pageHeight = 0; coord_t scrollPositionX = 0; coord_t scrollPositionY = 0; - WindowFlags windowFlags; - LcdFlags textFlags; + WindowFlags windowFlags = 0; + LcdFlags textFlags = 0; bool _deleted = false; static Window * focusWindow; From 8761b423c64012e911e4aaf269f242061c7cdc26 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 13:28:06 +0200 Subject: [PATCH 556/714] checkbox: use `lv_switch` instead of `lv_checkbox` `lv_checkbox` is actually a radio button with a mandatory label on the right side. --- .../src/thirdparty/libopenui/src/checkbox.cpp | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 2e914fe79..457e6326a 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -20,8 +20,6 @@ #include "checkbox.h" #include "theme.h" -lv_style_t style_indicator; -lv_style_t style_edit; static void checkbox_event_handler(lv_event_t* e) { @@ -35,38 +33,23 @@ static void checkbox_event_handler(lv_event_t* e) CheckBox::CheckBox(Window* parent, const rect_t& rect, std::function getValue, std::function setValue, WindowFlags flags) : - FormField(parent, rect, flags, 0, lv_checkbox_create), + FormField(parent, rect, flags, 0, lv_switch_create), _getValue(std::move(getValue)), _setValue(std::move(setValue)) { - coord_t size = min(rect.w, rect.h); - setWidth(size); - setHeight(size); + // TODO: migrate to default theme - //TODO: migrate to default theme - - // BORDER - lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), - LV_PART_INDICATOR); - lv_obj_set_style_border_width(lvobj, 1, LV_PART_INDICATOR); + if (height() > 0) + lv_obj_set_width(lvobj, (lv_coord_t)(height() * 1.7f)); - lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), - LV_PART_INDICATOR | LV_STATE_FOCUSED); - lv_obj_set_style_border_width(lvobj, 2, LV_PART_INDICATOR | LV_STATE_FOCUSED); - - // UNCHECKED BACKGROUND - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), - LV_PART_INDICATOR); - lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), - LV_PART_INDICATOR | LV_STATE_CHECKED); - - lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), - LV_PART_INDICATOR | LV_STATE_CHECKED); + // PART_MAIN + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_DISABLED), LV_PART_MAIN); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_DISABLED), LV_STATE_FOCUSED); if (_getValue()) lv_obj_add_state(lvobj, LV_STATE_CHECKED); - lv_obj_add_event_cb(lvobj, checkbox_event_handler, LV_EVENT_VALUE_CHANGED, - this); + lv_obj_add_event_cb(lvobj, checkbox_event_handler, + LV_EVENT_VALUE_CHANGED, this); } #if defined(HARDWARE_KEYS) @@ -95,7 +78,7 @@ bool CheckBox::onTouchEnd(coord_t x, coord_t y) if (!hasFocus()) { setFocus(SET_FOCUS_DEFAULT); } - + return true; } #endif From a0e97c001d8e146089864ba50a3d3decb58f04db Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 13:29:43 +0200 Subject: [PATCH 557/714] modal_window: implement title label --- .../thirdparty/libopenui/src/modal_window.cpp | 29 ++++++++++++------- .../thirdparty/libopenui/src/modal_window.h | 22 +++----------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 1ca084510..4be831de4 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -26,10 +26,8 @@ ModalWindow::ModalWindow(Window * parent, bool closeWhenClickOutside): Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}), closeWhenClickOutside(closeWhenClickOutside) { - lv_obj_set_parent(lvobj, lv_layer_top()); lv_obj_set_style_bg_color(lvobj, lv_color_black(), LV_PART_MAIN); lv_obj_set_style_bg_opa(lvobj, LV_OPA_50, LV_PART_MAIN); - lv_obj_add_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE); Layer::push(this); } @@ -38,7 +36,6 @@ void ModalWindow::deleteLater(bool detach, bool trash) if (_deleted) return; - lv_obj_clear_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE); Layer::pop(this); Window::deleteLater(detach, trash); @@ -55,15 +52,27 @@ bool ModalWindow::onTouchEnd(coord_t x, coord_t y) } #endif - -void ModalWindow::paint(BitmapBuffer * dc) +ModalWindowContent::ModalWindowContent(ModalWindow* parent, + const rect_t& rect) : + Window(parent, rect, OPAQUE) { - // dc->drawFilledRect(0, 0, width(), height(), SOLID, BLACK, OPACITY(6)); } -void ModalWindowContent::paint(BitmapBuffer * dc) +void ModalWindowContent::setTitle(const std::string& text) { - // dc->drawSolidFilledRect(0, 0, width(), POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY1); - // dc->drawText(FIELD_PADDING_LEFT, (POPUP_HEADER_HEIGHT - getFontHeight(FONT(STD))) / 2, title.c_str(), COLOR_THEME_PRIMARY2); - // dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, width(), height() - POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY3); + if (!title) { + title = lv_label_create(lvobj); + lv_obj_move_to_index(title, 0); + lv_obj_set_width(title, lv_pct(100)); + lv_obj_set_style_pad_all(title, PAGE_PADDING, LV_PART_MAIN); + // TODO: styling + // lv_obj_set_style_pad_left(title, FIELD_PADDING_LEFT, 0); + } + lv_label_set_text(title, text.c_str()); +} + +std::string ModalWindowContent::getTitle() const +{ + if (!title) return std::string(); + return lv_label_get_text(title); } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index fedaeea77..381c96ea7 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -35,8 +35,6 @@ class ModalWindow: public Window void deleteLater(bool detach = true, bool trash = true) override; // NOLINT(google-default-arguments) - void paint(BitmapBuffer * dc) override; - #if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t x, coord_t y) override; #endif @@ -53,10 +51,7 @@ class ModalWindow: public Window class ModalWindowContent: public Window { public: - explicit ModalWindowContent(ModalWindow * parent, const rect_t & rect): - Window(parent, rect, OPAQUE) - { - } + explicit ModalWindowContent(ModalWindow * parent, const rect_t & rect); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -65,19 +60,10 @@ class ModalWindowContent: public Window } #endif - void setTitle(std::string text) - { - title = std::move(text); - } - - const std::string & getTitle() - { - return title; - } - - void paint(BitmapBuffer * dc) override; + void setTitle(const std::string& text); + std::string getTitle() const; protected: - std::string title; + lv_obj_t* title = nullptr; }; From aca4b074ab07743915edd54fac249a65aaff882a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 13:34:54 +0200 Subject: [PATCH 558/714] menu: fix height --- radio/src/thirdparty/libopenui/src/menu.cpp | 15 ++++++++++++++- radio/src/thirdparty/libopenui/src/menu.h | 9 ++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 87eaf91ba..139dc7e6e 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -222,6 +222,19 @@ MenuWindowContent::MenuWindowContent(Menu* parent) : lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); } +coord_t MenuWindowContent::getHeaderHeight() const +{ + if (title) return lv_obj_get_height(title); + return 0; +} + +void MenuWindowContent::deleteLater(bool detach, bool trash) +{ + if (_deleted) return; + body.deleteLater(true, false); + ModalWindowContent::deleteLater(detach, trash); +} + Menu::Menu(Window * parent, bool multiple): ModalWindow(parent, true), content(createMenuWindow(this)), @@ -235,7 +248,7 @@ void Menu::updatePosition() if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen - auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; + auto headerHeight = content->getHeaderHeight(); auto bodyHeight = min(height, MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2); content->setHeight(headerHeight + bodyHeight); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 0798ec8d5..f0608d6f1 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -140,14 +140,9 @@ class MenuWindowContent: public ModalWindowContent public: explicit MenuWindowContent(Menu * parent); - void deleteLater(bool detach = true, bool trash = true) override - { - if (_deleted) - return; + coord_t getHeaderHeight() const; - body.deleteLater(true, false); - ModalWindowContent::deleteLater(detach, trash); - } + void deleteLater(bool detach = true, bool trash = true) override; #if defined(DEBUG_WINDOWS) std::string getName() const override From 6c8e8e5f983576c9de8c24708051ce3f3669b574 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 13:50:25 +0200 Subject: [PATCH 559/714] dialog: add `updateSize()` method --- radio/src/thirdparty/libopenui/src/dialog.cpp | 42 +++++++++++++++---- radio/src/thirdparty/libopenui/src/dialog.h | 37 +++++++--------- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index cd991edce..d87e5a6f3 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -21,18 +21,46 @@ #include "mainwindow.h" #include "theme.h" -DialogWindowContent::DialogWindowContent(Dialog * parent, const rect_t & rect): - ModalWindowContent(parent, rect), - form(this, {0, POPUP_HEADER_HEIGHT, rect.w, coord_t(rect.h - POPUP_HEADER_HEIGHT)}, FORM_NO_BORDER) +DialogWindowContent::DialogWindowContent(Dialog* parent, const rect_t& rect) : + ModalWindowContent(parent, rect), + form(this, + { 0, POPUP_HEADER_HEIGHT, rect.w, + coord_t(rect.h - POPUP_HEADER_HEIGHT) }, + FORM_NO_BORDER) { form.setFocus(SET_FOCUS_DEFAULT); } +void DialogWindowContent::updateSize() +{ + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); + lv_obj_center(lvobj); + lv_obj_update_layout(lvobj); -Dialog::Dialog(Window * parent, std::string title, const rect_t & rect): - ModalWindow(parent), - content(createDialogWindow(this, rect)) + rect.x = lv_obj_get_x(lvobj); + rect.y = lv_obj_get_y(lvobj); + rect.w = lv_obj_get_width(lvobj); + rect.h = lv_obj_get_height(lvobj); + invalidate(); +} + +void DialogWindowContent::deleteLater(bool detach, bool trash) +{ + if (_deleted) return; + form.deleteLater(true, false); + ModalWindowContent::deleteLater(detach, trash); +} + +#if defined(DEBUG_WINDOWS) +std::string DialogWindowContent::getName() const override +{ + return "DialogWindowContent"; +} +#endif + +Dialog::Dialog(Window* parent, std::string title, const rect_t& rect) : + ModalWindow(parent), content(new DialogWindowContent(this, rect)) { bringToTop(); - content->setTitle(std::move(title)); + if (!title.empty()) content->setTitle(std::move(title)); } diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 36140ec08..1519d7098 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -24,38 +24,29 @@ class Dialog; -class DialogWindowContent: public ModalWindowContent +class DialogWindowContent : public ModalWindowContent { friend class Dialog; - public: - DialogWindowContent(Dialog * parent, const rect_t & rect); + public: + DialogWindowContent(Dialog* parent, const rect_t& rect); - void deleteLater(bool detach = true, bool trash = true) override - { - if (_deleted) - return; - - form.deleteLater(true, false); - ModalWindowContent::deleteLater(detach, trash); - } + void updateSize(); + void deleteLater(bool detach = true, bool trash = true) override; #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "DialogWindowContent"; - } + std::string getName() const override; #endif - public: - FormGroup form; + public: + FormGroup form; }; -class Dialog: public ModalWindow { - public: - Dialog(Window * parent, std::string title, const rect_t & rect); +class Dialog : public ModalWindow +{ + public: + Dialog(Window* parent, std::string title, const rect_t& rect); - protected: - DialogWindowContent * content; + protected: + DialogWindowContent* content; }; - From 22499d1c16bf728dc8e3eb305009a97a3e339d18 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 14:41:27 +0200 Subject: [PATCH 560/714] lvgl: use edgetx fork to allow for cherry-picked bug-fixes --- radio/src/thirdparty/libopenui/.gitmodules | 2 +- radio/src/thirdparty/libopenui/thirdparty/lvgl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/.gitmodules b/radio/src/thirdparty/libopenui/.gitmodules index d7500db7a..825bab72f 100644 --- a/radio/src/thirdparty/libopenui/.gitmodules +++ b/radio/src/thirdparty/libopenui/.gitmodules @@ -3,5 +3,5 @@ url = https://github.com/nothings/stb.git [submodule "thirdparty/lvgl"] path = thirdparty/lvgl - url = https://github.com/lvgl/lvgl.git + url = https://github.com/EdgeTX/lvgl.git branch = release/v8.2 diff --git a/radio/src/thirdparty/libopenui/thirdparty/lvgl b/radio/src/thirdparty/libopenui/thirdparty/lvgl index 0b5a1d4b2..3e241a66b 160000 --- a/radio/src/thirdparty/libopenui/thirdparty/lvgl +++ b/radio/src/thirdparty/libopenui/thirdparty/lvgl @@ -1 +1 @@ -Subproject commit 0b5a1d4b23975b920ff841ea9cd038802f51711b +Subproject commit 3e241a66bd3d89f94315005cd6b886d07314a025 From f473271f657d981191545b1f41408f8dd06fe2b9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 14:42:21 +0200 Subject: [PATCH 561/714] WiP: flex layout --- radio/src/thirdparty/libopenui/src/choice.cpp | 66 +++++++++-------- radio/src/thirdparty/libopenui/src/choice.h | 10 +-- .../src/thirdparty/libopenui/src/choiceex.cpp | 8 +- radio/src/thirdparty/libopenui/src/choiceex.h | 12 +-- .../src/thirdparty/libopenui/src/flexlayout.h | 67 +++++++++++++++++ radio/src/thirdparty/libopenui/src/form.cpp | 73 ++++++++++++++++++- radio/src/thirdparty/libopenui/src/form.h | 42 +++++++---- radio/src/thirdparty/libopenui/src/static.cpp | 5 +- radio/src/thirdparty/libopenui/src/window.cpp | 27 ++++--- radio/src/thirdparty/libopenui/src/window.h | 5 +- 10 files changed, 240 insertions(+), 75 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/flexlayout.h diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index cd1ea8cb0..d74dfff7e 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -37,12 +37,14 @@ void choicePaintCallback(lv_event_t *e) } } -ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, WindowFlags windowFlags): - FormField(parent, rect, windowFlags), - type(type) +ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type, + WindowFlags windowFlags) : + FormField(parent, rect, windowFlags), type(type) { + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); lv_obj_add_event_cb(lvobj, choicePaintCallback, LV_EVENT_DRAW_MAIN_BEGIN, lvobj); label = lv_label_create(lvobj); + lv_group_t * def_group = lv_group_get_default(); if(def_group) { lv_group_add_obj(def_group, lvobj); @@ -55,7 +57,6 @@ ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, lv_obj_set_style_border_width(lvobj, 1, LV_PART_MAIN); lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN); lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); - lv_obj_set_style_text_font(lvobj, &lv_font_roboto_13, LV_PART_MAIN); // focused lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_FOCUSED); @@ -65,14 +66,12 @@ ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); - // add the image lv_obj_t *img = lv_img_create(lvobj); lv_img_set_src(img, type == CHOICE_TYPE_DROPOWN ? LV_SYMBOL_DOWN : LV_SYMBOL_DIRECTORY); lv_obj_set_align(img, LV_ALIGN_RIGHT_MID); } - std::string Choice::getLabelText() { std::string text = ""; @@ -89,7 +88,7 @@ std::string Choice::getLabelText() return text; } -Choice::Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, +Choice::Choice(Window* parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), vmin(vmin), @@ -99,41 +98,46 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, { } -Choice::Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int vmin, int vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : - ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), - vmin(vmin), - vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) +Choice::Choice(Window* parent, const rect_t& rect, const char* const values[], + int vmin, int vmax, std::function getValue, + std::function setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) { setValues(values); } -Choice::Choice(FormGroup * parent, const rect_t & rect, std::vector values, int vmin, int vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : - ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), - values(std::move(values)), - vmin(vmin), - vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) +Choice::Choice(Window* parent, const rect_t& rect, + std::vector values, int vmin, int vmax, + std::function getValue, std::function setValue, + WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + values(std::move(values)), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) { } -Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int vmin, int vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : - ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), - vmin(vmin), - vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) +Choice::Choice(Window* parent, const rect_t& rect, const char* values, int vmin, + int vmax, std::function getValue, + std::function setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) { if (values) { uint8_t len = values[0]; - const char * value = &values[1]; + const char* value = &values[1]; for (int i = vmin; i <= vmax; i++) { - this->values.emplace_back(std::string(value, min(len, strlen(value)))); + this->values.emplace_back( + std::string(value, min(len, strlen(value)))); value += len; } } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index b54d57676..52e6e513a 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -32,7 +32,7 @@ enum ChoiceType { class ChoiceBase : public FormField { public: - ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0); + ChoiceBase(Window* parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0); inline ChoiceType getType() const { @@ -48,20 +48,20 @@ class ChoiceBase : public FormField class Choice: public ChoiceBase { public: - Choice(FormGroup *parent, const rect_t &rect, int vmin, int vmax, + Choice(Window* parent, const rect_t &rect, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup *parent, const rect_t &rect, + Choice(Window* parent, const rect_t &rect, std::vector values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup *parent, const rect_t &rect, const char *const values[], + Choice(Window* parent, const rect_t &rect, const char *const values[], int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup *parent, const rect_t &rect, const char *values, int vmin, + Choice(Window* parent, const rect_t &rect, const char *values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); diff --git a/radio/src/thirdparty/libopenui/src/choiceex.cpp b/radio/src/thirdparty/libopenui/src/choiceex.cpp index d8a87698d..f1986f904 100644 --- a/radio/src/thirdparty/libopenui/src/choiceex.cpp +++ b/radio/src/thirdparty/libopenui/src/choiceex.cpp @@ -34,9 +34,11 @@ void ChoiceEx::setLongPressHandler(std::function handler) longPressHandler = handler; } -ChoiceEx::ChoiceEx(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, - std::function setValue, WindowFlags windowFlags) : - Choice(parent, rect, vmin, vmax, getValue, setValue, windowFlags) +ChoiceEx::ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, + int16_t vmax, std::function getValue, + std::function setValue, + WindowFlags windowFlags) : + Choice(parent, rect, vmin, vmax, getValue, setValue, windowFlags) { #if defined(HARDWARE_TOUCH) duration10ms = 0; diff --git a/radio/src/thirdparty/libopenui/src/choiceex.h b/radio/src/thirdparty/libopenui/src/choiceex.h index 4a63d13cb..4d1348153 100644 --- a/radio/src/thirdparty/libopenui/src/choiceex.h +++ b/radio/src/thirdparty/libopenui/src/choiceex.h @@ -25,10 +25,12 @@ class ChoiceEx : public Choice { public: - ChoiceEx(FormGroup * parent, const rect_t &rect, int16_t vmin, int16_t vmax, std::function getValue, - std::function setValue = nullptr, WindowFlags windowFlags = 0); - - void setLongPressHandler(std::function handler); + ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, int16_t vmax, + std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0); + + void setLongPressHandler(std::function handler); #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; @@ -57,4 +59,4 @@ protected: #endif }; -#endif \ No newline at end of file +#endif diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h new file mode 100644 index 000000000..e9cf3dc67 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include + +class FlexGridLayout +{ + const lv_coord_t* col_dsc = nullptr; + const lv_coord_t* row_dsc = nullptr; + lv_coord_t padding = 0; + + uint8_t col_pos = 0; + uint8_t col_span = 1; + uint8_t row_pos = 0; + uint8_t row_span = 1; + + public: + FlexGridLayout(const lv_coord_t col_dsc[], const lv_coord_t row_dsc[], + lv_coord_t padding = 4) : + col_dsc(col_dsc), row_dsc(row_dsc), padding(padding) + { + } + + void apply(lv_obj_t* obj) { + + // padding + lv_obj_set_style_pad_all(obj, padding, LV_PART_MAIN); + lv_obj_set_style_pad_left(obj, padding + PAGE_PADDING, LV_PART_MAIN); + + // layout + lv_obj_set_layout(obj, LV_LAYOUT_GRID); + if (col_dsc && row_dsc) { + lv_obj_set_grid_dsc_array(obj, col_dsc, row_dsc); + } + } + + void add(lv_obj_t* obj) { + if (col_dsc && row_dsc) { + lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_START, col_pos, col_span, + LV_GRID_ALIGN_CENTER, row_pos, row_span); + } + } + + void nextColumn() { col_pos++; } + void nextRow() { row_pos++; } + void resetPos() { col_pos = 0; row_pos = 0; } +}; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 52c36af1c..a2c7c2715 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -31,9 +31,9 @@ FormField::FormField(Window* parent, const rect_t& rect, Window(parent, rect, windowFlags, textFlags, objConstruct) { if (!(windowFlags & NO_FOCUS)) { - auto* form = dynamic_cast(parent); - if (form) { - form->addField(this, windowFlags & PUSH_FRONT); + auto cont = dynamic_cast(parent); + if (cont) { + cont->addField(this, windowFlags & PUSH_FRONT); } } @@ -109,6 +109,42 @@ void FormField::setFocus(uint8_t flag, Window * from) } } +FormGroup::Line::Line(FormGroup* parent, lv_obj_t* obj, + FlexGridLayout* layout) : + Window(parent, obj), layout(layout) +{ + // forward scroll and focus + windowFlags |= FORWARD_SCROLL | FORM_FORWARD_FOCUS; + + if (layout) { + layout->apply(obj); + } + + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); + lv_obj_set_width(lvobj, lv_pct(100)); +} + +void FormGroup::Line::addChild(Window* window, bool front) +{ + Window::addChild(window, front); + if (layout) { + layout->add(window->getLvObj()); + layout->nextColumn(); + } +} + +void FormGroup::Line::addField(FormField *field, bool front) +{ + auto form = static_cast(parent); + form->addField(field, front); +} + +void FormGroup::Line::removeField(FormField *field) +{ + auto form = static_cast(parent); + form->removeField(field); +} + static lv_style_t border_style; static lv_style_t focus_border_style; @@ -118,7 +154,6 @@ FormGroup::FormGroup(Window* parent, const rect_t& rect, { lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); - if (!(windowFlags & (FORM_NO_BORDER | FORM_FORWARD_FOCUS))) { lv_style_init(&border_style); lv_style_set_border_width(&border_style, 2); @@ -134,6 +169,36 @@ FormGroup::FormGroup(Window* parent, const rect_t& rect, } } +void FormGroup::clear() +{ + Window::clear(); + first = nullptr; + last = nullptr; + if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { + previous->setNextField(this); + } + lv_obj_set_layout(lvobj, 0); +} + +void FormGroup::setFlexLayout(lv_flex_flow_t flow, lv_coord_t padding) +{ + lv_obj_set_flex_flow(lvobj, flow); + if (_LV_FLEX_COLUMN & flow) { + lv_obj_set_style_pad_row(lvobj, padding, LV_PART_MAIN); + } else { + lv_obj_set_style_pad_column(lvobj, padding, LV_PART_MAIN); + } + lv_obj_set_width(lvobj, lv_pct(100)); + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); +} + +FormGroup::Line* FormGroup::newLine(FlexGridLayout* layout) +{ + layout->resetPos(); + auto lv_line = lv_obj_create(lvobj); + return new Line(this, lv_line, layout); +} + void FormGroup::removeField(FormField * field) { FormField* prev = field->getPreviousField(); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 6f75350d3..9e5df9b26 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -20,6 +20,7 @@ #pragma once #include "window.h" +#include "flexlayout.h" constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 1; constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 2; @@ -70,7 +71,7 @@ class FormField : public Window // } if (editMode && lvobj != nullptr) { - lv_obj_add_state(lvobj, LV_STATE_EDITED | LV_STATE_FOCUSED); + lv_obj_add_state(lvobj, LV_STATE_EDITED); } else if (lvobj != nullptr) { lv_obj_clear_state(lvobj, LV_STATE_EDITED); } @@ -114,9 +115,29 @@ class FormField : public Window std::function backgroundHandler = nullptr; }; -class FormGroup : public FormField +class FieldContainer { public: + virtual void addField(FormField *field, bool front = false) = 0; + virtual void removeField(FormField *field) = 0; +}; + +class FormGroup : public FormField, public FieldContainer +{ + public: + class Line : public Window, public FieldContainer + { + FlexGridLayout* layout; + + public: + Line(FormGroup *parent, lv_obj_t *lvobj, FlexGridLayout* layout); + + protected: + void addChild(Window* window, bool front = false) override; + void addField(FormField *field, bool front = false) override; + void removeField(FormField *field) override; + }; + FormGroup(Window *parent, const rect_t &rect, WindowFlags windowflags = 0, LvglCreate objConstruct = nullptr); @@ -124,21 +145,16 @@ class FormGroup : public FormField std::string getName() const override { return "FormGroup"; } #endif - void clear() - { - Window::clear(); - first = nullptr; - last = nullptr; - if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { - previous->setNextField(this); - } - } + void clear(); + void setFlexLayout(lv_flex_flow_t flow = LV_FLEX_FLOW_COLUMN, lv_coord_t padding = 0); + Line* newLine(FlexGridLayout* layout); + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window *from = nullptr) override; - virtual void addField(FormField *field, bool front = false); - virtual void removeField(FormField *field); + void addField(FormField *field, bool front = false) override; + void removeField(FormField *field) override; void setFirstField(FormField *field) { first = field; } void setLastField(FormField *field) { last = field; } diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 19e320b1b..18d84aaf3 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -26,10 +26,11 @@ StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, text(std::move(txt)) { lv_label_set_text(lvobj, text.c_str()); + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); - lv_obj_set_style_text_font(lvobj, LV_FONT_DEFAULT, LV_PART_MAIN); - lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN); + // lv_obj_set_style_text_font(lvobj, LV_FONT_DEFAULT, LV_PART_MAIN); + // lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN); if (textFlags & CENTERED) lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_CENTER, 0); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0c6eaf689..af1cf9d59 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -132,12 +132,10 @@ static void window_event_cb(lv_event_t * e) window->setScrollPositionX(scroll_x); } else if (code == LV_EVENT_FOCUSED) { - bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); - bool loiu_focused = ((Window *)target->user_data)->hasFocus(); - TRACE_WINDOWS("FOCUSED[%d|%d] %s", - lvgl_focused, loiu_focused, + bool focused = ((Window *)target->user_data)->hasFocus(); + TRACE_WINDOWS("FOCUSED[%d] %s", focused, window->getWindowDebugString().c_str()); - if (!loiu_focused) { window->setFocus(); } + if (!focused) { window->setFocus(); } } } @@ -327,16 +325,22 @@ void Window::setFocus(uint8_t flag, Window * from) if (focusWindow != this) { // synchronize lvgl focused state with libopenui - if (lvobj != nullptr && !lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) { - lv_obj_add_state(lvobj, LV_STATE_FOCUSED); + if (lvobj != nullptr && + !lv_obj_has_state(lvobj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY)) { + lv_obj_add_state(lvobj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY); if (focusWindow != nullptr) { - lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED); - lv_obj_clear_state(focusWindow->lvobj, LV_STATE_EDITED); - // lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | LV_STATE_EDITED); + lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | + LV_STATE_FOCUS_KEY | + LV_STATE_EDITED); } } - // scroll before calling focusHandler so that the window can adjust the scroll position if needed + // scroll before calling focusHandler so that the window can adjust the + // scroll position if needed +#if 1 + // lv_obj_scroll_to_view(lvobj, LV_ANIM_OFF); + lv_obj_scroll_to_view_recursive(lvobj, LV_ANIM_OFF); +#else Window * parent = this->parent; while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { parent = parent->parent; @@ -345,6 +349,7 @@ void Window::setFocus(uint8_t flag, Window * from) parent->scrollTo(this); invalidate(); } +#endif clearFocus(); focusWindow = this; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 67c4befd0..c0fd18435 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -143,6 +143,8 @@ class Window auto textColor = COLOR_VAL(flags); auto r = GET_RED(textColor), g = GET_GREEN(textColor), b = GET_BLUE(textColor); lv_obj_set_style_text_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); + + // rco: shouldn't this be done via 'setTextFlags()' on the children? for (uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { auto child = lv_obj_get_child(lvobj, i); lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN); @@ -444,10 +446,11 @@ class Window std::function closeHandler; std::function focusHandler; - void addChild(Window * window, bool front = false) + virtual void addChild(Window * window, bool front = false) { if (lv_obj_get_parent(window->lvobj) != nullptr) { lv_obj_set_parent(window->lvobj, this->lvobj); + // not used anywhere! if (front) lv_obj_move_to_index(window->lvobj, 0); } From cf8a532e2d3292effae535f4cc81a4fb051aaf3e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 14:42:38 +0200 Subject: [PATCH 562/714] coloredit: use LVGL color wheel --- .../thirdparty/libopenui/src/coloredit.cpp | 113 +++++++++++------- .../src/thirdparty/libopenui/src/coloredit.h | 22 ++-- 2 files changed, 82 insertions(+), 53 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp index 3c97cfb10..36be41dde 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ b/radio/src/thirdparty/libopenui/src/coloredit.cpp @@ -20,59 +20,82 @@ #include "coloredit.h" #include "numberedit.h" #include "bitfield.h" +#include "dialog.h" -constexpr uint8_t PART_BITS[3][2] = { {0, 5}, {5, 6}, {11, 5} }; +#define PICKER_SIZE lv_colorwheel_class.width_def -class ColorBox: public Window { - public: - ColorBox(Window * parent, const rect_t & rect, LcdFlags color): - Window(parent, rect), - color(color) - { - } +struct ColorWheelPopup : public Dialog { + ColorWheelPopup(Window* parent, lv_color_t color, + std::function setValue) : + Dialog(parent, std::string(), + rect_t{0, 0, PICKER_SIZE, + PICKER_SIZE + POPUP_HEADER_HEIGHT}) + { + auto lv_content = content->getLvObj(); + auto form = content->form.getLvObj(); + lv_obj_set_size(form, PICKER_SIZE, PICKER_SIZE); -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "ColorBox"; - } -#endif + lv_obj_center(lv_content); + lv_obj_center(form); - void paint(BitmapBuffer * dc) override - { - dc->drawSolidFilledRect(0, 0, width(), height(), COLOR_THEME_SECONDARY1); - dc->drawSolidFilledRect(1, 1, width() - 2, height() - 2, color << 16); - } + auto cw = lv_colorwheel_create(form, true); + lv_colorwheel_set_rgb(cw, color); - void setColor(pixel_t value) - { - color = value; - invalidate(); - } + auto wheel_size = 3 * PICKER_SIZE / 4; + lv_obj_set_style_arc_width(cw, wheel_size / 5, 0); + lv_obj_set_size(cw, wheel_size, wheel_size); + lv_obj_center(cw); - protected: - pixel_t color; + content->updateSize(); + + setCloseWhenClickOutside(true); + setCloseHandler([=]() { + lv_color_t lv_color = lv_colorwheel_get_rgb(cw); + setValue(lv_color); + }); + } }; -ColorEdit::ColorEdit(FormGroup * parent, const rect_t & rect, std::function getValue, std::function setValue): - FormGroup(parent, rect, FORWARD_SCROLL | FORM_FORWARD_FOCUS) +static void colorwheel_popup(lv_event_t* e) { - auto width = rect.w / 4 - 5; - - // The color box - auto box = new ColorBox(this, {0, 0, rect.w - 3 * width, rect.h}, getValue()); - - // The 3 parts of RGB - for (uint8_t part = 0; part < 3; part++) { - new NumberEdit(this, {rect.w - (3 - part) * width + 5, 0, width - 5, rect.h}, 0, (1 << PART_BITS[part][1]) - 1, - [=]() { - return bfGet(getValue(), PART_BITS[part][0], PART_BITS[part][1]); - }, - [=](uint16_t newValue) { - uint16_t value = getValue(); - value = bfSet(value, newValue, PART_BITS[part][0], PART_BITS[part][1]); - setValue(value); - box->setColor(value); - }); + auto edit = (ColorEdit*)lv_event_get_user_data(e); + if (edit) { + new ColorWheelPopup(edit, edit->getColor(), + [=](lv_color_t c) { edit->setColor(c); }); } } + +// based on LVGL default switch size +constexpr lv_coord_t COLOR_PAD_WIDTH = (4 * LV_DPI_DEF) / 10; +constexpr lv_coord_t COLOR_PAD_HEIGHT = (4 * LV_DPI_DEF) / 17; + +ColorEdit::ColorEdit(Window* parent, const rect_t& rect, + std::function getValue, + std::function setValue) : + FormField(parent, rect, 0, 0, lv_btn_create), + setValue(std::move(setValue)) +{ + auto value = getValue(); + color = lv_color_make(GET_RED(value), GET_GREEN(value), GET_BLUE(value)); + + lv_obj_set_style_bg_color(lvobj, color, LV_PART_MAIN); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); + + lv_obj_set_style_bg_color(lvobj, color, LV_STATE_FOCUS_KEY); + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_STATE_FOCUS_KEY); + + lv_obj_set_size(lvobj, COLOR_PAD_WIDTH, COLOR_PAD_HEIGHT); + lv_obj_add_event_cb(lvobj, colorwheel_popup, LV_EVENT_CLICKED, this); +} + +void ColorEdit::setColor(lv_color_t c) +{ + color = c; + setValue(RGB_JOIN(c.ch.red, c.ch.green, c.ch.blue)); + + lv_obj_set_style_bg_color(lvobj, color, LV_PART_MAIN); + lv_obj_set_style_bg_color(lvobj, color, LV_STATE_FOCUS_KEY); + + // TRACE("### color: %d %d %d ###", c.ch.red, c.ch.green, c.ch.blue); + invalidate(); +} diff --git a/radio/src/thirdparty/libopenui/src/coloredit.h b/radio/src/thirdparty/libopenui/src/coloredit.h index 0d862905f..601fa490d 100644 --- a/radio/src/thirdparty/libopenui/src/coloredit.h +++ b/radio/src/thirdparty/libopenui/src/coloredit.h @@ -21,15 +21,21 @@ #include "form.h" -class ColorEdit : public FormGroup { - public: - ColorEdit(FormGroup * parent, const rect_t & rect, std::function getValue, std::function setValue = nullptr); +class ColorEdit : public FormField +{ + lv_color_t color; + std::function setValue; +public: + + ColorEdit(Window* parent, const rect_t& rect, + std::function getValue, + std::function setValue = nullptr); + + void setColor(lv_color_t c); + lv_color_t getColor() const { return color; } + #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "ColorEdit"; - } + std::string getName() const override { return "ColorEdit"; } #endif }; - From ae5d1759f1c0f084bd6f8d070d141cc9b9defb82 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 22 Apr 2022 16:15:44 +0200 Subject: [PATCH 563/714] portrait: size adjustments --- radio/src/thirdparty/libopenui/src/choice.cpp | 5 ++++- radio/src/thirdparty/libopenui/src/numberedit.cpp | 11 ++++++++++- radio/src/thirdparty/libopenui/src/numberedit.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index d74dfff7e..9c7cdcf52 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -42,6 +42,10 @@ ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type, FormField(parent, rect, windowFlags), type(type) { lv_obj_set_height(lvobj, LV_SIZE_CONTENT); + lv_obj_set_width(lvobj, LV_SIZE_CONTENT); + lv_obj_set_layout(lvobj, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW); + lv_obj_add_event_cb(lvobj, choicePaintCallback, LV_EVENT_DRAW_MAIN_BEGIN, lvobj); label = lv_label_create(lvobj); @@ -62,7 +66,6 @@ ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type, lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_FOCUSED); lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_PRIMARY2), LV_PART_MAIN | LV_STATE_FOCUSED); - lv_obj_set_width(label, lv_pct(80)); lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 6db3c3d20..31bd11817 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -55,7 +55,16 @@ static void numberedit_cb(lv_event_t* e) } // create a new class for number edit so we can set the sytles differently if needed. -lv_obj_class_t lv_numberedit_class = lv_textarea_class; +const lv_obj_class_t lv_numberedit_class = { + .constructor_cb = lv_textarea_class.constructor_cb, + .destructor_cb = lv_textarea_class.destructor_cb, + .event_cb = lv_textarea_class.event_cb, + .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, + .width_def = LV_DPI_DEF / 2, + .height_def = LV_SIZE_CONTENT, + .instance_size = sizeof(lv_textarea_t), + .base_class = &lv_obj_class +}; lv_obj_t *lv_numberedit_create(lv_obj_t *parent) { diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 246b65f28..0a5de69d0 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -50,3 +50,4 @@ class NumberEdit : public BaseNumberEdit std::function isValueAvailable; }; +extern const lv_obj_class_t lv_numberedit_class; From 603d9230c89810f844e4eab79b404ee2a8d0c05a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 23 Apr 2022 10:01:59 +0200 Subject: [PATCH 564/714] window: moved updateSize() from dialog --- radio/src/thirdparty/libopenui/src/dialog.cpp | 13 ------------- radio/src/thirdparty/libopenui/src/dialog.h | 1 - radio/src/thirdparty/libopenui/src/window.cpp | 13 +++++++++++++ radio/src/thirdparty/libopenui/src/window.h | 2 ++ 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index d87e5a6f3..cddf648f3 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -31,19 +31,6 @@ DialogWindowContent::DialogWindowContent(Dialog* parent, const rect_t& rect) : form.setFocus(SET_FOCUS_DEFAULT); } -void DialogWindowContent::updateSize() -{ - lv_obj_set_height(lvobj, LV_SIZE_CONTENT); - lv_obj_center(lvobj); - lv_obj_update_layout(lvobj); - - rect.x = lv_obj_get_x(lvobj); - rect.y = lv_obj_get_y(lvobj); - rect.w = lv_obj_get_width(lvobj); - rect.h = lv_obj_get_height(lvobj); - invalidate(); -} - void DialogWindowContent::deleteLater(bool detach, bool trash) { if (_deleted) return; diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 1519d7098..19110f25a 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -31,7 +31,6 @@ class DialogWindowContent : public ModalWindowContent public: DialogWindowContent(Dialog* parent, const rect_t& rect); - void updateSize(); void deleteLater(bool detach = true, bool trash = true) override; #if defined(DEBUG_WINDOWS) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index af1cf9d59..e85b253ac 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -673,6 +673,19 @@ void Window::moveWindowsTop(coord_t y, coord_t delta) } } +void Window::updateSize() +{ + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); + lv_obj_center(lvobj); + lv_obj_update_layout(lvobj); + + rect.x = lv_obj_get_x(lvobj); + rect.y = lv_obj_get_y(lvobj); + rect.w = lv_obj_get_width(lvobj); + rect.h = lv_obj_get_height(lvobj); + invalidate(); +} + void Window::invalidate(const rect_t & rect) { if (lvobj) lv_obj_invalidate(lvobj); diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index c0fd18435..e4304304c 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -391,6 +391,8 @@ class Window void moveWindowsTop(coord_t y, coord_t delta); + void updateSize(); + void invalidate() { invalidate({0, 0, rect.w, rect.h}); From 6605e2cb1ccc383b93ff10917ba6d40485126491 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 23 Apr 2022 10:02:24 +0200 Subject: [PATCH 565/714] static text: add getText() --- radio/src/thirdparty/libopenui/src/static.cpp | 11 +++++++---- radio/src/thirdparty/libopenui/src/static.h | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 18d84aaf3..9dc09a471 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -29,17 +29,15 @@ StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, lv_obj_set_height(lvobj, LV_SIZE_CONTENT); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); + // TODO: select proper font based on 'textFlags' // lv_obj_set_style_text_font(lvobj, LV_FONT_DEFAULT, LV_PART_MAIN); - // lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN); + lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN); if (textFlags & CENTERED) lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_CENTER, 0); else if (textFlags & RIGHT) lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_RIGHT, 0); - // if (textFlags & FONT(BOLD)) { - // } - if (windowFlags & BUTTON_BACKGROUND) { lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); setBackgroundColor(COLOR_THEME_SECONDARY2); @@ -61,6 +59,11 @@ void StaticText::setText(std::string value) } } +const std::string& StaticText::getText() const +{ + return text; +} + void StaticText::setBackgroundColor(LcdFlags color) { bgColor = color; diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 5269e93df..1659dd7d4 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -33,6 +33,8 @@ class StaticText: public Window #endif void setText(std::string value); + const std::string& getText() const; + void setBackgroundColor(LcdFlags color); protected: From 48c09fed25121bdfa3cdd25eac29dcccc933f1fa Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 23 Apr 2022 10:02:50 +0200 Subject: [PATCH 566/714] text edit: default size to LV_DPI_DEF --- radio/src/thirdparty/libopenui/src/textedit.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 704bd0fa1..1563547a9 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -44,7 +44,7 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, uint8_t length, LcdFlags windowFlags, const char *_extra_chars) : - FormField(parent, {rect.x, rect.y, rect.w, rect.h - 2}, windowFlags, 0, + FormField(parent, rect, windowFlags, 0, lv_textarea_create), value(value), length(length) @@ -59,6 +59,10 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); lv_textarea_set_cursor_pos(lvobj, 0); + + if (width() == 0) { + lv_obj_set_width(lvobj, LV_DPI_DEF); + } } From fc705d5d0b6ac12a0b96be19f07b5f239ae96afe Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 23 Apr 2022 10:03:28 +0200 Subject: [PATCH 567/714] flex layout: grid col/row span --- radio/src/thirdparty/libopenui/src/flexlayout.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h index e9cf3dc67..4d2fa2ee8 100644 --- a/radio/src/thirdparty/libopenui/src/flexlayout.h +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -63,5 +63,9 @@ class FlexGridLayout void nextColumn() { col_pos++; } void nextRow() { row_pos++; } + void resetPos() { col_pos = 0; row_pos = 0; } + + void setColSpan(uint8_t span) { col_span = span; } + void setRowSpan(uint8_t span) { row_span = span; } }; From 40268f8e520e5ccdbc0d9b6444dd3bcfb76f462d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 23 Apr 2022 10:04:20 +0200 Subject: [PATCH 568/714] button: Window parent instead of FormGroup --- radio/src/thirdparty/libopenui/src/button.cpp | 6 ++---- radio/src/thirdparty/libopenui/src/button.h | 11 ++++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index cf69bb42f..c15f44dcb 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -31,7 +31,7 @@ static void lvglEvent(lv_event_t* e) btn->onEvent(e); } -Button::Button(FormGroup* parent, const rect_t& rect, +Button::Button(Window* parent, const rect_t& rect, std::function pressHandler, WindowFlags windowFlag, LcdFlags textFlags, LvglCreate objConstruct) : @@ -101,9 +101,7 @@ void Button::checkEvents() checkHandler(); } -lv_style_t style_btn; - -TextButton::TextButton(FormGroup* parent, const rect_t& rect, std::string text, +TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, std::function pressHandler, WindowFlags windowFlags, LcdFlags textFlags) : Button(parent, rect, std::move(pressHandler), windowFlags, textFlags, diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index a74f86c8d..b06560f24 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -28,7 +28,7 @@ constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3u; class Button: public FormField { public: - Button(FormGroup* parent, const rect_t& rect, + Button(Window* parent, const rect_t& rect, std::function pressHandler = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0, LvglCreate objConstruct = nullptr); @@ -88,7 +88,7 @@ class Button: public FormField class TextButton: public Button { public: - TextButton(FormGroup* parent, const rect_t& rect, std::string text, + TextButton(Window* parent, const rect_t& rect, std::string text, std::function pressHandler = nullptr, WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE, LcdFlags textFlags = 0); @@ -123,9 +123,10 @@ class TextButton: public Button class IconButton: public Button { public: - IconButton(FormGroup * parent, const rect_t & rect, uint8_t icon, std::function pressHandler, WindowFlags flags = 0): - Button(parent, rect, std::move(pressHandler), flags), - icon(icon) + IconButton(Window* parent, const rect_t& rect, uint8_t icon, + std::function pressHandler, + WindowFlags flags = 0) : + Button(parent, rect, std::move(pressHandler), flags), icon(icon) { } From 96d802238415f4de147549d43de6ba09ed0bc9f2 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 23 Apr 2022 10:04:54 +0200 Subject: [PATCH 569/714] FormGroup::Line: split parent and form group --- radio/src/thirdparty/libopenui/src/form.cpp | 31 ++++++++++++++------- radio/src/thirdparty/libopenui/src/form.h | 6 +++- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index a2c7c2715..daa2706af 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -111,13 +111,25 @@ void FormField::setFocus(uint8_t flag, Window * from) FormGroup::Line::Line(FormGroup* parent, lv_obj_t* obj, FlexGridLayout* layout) : - Window(parent, obj), layout(layout) + Window(parent, obj), layout(layout), group(parent) +{ + construct(); +} + +FormGroup::Line::Line(Window* parent, lv_obj_t* obj, FlexGridLayout* layout, + FormGroup* group) : + Window(parent, obj), layout(layout), group(group) +{ + construct(); +} + +void FormGroup::Line::construct() { // forward scroll and focus windowFlags |= FORWARD_SCROLL | FORM_FORWARD_FOCUS; if (layout) { - layout->apply(obj); + layout->apply(lvobj); } lv_obj_set_height(lvobj, LV_SIZE_CONTENT); @@ -135,8 +147,7 @@ void FormGroup::Line::addChild(Window* window, bool front) void FormGroup::Line::addField(FormField *field, bool front) { - auto form = static_cast(parent); - form->addField(field, front); + group->addField(field, front); } void FormGroup::Line::removeField(FormField *field) @@ -257,12 +268,12 @@ void FormGroup::addField(FormField * field, bool front) } } - if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { - field->setFocus(SET_FOCUS_DEFAULT); - } - else if (focusWindow == this && (windowFlags & FORM_FORWARD_FOCUS)) { - field->setFocus(SET_FOCUS_DEFAULT); - } + // if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { + // field->setFocus(SET_FOCUS_DEFAULT); + // } + // else if (focusWindow == this && (windowFlags & FORM_FORWARD_FOCUS)) { + // field->setFocus(SET_FOCUS_DEFAULT); + // } } void FormGroup::setFocus(uint8_t flag, Window * from) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 9e5df9b26..4ef75f583 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -128,9 +128,13 @@ class FormGroup : public FormField, public FieldContainer class Line : public Window, public FieldContainer { FlexGridLayout* layout; + FormGroup* group; + void construct(); + public: - Line(FormGroup *parent, lv_obj_t *lvobj, FlexGridLayout* layout); + Line(FormGroup *parent, lv_obj_t *obj, FlexGridLayout* layout); + Line(Window *parent, lv_obj_t *obj, FlexGridLayout* layout, FormGroup* group); protected: void addChild(Window* window, bool front = false) override; From d58fbe8c86f65a2a4ce62be74df581e351973cfb Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sat, 23 Apr 2022 10:00:51 +0200 Subject: [PATCH 570/714] fix simu compilation on linux --- radio/src/thirdparty/libopenui/src/menu.h | 1 + radio/src/thirdparty/libopenui/src/numberedit.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index f0608d6f1..a70435e6e 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "modal_window.h" class Menu; diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 31bd11817..e43bdb1d1 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -56,14 +56,14 @@ static void numberedit_cb(lv_event_t* e) // create a new class for number edit so we can set the sytles differently if needed. const lv_obj_class_t lv_numberedit_class = { + .base_class = &lv_obj_class, .constructor_cb = lv_textarea_class.constructor_cb, .destructor_cb = lv_textarea_class.destructor_cb, .event_cb = lv_textarea_class.event_cb, - .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, .width_def = LV_DPI_DEF / 2, .height_def = LV_SIZE_CONTENT, - .instance_size = sizeof(lv_textarea_t), - .base_class = &lv_obj_class + .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, + .instance_size = sizeof(lv_textarea_t) }; lv_obj_t *lv_numberedit_create(lv_obj_t *parent) From c1136c720e2fc601dde184439b3779ff06c2672e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 23 Apr 2022 10:31:36 +0200 Subject: [PATCH 571/714] window: do not center by default on updateSize() --- radio/src/thirdparty/libopenui/src/dialog.cpp | 6 ++++++ radio/src/thirdparty/libopenui/src/dialog.h | 1 + radio/src/thirdparty/libopenui/src/window.cpp | 1 - radio/src/thirdparty/libopenui/src/window.h | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index cddf648f3..745c0a6eb 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -38,6 +38,12 @@ void DialogWindowContent::deleteLater(bool detach, bool trash) ModalWindowContent::deleteLater(detach, trash); } +void DialogWindowContent::updateSize() +{ + lv_obj_center(lvobj); + ModalWindowContent::updateSize(); +} + #if defined(DEBUG_WINDOWS) std::string DialogWindowContent::getName() const override { diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 19110f25a..2cbb86680 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -32,6 +32,7 @@ class DialogWindowContent : public ModalWindowContent DialogWindowContent(Dialog* parent, const rect_t& rect); void deleteLater(bool detach = true, bool trash = true) override; + void updateSize() override; #if defined(DEBUG_WINDOWS) std::string getName() const override; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index e85b253ac..b5b4ef9ad 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -676,7 +676,6 @@ void Window::moveWindowsTop(coord_t y, coord_t delta) void Window::updateSize() { lv_obj_set_height(lvobj, LV_SIZE_CONTENT); - lv_obj_center(lvobj); lv_obj_update_layout(lvobj); rect.x = lv_obj_get_x(lvobj); diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index e4304304c..f1badf260 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -391,7 +391,7 @@ class Window void moveWindowsTop(coord_t y, coord_t delta); - void updateSize(); + virtual void updateSize(); void invalidate() { From 9529151bea15482497ea31a83d761e23a803227d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 24 Apr 2022 13:17:51 +0200 Subject: [PATCH 572/714] static: select font according to textFlags --- radio/src/thirdparty/libopenui/src/static.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 9dc09a471..88b69daf3 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -29,8 +29,7 @@ StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, lv_obj_set_height(lvobj, LV_SIZE_CONTENT); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); - // TODO: select proper font based on 'textFlags' - // lv_obj_set_style_text_font(lvobj, LV_FONT_DEFAULT, LV_PART_MAIN); + lv_obj_set_style_text_font(lvobj, getFont(textFlags), LV_PART_MAIN); lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN); if (textFlags & CENTERED) From 2caea0eb53be2860246afb59aa21fa8128359a62 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 24 Apr 2022 13:19:16 +0200 Subject: [PATCH 573/714] flex layout: add fields switches automatically to next row --- radio/src/thirdparty/libopenui/src/flexlayout.h | 15 ++++++++++++--- radio/src/thirdparty/libopenui/src/form.cpp | 4 ++-- radio/src/thirdparty/libopenui/src/form.h | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h index 4d2fa2ee8..756d1d201 100644 --- a/radio/src/thirdparty/libopenui/src/flexlayout.h +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -61,11 +61,20 @@ class FlexGridLayout } } - void nextColumn() { col_pos++; } - void nextRow() { row_pos++; } - void resetPos() { col_pos = 0; row_pos = 0; } + void nextColumn() { col_pos += col_span; } + void nextRow() { row_pos += row_span; } + + void nextCell() + { + nextColumn(); + if (col_dsc[col_pos] == LV_GRID_TEMPLATE_LAST) { + nextRow(); + col_pos = 0; + } + } + void setColSpan(uint8_t span) { col_span = span; } void setRowSpan(uint8_t span) { row_span = span; } }; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index daa2706af..5bedb2009 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -141,7 +141,7 @@ void FormGroup::Line::addChild(Window* window, bool front) Window::addChild(window, front); if (layout) { layout->add(window->getLvObj()); - layout->nextColumn(); + layout->nextCell(); } } @@ -205,7 +205,7 @@ void FormGroup::setFlexLayout(lv_flex_flow_t flow, lv_coord_t padding) FormGroup::Line* FormGroup::newLine(FlexGridLayout* layout) { - layout->resetPos(); + if (layout) layout->resetPos(); auto lv_line = lv_obj_create(lvobj); return new Line(this, lv_line, layout); } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 4ef75f583..6e2952571 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -152,7 +152,7 @@ class FormGroup : public FormField, public FieldContainer void clear(); void setFlexLayout(lv_flex_flow_t flow = LV_FLEX_FLOW_COLUMN, lv_coord_t padding = 0); - Line* newLine(FlexGridLayout* layout); + Line* newLine(FlexGridLayout* layout = nullptr); void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window *from = nullptr) override; From 0cf7d7b450c27444c9ca5dfcaad37424a59be72a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 24 Apr 2022 16:39:13 +0200 Subject: [PATCH 574/714] menu: fixed lvgl include --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 139dc7e6e..05ab48b5d 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,7 +21,7 @@ #include "font.h" #include "theme.h" -#include "lvgl/src/widgets/lv_canvas.h" +#include void menuBodyEventCallback(lv_event_t *e) { From 9b66ce6b2546ad1f161f0c3c00e40cfca6e6d243 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 25 Apr 2022 11:35:19 +0200 Subject: [PATCH 575/714] focus fixes --- radio/src/thirdparty/libopenui/src/button.cpp | 8 ++++---- radio/src/thirdparty/libopenui/src/button.h | 2 +- radio/src/thirdparty/libopenui/src/form.cpp | 9 +++------ radio/src/thirdparty/libopenui/src/menu.cpp | 1 + radio/src/thirdparty/libopenui/src/modal_window.cpp | 3 +-- radio/src/thirdparty/libopenui/src/window.cpp | 1 + 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index c15f44dcb..feda35bfa 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -45,11 +45,11 @@ Button::Button(Window* parent, const rect_t& rect, void Button::onPress() { bool check = (pressHandler && pressHandler()); - windowFlags = check ? windowFlags | BUTTON_CHECKED : windowFlags & ~BUTTON_CHECKED; - if(lvobj == nullptr) - return; + windowFlags = + check ? windowFlags | BUTTON_CHECKED : windowFlags & ~BUTTON_CHECKED; + if (lvobj == nullptr) return; - if (! (windowFlags & BUTTON_CHECKED)) + if (!(windowFlags & BUTTON_CHECKED)) lv_obj_clear_state(lvobj, LV_STATE_CHECKED); else lv_obj_add_state(lvobj, LV_STATE_CHECKED); diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index b06560f24..dcd9a476b 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -81,7 +81,7 @@ class Button: public FormField std::function pressHandler; std::function checkHandler; - void onPress(); + virtual void onPress(); }; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 5bedb2009..0a8f7fa91 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -88,12 +88,9 @@ void FormField::setFocus(uint8_t flag, Window * from) { if (enabled) { Window::setFocus(flag, from); -// if(lvobj != nullptr) -// { -// lv_group_focus_freeze(inputGroup, false); -// lv_group_focus_obj(lvobj); -// lv_group_focus_freeze(inputGroup, true); -// } + if(lvobj != nullptr) { + lv_group_focus_obj(lvobj); + } } else { if (flag == SET_FOCUS_BACKWARD) { diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 05ab48b5d..a888a2679 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -134,6 +134,7 @@ void MenuBody::setIndex(int index) lv_obj_add_state(btn, LV_STATE_CHECKED); lv_obj_add_state(btn, LV_STATE_FOCUS_KEY); lv_obj_scroll_to_view(btn, LV_ANIM_OFF); + lv_group_focus_obj(btn); } } } diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 4be831de4..270522bcf 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -35,10 +35,9 @@ void ModalWindow::deleteLater(bool detach, bool trash) { if (_deleted) return; - - Layer::pop(this); Window::deleteLater(detach, trash); + Layer::pop(this); } #if defined(HARDWARE_TOUCH) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index b5b4ef9ad..eb9456b40 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -296,6 +296,7 @@ void Window::clearLvgl() } lv_obj_set_user_data(lvobj, nullptr); + lv_group_remove_obj(lvobj); lvobj = nullptr; } From 037b5707d8537dfddef2a8e3f1e43a3a116d3fa8 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 25 Apr 2022 11:46:43 +0200 Subject: [PATCH 576/714] form group: no need to reset layout anymore TabsGroup now create a new form for each tab --- radio/src/thirdparty/libopenui/src/form.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 0a8f7fa91..9896487a7 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -185,7 +185,6 @@ void FormGroup::clear() if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { previous->setNextField(this); } - lv_obj_set_layout(lvobj, 0); } void FormGroup::setFlexLayout(lv_flex_flow_t flow, lv_coord_t padding) From 12eccdf68553e3af6cc1f7c310c88c229d3f5a1a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 25 Apr 2022 12:08:09 +0200 Subject: [PATCH 577/714] cleanup --- radio/src/thirdparty/libopenui/src/form.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 9896487a7..f6910d743 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -263,13 +263,6 @@ void FormGroup::addField(FormField * field, bool front) link(previous, first); } } - - // if (!focusWindow && !(field->getWindowFlags() & FORM_FORWARD_FOCUS)) { - // field->setFocus(SET_FOCUS_DEFAULT); - // } - // else if (focusWindow == this && (windowFlags & FORM_FORWARD_FOCUS)) { - // field->setFocus(SET_FOCUS_DEFAULT); - // } } void FormGroup::setFocus(uint8_t flag, Window * from) From bd3eb7a9ba0c64f9912d55f19511a8c921517e7f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 25 Apr 2022 13:51:40 +0200 Subject: [PATCH 578/714] menu: some clean --- radio/src/thirdparty/libopenui/src/menu.cpp | 86 ++++++++++++--------- radio/src/thirdparty/libopenui/src/menu.h | 3 +- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index a888a2679..d70253feb 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -23,35 +23,35 @@ #include -void menuBodyEventCallback(lv_event_t *e) +static void menu_clicked(lv_event_t *e) { auto code = lv_event_get_code(e); if (code == LV_EVENT_CLICKED) { - lv_obj_t *target = lv_event_get_target(e); - if (target != nullptr) { - long index = (long)lv_event_get_user_data(e); - MenuBody *mb = (MenuBody *) lv_obj_get_user_data(lv_obj_get_parent(target)); - if (mb) { - Menu *menu = mb->getParentMenu(); - if (index < (int)mb->lines.size()) { - onKeyPress(); - if (menu->multiple) { - if (mb->selectedIndex == index) - mb->lines[index].onPress(); - else - mb->setIndex(index); - } - else { - mb->setIndex(index); - mb->lines[index].onPress(); - menu->deleteLater(); - } - } - } - } + + lv_obj_t* target = lv_event_get_target(e); + if (!target) return; + + lv_obj_t* parent = lv_obj_get_parent(target); + if (!parent) return; + + MenuBody* mb = (MenuBody*)lv_obj_get_user_data(parent); + if (!mb) return; + + size_t idx = (size_t)lv_event_get_user_data(e); + mb->onPress(idx); } } +static void add_list_button(lv_obj_t* list, const char* icon, const char* txt, size_t idx) +{ + lv_obj_t* btn = lv_list_add_btn(list, icon, txt); + lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); + + void* user_data = (void *)idx; + lv_obj_add_event_cb(btn, menu_clicked, LV_EVENT_CLICKED, user_data); +} + + MenuBody::MenuBody(Window * parent, const rect_t & rect): Window(parent, rect, OPAQUE, 0, lv_list_create) { @@ -62,10 +62,7 @@ void MenuBody::addLine(const std::string &text, std::function onPress, std::function isChecked) { lines.emplace_back(std::move(onPress), std::move(isChecked), nullptr); - lv_obj_t *btn = lv_list_add_btn(lvobj, nullptr, text.c_str()); - lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); - lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, - (void *)(lines.size() - 1)); + add_list_button(lvobj, nullptr, text.c_str(), lines.size() - 1); } void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, @@ -80,12 +77,8 @@ void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, void* buf = (void*)(icon_mask + 4); lv_canvas_set_buffer(canvas, buf, w, h, LV_IMG_CF_ALPHA_8BIT); - lv_obj_t *btn = lv_list_add_btn( - lvobj, (const char *)lv_canvas_get_img(canvas), text.c_str()); - - lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); - lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, - (void *)(lines.size() - 1)); + const char* icon = (const char *)lv_canvas_get_img(canvas); + add_list_button(lvobj, icon, text.c_str(), lines.size() - 1); } void MenuBody::removeLines() @@ -106,16 +99,33 @@ coord_t MenuBody::getContentHeight() return h; } +void MenuBody::onPress(size_t index) +{ + Menu *menu = getParentMenu(); + if (index < lines.size()) { + onKeyPress(); + if (menu->multiple) { + if (selectedIndex == index) + lines[index].onPress(); + else + setIndex(index); + } else { + setIndex(index); + lines[index].onPress(); + menu->deleteLater(); + } + } +} // ensure index is in range and also handle wrapping index int MenuBody::rangeCheck(int index) { - if (index < 0) - index = lines.size() - 1; - else if (index > (signed)lines.size() - 1) - index = 0; + if (index < 0) + index = lines.size() - 1; + else if (index > (signed)lines.size() - 1) + index = 0; - return index; + return index; } void MenuBody::setIndex(int index) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index a70435e6e..ea8b82132 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -62,7 +62,6 @@ class MenuBody: public Window MenuLine(MenuLine &&) = default; protected: - friend void menuBodyEventCallback(lv_event_t *e); bool isSeparator = false; std::function onPress; std::function isChecked; @@ -121,6 +120,8 @@ class MenuBody: public Window coord_t getContentHeight(); + void onPress(size_t index); + protected: friend void menuBodyEventCallback(lv_event_t *); void selectNext(MENU_DIRECTION direction); From bc7f9f6f09cc250b7ed48eee1b6bcb916a5e038f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 27 Apr 2022 06:45:20 +0200 Subject: [PATCH 579/714] fix: onTouchStart & on TouchEnd exist only if HARDWARE_TOUCH is defined --- radio/src/thirdparty/libopenui/src/window.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index eb9456b40..948768a70 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -77,7 +77,9 @@ static void window_event_cb(lv_event_t * e) window->paint(&buf); - } else if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) { + } +#if defined(HARDWARE_TOUCH) + else if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) { TRACE_WINDOWS("PRESSED: %s", window->getWindowDebugString().c_str()); lv_indev_t *click_source = (lv_indev_t *)lv_event_get_param(e); @@ -120,7 +122,9 @@ static void window_event_cb(lv_event_t * e) if (vect_act.x != 0 || vect_act.y != 0) return; window->onTouchEnd(rel_pos.x, rel_pos.y); } - } else if (code == LV_EVENT_SCROLL) { + } +#endif + else if (code == LV_EVENT_SCROLL) { lv_coord_t scroll_y = lv_obj_get_scroll_y(target); lv_coord_t scroll_x = lv_obj_get_scroll_x(target); From 6d7c8ed05920655e3a7f467ba693cb6393de2aba Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 27 Apr 2022 09:26:41 +0200 Subject: [PATCH 580/714] fix: add missing init params in lv_numberedit_class to please old GCC --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index e43bdb1d1..9439be58d 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -59,9 +59,11 @@ const lv_obj_class_t lv_numberedit_class = { .base_class = &lv_obj_class, .constructor_cb = lv_textarea_class.constructor_cb, .destructor_cb = lv_textarea_class.destructor_cb, + .user_data = nullptr, .event_cb = lv_textarea_class.event_cb, .width_def = LV_DPI_DEF / 2, .height_def = LV_SIZE_CONTENT, + .editable = LV_OBJ_CLASS_EDITABLE_TRUE, .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, .instance_size = sizeof(lv_textarea_t) }; From b44ba6c60c75f9bcf3722f5ee10e9ac26a1d0d4f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 27 Apr 2022 12:14:51 +0200 Subject: [PATCH 581/714] fix: do not access null lvobj --- radio/src/thirdparty/libopenui/src/window.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index f1badf260..d0dbf8e72 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -138,6 +138,7 @@ class Window void setTextFlags(LcdFlags flags) { textFlags = flags; + if (!lvobj) return; // lv integration for colors auto textColor = COLOR_VAL(flags); From 5052b762c9c2df5ec99f8908a65f88f678e48601 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 28 Apr 2022 06:28:34 +0200 Subject: [PATCH 582/714] form window: forward events to parent --- radio/src/thirdparty/libopenui/src/form.cpp | 18 ------------------ radio/src/thirdparty/libopenui/src/form.h | 4 ---- 2 files changed, 22 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index f6910d743..0a0c07a85 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -362,21 +362,3 @@ void FormGroup::onEvent(event_t event) } } #endif - -#if defined(HARDWARE_KEYS) -void FormWindow::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormWindow").c_str(), event); - - if (event == EVT_KEY_BREAK(KEY_EXIT) && (windowFlags & FORM_FORWARD_FOCUS) && first) { - onKeyPress(); - Window * currentFocus = getFocus(); - first->setFocus(SET_FOCUS_FIRST); - if (getFocus() != currentFocus) { - return; - } - } - - FormGroup::onEvent(event); -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 6e2952571..7d35ddc61 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -211,8 +211,4 @@ class FormWindow : public FormGroup protected: void paint(BitmapBuffer *) override {} - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif }; From bf136376b2d2f9e6725627384a17c814fcc1c930 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 28 Apr 2022 07:38:56 +0200 Subject: [PATCH 583/714] fix: numberedit_cb handles only keys --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 9439be58d..7d7e03ee2 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -90,7 +90,7 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, auto value = _getValue(); this->setValue(value); - lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_ALL, this); + lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this); } void NumberEdit::onEvent(event_t event) From f8b8232d073384eff94114da13ec6e83dab7f5b2 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 28 Apr 2022 09:48:11 +0200 Subject: [PATCH 584/714] lvgl: add support for TRUE_COLOR snapshots --- radio/src/thirdparty/libopenui/thirdparty/lvgl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/thirdparty/lvgl b/radio/src/thirdparty/libopenui/thirdparty/lvgl index 3e241a66b..f3deb17e3 160000 --- a/radio/src/thirdparty/libopenui/thirdparty/lvgl +++ b/radio/src/thirdparty/libopenui/thirdparty/lvgl @@ -1 +1 @@ -Subproject commit 3e241a66bd3d89f94315005cd6b886d07314a025 +Subproject commit f3deb17e3a4b62e2beb9c9e1faf5d864ce0ac337 From f6598a11e4c9e05a45e64d3cf9c2cd311226ed2c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 28 Apr 2022 10:17:44 +0200 Subject: [PATCH 585/714] fix: static text lvobj NULL access --- radio/src/thirdparty/libopenui/src/static.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 88b69daf3..7fd9b9813 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -54,7 +54,7 @@ void StaticText::setText(std::string value) { if (text != value) { text = std::move(value); - lv_label_set_text(lvobj, text.c_str()); + if (lvobj) lv_label_set_text(lvobj, text.c_str()); } } @@ -66,10 +66,9 @@ const std::string& StaticText::getText() const void StaticText::setBackgroundColor(LcdFlags color) { bgColor = color; - auto actualColor = COLOR_VAL(bgColor); - lv_obj_set_style_bg_color( - lvobj, - lv_color_make(GET_RED(actualColor), GET_GREEN(actualColor), - GET_BLUE(actualColor)), - LV_PART_MAIN); + + if (!lvobj) return; + + lv_color_t c = makeLvColor(bgColor); + lv_obj_set_style_bg_color(lvobj, c, LV_PART_MAIN); } From bc793651f4e4585c1469312a39a0d50adf0e7ad1 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 30 Apr 2022 08:12:46 +0200 Subject: [PATCH 586/714] window: avoid window_event_cb to be called on destroyed children This uses quite some CPU resources because of the refocus happening when FormFields are cleared. --- radio/src/thirdparty/libopenui/src/window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 948768a70..f696ed2f6 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -299,6 +299,7 @@ void Window::clearLvgl() window->clearLvgl(); } + lv_obj_remove_event_cb(lvobj, window_event_cb); lv_obj_set_user_data(lvobj, nullptr); lv_group_remove_obj(lvobj); lvobj = nullptr; From ad658a92a45ce948dd94fedc7217fdd1cb852bcd Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 1 May 2022 09:31:05 +0200 Subject: [PATCH 587/714] window: prevent focus events while mass-deleting --- radio/src/thirdparty/libopenui/src/window.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index f696ed2f6..fa6c544d8 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -29,6 +29,7 @@ std::list Window::trash; extern lv_obj_t *virtual_kb; static bool is_scrolling = false; +static bool inhibit_focus = false; static void window_event_cb(lv_event_t * e) { @@ -136,6 +137,7 @@ static void window_event_cb(lv_event_t * e) window->setScrollPositionX(scroll_x); } else if (code == LV_EVENT_FOCUSED) { + if (inhibit_focus) return; bool focused = ((Window *)target->user_data)->hasFocus(); TRACE_WINDOWS("FOCUSED[%d] %s", focused, window->getWindowDebugString().c_str()); @@ -307,10 +309,13 @@ void Window::clearLvgl() void Window::deleteChildren() { + // prevent LVGL refocus while mass-deleting + inhibit_focus = true; for (auto window: children) { window->clearLvgl(); window->deleteLater(false); } + inhibit_focus = false; children.clear(); } From fb059887e73bbd24aae8f5eb4658767e77a99f0b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 1 May 2022 11:03:09 +0200 Subject: [PATCH 588/714] BitmapBuffer: fixed RIGHT align --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 39b27015a..34cbec2e6 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1112,6 +1112,8 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, if (flags & RIGHT) { label_draw_dsc.align = LV_TEXT_ALIGN_RIGHT; + coords.x1 -= p.x; + coords.x2 -= p.x; } else if (flags & CENTERED) { label_draw_dsc.align = LV_TEXT_ALIGN_CENTER; coords.x1 -= p.x / 2; From 7dc960a51472eed3873720fc5f8d266f6be8ba92 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 1 May 2022 11:03:25 +0200 Subject: [PATCH 589/714] menu: fixed signedness --- radio/src/thirdparty/libopenui/src/menu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index d70253feb..21ec6eefa 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -91,7 +91,7 @@ coord_t MenuBody::getContentHeight() { coord_t h = 0; lv_obj_update_layout(lvobj); - for(int i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { + for(uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { auto child = lv_obj_get_child(lvobj, i); h += lv_obj_get_height(child); } @@ -105,7 +105,7 @@ void MenuBody::onPress(size_t index) if (index < lines.size()) { onKeyPress(); if (menu->multiple) { - if (selectedIndex == index) + if (selectedIndex == (int)index) lines[index].onPress(); else setIndex(index); From 70b8b3d186061111395653e986266394dcb0a291 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 1 May 2022 18:37:48 +0200 Subject: [PATCH 590/714] small optimization Gains around 10% when building tabs --- radio/src/thirdparty/libopenui/src/form.cpp | 3 +++ radio/src/thirdparty/libopenui/src/window.h | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 0a0c07a85..d430de87b 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -37,6 +37,7 @@ FormField::FormField(Window* parent, const rect_t& rect, } } + lv_obj_enable_style_refresh(false); lv_style_init(&bg_style); lv_style_set_bg_color(&bg_style, makeLvColor(COLOR_THEME_PRIMARY2)); lv_style_set_bg_opa(&bg_style, LV_OPA_COVER); @@ -49,6 +50,8 @@ FormField::FormField(Window* parent, const rect_t& rect, lv_style_init(&edit_style); lv_style_set_bg_color(&edit_style, makeLvColor(COLOR_THEME_EDIT)); lv_obj_add_style(lvobj, &edit_style, LV_PART_MAIN | LV_STATE_EDITED); + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index d0dbf8e72..1517c8bf9 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -451,8 +451,9 @@ class Window virtual void addChild(Window * window, bool front = false) { - if (lv_obj_get_parent(window->lvobj) != nullptr) { - lv_obj_set_parent(window->lvobj, this->lvobj); + auto lv_parent = lv_obj_get_parent(window->lvobj); + if (lv_parent && (lv_parent != lvobj)) { + lv_obj_set_parent(window->lvobj, lvobj); // not used anywhere! if (front) lv_obj_move_to_index(window->lvobj, 0); From 4ef479bf3f5f69980b23a96bd8dc979947ef1f7e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 1 May 2022 19:27:43 +0200 Subject: [PATCH 591/714] fix: style memory leak --- radio/src/thirdparty/libopenui/src/form.cpp | 56 ++++++++++++++------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index d430de87b..4a83e5cb6 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -21,10 +21,43 @@ #include "bitmapbuffer.h" #include "libopenui_config.h" +// FormField styles static lv_style_t bg_style; static lv_style_t focus_style; static lv_style_t edit_style; +// FormGroup styles +static lv_style_t border_style; +static lv_style_t focus_border_style; + +static void init_styles() +{ + static bool inited = false; + if (inited) return; + + inited = true; + + // FormField styles + lv_style_init(&bg_style); + lv_style_set_bg_color(&bg_style, makeLvColor(COLOR_THEME_PRIMARY2)); + lv_style_set_bg_opa(&bg_style, LV_OPA_COVER); + + lv_style_init(&focus_style); + lv_style_set_bg_color(&focus_style, makeLvColor(COLOR_THEME_FOCUS)); + + lv_style_init(&edit_style); + lv_style_set_bg_color(&edit_style, makeLvColor(COLOR_THEME_EDIT)); + + // FormGroup styles + lv_style_init(&border_style); + lv_style_set_border_width(&border_style, 2); + lv_style_set_border_color(&border_style, makeLvColor(COLOR_THEME_FOCUS)); + + lv_style_init(&focus_border_style); + lv_style_set_border_width(&focus_border_style, 1); + lv_style_set_border_color(&focus_border_style, makeLvColor(COLOR_THEME_SECONDARY2)); +} + FormField::FormField(Window* parent, const rect_t& rect, WindowFlags windowFlags, LcdFlags textFlags, LvglCreate objConstruct) : @@ -38,18 +71,12 @@ FormField::FormField(Window* parent, const rect_t& rect, } lv_obj_enable_style_refresh(false); - lv_style_init(&bg_style); - lv_style_set_bg_color(&bg_style, makeLvColor(COLOR_THEME_PRIMARY2)); - lv_style_set_bg_opa(&bg_style, LV_OPA_COVER); + init_styles(); + lv_obj_add_style(lvobj, &bg_style, LV_PART_MAIN); - - lv_style_init(&focus_style); - lv_style_set_bg_color(&focus_style, makeLvColor(COLOR_THEME_FOCUS)); lv_obj_add_style(lvobj, &focus_style, LV_PART_MAIN | LV_STATE_FOCUSED); - - lv_style_init(&edit_style); - lv_style_set_bg_color(&edit_style, makeLvColor(COLOR_THEME_EDIT)); lv_obj_add_style(lvobj, &edit_style, LV_PART_MAIN | LV_STATE_EDITED); + lv_obj_enable_style_refresh(true); lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } @@ -156,9 +183,6 @@ void FormGroup::Line::removeField(FormField *field) form->removeField(field); } -static lv_style_t border_style; -static lv_style_t focus_border_style; - FormGroup::FormGroup(Window* parent, const rect_t& rect, WindowFlags windowflags, LvglCreate objConstruct) : FormField(parent, rect, windowflags, 0, objConstruct) @@ -166,15 +190,11 @@ FormGroup::FormGroup(Window* parent, const rect_t& rect, lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); if (!(windowFlags & (FORM_NO_BORDER | FORM_FORWARD_FOCUS))) { - lv_style_init(&border_style); - lv_style_set_border_width(&border_style, 2); - lv_style_set_border_color(&border_style, makeLvColor(COLOR_THEME_FOCUS)); + + init_styles(); lv_obj_add_style(lvobj, &border_style, LV_PART_MAIN | LV_STATE_FOCUSED); if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - lv_style_init(&focus_border_style); - lv_style_set_border_width(&focus_border_style, 1); - lv_style_set_border_color(&focus_border_style, makeLvColor(COLOR_THEME_SECONDARY2)); lv_obj_add_style(lvobj, &focus_border_style, LV_PART_MAIN); } } From bd7e893a0b70d85df5a3d7c4db8f67593ce85a55 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 3 May 2022 11:50:46 +0200 Subject: [PATCH 592/714] window: scrolling now only with LVGL --- .../src/thirdparty/libopenui/src/carousel.cpp | 3 - radio/src/thirdparty/libopenui/src/form.cpp | 47 +--- radio/src/thirdparty/libopenui/src/form.h | 29 +- radio/src/thirdparty/libopenui/src/menu.cpp | 1 - .../thirdparty/libopenui/src/menutoolbar.h | 1 - radio/src/thirdparty/libopenui/src/table.h | 1 - radio/src/thirdparty/libopenui/src/window.cpp | 216 ++++++-------- radio/src/thirdparty/libopenui/src/window.h | 266 +++--------------- 8 files changed, 140 insertions(+), 424 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/carousel.cpp b/radio/src/thirdparty/libopenui/src/carousel.cpp index 1a6c3d371..2a3731bcb 100644 --- a/radio/src/thirdparty/libopenui/src/carousel.cpp +++ b/radio/src/thirdparty/libopenui/src/carousel.cpp @@ -42,13 +42,10 @@ void CarouselWindow::update() window->attach(this); window->setLeft(lastPosition); window->setTop((height() - window->height()) / 2); - if (index == first) - setScrollPositionX(lastPosition); lastPosition += window->width() + spacing; index += 1; } - setInnerWidth(lastPosition); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 4a83e5cb6..fa7a0ab07 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -66,7 +66,7 @@ FormField::FormField(Window* parent, const rect_t& rect, if (!(windowFlags & NO_FOCUS)) { auto cont = dynamic_cast(parent); if (cont) { - cont->addField(this, windowFlags & PUSH_FRONT); + cont->addField(this); } } @@ -163,18 +163,18 @@ void FormGroup::Line::construct() lv_obj_set_width(lvobj, lv_pct(100)); } -void FormGroup::Line::addChild(Window* window, bool front) +void FormGroup::Line::addChild(Window* window) { - Window::addChild(window, front); + Window::addChild(window); if (layout) { layout->add(window->getLvObj()); layout->nextCell(); } } -void FormGroup::Line::addField(FormField *field, bool front) +void FormGroup::Line::addField(FormField *field) { - group->addField(field, front); + group->addField(field); } void FormGroup::Line::removeField(FormField *field) @@ -250,25 +250,16 @@ void FormGroup::removeField(FormField * field) } } -void FormGroup::addField(FormField * field, bool front) +void FormGroup::addField(FormField * field) { if (field->getWindowFlags() & FORM_DETACHED) return; - if (front) { - if (first) - link(field, first); + if (last) + link(last, field); + last = field; + if (!first) first = field; - if (!last) - last = field; - } - else { - if (last) - link(last, field); - last = field; - if (!first) - first = field; - } if (WRAP_FORM_FIELDS_WITHIN_PAGE) { if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { @@ -366,22 +357,6 @@ void FormGroup::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormGroup").c_str(), event); - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - onKeyPress(); - setFocusOnFirstVisibleField(SET_FOCUS_FIRST); - } else if (event == EVT_KEY_FIRST(KEY_EXIT) && !hasFocus() && - !(windowFlags & FORM_FORWARD_FOCUS)) { - killEvents(event); - onKeyPress(); - setFocus(SET_FOCUS_DEFAULT); // opentx - model - timers settings - } else if (event == EVT_ROTARY_RIGHT && !next) { - onKeyPress(); - setFocusOnFirstVisibleField(SET_FOCUS_FIRST); - } else if (event == EVT_ROTARY_LEFT && !previous) { - onKeyPress(); - setFocusOnLastVisibleField(SET_FOCUS_BACKWARD); - } else { - FormField::onEvent(event); - } + FormField::onEvent(event); } #endif diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 7d35ddc61..8ecc1e617 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -118,7 +118,7 @@ class FormField : public Window class FieldContainer { public: - virtual void addField(FormField *field, bool front = false) = 0; + virtual void addField(FormField *field) = 0; virtual void removeField(FormField *field) = 0; }; @@ -137,8 +137,8 @@ class FormGroup : public FormField, public FieldContainer Line(Window *parent, lv_obj_t *obj, FlexGridLayout* layout, FormGroup* group); protected: - void addChild(Window* window, bool front = false) override; - void addField(FormField *field, bool front = false) override; + void addChild(Window* window) override; + void addField(FormField *field) override; void removeField(FormField *field) override; }; @@ -157,7 +157,7 @@ class FormGroup : public FormField, public FieldContainer void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window *from = nullptr) override; - void addField(FormField *field, bool front = false) override; + void addField(FormField *field) override; void removeField(FormField *field) override; void setFirstField(FormField *field) { first = field; } @@ -165,27 +165,6 @@ class FormGroup : public FormField, public FieldContainer FormField *getFirstField() const { return first; } FormField *getLastField() const { return last; } - void setFocusOnFirstVisibleField(uint8_t flag = SET_FOCUS_DEFAULT) const - { - auto field = getFirstField(); - while (field && !field->isInsideParentScrollingArea()) { - field = field->getNextField(); - } - if (field) { - field->setFocus(flag); - } - } - - void setFocusOnLastVisibleField(uint8_t flag = SET_FOCUS_DEFAULT) const - { - auto field = getLastField(); - while (field && !field->isInsideParentScrollingArea()) { - field = field->getPreviousField(); - } - if (field) { - field->setFocus(flag); - } - } protected: FormField *first = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 21ec6eefa..f444a40db 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -267,7 +267,6 @@ void Menu::updatePosition() content->body.setHeight(bodyHeight); } - content->body.setInnerHeight(height); } void Menu::setTitle(std::string text) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index dd858af3a..b0d3d39c5 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -158,7 +158,6 @@ class MenuToolbar: public FormGroup { y += MENUS_TOOLBAR_BUTTON_WIDTH; - setInnerHeight(y); } }; diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index ecbcaca7e..639430cca 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -171,7 +171,6 @@ class Table: public FormField void addLine(Line * line) { lines.push_back(line); - setInnerHeight((int)lines.size() * TABLE_LINE_HEIGHT - 2); if (hasFocus() && selection < 0) { select(0, true); } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index fa6c544d8..7f3f6d065 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -22,8 +22,6 @@ #include "mainwindow.h" Window * Window::focusWindow = nullptr; -Window * Window::slidingWindow = nullptr; -Window * Window::capturedWindow = nullptr; std::list Window::trash; extern lv_obj_t *virtual_kb; @@ -107,8 +105,6 @@ static void window_event_cb(lv_event_t * e) rel_pos.x = point_act.x - obj_coords.x1; rel_pos.y = point_act.y - obj_coords.y1; - rel_pos.x += window->getScrollPositionX(); - rel_pos.y += window->getScrollPositionY(); if (code == LV_EVENT_PRESSED) { TRACE("PRESSED[%d|%d]", rel_pos.x, rel_pos.y); @@ -127,14 +123,14 @@ static void window_event_cb(lv_event_t * e) #endif else if (code == LV_EVENT_SCROLL) { +#if defined(DEBUG_WINDOWS) lv_coord_t scroll_y = lv_obj_get_scroll_y(target); lv_coord_t scroll_x = lv_obj_get_scroll_x(target); TRACE_WINDOWS("SCROLL[x=%d;y=%d] %s", scroll_x, scroll_y, window->getWindowDebugString().c_str()); +#endif - window->setScrollPositionY(scroll_y); - window->setScrollPositionX(scroll_x); } else if (code == LV_EVENT_FOCUSED) { if (inhibit_focus) return; @@ -147,10 +143,8 @@ static void window_event_cb(lv_event_t * e) Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, LcdFlags textFlags, LvglCreate objConstruct) : - parent(parent), rect(rect), - innerWidth(rect.w), - innerHeight(rect.h), + parent(parent), windowFlags(windowFlags), textFlags(textFlags) { @@ -174,7 +168,7 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); if (parent) { - parent->addChild(this, windowFlags & PUSH_FRONT); + parent->addChild(this); if (!(windowFlags & TRANSPARENT)) { invalidate(); } @@ -187,9 +181,9 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, } Window::Window(Window *parent, lv_obj_t* lvobj) - : lvobj(lvobj), + : rect(nullRect), parent(parent), - rect(nullRect) + lvobj(lvobj) { lv_obj_set_user_data(lvobj, this); @@ -202,7 +196,7 @@ Window::Window(Window *parent, lv_obj_t* lvobj) lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); if (parent) { - parent->addChild(this, windowFlags & PUSH_FRONT); + parent->addChild(this); if (!(windowFlags & TRANSPARENT)) { invalidate(); } @@ -230,6 +224,65 @@ Window::~Window() } } +#if defined(DEBUG_WINDOWS) +std::string Window::getName() const { return "Window"; } + +std::string Window::getRectString() const +{ + char result[32]; + sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); + return result; +} + +std::string Window::getIndentString() const +{ + std::string result; + auto tmp = parent; + while (tmp) { + result += " "; + tmp = tmp->getParent(); + } + return result; +} + +std::string Window::getWindowDebugString(const char *name = nullptr) const +{ + return getName() + (name ? std::string(" [") + name + "] " : " ") + + getRectString(); +} +#endif + +Window *Window::getFullScreenWindow() +{ + if (width() == LCD_W && height() == LCD_H) return this; + if (parent) return parent->getFullScreenWindow(); + return nullptr; +} + +void Window::setWindowFlags(WindowFlags flags) +{ + // TODO: check if LVGL flags need updating... + windowFlags = flags; +} + +void Window::setTextFlags(LcdFlags flags) +{ + textFlags = flags; + if (!lvobj) return; + + // lv integration for colors + auto textColor = COLOR_VAL(flags); + auto r = GET_RED(textColor), g = GET_GREEN(textColor), + b = GET_BLUE(textColor); + lv_obj_set_style_text_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); + + // rco: shouldn't this be done via 'setTextFlags()' on the children? + for (uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { + auto child = lv_obj_get_child(lvobj, i); + lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN); + } +} + void Window::attach(Window *newParent) { if (parent) detach(); @@ -279,10 +332,6 @@ void Window::deleteLater(bool detach, bool trash) void Window::clear() { - scrollPositionX = 0; - scrollPositionY = 0; - innerWidth = rect.w; - innerHeight = rect.h; deleteChildren(); if(lvobj != nullptr) { lv_obj_clean(lvobj); @@ -370,71 +419,15 @@ void Window::setFocus(uint8_t flag, Window * from) } } -void Window::setScrollPositionX(coord_t value) -{ - auto newScrollPosition = max(0, min(innerWidth - width(), value)); - if (newScrollPosition != scrollPositionX) { - scrollPositionX = newScrollPosition; - invalidate(); - } -} - -void Window::setScrollPositionY(coord_t value) -{ - auto newScrollPosition = min(innerHeight - height(), value); - - if (newScrollPosition < 0 && innerHeight != INFINITE_HEIGHT) { - newScrollPosition = 0; - } - - if (newScrollPosition != scrollPositionY) { - scrollPositionY = newScrollPosition; - invalidate(); - } -} - void Window::scrollTo(Window * child) { - coord_t offsetX = 0; - coord_t offsetY = 0; - Window * parentWindow = child->getParent(); - while (parentWindow && parentWindow != this) { - offsetX += parentWindow->left(); - offsetY += parentWindow->top(); - parentWindow = parentWindow->getParent(); - } - const rect_t scrollRect = { - offsetX + child->left(), - offsetY + child->top(), - min(child->width(), width()), - min(child->height(), height()) - }; - scrollTo(scrollRect); } void Window::scrollTo(const rect_t & rect) { - if (rect.top() < scrollPositionY) { - auto y = - pageHeight ? rect.top() - (rect.top() % pageHeight) : rect.top(); - lv_obj_scroll_to_y(lvobj, y, LV_ANIM_OFF); - } else if (rect.bottom() > scrollPositionY + height()) { - auto y = pageHeight ? rect.top() - (rect.top() % pageHeight) - : rect.bottom() - height(); - lv_obj_scroll_to_y(lvobj, y, LV_ANIM_OFF); - } - - if (rect.left() < scrollPositionX) { - auto x = pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.left(); - lv_obj_scroll_to_x(lvobj, x, LV_ANIM_OFF); - } - else if (rect.right() > scrollPositionX + width()) { - auto x = pageWidth ? rect.left() - (rect.left() % pageWidth) : rect.right() - width(); - lv_obj_scroll_to_x(lvobj, x, LV_ANIM_OFF); - } } bool Window::hasOpaqueRect(const rect_t & testRect) const @@ -495,9 +488,6 @@ void Window::fullPaint(BitmapBuffer * dc) TRACE_WINDOWS_INDENT("%s (skipped)", getWindowDebugString().c_str()); } - if (!(windowFlags & NO_SCROLLBAR)) { - drawVerticalScrollbar(dc); - } if (!(windowFlags & PAINT_CHILDREN_FIRST)) { paintChildren(dc, firstChild); @@ -523,17 +513,6 @@ bool Window::isChildVisible(const Window * window) const return false; } -void Window::setInsideParentScrollingArea() -{ - Window * parent = getParent(); - while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { - parent = parent->parent; - } - if (parent) { - parent->scrollTo(this); - invalidate(); - } -} void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) { @@ -558,11 +537,11 @@ void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) if (child_ymax <= ymin) continue; - dc->setOffset(x + child->rect.x - child->scrollPositionX, y + child->rect.y - child->scrollPositionY); - dc->setClippingRect(max(xmin, x + child->rect.left()), - min(xmax, x + child->rect.right()), - max(ymin, y + child->rect.top()), - min(ymax, y + child->rect.bottom())); + // dc->setOffset(x + child->rect.x - child->scrollPositionX, + // y + child->rect.y - child->scrollPositionY); + dc->setClippingRect( + max(xmin, x + child->rect.left()), min(xmax, x + child->rect.right()), + max(ymin, y + child->rect.top()), min(ymax, y + child->rect.bottom())); child->fullPaint(dc); } } @@ -636,8 +615,6 @@ bool Window::onTouchEnd(coord_t x, coord_t y) bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) { - startX += getScrollPositionX(); - startY += getScrollPositionY(); for (auto it = children.rbegin(); it != children.rend(); ++it) { auto child = *it; @@ -654,19 +631,13 @@ bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, void Window::adjustInnerHeight() { - coord_t bottomMax = 0; - for (auto child: children) { - bottomMax = max(bottomMax, child->rect.y + child->rect.h); - } - setInnerHeight(bottomMax); } coord_t Window::adjustHeight() { coord_t old = rect.h; adjustInnerHeight(); - rect.h = innerHeight; - lv_obj_set_style_height(getLvObj(), rect.h, LV_PART_MAIN); + if (lvobj) lv_obj_set_style_height(lvobj, rect.h, LV_PART_MAIN); return rect.h - old; } @@ -696,33 +667,24 @@ void Window::updateSize() invalidate(); } +void Window::addChild(Window* window) +{ + auto lv_parent = lv_obj_get_parent(window->lvobj); + if (lv_parent && (lv_parent != lvobj)) { + lv_obj_set_parent(window->lvobj, lvobj); + } + + children.push_back(window); +} + +void Window::removeChild(Window* window) +{ + children.remove(window); + if (window->lvobj != nullptr) lv_obj_set_parent(window->lvobj, nullptr); + invalidate(); +} + void Window::invalidate(const rect_t & rect) { if (lvobj) lv_obj_invalidate(lvobj); } - -void Window::drawVerticalScrollbar(BitmapBuffer * dc) -{ - if (innerHeight > rect.h) { - coord_t yofs = divRoundClosest(rect.h * scrollPositionY, innerHeight); - coord_t yhgt = divRoundClosest(rect.h * rect.h, innerHeight); - if (yhgt < 15) - yhgt = 15; - if (yhgt + yofs > rect.h) - yhgt = rect.h - yofs; - dc->drawSolidFilledRect(rect.w - SCROLLBAR_WIDTH, scrollPositionY + yofs, SCROLLBAR_WIDTH, yhgt, COLOR_THEME_PRIMARY3); - } -} - -void Window::drawHorizontalScrollbar(BitmapBuffer * dc) -{ - if (innerWidth > rect.w) { - coord_t xofs = divRoundClosest(rect.w * scrollPositionX, innerWidth); - coord_t xwdth = divRoundClosest(rect.w * rect.w, innerWidth); - if (xwdth < 15) - xwdth = 15; - if (xwdth + xofs > rect.w) - xwdth = rect.w - xofs; - dc->drawSolidFilledRect(scrollPositionX + xofs, rect.h - SCROLLBAR_WIDTH, xwdth, SCROLLBAR_WIDTH, COLOR_THEME_PRIMARY3); - } -} diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 1517c8bf9..bbf2f5a1c 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -74,105 +74,40 @@ class Window virtual ~Window(); #if defined(DEBUG_WINDOWS) - virtual std::string getName() const - { - return "Window"; - } - - std::string getRectString() const - { - char result[32]; - sprintf(result, "[%d, %d, %d, %d]", left(), top(), width(), height()); - return result; - } - - std::string getIndentString() const - { - std::string result; - auto tmp = parent; - while (tmp) { - result += " "; - tmp = tmp->getParent(); - } - return result; - } - - std::string getWindowDebugString(const char * name = nullptr) const - { - return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); - } + virtual std::string getName() const; + std::string getRectString() const; + std::string getIndentString() const; + std::string getWindowDebugString(const char * name = nullptr) const; #endif - Window * getParent() const - { - return parent; - } + Window *getParent() const { return parent; } - bool isChild(Window * window) const + bool isChild(Window *window) const { return window == this || (parent && parent->isChild(window)); } - Window *getFullScreenWindow() - { - if (width() == LCD_W && height() == LCD_H) return this; - if (parent) return parent->getFullScreenWindow(); - return nullptr; - } + Window *getFullScreenWindow(); - WindowFlags getWindowFlags() const - { - return windowFlags; - } + WindowFlags getWindowFlags() const { return windowFlags; } + void setWindowFlags(WindowFlags flags); - void setWindowFlags(WindowFlags flags) - { - windowFlags = flags; - } + LcdFlags getTextFlags() const { return textFlags; } + void setTextFlags(LcdFlags flags); - LcdFlags getTextFlags() const - { - return textFlags; - } + typedef std::function CloseHandler; + void setCloseHandler(CloseHandler h) { closeHandler = std::move(h); } - void setTextFlags(LcdFlags flags) - { - textFlags = flags; - if (!lvobj) return; + typedef std::function FocusHandler; + void setFocusHandler(FocusHandler h) { focusHandler = std::move(h); } - // lv integration for colors - auto textColor = COLOR_VAL(flags); - auto r = GET_RED(textColor), g = GET_GREEN(textColor), b = GET_BLUE(textColor); - lv_obj_set_style_text_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); - - // rco: shouldn't this be done via 'setTextFlags()' on the children? - for (uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { - auto child = lv_obj_get_child(lvobj, i); - lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN); - } - } - - void setCloseHandler(std::function handler) - { - closeHandler = std::move(handler); - } - - void setFocusHandler(std::function handler) - { - focusHandler = std::move(handler); - } - - const std::list getChildren() - { - return children; - } - - virtual void deleteLater(bool detach = true, bool trash = true); + const std::list getChildren() { return children; } void clear(); + virtual void deleteLater(bool detach = true, bool trash = true); + void clearLvgl(); - void deleteChildren(); bool hasFocus() const { @@ -218,14 +153,7 @@ class Window void setHeight(coord_t value) { rect.h = value; - if (lvobj != nullptr) - lv_obj_set_height(lvobj, rect.h); - - if (windowFlags & FORWARD_SCROLL) - innerHeight = value; - else if (lvobj != nullptr && innerHeight <= value) { - lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); - } + if (lvobj != nullptr) lv_obj_set_height(lvobj, rect.h); invalidate(); } @@ -278,94 +206,7 @@ class Window return rect; } - coord_t getInnerWidth() const - { - return innerWidth; - } - - void setInnerWidth(coord_t w) - { - innerWidth = w; - if (width() >= w) { - scrollPositionX = 0; - } - } - - void setPageWidth(coord_t w) - { - pageWidth = w; - } - - void setPageHeight(coord_t h) - { - pageHeight = h; - } - - uint8_t getPageCount() const - { - if (pageWidth) - return innerWidth / pageWidth; - else if (pageHeight) - return innerHeight / pageHeight; - else - return 1; - } - - uint8_t getPageIndex() const - { - if (pageWidth) - return (getScrollPositionX() + (pageWidth / 2)) / pageWidth; - else if (pageHeight) - return (getScrollPositionY() + (pageHeight / 2)) / pageHeight; - else - return 0; - } - - coord_t getInnerHeight() const - { - return innerHeight; - } - - void setInnerHeight(coord_t h) - { - innerHeight = h; - if (windowFlags & FORWARD_SCROLL) { - rect.h = innerHeight; - parent->adjustInnerHeight(); - } - else if (height() >= h) { - lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); - } - else { - coord_t maxScrollPosition = h - height(); - if (scrollPositionY > maxScrollPosition) { - lv_obj_scroll_to_y(lvobj, maxScrollPosition, LV_ANIM_OFF); - } - } - invalidate(); - } - - coord_t getScrollPositionX() const - { - return scrollPositionX; - } - - void captureWindow(Window *window) - { - capturedWindow = window; - } - - coord_t getScrollPositionY() const - { - return scrollPositionY; - } - - virtual void setScrollPositionX(coord_t value); - - virtual void setScrollPositionY(coord_t value); - bool isChildVisible(const Window * window) const; - bool isChildFullSize(const Window * window) const; bool isVisible() const @@ -373,17 +214,6 @@ class Window return parent && parent->isChildVisible(this); } - bool isInsideParentScrollingArea() const - { - return parent && right() >= parent->getScrollPositionX() && left() <= parent->getScrollPositionX() + parent->width(); - } - - void setInsideParentScrollingArea(); - - void drawVerticalScrollbar(BitmapBuffer * dc); - - void drawHorizontalScrollbar(BitmapBuffer * dc); - virtual void onEvent(event_t event); void adjustInnerHeight(); @@ -427,57 +257,33 @@ class Window inline lv_obj_t *getLvObj() { return lvobj; } protected: - lv_obj_t *lvobj = nullptr; - Window * parent; - std::list children; - rect_t rect; - coord_t innerWidth = 0; - coord_t innerHeight = 0; - coord_t pageWidth = 0; - coord_t pageHeight = 0; - coord_t scrollPositionX = 0; - coord_t scrollPositionY = 0; - WindowFlags windowFlags = 0; - LcdFlags textFlags = 0; - bool _deleted = false; + static Window* focusWindow; + static std::list trash; - static Window * focusWindow; - static Window * slidingWindow; - static Window * capturedWindow; - static std::list trash; + rect_t rect; + + Window* parent = nullptr; + lv_obj_t* lvobj = nullptr; + + std::list children; + + WindowFlags windowFlags = 0; + LcdFlags textFlags = 0; + + bool _deleted = false; std::function closeHandler; std::function focusHandler; - virtual void addChild(Window * window, bool front = false) - { - auto lv_parent = lv_obj_get_parent(window->lvobj); - if (lv_parent && (lv_parent != lvobj)) { - lv_obj_set_parent(window->lvobj, lvobj); - // not used anywhere! - if (front) - lv_obj_move_to_index(window->lvobj, 0); - } + void deleteChildren(); - if (front) - children.push_front(window); - else - children.push_back(window); - } - - void removeChild(Window * window) - { - children.remove(window); - if (window->lvobj != nullptr) - lv_obj_set_parent(window->lvobj, nullptr); - invalidate(); - } + virtual void addChild(Window * window); + void removeChild(Window * window); virtual void invalidate(const rect_t & rect); - void paintChildren(BitmapBuffer * dc, std::list::iterator it); - void fullPaint(BitmapBuffer * dc); + void paintChildren(BitmapBuffer * dc, std::list::iterator it); virtual void onFocusLost() { From 07e4f494b139988d4e455c0b301e37e99a409300 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 7 May 2022 12:18:20 +0200 Subject: [PATCH 593/714] menu: fix position / size --- radio/src/thirdparty/libopenui/src/menu.cpp | 8 ++++++++ radio/src/thirdparty/libopenui/src/menu.h | 8 +------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index f444a40db..e44f5eb01 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -253,6 +253,14 @@ Menu::Menu(Window * parent, bool multiple): { } +void Menu::setToolbar(Window * window) +{ + toolbar = window; + toolbar->setLeft(content->left() - toolbar->width()); + toolbar->setTop(content->top()); + toolbar->setHeight(content->height()); +} + void Menu::updatePosition() { coord_t height = content->body.getContentHeight(); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index ea8b82132..9f3e8b2ca 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -186,13 +186,7 @@ class Menu: public ModalWindow content->body.setFocus(flag); } - void setToolbar(Window * window) - { - toolbar = window; - content->setLeft(toolbar->right()); - content->setTop(toolbar->top()); - content->setHeight(toolbar->height()); - } + void setToolbar(Window * window); void setTitle(std::string text); From 0d69b7a39135798fedbe1f9b2c52a6c270ad1cb0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 7 May 2022 12:19:07 +0200 Subject: [PATCH 594/714] keyboard: simplify showing/scrolling --- radio/src/thirdparty/libopenui/src/form.cpp | 3 +- .../libopenui/src/keyboard_base.cpp | 116 ++++-------------- .../thirdparty/libopenui/src/keyboard_base.h | 5 +- 3 files changed, 26 insertions(+), 98 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index fa7a0ab07..e652d4c59 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -153,7 +153,8 @@ FormGroup::Line::Line(Window* parent, lv_obj_t* obj, FlexGridLayout* layout, void FormGroup::Line::construct() { // forward scroll and focus - windowFlags |= FORWARD_SCROLL | FORM_FORWARD_FOCUS; + windowFlags |= FORWARD_SCROLL | NO_FOCUS | FORM_FORWARD_FOCUS; + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); if (layout) { layout->apply(lvobj); diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 7cd47f006..979ebd9ba 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -31,7 +31,6 @@ static void keyboard_event_cb(lv_event_t * e) } } - Keyboard::Keyboard(coord_t height) : FormWindow(MainWindow::instance(), {0, LCD_H - height, LCD_W, height}, OPAQUE) { @@ -55,14 +54,13 @@ void Keyboard::clearField() } detach(); - if (fields) { - fields->setHeight(oldHeight); - fields = nullptr; - } + if (lvobj != nullptr) lv_obj_set_parent(lvobj, nullptr); + if (fieldContainer) { - fieldContainer->setHeight(LCD_H - 0 - fieldContainer->top()); + lv_obj_scroll_to_y(fieldContainer->getLvObj(), scroll_pos, LV_ANIM_OFF); fieldContainer = nullptr; } + if (field) { field->setEditMode(false); field->changeEnd(); @@ -79,24 +77,6 @@ void Keyboard::hide() } } - -coord_t calcScrollOffsetForField(FormField *newField, Window *topWindow) -{ - // now we need to calculate the offset of the field in the fields scroll container - // start with the current fields top and walk the hierarchy to calculate positon - coord_t offsetY = newField->top(); - - Window* parentWindow = newField->getParent(); - while (parentWindow && parentWindow != topWindow) { - offsetY += parentWindow->top(); - parentWindow = parentWindow->getParent(); - } - - // try and place it in the middle of the screen. The containing window MUST have - // already been resized - return offsetY - topWindow->height() * 2 / 7; -} - bool Keyboard::attachKeyboard() { if (activeKeyboard) { @@ -108,84 +88,34 @@ bool Keyboard::attachKeyboard() return true; } - -// this routine finds the window that is a FormWindow. This is the window -// that contains all of the editable fields. This is the window that needs -// to be scrolled into view. -FormWindow *Keyboard::findFormWindow(Window *parent) -{ - if (parent) { - auto children = parent->getChildren(); - auto childIterator = children.begin(); - while (childIterator != children.end()) - { -#if defined(DEBUG_WINDOWS) - std::string windowName; - Window *window = dynamic_cast(*childIterator); - if (window) - windowName = window->getName(); -#endif - - FormWindow *formWindow = dynamic_cast(*childIterator); - if (formWindow) - return formWindow; - - childIterator++; - } - } - - return nullptr; -} - void Keyboard::setField(FormField* newField) { - TRACE("SET FIELD"); - if (!attachKeyboard()) return; - fieldContainer = getFieldContainer(newField); - if (fieldContainer) { -#if defined(DEBUG_WINDOWS) - auto windowName = fieldContainer->getName(); -#endif - coord_t newHeight = LCD_H - height(); - fieldContainer->setHeight(newHeight); + lv_obj_t* obj = newField->getLvObj(); + if (obj) { - fields = findFormWindow(fieldContainer); + fieldContainer = newField->getFullScreenWindow(); + if (fieldContainer) { - if (fields) { -#if defined(DEBUG_WINDOWS) - windowName = fields->getName(); -#endif - // scroll the header of the window out of view to get more space to - // see the field being edited - - // TODO: this no longer works. we need to figure out something else - //lv_obj_scroll_to_y(fieldContainer->getLvObj(), fields->top(), LV_ANIM_OFF); + attach(fieldContainer); - oldHeight = fields->height(); - fields->setHeight(newHeight); + lv_area_t coords; + lv_obj_get_coords(obj, &coords); - coord_t offsetY = calcScrollOffsetForField(newField, fields); - lv_obj_scroll_to_y(fields->getLvObj(), offsetY, LV_ANIM_OFF); - } else { - // TODO: what happens if field container is null. - // we will need to interrogate the fieldContainer for its - // FormGroup. That is really how it should work anyway. + // place keyboard bellow the field with some margin + setTop(max(coords.y2 + 21, LCD_H - height())); + + // save scroll position + scroll_pos = lv_obj_get_scroll_y(fieldContainer->getLvObj()); + lv_obj_scroll_to_view(lvobj, LV_ANIM_OFF); + + invalidate(); + newField->setEditMode(true); + lv_keyboard_set_textarea(keyboard, newField->getLvObj()); + + field = newField; } - - invalidate(); - newField->setEditMode(true); - lv_keyboard_set_textarea(keyboard, newField->getLvObj()); - - field = newField; - } else { - clearField(); } } - -Window* Keyboard::getFieldContainer(FormField* field) -{ - return field->getFullScreenWindow(); -} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 9f41d0bb7..4ced4488c 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -34,12 +34,9 @@ class Keyboard: public FormWindow FormField *field = nullptr; Window *fieldContainer = nullptr; - FormWindow *fields = nullptr; - coord_t oldHeight = 0; + lv_coord_t scroll_pos = 0; void setField(FormField *newField); - Window *getFieldContainer(FormField * field); bool attachKeyboard(); - FormWindow *findFormWindow(Window *parent); }; From bcc30638a9823791281c72fd85b7dfab75b4b4ca Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 7 May 2022 12:46:15 +0200 Subject: [PATCH 595/714] window: fix bringToTop() --- radio/src/thirdparty/libopenui/src/window.cpp | 6 ++++++ radio/src/thirdparty/libopenui/src/window.h | 5 +---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7f3f6d065..39e9972e9 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -567,6 +567,12 @@ coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) } #endif +void Window::bringToTop() +{ + attach(parent); // does a detach + attach + if (lvobj) lv_obj_move_foreground(lvobj); +} + void lvglPushEncoderEvent(event_t& evt); void Window::checkEvents() { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index bbf2f5a1c..ef43dd78d 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -229,10 +229,7 @@ class Window invalidate({0, 0, rect.w, rect.h}); } - void bringToTop() - { - attach(parent); // does a detach + attach - } + void bringToTop(); virtual void checkEvents(); From 904eb620293b951c161443e5d801be250c15cac6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 7 May 2022 12:55:13 +0200 Subject: [PATCH 596/714] window: removed getSnapStep() --- radio/src/thirdparty/libopenui/src/window.cpp | 21 ------------------- radio/src/thirdparty/libopenui/src/window.h | 3 --- 2 files changed, 24 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 39e9972e9..1567a77f9 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -546,27 +546,6 @@ void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) } } -#if defined(HARDWARE_TOUCH) -coord_t Window::getSnapStep(coord_t relativeScrollPosition, coord_t pageSize) -{ - coord_t result = 0; - if (relativeScrollPosition > pageSize / 2) { - // closer to next page - result = (pageSize - relativeScrollPosition); - } - else { - // closer to previous page - result = (0 - relativeScrollPosition); - } - - // do not get too slow - if (abs(result) > 32) - result /= 2; - - return result; -} -#endif - void Window::bringToTop() { attach(parent); // does a detach + attach diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index ef43dd78d..6b9b3db15 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -292,9 +292,6 @@ class Window } #if defined(HARDWARE_TOUCH) - static coord_t getSnapStep(coord_t relativeScrollPosition, - coord_t pageSize); - virtual bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY); #endif From 94c6d25f1827a9d21e136b3c25b388c853fdfbd4 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 7 May 2022 15:19:36 +0200 Subject: [PATCH 597/714] window: fixed adjustHeight() & moveWindowsTop() --- radio/src/thirdparty/libopenui/src/window.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 1567a77f9..f87ceda2d 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -621,8 +621,12 @@ void Window::adjustInnerHeight() coord_t Window::adjustHeight() { coord_t old = rect.h; - adjustInnerHeight(); - if (lvobj) lv_obj_set_style_height(lvobj, rect.h, LV_PART_MAIN); + if (lvobj) { + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); + lv_obj_update_layout(lvobj); + rect.h = lv_obj_get_height(lvobj); + lv_obj_set_style_height(lvobj, rect.h, LV_PART_MAIN); + } return rect.h - old; } @@ -634,7 +638,7 @@ void Window::moveWindowsTop(coord_t y, coord_t delta) for (auto child: children) { if (child->rect.y >= y) { - child->rect.y += delta; + child->setTop(child->top() + delta); invalidate(); } } From b747b792733e1994d283f8a35c0e3d6195c3b88c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 9 May 2022 10:16:14 +0200 Subject: [PATCH 598/714] clean up --- radio/src/thirdparty/libopenui/src/window.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index f87ceda2d..137018b92 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -397,19 +397,7 @@ void Window::setFocus(uint8_t flag, Window * from) // scroll before calling focusHandler so that the window can adjust the // scroll position if needed -#if 1 - // lv_obj_scroll_to_view(lvobj, LV_ANIM_OFF); lv_obj_scroll_to_view_recursive(lvobj, LV_ANIM_OFF); -#else - Window * parent = this->parent; - while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { - parent = parent->parent; - } - if (parent) { - parent->scrollTo(this); - invalidate(); - } -#endif clearFocus(); focusWindow = this; @@ -421,9 +409,6 @@ void Window::setFocus(uint8_t flag, Window * from) void Window::scrollTo(Window * child) { - - - } void Window::scrollTo(const rect_t & rect) From 5878802c9b66358a3cb53b9eca58dd4b32dfa836 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 9 May 2022 10:16:56 +0200 Subject: [PATCH 599/714] form: add field even if form cannot have focus (but forwards it) --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index e652d4c59..5a56589ac 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -63,7 +63,7 @@ FormField::FormField(Window* parent, const rect_t& rect, LvglCreate objConstruct) : Window(parent, rect, windowFlags, textFlags, objConstruct) { - if (!(windowFlags & NO_FOCUS)) { + if (!(windowFlags & NO_FOCUS) || (windowFlags & FORM_FORWARD_FOCUS)) { auto cont = dynamic_cast(parent); if (cont) { cont->addField(this); From dd543c151b24256d570317e28169437688931dd5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 9 May 2022 10:17:28 +0200 Subject: [PATCH 600/714] file choice: parent is Window --- radio/src/thirdparty/libopenui/src/filechoice.cpp | 2 +- radio/src/thirdparty/libopenui/src/filechoice.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 9a9f3972c..ba82b1e3a 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -33,7 +33,7 @@ #define STR_NO_FILES_ON_SD "No files on SD Card!" #endif -FileChoice::FileChoice(FormGroup *parent, const rect_t &rect, +FileChoice::FileChoice(Window* parent, const rect_t &rect, std::string folder, const char *extension, int maxlen, std::function getValue, std::function setValue, diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index 94cafc3c3..f3003c4c5 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -25,7 +25,7 @@ class FileChoice : public ChoiceBase { public: - FileChoice(FormGroup* parent, const rect_t& rect, std::string folder, + FileChoice(Window* parent, const rect_t& rect, std::string folder, const char* extension, int maxlen, std::function getValue, std::function setValue, From 309e672622163462cd380f60c601006681ba607b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 9 May 2022 10:17:55 +0200 Subject: [PATCH 601/714] choice: added getIntValue() --- radio/src/thirdparty/libopenui/src/choice.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 52e6e513a..059eb0d53 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -136,6 +136,11 @@ class Choice: public ChoiceBase { return value; } + int getIntValue() const + { + return getValue(); + } + unsigned getValuesCount() const { return getIndexFromValue(vmax + 1); From fc4babfb4e163e2f9caa056c4b9b59e705dd8b41 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 9 May 2022 10:18:19 +0200 Subject: [PATCH 602/714] choice: send LV_EVENT_VALUE_CHANGED on setValue() --- radio/src/thirdparty/libopenui/src/choice.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 9c7cdcf52..f17d47a1e 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -209,16 +209,19 @@ void Choice::openMenu() if (textHandler) { menu->addLine(textHandler(i), [=]() { setValue(i); + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); }); } else if (unsigned(i - vmin) < values.size()) { menu->addLine(values[i - vmin], [=]() { setValue(i); + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); }); } else { menu->addLine(std::to_string(i), [=]() { setValue(i); + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); }); } if (value == i) { From 6e6d6e4a24e18e14560bb4693e4e15cc3bde4f30 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 9 May 2022 10:18:33 +0200 Subject: [PATCH 603/714] added ButtonMatrix --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + .../libopenui/src/button_matrix.cpp | 136 ++++++++++++++++++ .../thirdparty/libopenui/src/button_matrix.h | 46 ++++++ 3 files changed, 183 insertions(+) create mode 100644 radio/src/thirdparty/libopenui/src/button_matrix.cpp create mode 100644 radio/src/thirdparty/libopenui/src/button_matrix.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 9e9b31f6a..87fd91e79 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -17,6 +17,7 @@ set(LIBOPENUI_SRC layer.cpp form.cpp button.cpp + button_matrix.cpp static.cpp checkbox.cpp carousel.cpp diff --git a/radio/src/thirdparty/libopenui/src/button_matrix.cpp b/radio/src/thirdparty/libopenui/src/button_matrix.cpp new file mode 100644 index 000000000..3f0551bc3 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/button_matrix.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) EdgeTX + * + * Source: + * https://github.com/EdgeTX/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "button_matrix.h" + +#include +#include + +static const char _filler[] = "0"; +static const char _newline[] = "\n"; +static const char _map_end[] = ""; + +static void btn_matrix_event(lv_event_t *e) +{ + static lv_area_t draw_area; + + lv_event_code_t code = lv_event_get_code(e); + + if (code == LV_EVENT_VALUE_CHANGED) { + auto btn_id = (uint32_t *)lv_event_get_param(e); + auto btnm = (ButtonMatrix*)lv_event_get_user_data(e); + + btnm->onPress((uint8_t)*btn_id); + } + else if (code == LV_EVENT_DRAW_PART_BEGIN) { + + lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); + if (dsc->part != LV_PART_ITEMS) return; + + auto btnm = (ButtonMatrix*)lv_event_get_user_data(e); + if (btnm->isActive((uint8_t)dsc->id)) { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_ACTIVE); + dsc->label_dsc->color = makeLvColor(COLOR_THEME_PRIMARY2); + } else { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_PRIMARY2); + dsc->label_dsc->color = makeLvColor(COLOR_THEME_SECONDARY1); + } + + // backup draw area as it gets modified for the label + lv_area_copy(&draw_area, dsc->draw_area); + } +} + +ButtonMatrix::ButtonMatrix(Window* parent, const rect_t& r) : + FormField(parent, r, 0, 0, lv_btnmatrix_create) +{ + lv_obj_add_event_cb(lvobj, btn_matrix_event, LV_EVENT_ALL, this); +} + +ButtonMatrix::~ButtonMatrix() +{ + deallocate(); +} + +void ButtonMatrix::deallocate() +{ + if (txt_cnt == 0) return; + + for (uint8_t i = 0; i < txt_cnt; i++) { + char* txt = lv_btnm_map[i]; + if (txt != _filler && txt != _newline && txt != _map_end) free(txt); + } + + free(lv_btnm_map); + free(txt_index); + + txt_cnt = 0; + btn_cnt = 0; +} + +void ButtonMatrix::initBtnMap(uint8_t cols, uint8_t btns) +{ + deallocate(); + + uint8_t rows = ((btns - 1) / 4) + 1; + txt_cnt = btns + rows; + btn_cnt = btns; + + lv_btnm_map = (char**)malloc(sizeof(char*) * txt_cnt); + txt_index = (uint8_t*)malloc(sizeof(uint8_t) * btns); + + uint8_t col = 0; + uint8_t btn = 0; + uint8_t txt_i = 0; + + while (btn < btns) { + + if (col == cols) { + lv_btnm_map[txt_i++] = (char*)_newline; + col = 0; + } + + txt_index[btn] = txt_i; + lv_btnm_map[txt_i++] = (char*)_filler; + btn++; + col++; + } + lv_btnm_map[txt_i++] = (char*)_map_end; +} + +void ButtonMatrix::setText(uint8_t btn_id, const char* txt) +{ + if (btn_id >= btn_cnt) return; + + uint8_t txt_i = txt_index[btn_id]; + lv_btnm_map[txt_i] = strdup(txt); +} + +void ButtonMatrix::update() +{ + lv_btnmatrix_set_map(lvobj, (const char**)lv_btnm_map); + + lv_btnmatrix_ctrl_t ctrl = LV_BTNMATRIX_CTRL_CLICK_TRIG | + LV_BTNMATRIX_CTRL_NO_REPEAT; + + lv_btnmatrix_set_btn_ctrl_all(lvobj, ctrl); +} + +void ButtonMatrix::onEvent(event_t event) {} +void ButtonMatrix::setFocus(uint8_t flag, Window* from) {} diff --git a/radio/src/thirdparty/libopenui/src/button_matrix.h b/radio/src/thirdparty/libopenui/src/button_matrix.h new file mode 100644 index 000000000..2438b4b0d --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/button_matrix.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) EdgeTX + * + * Source: + * https://github.com/EdgeTX/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#pragma once + +#include "form.h" + +class ButtonMatrix : public FormField +{ + char** lv_btnm_map = nullptr; + uint8_t* txt_index = nullptr; + uint8_t txt_cnt = 0; + uint8_t btn_cnt = 0; + + public: + ButtonMatrix(Window* parent, const rect_t& rect); + ~ButtonMatrix(); + + virtual void onPress(uint8_t btn_id) {} + virtual bool isActive(uint8_t btn_id) { return false; } + + protected: + void deallocate(); + void initBtnMap(uint8_t cols, uint8_t btns); + void setText(uint8_t btn_id, const char* txt); + void update(); + + void onEvent(event_t event) override; + void setFocus(uint8_t flag, Window* from) override; +}; From 6a6ca7e57861454b65b7cb51d81453df5e4a6870 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 9 May 2022 11:37:12 +0200 Subject: [PATCH 604/714] button matrix: added focus and key handling --- .../libopenui/src/button_matrix.cpp | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button_matrix.cpp b/radio/src/thirdparty/libopenui/src/button_matrix.cpp index 3f0551bc3..74dc5675c 100644 --- a/radio/src/thirdparty/libopenui/src/button_matrix.cpp +++ b/radio/src/thirdparty/libopenui/src/button_matrix.cpp @@ -132,5 +132,49 @@ void ButtonMatrix::update() lv_btnmatrix_set_btn_ctrl_all(lvobj, ctrl); } -void ButtonMatrix::onEvent(event_t event) {} -void ButtonMatrix::setFocus(uint8_t flag, Window* from) {} +void ButtonMatrix::onEvent(event_t event) +{ + char c = 0; + lv_event_code_t code = LV_EVENT_ALL; + +#if defined (HARDWARE_KEYS) + switch (event) { + case EVT_KEY_FIRST(KEY_ENTER): + code = LV_EVENT_PRESSED; + break; + + case EVT_KEY_BREAK(KEY_ENTER): + code = LV_EVENT_RELEASED; + break; + + case EVT_ROTARY_LEFT: + if (lv_btnmatrix_get_selected_btn(lvobj) != 0) { + code = LV_EVENT_KEY; + c = LV_KEY_LEFT; + } + break; + case EVT_ROTARY_RIGHT: + if (lv_btnmatrix_get_selected_btn(lvobj) < btn_cnt - 1) { + code = LV_EVENT_KEY; + c = LV_KEY_RIGHT; + } + break; + } +#endif + + if ((code == LV_EVENT_KEY) && c != 0) lv_event_send(lvobj, code, &c); + else if (code != LV_EVENT_ALL) lv_event_send(lvobj, code, lv_indev_get_act()); + else FormField::onEvent(event); +} + +void ButtonMatrix::setFocus(uint8_t flag, Window* from) +{ + if (lv_btnmatrix_get_selected_btn(lvobj) == LV_BTNMATRIX_BTN_NONE) { + if (flag == SET_FOCUS_BACKWARD) + lv_btnmatrix_set_selected_btn(lvobj, btn_cnt - 1); + else + lv_btnmatrix_set_selected_btn(lvobj, 0); + } + + FormField::setFocus(flag, from); +} From cc410438832c6a549043488a073dd4eb50e3bdbc Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 9 May 2022 12:02:37 +0200 Subject: [PATCH 605/714] fix: button matrix rows --- radio/src/thirdparty/libopenui/src/button_matrix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/button_matrix.cpp b/radio/src/thirdparty/libopenui/src/button_matrix.cpp index 74dc5675c..5e774aa0b 100644 --- a/radio/src/thirdparty/libopenui/src/button_matrix.cpp +++ b/radio/src/thirdparty/libopenui/src/button_matrix.cpp @@ -88,7 +88,7 @@ void ButtonMatrix::initBtnMap(uint8_t cols, uint8_t btns) { deallocate(); - uint8_t rows = ((btns - 1) / 4) + 1; + uint8_t rows = ((btns - 1) / cols) + 1; txt_cnt = btns + rows; btn_cnt = btns; From 6cb2e332e11f7a1c95d7a0992c74bf136c74236c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 11 May 2022 11:34:01 +0200 Subject: [PATCH 606/714] fix: check `first` before derefering it --- radio/src/thirdparty/libopenui/src/form.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 5a56589ac..e2d8f2de5 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -312,7 +312,8 @@ void FormGroup::setFocus(uint8_t flag, Window * from) case SET_FOCUS_FORWARD: if (from && from->isChild(this)) { if (next == this) { - first->setFocus(SET_FOCUS_FORWARD, this); + if (first) + first->setFocus(SET_FOCUS_FORWARD, this); } else if (next) { next->setFocus(SET_FOCUS_FORWARD, this); From c7da6b27966e4073140599054fb1a734e5e5e5de Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 11 May 2022 11:34:15 +0200 Subject: [PATCH 607/714] add Layer::back() --- radio/src/thirdparty/libopenui/src/layer.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index 94c0677a6..f66303ba9 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -80,4 +80,9 @@ class Layer } #endif } + + static Window* back() + { + return stack.back().main; + } }; From d71ad73eb14890581120751aa8f71d186e67ffa6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 11 May 2022 16:04:57 +0200 Subject: [PATCH 608/714] simplify LVGL object removal Fixes as well some issues with widgets being displayed after libopenui object has been deleted. --- .../libopenui/src/keyboard_base.cpp | 1 - radio/src/thirdparty/libopenui/src/window.cpp | 25 ++++--------------- radio/src/thirdparty/libopenui/src/window.h | 3 --- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 979ebd9ba..d77524bd9 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -54,7 +54,6 @@ void Keyboard::clearField() } detach(); - if (lvobj != nullptr) lv_obj_set_parent(lvobj, nullptr); if (fieldContainer) { lv_obj_scroll_to_y(fieldContainer->getLvObj(), scroll_pos, LV_ANIM_OFF); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 137018b92..9e3640d37 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -297,7 +297,6 @@ void Window::detach() if (parent) { parent->removeChild(this); parent = nullptr; - // if (lvobj != nullptr) lv_obj_set_parent(lvobj, nullptr); } } @@ -325,6 +324,11 @@ void Window::deleteLater(bool detach, bool trash) deleteChildren(); + if (lvobj != nullptr) { + lv_obj_del(lvobj); + lvobj = nullptr; + } + if (closeHandler) { closeHandler(); } @@ -339,29 +343,11 @@ void Window::clear() invalidate(); } -void Window::clearLvgl() -{ - if(lvobj == nullptr) - return; - - for (auto window: children) - { - if(window->lvobj != nullptr && window->lvobj->parent == lvobj) - window->clearLvgl(); - } - - lv_obj_remove_event_cb(lvobj, window_event_cb); - lv_obj_set_user_data(lvobj, nullptr); - lv_group_remove_obj(lvobj); - lvobj = nullptr; -} - void Window::deleteChildren() { // prevent LVGL refocus while mass-deleting inhibit_focus = true; for (auto window: children) { - window->clearLvgl(); window->deleteLater(false); } inhibit_focus = false; @@ -654,7 +640,6 @@ void Window::addChild(Window* window) void Window::removeChild(Window* window) { children.remove(window); - if (window->lvobj != nullptr) lv_obj_set_parent(window->lvobj, nullptr); invalidate(); } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 6b9b3db15..dae741aa5 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -106,9 +106,6 @@ class Window void clear(); virtual void deleteLater(bool detach = true, bool trash = true); - void clearLvgl(); - - bool hasFocus() const { return focusWindow == this; From 7c409a445439d937dfa968c00c1f613227429e9e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 13 May 2022 12:59:23 +0200 Subject: [PATCH 609/714] main window: call checkEvents() only on top most opaque window --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 51f4f913b..3511d1d4f 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -33,7 +33,7 @@ TouchState touchState; MainWindow * MainWindow::_instance = nullptr; MainWindow::MainWindow() : - Window(nullptr, {0, 0, LCD_W, LCD_H}), + Window(nullptr, {0, 0, LCD_W, LCD_H}, OPAQUE), invalidatedRect(rect) { Layer::push(this); @@ -101,7 +101,14 @@ void MainWindow::run(bool trash) // KLK: removed for now. This is now // called from lvgl event processing when // necessary - checkEvents(); + auto layer = Layer::stack.rbegin(); + while (layer != Layer::stack.rend()) { + if (layer->main && (layer->main->getWindowFlags() & OPAQUE)) { + layer->main->checkEvents(); + break; + } + ++layer; + } if (trash) { emptyTrash(); From 2ba86196547b66bd219580f27b6fc9adeee3577a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 14 May 2022 07:58:06 +0200 Subject: [PATCH 610/714] menu: use lv_table and draw entries dynamically Much faster than the previous implementation in creation time and rendering. --- radio/src/thirdparty/libopenui/src/menu.cpp | 138 ++++++++++++------ radio/src/thirdparty/libopenui/src/menu.h | 6 +- .../thirdparty/libopenui/src/modal_window.cpp | 11 +- 3 files changed, 107 insertions(+), 48 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index e44f5eb01..ccaa306b4 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -25,44 +25,88 @@ static void menu_clicked(lv_event_t *e) { - auto code = lv_event_get_code(e); - if (code == LV_EVENT_CLICKED) { + lv_obj_t* target = lv_event_get_target(e); + if (!target) return; - lv_obj_t* target = lv_event_get_target(e); - if (!target) return; - - lv_obj_t* parent = lv_obj_get_parent(target); - if (!parent) return; + uint16_t row; + uint16_t col; + lv_table_get_selected_cell(target, &row, &col); - MenuBody* mb = (MenuBody*)lv_obj_get_user_data(parent); - if (!mb) return; + MenuBody* mb = (MenuBody*)lv_obj_get_user_data(target); + if (!mb) return; - size_t idx = (size_t)lv_event_get_user_data(e); - mb->onPress(idx); - } + mb->onPress(row); } -static void add_list_button(lv_obj_t* list, const char* icon, const char* txt, size_t idx) +void menu_draw_begin(lv_event_t* e) { - lv_obj_t* btn = lv_list_add_btn(list, icon, txt); - lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); + lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); + if (dsc->part != LV_PART_ITEMS) return; - void* user_data = (void *)idx; - lv_obj_add_event_cb(btn, menu_clicked, LV_EVENT_CLICKED, user_data); + lv_obj_t* table = lv_event_get_target(e); + if (!table) return; + + MenuBody* mb = (MenuBody*)lv_obj_get_user_data(table); + if (!mb) return; + + uint16_t row = dsc->id; + lv_canvas_t* icon = (lv_canvas_t*)mb->lines[row].getIcon(); + if (!icon) return; + + lv_img_t* img = &icon->img; + lv_coord_t cell_left = lv_obj_get_style_pad_left(table, LV_PART_ITEMS); + dsc->label_dsc->ofs_x = img->w + cell_left; } +void menu_draw_end(lv_event_t* e) +{ + lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); + if (dsc->part != LV_PART_ITEMS) return; + + lv_obj_t* table = lv_event_get_target(e); + if (!table) return; + + MenuBody* mb = (MenuBody*)lv_obj_get_user_data(table); + if (!mb) return; + + uint16_t row = dsc->id; + lv_obj_t* icon = mb->lines[row].getIcon(); + if (!icon) return; + + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + + lv_img_dsc_t* img = lv_canvas_get_img(icon); + lv_area_t coords; + + lv_coord_t area_h = lv_area_get_height(dsc->draw_area); + + lv_coord_t cell_left = lv_obj_get_style_pad_left(table, LV_PART_ITEMS); + coords.x1 = dsc->draw_area->x1 + cell_left; + coords.x2 = coords.x1 + img->header.w - 1; + coords.y1 = dsc->draw_area->y1 + (area_h - img->header.h) / 2; + coords.y2 = coords.y1 + img->header.h - 1; + + lv_draw_img(dsc->draw_ctx, &img_dsc, &coords, img); +} MenuBody::MenuBody(Window * parent, const rect_t & rect): - Window(parent, rect, OPAQUE, 0, lv_list_create) + Window(parent, rect, OPAQUE, 0, lv_table_create) { - setFocus(); + lv_table_set_col_cnt(lvobj, 1); + lv_table_set_col_width(lvobj, 0, rect.w); + lv_obj_add_event_cb(lvobj, menu_clicked, LV_EVENT_VALUE_CHANGED, nullptr); + lv_obj_add_event_cb(lvobj, menu_draw_begin, LV_EVENT_DRAW_PART_BEGIN, nullptr); + lv_obj_add_event_cb(lvobj, menu_draw_end, LV_EVENT_DRAW_PART_END, nullptr); } void MenuBody::addLine(const std::string &text, std::function onPress, std::function isChecked) { lines.emplace_back(std::move(onPress), std::move(isChecked), nullptr); - add_list_button(lvobj, nullptr, text.c_str(), lines.size() - 1); + + auto idx = lines.size() - 1; + lv_table_set_cell_value_fmt(lvobj, idx, 0, text.c_str()); } void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, @@ -77,8 +121,8 @@ void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, void* buf = (void*)(icon_mask + 4); lv_canvas_set_buffer(canvas, buf, w, h, LV_IMG_CF_ALPHA_8BIT); - const char* icon = (const char *)lv_canvas_get_img(canvas); - add_list_button(lvobj, icon, text.c_str(), lines.size() - 1); + auto idx = lines.size() - 1; + lv_table_set_cell_value_fmt(lvobj, idx, 0, text.c_str()); } void MenuBody::removeLines() @@ -89,14 +133,7 @@ void MenuBody::removeLines() coord_t MenuBody::getContentHeight() { - coord_t h = 0; - lv_obj_update_layout(lvobj); - for(uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { - auto child = lv_obj_get_child(lvobj, i); - h += lv_obj_get_height(child); - } - - return h; + return lv_obj_get_self_height(lvobj); } void MenuBody::onPress(size_t index) @@ -131,21 +168,33 @@ int MenuBody::rangeCheck(int index) void MenuBody::setIndex(int index) { if (index < (int)lines.size()) { - lv_obj_t *btn = lv_obj_get_child(lvobj, selectedIndex); - if (btn != nullptr) { - lv_obj_clear_state(btn, LV_STATE_CHECKED); - lv_obj_clear_state(btn, LV_STATE_FOCUS_KEY); - } - + if (index == selectedIndex) return; selectedIndex = index; - btn = lv_obj_get_child(lvobj, selectedIndex); - if (btn != nullptr) { - lv_obj_add_state(btn, LV_STATE_CHECKED); - lv_obj_add_state(btn, LV_STATE_FOCUS_KEY); - lv_obj_scroll_to_view(btn, LV_ANIM_OFF); - lv_group_focus_obj(btn); + lv_obj_invalidate(lvobj); + lv_table_t* table = (lv_table_t*)lvobj; + + table->row_act = index; + table->col_act = 0; + + lv_coord_t h_before = 0; + for (uint16_t i = 0; i < table->row_act; i++) + h_before += table->row_h[i]; + + lv_coord_t row_h = table->row_h[table->row_act]; + lv_coord_t scroll_y = lv_obj_get_scroll_y(lvobj); + lv_coord_t h = lv_obj_get_height(lvobj); + + lv_coord_t diff_y = 0; + if (h_before < scroll_y) { + diff_y = scroll_y - h_before; + } else if (scroll_y + h < h_before + row_h) { + diff_y = scroll_y + h - h_before - row_h; + } else { + return; } + + lv_obj_scroll_by_bounded(lvobj, 0, diff_y, LV_ANIM_OFF); } } @@ -268,13 +317,12 @@ void Menu::updatePosition() if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen auto headerHeight = content->getHeaderHeight(); - auto bodyHeight = min(height, MENUS_MAX_HEIGHT); + auto bodyHeight = min(height, MENUS_MAX_HEIGHT - headerHeight); content->setTop((LCD_H - headerHeight - bodyHeight) / 2); content->setHeight(headerHeight + bodyHeight); content->body.setTop(headerHeight); - content->body.setHeight(bodyHeight); + content->body.setHeight(bodyHeight); } - } void Menu::setTitle(std::string text) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 9f3e8b2ca..c923f9d13 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -61,6 +61,8 @@ class MenuBody: public Window MenuLine(MenuLine &) = delete; MenuLine(MenuLine &&) = default; + lv_obj_t* getIcon() { return icon.get(); } + protected: bool isSeparator = false; std::function onPress; @@ -123,7 +125,9 @@ class MenuBody: public Window void onPress(size_t index); protected: - friend void menuBodyEventCallback(lv_event_t *); + friend void menu_draw_begin(lv_event_t *); + friend void menu_draw_end(lv_event_t *); + void selectNext(MENU_DIRECTION direction); int rangeCheck(int); void setIndex(int index); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 270522bcf..ab5875050 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -64,8 +64,15 @@ void ModalWindowContent::setTitle(const std::string& text) lv_obj_move_to_index(title, 0); lv_obj_set_width(title, lv_pct(100)); lv_obj_set_style_pad_all(title, PAGE_PADDING, LV_PART_MAIN); - // TODO: styling - // lv_obj_set_style_pad_left(title, FIELD_PADDING_LEFT, 0); + + lv_color_t bg_color = makeLvColor(COLOR_THEME_SECONDARY1); + lv_obj_set_style_bg_color(title, bg_color, LV_PART_MAIN); + lv_obj_set_style_bg_opa(title, LV_OPA_100, LV_PART_MAIN); + + lv_color_t txt_color = makeLvColor(COLOR_THEME_PRIMARY2); + lv_obj_set_style_text_color(title, txt_color, LV_PART_MAIN); + + lv_obj_update_layout(lvobj); } lv_label_set_text(title, text.c_str()); } From 170fa8152e5d859e0ea6f8b4e222ee1de50fd9ed Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 14 May 2022 08:00:04 +0200 Subject: [PATCH 611/714] choice: dramatically reduce refresh frequency Done now only when a real change happened --- radio/src/thirdparty/libopenui/src/choice.cpp | 10 +++++++--- radio/src/thirdparty/libopenui/src/choice.h | 3 ++- radio/src/thirdparty/libopenui/src/filechoice.cpp | 6 +++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index f17d47a1e..1ed2fe7a3 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -21,11 +21,11 @@ #include "menu.h" #include "theme.h" -void choicePaintCallback(lv_event_t *e) +void choice_changed_cb(lv_event_t *e) { auto code = lv_event_get_code(e); - if (code == LV_EVENT_DRAW_MAIN_BEGIN) { + if (code == LV_EVENT_VALUE_CHANGED) { lv_obj_t *target = lv_event_get_target(e); if (target != nullptr) { ChoiceBase *cb = (ChoiceBase*)lv_obj_get_user_data(target); @@ -46,7 +46,7 @@ ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type, lv_obj_set_layout(lvobj, LV_LAYOUT_FLEX); lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW); - lv_obj_add_event_cb(lvobj, choicePaintCallback, LV_EVENT_DRAW_MAIN_BEGIN, lvobj); + lv_obj_add_event_cb(lvobj, choice_changed_cb, LV_EVENT_VALUE_CHANGED, lvobj); label = lv_label_create(lvobj); lv_group_t * def_group = lv_group_get_default(); @@ -99,6 +99,7 @@ Choice::Choice(Window* parent, const rect_t & rect, int vmin, int vmax, getValue(std::move(getValue)), setValue(std::move(setValue)) { + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); } Choice::Choice(Window* parent, const rect_t& rect, const char* const values[], @@ -111,6 +112,7 @@ Choice::Choice(Window* parent, const rect_t& rect, const char* const values[], setValue(std::move(setValue)) { setValues(values); + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); } Choice::Choice(Window* parent, const rect_t& rect, @@ -124,6 +126,7 @@ Choice::Choice(Window* parent, const rect_t& rect, getValue(std::move(getValue)), setValue(std::move(setValue)) { + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); } Choice::Choice(Window* parent, const rect_t& rect, const char* values, int vmin, @@ -144,6 +147,7 @@ Choice::Choice(Window* parent, const rect_t& rect, const char* values, int vmin, value += len; } } + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); } void Choice::addValue(const char * value) diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 059eb0d53..5b6d6e53c 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -40,7 +40,7 @@ class ChoiceBase : public FormField } protected: - friend void choicePaintCallback(lv_event_t *e); + friend void choice_changed_cb(lv_event_t *e); ChoiceType type; lv_obj_t *label; virtual std::string getLabelText() { return ""; }; @@ -149,6 +149,7 @@ class Choice: public ChoiceBase { void setTextHandler(std::function handler) { textHandler = std::move(handler); + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); } void setMenuTitle(std::string value) diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index ba82b1e3a..0e153fd49 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -46,6 +46,7 @@ FileChoice::FileChoice(Window* parent, const rect_t &rect, setValue(std::move(setValue)), stripExtension(stripExtension) { + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); } std::string FileChoice::getLabelText() @@ -100,7 +101,10 @@ bool FileChoice::openMenu() int current = -1; std::string value = getValue(); for (const auto &file : files) { - menu->addLine(file, [=]() { setValue(file); }); + menu->addLine(file, [=]() { + setValue(file); + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); + }); // TRACE("%s %d %s %d", value.c_str(), value.size(), file.c_str(), // file.size()); if (value.compare(file) == 0) { From ff080ff3d8915c12bb6047c45743c5e682273d8e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 15 May 2022 14:21:01 +0200 Subject: [PATCH 612/714] window: do not scroll on touch-focus --- radio/src/thirdparty/libopenui/src/window.cpp | 15 +++++++++++++-- radio/src/thirdparty/libopenui/src/window.h | 9 +++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 9e3640d37..4dc918e2d 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -134,10 +134,19 @@ static void window_event_cb(lv_event_t * e) } else if (code == LV_EVENT_FOCUSED) { if (inhibit_focus) return; + bool focused = ((Window *)target->user_data)->hasFocus(); TRACE_WINDOWS("FOCUSED[%d] %s", focused, window->getWindowDebugString().c_str()); - if (!focused) { window->setFocus(); } + + if (!focused) { + SetFocusFlag flag = SET_FOCUS_DEFAULT; + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + if(indev_type == LV_INDEV_TYPE_POINTER) { + flag = SET_FOCUS_NO_SCROLL; + } + window->setFocus(flag); + } } } @@ -383,7 +392,9 @@ void Window::setFocus(uint8_t flag, Window * from) // scroll before calling focusHandler so that the window can adjust the // scroll position if needed - lv_obj_scroll_to_view_recursive(lvobj, LV_ANIM_OFF); + if (flag != SET_FOCUS_NO_SCROLL) { + lv_obj_scroll_to_view_recursive(lvobj, LV_ANIM_OFF); + } clearFocus(); focusWindow = this; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index dae741aa5..8513a6089 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -53,10 +53,11 @@ constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; enum SetFocusFlag { - SET_FOCUS_DEFAULT, - SET_FOCUS_FORWARD, - SET_FOCUS_BACKWARD, - SET_FOCUS_FIRST + SET_FOCUS_DEFAULT = 0, + SET_FOCUS_FORWARD = 1, + SET_FOCUS_BACKWARD = 2, + SET_FOCUS_FIRST = 3, + SET_FOCUS_NO_SCROLL= 4 }; typedef lv_obj_t *(*LvglCreate)(lv_obj_t *); From 00fda7f4ae2729b59c74f50d31bc503c28f9f70a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 15 May 2022 14:21:26 +0200 Subject: [PATCH 613/714] keyboard: prevent keyboard being freed --- radio/src/thirdparty/libopenui/src/keyboard_base.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index d77524bd9..1ac4a3b24 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -54,6 +54,7 @@ void Keyboard::clearField() } detach(); + lv_obj_set_parent(lvobj, lv_layer_top()); if (fieldContainer) { lv_obj_scroll_to_y(fieldContainer->getLvObj(), scroll_pos, LV_ANIM_OFF); From dbe8405fe22188194973d65a2324656a508cbaa5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 20 May 2022 11:49:20 +0200 Subject: [PATCH 614/714] Small improvements / fixes --- radio/src/thirdparty/libopenui/src/choice.cpp | 5 +++-- radio/src/thirdparty/libopenui/src/choice.h | 5 +++++ radio/src/thirdparty/libopenui/src/flexlayout.h | 3 +-- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- radio/src/thirdparty/libopenui/src/keyboard_number.cpp | 3 ++- radio/src/thirdparty/libopenui/src/menu.cpp | 2 ++ radio/src/thirdparty/libopenui/src/textedit.cpp | 1 - radio/src/thirdparty/libopenui/src/window.cpp | 1 + 8 files changed, 15 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 1ed2fe7a3..bfcf73556 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -77,10 +77,11 @@ ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type, std::string Choice::getLabelText() { - std::string text = ""; + std::string text; + if (textHandler != nullptr) text = textHandler(getValue()); - else { + else if (getValue) { int val = getValue(); val -= vmin; if (val >= 0 && val < (int)values.size()) { diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 5b6d6e53c..c338f5707 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -99,6 +99,11 @@ class Choice: public ChoiceBase { setValue = std::move(handler); } + void setGetValueHandler(std::function handler) + { + getValue = std::move(handler); + } + void setAvailableHandler(std::function handler) { isValueAvailable = std::move(handler); diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h index 756d1d201..dcc3abe70 100644 --- a/radio/src/thirdparty/libopenui/src/flexlayout.h +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -64,14 +64,13 @@ class FlexGridLayout void resetPos() { col_pos = 0; row_pos = 0; } void nextColumn() { col_pos += col_span; } - void nextRow() { row_pos += row_span; } + void nextRow() { row_pos += row_span; col_pos = 0; } void nextCell() { nextColumn(); if (col_dsc[col_pos] == LV_GRID_TEMPLATE_LAST) { nextRow(); - col_pos = 0; } } diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index e2d8f2de5..d8d0189a2 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -116,7 +116,7 @@ void FormField::onEvent(event_t event) extern lv_group_t* inputGroup; void FormField::setFocus(uint8_t flag, Window * from) { - if (enabled) { + if (enabled && !lv_obj_has_flag(lvobj, LV_OBJ_FLAG_HIDDEN)) { Window::setFocus(flag, from); if(lvobj != nullptr) { lv_group_focus_obj(lvobj); diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index 2a4b5fad9..b55c9f174 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -26,7 +26,8 @@ NumberKeyboard * NumberKeyboard::_instance = nullptr; NumberKeyboard::NumberKeyboard() : Keyboard(KEYBOARD_HEIGHT) { - lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); // ToDo: for now we are still using the old keyboard + // ToDo: for now we are still using the old keyboard + lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); new TextButton(this, {LCD_W / 2 - 115, 10, 50, 30}, "<<", [=]() -> uint8_t { diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index ccaa306b4..31ee8f269 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -183,6 +183,8 @@ void MenuBody::setIndex(int index) lv_coord_t row_h = table->row_h[table->row_act]; lv_coord_t scroll_y = lv_obj_get_scroll_y(lvobj); + + lv_obj_update_layout(lvobj); lv_coord_t h = lv_obj_get_height(lvobj); lv_coord_t diff_y = 0; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 1563547a9..8458fbbb7 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -58,7 +58,6 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_textarea_set_text(lvobj, value); lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); - lv_textarea_set_cursor_pos(lvobj, 0); if (width() == 0) { lv_obj_set_width(lvobj, LV_DPI_DEF); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 4dc918e2d..36da8f89e 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -395,6 +395,7 @@ void Window::setFocus(uint8_t flag, Window * from) if (flag != SET_FOCUS_NO_SCROLL) { lv_obj_scroll_to_view_recursive(lvobj, LV_ANIM_OFF); } + invalidate(); clearFocus(); focusWindow = this; From 28c28fb5213114ed0bc93bcd41996ca7d2b92ca9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 20 May 2022 11:50:46 +0200 Subject: [PATCH 615/714] button: better track state --- radio/src/thirdparty/libopenui/src/button.cpp | 56 ++++++++++++++----- radio/src/thirdparty/libopenui/src/button.h | 17 +----- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index feda35bfa..2aefde3a0 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -31,6 +31,14 @@ static void lvglEvent(lv_event_t* e) btn->onEvent(e); } +static void update_checked_flag(lv_obj_t* obj, WindowFlags flags) +{ + if (!(flags & BUTTON_CHECKED)) + lv_obj_clear_state(obj, LV_STATE_CHECKED); + else + lv_obj_add_state(obj, LV_STATE_CHECKED); +} + Button::Button(Window* parent, const rect_t& rect, std::function pressHandler, WindowFlags windowFlag, LcdFlags textFlags, @@ -38,8 +46,27 @@ Button::Button(Window* parent, const rect_t& rect, FormField(parent, rect, windowFlag, textFlags, objConstruct), pressHandler(std::move(pressHandler)) { - lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); lv_obj_add_event_cb(lvobj, lvglEvent, LV_EVENT_CLICKED, this); + + if (windowFlag & BUTTON_CHECKED) + lv_obj_add_state(lvobj, LV_STATE_CHECKED); +} + +void Button::check(bool checked) +{ + if (checked != bool(windowFlags & BUTTON_CHECKED)) { + windowFlags ^= BUTTON_CHECKED; + update_checked_flag(lvobj, windowFlags); + invalidate(); + } +} + +bool Button::checked() const +{ + if (windowFlags & BUTTON_CHECKED_ON_FOCUS) + return hasFocus(); + else + return windowFlags & BUTTON_CHECKED; } void Button::onPress() @@ -48,25 +75,26 @@ void Button::onPress() windowFlags = check ? windowFlags | BUTTON_CHECKED : windowFlags & ~BUTTON_CHECKED; if (lvobj == nullptr) return; - - if (!(windowFlags & BUTTON_CHECKED)) - lv_obj_clear_state(lvobj, LV_STATE_CHECKED); - else - lv_obj_add_state(lvobj, LV_STATE_CHECKED); + update_checked_flag(lvobj, windowFlags); } #if defined(HARDWARE_KEYS) void Button::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("Button").c_str(), event); + auto indev_act = lv_indev_get_act(); + switch (event) { + case EVT_KEY_FIRST(KEY_ENTER): + lv_event_send(lvobj, LV_EVENT_PRESSED, indev_act); + break; - if (enabled && event == EVT_KEY_FIRST(KEY_ENTER)) { - killEvents(event); - onKeyPress(); - onPress(); - } - else { - FormField::onEvent(event); + case EVT_KEY_BREAK(KEY_ENTER): + lv_event_send(lvobj, LV_EVENT_RELEASED, indev_act); + lv_event_send(lvobj, LV_EVENT_CLICKED, indev_act); + break; + + default: + FormField::onEvent(event); + break; } } #endif diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index dcd9a476b..17209f7f2 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -40,21 +40,8 @@ class Button: public FormField } #endif - void check(bool checked = true) - { - if (checked != bool(windowFlags & BUTTON_CHECKED)) { - windowFlags ^= BUTTON_CHECKED; - invalidate(); - } - } - - bool checked() const - { - if (windowFlags & BUTTON_CHECKED_ON_FOCUS) - return hasFocus(); - else - return windowFlags & BUTTON_CHECKED; - } + void check(bool checked = true); + bool checked() const; void setPressHandler(std::function handler) { From eac3f4fce5693741dd98f6d10b7298a8547729c1 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 20 May 2022 12:03:43 +0200 Subject: [PATCH 616/714] Use own LVGL theme --- radio/src/thirdparty/libopenui/src/button.cpp | 5 +- radio/src/thirdparty/libopenui/src/form.cpp | 54 ------------------- .../libopenui/thirdparty/CMakeLists.txt | 6 +-- 3 files changed, 4 insertions(+), 61 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 2aefde3a0..d88ea9456 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -138,16 +138,13 @@ TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, { setTextFlags(textFlags | COLOR_THEME_PRIMARY1); lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CHECKABLE); + if (windowFlags & BUTTON_CHECKED) lv_obj_add_state(lvobj, LV_STATE_CHECKED); label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); lv_obj_center(label); - lv_obj_set_style_text_color(label, makeLvColor(this->getTextFlags()), - LV_PART_MAIN); - lv_obj_set_style_text_color(label, makeLvColor(COLOR_THEME_PRIMARY2), - LV_PART_MAIN | LV_STATE_FOCUSED); } void IconButton::paint(BitmapBuffer * dc) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index d8d0189a2..d1a959b38 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -21,43 +21,6 @@ #include "bitmapbuffer.h" #include "libopenui_config.h" -// FormField styles -static lv_style_t bg_style; -static lv_style_t focus_style; -static lv_style_t edit_style; - -// FormGroup styles -static lv_style_t border_style; -static lv_style_t focus_border_style; - -static void init_styles() -{ - static bool inited = false; - if (inited) return; - - inited = true; - - // FormField styles - lv_style_init(&bg_style); - lv_style_set_bg_color(&bg_style, makeLvColor(COLOR_THEME_PRIMARY2)); - lv_style_set_bg_opa(&bg_style, LV_OPA_COVER); - - lv_style_init(&focus_style); - lv_style_set_bg_color(&focus_style, makeLvColor(COLOR_THEME_FOCUS)); - - lv_style_init(&edit_style); - lv_style_set_bg_color(&edit_style, makeLvColor(COLOR_THEME_EDIT)); - - // FormGroup styles - lv_style_init(&border_style); - lv_style_set_border_width(&border_style, 2); - lv_style_set_border_color(&border_style, makeLvColor(COLOR_THEME_FOCUS)); - - lv_style_init(&focus_border_style); - lv_style_set_border_width(&focus_border_style, 1); - lv_style_set_border_color(&focus_border_style, makeLvColor(COLOR_THEME_SECONDARY2)); -} - FormField::FormField(Window* parent, const rect_t& rect, WindowFlags windowFlags, LcdFlags textFlags, LvglCreate objConstruct) : @@ -69,16 +32,6 @@ FormField::FormField(Window* parent, const rect_t& rect, cont->addField(this); } } - - lv_obj_enable_style_refresh(false); - init_styles(); - - lv_obj_add_style(lvobj, &bg_style, LV_PART_MAIN); - lv_obj_add_style(lvobj, &focus_style, LV_PART_MAIN | LV_STATE_FOCUSED); - lv_obj_add_style(lvobj, &edit_style, LV_PART_MAIN | LV_STATE_EDITED); - - lv_obj_enable_style_refresh(true); - lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } #if defined(HARDWARE_KEYS) @@ -190,15 +143,8 @@ FormGroup::FormGroup(Window* parent, const rect_t& rect, { lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); - if (!(windowFlags & (FORM_NO_BORDER | FORM_FORWARD_FOCUS))) { - init_styles(); - lv_obj_add_style(lvobj, &border_style, LV_PART_MAIN | LV_STATE_FOCUSED); - if (!(windowFlags & FORM_BORDER_FOCUS_ONLY)) { - lv_obj_add_style(lvobj, &focus_border_style, LV_PART_MAIN); - } - } } void FormGroup::clear() diff --git a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt index 67da934b1..303deb2c8 100644 --- a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt @@ -174,9 +174,9 @@ set(LVGL_SOURCES extra/widgets/span/lv_span.c extra/widgets/keyboard/lv_keyboard.c extra/widgets/msgbox/lv_msgbox.c - extra/themes/basic/lv_theme_basic.c - extra/themes/default/lv_theme_default.c - extra/themes/mono/lv_theme_mono.c + # extra/themes/basic/lv_theme_basic.c + # extra/themes/default/lv_theme_default.c + # extra/themes/mono/lv_theme_mono.c ) foreach(LVGL_FILE ${LVGL_SOURCES_MINIMAL}) From 5ace4d010e08ade15f2f5a70dcba4f04773e2ae0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 20 May 2022 12:05:27 +0200 Subject: [PATCH 617/714] Own window base LVGL object --- radio/src/thirdparty/libopenui/src/form.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.cpp | 52 +++++++++++++++---- radio/src/thirdparty/libopenui/src/window.h | 3 ++ 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index d1a959b38..0c4bb998e 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -172,7 +172,7 @@ void FormGroup::setFlexLayout(lv_flex_flow_t flow, lv_coord_t padding) FormGroup::Line* FormGroup::newLine(FlexGridLayout* layout) { if (layout) layout->resetPos(); - auto lv_line = lv_obj_create(lvobj); + auto lv_line = window_create(lvobj); return new Line(this, lv_line, layout); } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 36da8f89e..fdbd2f4eb 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -150,6 +150,35 @@ static void window_event_cb(lv_event_t * e) } } +static void window_base_event(const lv_obj_class_t* class_p, lv_event_t* e) +{ + /*Call the ancestor's event handler*/ + lv_res_t res = lv_obj_event_base(&window_base_class, e); + if(res != LV_RES_OK) return; + + window_event_cb(e); +} + +const lv_obj_class_t window_base_class = { + .base_class = &lv_obj_class, + .constructor_cb = nullptr, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = window_base_event, + .width_def = LV_DPI_DEF, + .height_def = LV_DPI_DEF, + .editable = LV_OBJ_CLASS_EDITABLE_FALSE, + .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE, + .instance_size = sizeof(lv_obj_t) +}; + +lv_obj_t* window_create(lv_obj_t* parent) +{ + lv_obj_t * obj = lv_obj_class_create_obj(&window_base_class, parent); + lv_obj_class_init_obj(obj); + return obj; +} + Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, LcdFlags textFlags, LvglCreate objConstruct) : rect(rect), @@ -157,9 +186,15 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, windowFlags(windowFlags), textFlags(textFlags) { - lv_obj_t *lvParent = parent != nullptr ? parent->lvobj : nullptr; - lvobj = (objConstruct == nullptr) ? lv_obj_create(lvParent) - : objConstruct(lvParent); + lv_obj_t* lv_parent = nullptr; + if (parent) lv_parent = parent->lvobj; + + if (objConstruct != nullptr) { + lvobj = objConstruct(lv_parent); + lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, nullptr); + } else { + lvobj = window_create(lv_parent); + } lv_obj_set_user_data(lvobj, this); @@ -174,7 +209,6 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC); - lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, this); if (parent) { parent->addChild(this); @@ -285,11 +319,11 @@ void Window::setTextFlags(LcdFlags flags) b = GET_BLUE(textColor); lv_obj_set_style_text_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); - // rco: shouldn't this be done via 'setTextFlags()' on the children? - for (uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { - auto child = lv_obj_get_child(lvobj, i); - lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN); - } + // // rco: shouldn't this be done via 'setTextFlags()' on the children? + // for (uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { + // auto child = lv_obj_get_child(lvobj, i); + // lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN); + // } } void Window::attach(Window *newParent) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 8513a6089..deee76402 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -61,6 +61,9 @@ enum SetFocusFlag }; typedef lv_obj_t *(*LvglCreate)(lv_obj_t *); +extern const lv_obj_class_t window_base_class; + +lv_obj_t* window_create(lv_obj_t* parent); class Window { From 4a77acf6aff0d93ddf76ecfb9f1b645027e3f16c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 21 May 2022 08:28:57 +0200 Subject: [PATCH 618/714] menu: removed separator --- radio/src/thirdparty/libopenui/src/menu.cpp | 30 +++------------------ radio/src/thirdparty/libopenui/src/menu.h | 19 +++---------- 2 files changed, 6 insertions(+), 43 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 31ee8f269..fe95e4010 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -106,7 +106,7 @@ void MenuBody::addLine(const std::string &text, std::function onPress, lines.emplace_back(std::move(onPress), std::move(isChecked), nullptr); auto idx = lines.size() - 1; - lv_table_set_cell_value_fmt(lvobj, idx, 0, text.c_str()); + lv_table_set_cell_value(lvobj, idx, 0, text.c_str()); } void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, @@ -200,29 +200,11 @@ void MenuBody::setIndex(int index) } } -void MenuBody::select(int index) -{ - // adjust the selection based on separators - for (int i = 0; i <= index; i++) { - if (lines[i].isSeparator) - index++; - index = rangeCheck(index); - } - - setIndex(index); -} - void MenuBody::selectNext(MENU_DIRECTION direction) { // look for the next non separator line int index = selectedIndex + direction; - index = rangeCheck(index); - while (lines[index].isSeparator) { - index += direction; - index = rangeCheck(index); - } - - setIndex(index); + setIndex(rangeCheck(index)); } @@ -247,7 +229,7 @@ void MenuBody::onEvent(event_t event) if (!lines.empty()) { onKeyPress(); if (selectedIndex < 0) { - select(0); + setIndex(0); } else { Menu * menu = getParentMenu(); @@ -348,12 +330,6 @@ void Menu::addLine(const uint8_t *icon_mask, const std::string &text, updatePosition(); } -void Menu::addSeparator() -{ - content->body.addSeparator(); - updatePosition(); -} - void Menu::removeLines() { content->body.removeLines(); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index c923f9d13..e58555b62 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -57,14 +57,12 @@ class MenuBody: public Window { } - MenuLine(bool isSeparator) : isSeparator(true) {} MenuLine(MenuLine &) = delete; MenuLine(MenuLine &&) = default; lv_obj_t* getIcon() { return icon.get(); } protected: - bool isSeparator = false; std::function onPress; std::function isChecked; std::unique_ptr icon; @@ -80,16 +78,11 @@ class MenuBody: public Window } #endif - void select(int index); + void setIndex(int index); int selection() const { - int index = selectedIndex; - for (int i = 0; i < selectedIndex; i++) - if (lines[i].isSeparator) - index--; - - return index; + return selectedIndex; } int count() const @@ -108,11 +101,6 @@ class MenuBody: public Window std::function onPress, std::function isChecked); - void addSeparator() - { - lines.emplace_back(true); - } - void removeLines(); void setCancelHandler(std::function handler) @@ -130,7 +118,6 @@ class MenuBody: public Window void selectNext(MENU_DIRECTION direction); int rangeCheck(int); - void setIndex(int index); std::vector lines; int selectedIndex = 0; @@ -217,7 +204,7 @@ class Menu: public ModalWindow void select(int index) { - content->body.select(index); + content->body.setIndex(index); } #if defined(HARDWARE_KEYS) From 2eef7755a56f949f60bb324ce64ed51b08b53fd3 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 21 May 2022 11:55:35 +0200 Subject: [PATCH 619/714] New TableField --- radio/src/thirdparty/libopenui/src/menu.cpp | 132 +++---- radio/src/thirdparty/libopenui/src/menu.h | 9 +- radio/src/thirdparty/libopenui/src/table.cpp | 283 +++++++++------ radio/src/thirdparty/libopenui/src/table.h | 363 ++----------------- 4 files changed, 250 insertions(+), 537 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index fe95e4010..8115907a4 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -23,54 +23,19 @@ #include -static void menu_clicked(lv_event_t *e) +void MenuBody::onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) { - lv_obj_t* target = lv_event_get_target(e); - if (!target) return; - - uint16_t row; - uint16_t col; - lv_table_get_selected_cell(target, &row, &col); - - MenuBody* mb = (MenuBody*)lv_obj_get_user_data(target); - if (!mb) return; - - mb->onPress(row); -} - -void menu_draw_begin(lv_event_t* e) -{ - lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); - if (dsc->part != LV_PART_ITEMS) return; - - lv_obj_t* table = lv_event_get_target(e); - if (!table) return; - - MenuBody* mb = (MenuBody*)lv_obj_get_user_data(table); - if (!mb) return; - - uint16_t row = dsc->id; - lv_canvas_t* icon = (lv_canvas_t*)mb->lines[row].getIcon(); + lv_canvas_t* icon = (lv_canvas_t*)lines[row].getIcon(); if (!icon) return; lv_img_t* img = &icon->img; - lv_coord_t cell_left = lv_obj_get_style_pad_left(table, LV_PART_ITEMS); + lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS); dsc->label_dsc->ofs_x = img->w + cell_left; } -void menu_draw_end(lv_event_t* e) +void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) { - lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); - if (dsc->part != LV_PART_ITEMS) return; - - lv_obj_t* table = lv_event_get_target(e); - if (!table) return; - - MenuBody* mb = (MenuBody*)lv_obj_get_user_data(table); - if (!mb) return; - - uint16_t row = dsc->id; - lv_obj_t* icon = mb->lines[row].getIcon(); + lv_obj_t* icon = lines[row].getIcon(); if (!icon) return; lv_draw_img_dsc_t img_dsc; @@ -81,7 +46,7 @@ void menu_draw_end(lv_event_t* e) lv_coord_t area_h = lv_area_get_height(dsc->draw_area); - lv_coord_t cell_left = lv_obj_get_style_pad_left(table, LV_PART_ITEMS); + lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS); coords.x1 = dsc->draw_area->x1 + cell_left; coords.x2 = coords.x1 + img->header.w - 1; coords.y1 = dsc->draw_area->y1 + (area_h - img->header.h) / 2; @@ -91,13 +56,10 @@ void menu_draw_end(lv_event_t* e) } MenuBody::MenuBody(Window * parent, const rect_t & rect): - Window(parent, rect, OPAQUE, 0, lv_table_create) + TableField(parent, rect) { - lv_table_set_col_cnt(lvobj, 1); - lv_table_set_col_width(lvobj, 0, rect.w); - lv_obj_add_event_cb(lvobj, menu_clicked, LV_EVENT_VALUE_CHANGED, nullptr); - lv_obj_add_event_cb(lvobj, menu_draw_begin, LV_EVENT_DRAW_PART_BEGIN, nullptr); - lv_obj_add_event_cb(lvobj, menu_draw_end, LV_EVENT_DRAW_PART_END, nullptr); + setColumnCount(1); + setColumnWidth(0, rect.w); } void MenuBody::addLine(const std::string &text, std::function onPress, @@ -128,7 +90,7 @@ void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, void MenuBody::removeLines() { lines.clear(); - lv_obj_clean(lvobj); + setRowCount(0); } coord_t MenuBody::getContentHeight() @@ -200,6 +162,11 @@ void MenuBody::setIndex(int index) } } +void MenuBody::onPress(uint16_t row, uint16_t col) +{ + onPress(row); +} + void MenuBody::selectNext(MENU_DIRECTION direction) { // look for the next non separator line @@ -211,40 +178,41 @@ void MenuBody::selectNext(MENU_DIRECTION direction) #if defined(HARDWARE_KEYS) void MenuBody::onEvent(event_t event) { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + // TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - if (event == EVT_ROTARY_RIGHT) { - if (!lines.empty()) { - selectNext(DIRECTION_UP); - onKeyPress(); - } - } - else if (event == EVT_ROTARY_LEFT) { - if (!lines.empty()) { - selectNext(DIRECTION_DOWN); - onKeyPress(); - } - } - else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - if (!lines.empty()) { - onKeyPress(); - if (selectedIndex < 0) { - setIndex(0); - } - else { - Menu * menu = getParentMenu(); - if (menu->multiple) { - lines[selectedIndex].onPress(); - menu->invalidate(); - } - else { - lines[selectedIndex].onPress(); - menu->deleteLater(); - } - } - } - } - else if (event == EVT_KEY_BREAK(KEY_EXIT)) { + // if (event == EVT_ROTARY_RIGHT) { + // if (!lines.empty()) { + // selectNext(DIRECTION_UP); + // onKeyPress(); + // } + // } + // else if (event == EVT_ROTARY_LEFT) { + // if (!lines.empty()) { + // selectNext(DIRECTION_DOWN); + // onKeyPress(); + // } + // } + // else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + // if (!lines.empty()) { + // onKeyPress(); + // if (selectedIndex < 0) { + // setIndex(0); + // } + // else { + // Menu * menu = getParentMenu(); + // if (menu->multiple) { + // lines[selectedIndex].onPress(); + // menu->invalidate(); + // } + // else { + // lines[selectedIndex].onPress(); + // menu->deleteLater(); + // } + // } + // } + // } + // else + if (event == EVT_KEY_BREAK(KEY_EXIT)) { onKeyPress(); if (onCancel) { onCancel(); @@ -252,7 +220,7 @@ void MenuBody::onEvent(event_t event) Window::onEvent(event); } else { - Window::onEvent(event); + TableField::onEvent(event); } } #endif diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index e58555b62..36b7fc017 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -23,7 +23,9 @@ #include #include #include + #include "modal_window.h" +#include "table.h" class Menu; class MenuWindowContent; @@ -35,7 +37,7 @@ struct lvobj_delete { } }; -class MenuBody: public Window +class MenuBody: public TableField { friend class MenuWindowContent; friend class Menu; @@ -113,8 +115,9 @@ class MenuBody: public Window void onPress(size_t index); protected: - friend void menu_draw_begin(lv_event_t *); - friend void menu_draw_end(lv_event_t *); + void onPress(uint16_t row, uint16_t col) override; + void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) override; + void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) override; void selectNext(MENU_DIRECTION direction); int rangeCheck(int); diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index a4401d838..d1b3a2188 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -18,146 +18,203 @@ */ #include "table.h" -#include "font.h" -void Table::Header::paint(BitmapBuffer * dc) +void TableField::clicked(lv_event_t *e) { - coord_t x = 10; - if (!cells.empty()) { - dc->clear(COLOR_THEME_FOCUS); - for (unsigned i = 0; i < cells.size(); i++) { - auto cell = cells[i]; - if (cell) { - cell->paint(dc, x, 0, TABLE_HEADER_FONT); - } - x += static_cast
(parent)->columnsWidth[i]; - } - } + lv_obj_t* target = lv_event_get_target(e); + if (!target) return; + + uint16_t row; + uint16_t col; + lv_table_get_selected_cell(target, &row, &col); + + TableField* tf = (TableField*)lv_obj_get_user_data(target); + if (!tf) return; + + tf->onPress(row, col); } -void Table::Body::checkEvents() +void TableField::draw_begin(lv_event_t* e) { - Window::checkEvents(); + lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); + if (dsc->part != LV_PART_ITEMS) return; - if (_deleted) + lv_obj_t* table = lv_event_get_target(e); + if (!table) return; + + uint16_t cols = lv_table_get_col_cnt(table); + if (cols == 0) return; + + TableField* tf = (TableField*)lv_obj_get_user_data(table); + if (!tf) return; + + uint16_t row = dsc->id / cols; + uint16_t col = dsc->id % cols; + tf->onDrawBegin(row, col, dsc); +} + +void TableField::draw_end(lv_event_t* e) +{ + lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); + if (dsc->part != LV_PART_ITEMS) return; + + lv_obj_t* table = lv_event_get_target(e); + if (!table) return; + + uint16_t cols = lv_table_get_col_cnt(table); + if (cols == 0) return; + + TableField* tf = (TableField*)lv_obj_get_user_data(table); + if (!tf) return; + + uint16_t row = dsc->id / cols; + uint16_t col = dsc->id % cols; + tf->onDrawEnd(row, col, dsc); +} + +TableField::TableField(Window* parent, const rect_t& rect, WindowFlags windowFlags) : + FormField(parent, rect, windowFlags, 0, lv_table_create) +{ + lv_obj_add_event_cb(lvobj, TableField::clicked, LV_EVENT_VALUE_CHANGED, nullptr); + lv_obj_add_event_cb(lvobj, TableField::draw_begin, LV_EVENT_DRAW_PART_BEGIN, nullptr); + lv_obj_add_event_cb(lvobj, TableField::draw_end, LV_EVENT_DRAW_PART_END, nullptr); +} + +void TableField::setColumnCount(uint16_t cols) +{ + lv_table_set_col_cnt(lvobj, cols); +} + +uint16_t TableField::getColumnCount() const +{ + return lv_table_get_col_cnt(lvobj); +} + +void TableField::setRowCount(uint16_t rows) +{ + lv_table_set_row_cnt(lvobj, rows); +} + +uint16_t TableField::getRowCount() const +{ + return lv_table_get_row_cnt(lvobj); +} + +void TableField::setColumnWidth(uint16_t col, coord_t w) +{ + lv_table_set_col_width(lvobj, col, w); +} + +coord_t TableField::getColumnWidth(uint16_t col) const +{ + return lv_table_get_col_width(lvobj, col); +} + +void TableField::select(uint16_t row, uint16_t col) +{ + lv_table_t* table = (lv_table_t*)lvobj; + if (table->row_act == row && table->col_act == row) return; + + if (row >= table->row_cnt || col >= table->col_cnt) { + table->col_act = LV_TABLE_CELL_NONE; + table->row_act = LV_TABLE_CELL_NONE; return; - - coord_t y = 0; - for (auto line: lines) { - coord_t x = 10; - for (unsigned i = 0; i < line->cells.size(); i++) { - auto cell = line->cells[i]; - auto width = static_cast
(parent)->columnsWidth[i]; - if (cell && cell->needsInvalidate()) { - invalidate({x, y, width, TABLE_LINE_HEIGHT - 2}); - } - x += width; - } - y += TABLE_LINE_HEIGHT; } + + table->row_act = row; + table->col_act = col; + + lv_obj_invalidate(lvobj); + adjustScroll(); } -void Table::Body::paint(BitmapBuffer * dc) +void TableField::adjustScroll() { - coord_t y = 0; - int index = 0; - dc->clear(COLOR_THEME_SECONDARY3); - for (auto line: lines) { - bool highlight = (index == selection); - dc->drawSolidFilledRect(0, y, width(), TABLE_LINE_HEIGHT - 2, highlight ? COLOR_THEME_FOCUS : COLOR_THEME_SECONDARY3); - coord_t x = 10; - for (unsigned i = 0; i < line->cells.size(); i++) { - auto cell = line->cells[i]; - if (cell) { - cell->paint(dc, x, y, line->flags + (highlight ? COLOR_THEME_PRIMARY2 - COLOR_MASK(line->flags) : COLOR_THEME_SECONDARY1)); - } - x += static_cast
(parent)->columnsWidth[i]; - } - y += TABLE_LINE_HEIGHT; - index += 1; + lv_table_t* table = (lv_table_t*)lvobj; + + // only vertical scroll for now + lv_coord_t h_before = 0; + for (uint16_t i = 0; i < table->row_act; i++) h_before += table->row_h[i]; + + lv_coord_t row_h = table->row_h[table->row_act]; + lv_coord_t scroll_y = lv_obj_get_scroll_y(lvobj); + + lv_obj_update_layout(lvobj); + lv_coord_t h = lv_obj_get_height(lvobj); + + lv_coord_t diff_y = 0; + if (h_before < scroll_y) { + diff_y = scroll_y - h_before; + } else if (scroll_y + h < h_before + row_h) { + diff_y = scroll_y + h - h_before - row_h; + } else { + return; } + + lv_obj_scroll_by_bounded(lvobj, 0, diff_y, LV_ANIM_OFF); } -#if defined(HARDWARE_TOUCH) -bool Table::Body::onTouchEnd(coord_t x, coord_t y) +void TableField::selectNext(int16_t dir) { - unsigned index = y / TABLE_LINE_HEIGHT; - if (index < lines.size()) { - onKeyPress(); - setFocus(SET_FOCUS_DEFAULT); - auto onPress = lines[index]->onPress; - if (onPress) - onPress(); - } - return true; -} -#endif + lv_table_t* table = (lv_table_t*)lvobj; -#if defined(HARDWARE_KEYS) -void Table::Body::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + if (table->col_act == LV_TABLE_CELL_NONE || + table->row_act == LV_TABLE_CELL_NONE) { - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - if (selection >= 0) { - onKeyPress(); - auto onPress = lines[selection]->onPress; - if (onPress) - onPress(); + if (table->col_cnt > 0 && table->row_cnt > 0) { + table->col_act = 0; + table->row_act = 0; } - } - if (event == EVT_ROTARY_RIGHT) { - onKeyPress(); - auto table = static_cast
(parent); - if (table->getWindowFlags() & FORWARD_SCROLL) { - auto index = selection + 1; - if (index < int(lines.size())) { - select(index, true); - } - else { - auto next = table->getNextField(); - if (next) { - next->setFocus(SET_FOCUS_FORWARD, this); - if (!hasFocus()) { - select(-1, false); + } else { + table->col_act += dir; + if (table->col_act >= table->col_cnt) { + + table->col_act = 0; + table->row_act += dir; + + if (table->row_act >= table->row_cnt) { + table->col_act = LV_TABLE_CELL_NONE; + table->row_act = LV_TABLE_CELL_NONE; + + // wrap around + if (table->col_cnt > 0 && table->row_cnt > 0) { + + if (dir < 0) { + table->col_act = table->col_cnt - 1; + table->row_act = table->row_cnt - 1; + } else { + table->col_act = 0; + table->row_act = 0; } } } } - else { - if (!lines.empty()) { - select((selection + 1) % lines.size(), true); - } - } + } + + lv_obj_invalidate(lvobj); + adjustScroll(); +} + +void TableField::onEvent(event_t event) +{ + if (event == EVT_ROTARY_RIGHT) { + onKeyPress(); + selectNext(1); } else if (event == EVT_ROTARY_LEFT) { onKeyPress(); - auto table = static_cast
(parent); - if (table->getWindowFlags() & FORWARD_SCROLL) { - auto index = selection - 1; - if (index >= 0) { - select(index, true); - } - else { - auto previous = table->getPreviousField(); - if (previous) { - select(-1, false); - previous->setFocus(SET_FOCUS_BACKWARD); - } - } - } - else { - if (!lines.empty()) { - select(selection <= 0 ? lines.size() - 1 : selection - 1, true); - } - } + selectNext(-1); } - else if (event == EVT_KEY_BREAK(KEY_EXIT) && selection >= 0) { - select(-1, true); - Window::onEvent(event); + else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + onKeyPress(); + + uint16_t row; + uint16_t col; + lv_table_get_selected_cell(lvobj, &row, &col); + + onPress(row, col); } else { Window::onEvent(event); - } + } } -#endif diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 639430cca..2d0b4e192 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -19,354 +19,39 @@ #pragma once -#include -#include #include "form.h" -#include "libopenui_config.h" -#include "font.h" -class Table: public FormField +class TableField : public FormField { - public: - class Cell - { - public: - virtual ~Cell() = default; - - virtual void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) = 0; - - virtual bool needsInvalidate() = 0; - }; - - class StringCell : public Cell - { - public: - StringCell() = default; - - explicit StringCell(std::string value): - value(std::move(value)) - { - } - - void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override - { - dc->drawText(x, y - 2 + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_HEADER_FONT)) / 2 + 3, value.c_str(), flags); - } - - bool needsInvalidate() override - { - return false; - } - - protected: - std::string value; - }; - - class DynamicStringCell : public Cell - { - public: - explicit DynamicStringCell(std::function getText): - getText(std::move(getText)) - { - } - - void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override - { - auto text = getText(); - dc->drawText(x, y - 2 + (TABLE_LINE_HEIGHT - getFontHeight(TABLE_BODY_FONT)) / 2 + 3, text.c_str(), SPACING_NUMBERS_CONST | flags); - } - - bool needsInvalidate() override - { - return true; // TODO optimize this - } - - protected: - std::function getText; - }; - - class CustomCell : public Cell - { - public: - explicit CustomCell(std::function paintFunction): - paintFunction(std::move(paintFunction)) - { - } - - void paint(BitmapBuffer * dc, coord_t x, coord_t y, LcdFlags flags) override - { - paintFunction(dc, x, y, flags); - } - - bool needsInvalidate() override - { - return true; - } - - protected: - std::function paintFunction; - }; - - class Line - { - public: - explicit Line(uint8_t columnsCount): - cells(columnsCount, nullptr) - { - } - - ~Line() - { - for (auto cell: cells) { - delete cell; - } - } - std::vector cells; - std::function onPress; - std::function onSelect; - LcdFlags flags = TABLE_BODY_FONT; - }; - - class Header: public Window, public Line - { - public: - Header(Table * parent, const rect_t & rect, uint8_t columnsCount): - Window(parent, rect, OPAQUE), - Line(columnsCount) - { - } + public: + TableField(Window* parent, const rect_t& rect, + WindowFlags windowFlags = OPAQUE | FORM_NO_BORDER); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "Table::Header"; - } + std::string getName() const override { return "Table"; } #endif - void paint(BitmapBuffer * dc) override; - }; + void setColumnCount(uint16_t cols); + uint16_t getColumnCount() const; + + void setRowCount(uint16_t rows); + uint16_t getRowCount() const; + + void setColumnWidth(uint16_t col, coord_t w); + coord_t getColumnWidth(uint16_t col) const; - class Body: public Window - { - friend class Table; + void select(uint16_t row, uint16_t col); + virtual void onPress(uint16_t row, uint16_t col) {} - public: - Body(Table * parent, const rect_t & rect, WindowFlags windowFlags): - Window(parent, rect, windowFlags) - { - } + protected: + void adjustScroll(); + void selectNext(int16_t dir); + void onEvent(event_t event) override; - ~Body() override - { - clear(); - } + virtual void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {} + virtual void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {} -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "Table::Body"; - } -#endif - - void addLine(Line * line) - { - lines.push_back(line); - if (hasFocus() && selection < 0) { - select(0, true); - } - } - - void setLineFlags(uint8_t index, LcdFlags flags) - { - if (lines[index]->flags != flags) { - lines[index]->flags = flags; - invalidate({0, index * TABLE_LINE_HEIGHT, width(), TABLE_LINE_HEIGHT}); - } - } - - void clear() - { - for (auto line: lines) { - delete line; - } - lines.clear(); - } - - void select(int index, bool scroll) - { - selection = index; - if (scroll) { - coord_t y = index * TABLE_LINE_HEIGHT; - Window * window = this; - while (window->getWindowFlags() & FORWARD_SCROLL) { - y += window->top(); - window = window->getParent(); - } - const rect_t rect = { - 0, - y, - width(), - TABLE_LINE_HEIGHT - }; - window->scrollTo(rect); - } - invalidate(); - if (index >= 0) { - auto onSelect = lines[index]->onSelect; - if (onSelect) { - onSelect(); - } - } - } - - void paint(BitmapBuffer * dc) override; - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif - - void checkEvents() override; - - protected: - std::vector lines; - int selection = -1; - }; - - public: - Table(Window * parent, const rect_t & rect, uint8_t columnsCount, WindowFlags windowFlags = OPAQUE | FORM_NO_BORDER): - FormField(parent, rect, windowFlags), - columnsCount(columnsCount), - columnsWidth(columnsCount, width() / columnsCount), - header(this, {0, 0, width(), 0}, columnsCount), - body(this, {0, 0, width(), height()}, windowFlags) - { - } - -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "Table"; - } -#endif - - void deleteLater(bool detach = true, bool trash = true) override // NOLINT(google-default-arguments) - { - if (_deleted) - return; - - header.deleteLater(true, false); - body.deleteLater(true, false); - - FormField::deleteLater(detach, trash); - } - - void setColumnsWidth(const coord_t values[]) - { - int restColumn = -1; - coord_t restWidth = width(); - for (uint8_t i = 0; i < columnsCount; i++) { - auto columnWidth = values[i]; - columnsWidth[i] = columnWidth; - if (columnWidth == 0) { - restColumn = i; - } - else { - restWidth -= columnWidth; - } - } - if (restColumn >= 0) { - columnsWidth[restColumn] = restWidth; - } - } - - int getSelection() const - { - return body.selection; - } - - void clearSelection() - { - body.selection = -1; - body.invalidate(); - } - - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override // NOLINT(google-default-arguments) - { - if (body.lines.empty()) { - if (flag == SET_FOCUS_BACKWARD) { - if (previous) { - previous->setFocus(flag, this); - } - } - else { - if (next) { - next->setFocus(flag, this); - } - } - } - else { - body.setFocus(flag, from); - if (body.selection < 0) { - select(flag == SET_FOCUS_BACKWARD ? (int)body.lines.size() - 1 : 0); - } - } - } - - void select(int index, bool scroll = true) - { - body.select(index, scroll); - } - - void setLineFlags(uint8_t index, LcdFlags flags) - { - body.setLineFlags(index, flags); - } - - void setHeader(const char * const values[]) - { - header.setHeight(TABLE_HEADER_HEIGHT); - body.setTop(TABLE_HEADER_HEIGHT); - body.setHeight(height() - TABLE_HEADER_HEIGHT); - for (uint8_t i = 0; i < columnsCount; i++) { - header.cells[i] = new StringCell(values[i]); - } - } - - void addLine(Line * line, std::function onPress = nullptr, std::function onSelect = nullptr) - { - line->onPress = std::move(onPress); - line->onSelect = std::move(onSelect); - body.addLine(line); - } - - void addLine(const char * const values[], std::function onPress = nullptr, std::function onSelect = nullptr) - { - Line * line = new Line(columnsCount); - for (uint8_t i = 0; i < columnsCount; i++) { - line->cells[i] = new StringCell(values[i]); - } - addLine(line, std::move(onPress), std::move(onSelect)); - } - - void clear() - { - clearSelection(); - body.clear(); - } - - uint8_t size() const - { - return body.lines.size(); - } - - protected: - uint8_t columnsCount; - std::vector columnsWidth; - Header header; - Body body; + static void clicked(lv_event_t *e); + static void draw_begin(lv_event_t *e); + static void draw_end(lv_event_t *e); }; - From f8612a0dbece9b4e97421f7e55c5c461e54f9caf Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 21 May 2022 12:14:27 +0200 Subject: [PATCH 620/714] menu: onPress() must be executed after menu is removed --- radio/src/thirdparty/libopenui/src/menu.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 8115907a4..a880947b5 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -109,9 +109,10 @@ void MenuBody::onPress(size_t index) else setIndex(index); } else { - setIndex(index); - lines[index].onPress(); + // delete menu first to avoid + // focus issues with onPress() menu->deleteLater(); + lines[index].onPress(); } } } From e97cff4dcb17b42f555eda1d2d204f9461f65dfc Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 22 May 2022 14:14:13 +0200 Subject: [PATCH 621/714] dialog: handle EXIT key --- radio/src/thirdparty/libopenui/src/dialog.cpp | 10 ++++++++++ radio/src/thirdparty/libopenui/src/dialog.h | 1 + 2 files changed, 11 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 745c0a6eb..6c2cd1af7 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -57,3 +57,13 @@ Dialog::Dialog(Window* parent, std::string title, const rect_t& rect) : bringToTop(); if (!title.empty()) content->setTitle(std::move(title)); } + +void Dialog::onEvent(event_t event) +{ +#if defined(HARDWARE_KEYS) + if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { + killEvents(event); + deleteLater(); + } +#endif +} diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 2cbb86680..3b00520b8 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -48,5 +48,6 @@ class Dialog : public ModalWindow Dialog(Window* parent, std::string title, const rect_t& rect); protected: + void onEvent(event_t event) override; DialogWindowContent* content; }; From c136ddda879ec0a210d70e1df6a19ff6d9a2f827 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 23 May 2022 08:55:23 +0200 Subject: [PATCH 622/714] dialog: content shall forward focus --- radio/src/thirdparty/libopenui/src/dialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 6c2cd1af7..00f599ccd 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -26,7 +26,7 @@ DialogWindowContent::DialogWindowContent(Dialog* parent, const rect_t& rect) : form(this, { 0, POPUP_HEADER_HEIGHT, rect.w, coord_t(rect.h - POPUP_HEADER_HEIGHT) }, - FORM_NO_BORDER) + FORM_NO_BORDER | FORM_FORWARD_FOCUS) { form.setFocus(SET_FOCUS_DEFAULT); } From 3d647f69a058ee445bcfb609ed00de6a667f523c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 23 May 2022 18:38:48 +0200 Subject: [PATCH 623/714] LZ4 bitmap compression --- .../thirdparty/libopenui/src/CMakeLists.txt | 4 +- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 74 +- .../thirdparty/libopenui/src/bitmapbuffer.h | 16 + .../libopenui/thirdparty/lz4/LICENSE | 24 + .../libopenui/thirdparty/lz4/README.md | 137 + .../thirdparty/libopenui/thirdparty/lz4/lz4.c | 2495 +++++++++++++++++ .../thirdparty/libopenui/thirdparty/lz4/lz4.h | 774 +++++ .../libopenui/tools/encode-bitmap.py | 30 +- 8 files changed, 3546 insertions(+), 8 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/thirdparty/lz4/LICENSE create mode 100644 radio/src/thirdparty/libopenui/thirdparty/lz4/README.md create mode 100644 radio/src/thirdparty/libopenui/thirdparty/lz4/lz4.c create mode 100644 radio/src/thirdparty/libopenui/thirdparty/lz4/lz4.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 87fd91e79..07c62cea4 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -51,7 +51,9 @@ foreach(LIBOPENUI_FILE ${LIBOPENUI_SRC}) set(LIBOPENUI_SRC_FILES ${LIBOPENUI_SRC_FILES} ${LIBOPENUI_SRC_DIR}/src/${LIBOPENUI_FILE}) endforeach() -set(SRC ${SRC} ${LIBOPENUI_SRC_FILES}) +set(SRC ${SRC} ${LIBOPENUI_SRC_FILES} + ${LIBOPENUI_SRC_DIR}/thirdparty/lz4/lz4.c + ) # add_library(libopenui_native OBJECT ${LIBOPENUI_SRC_FILES}) # add_library(libopenui_stm32 OBJECT ${LIBOPENUI_SRC_FILES}) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 34cbec2e6..913104421 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -125,16 +125,13 @@ void BitmapBuffer::drawBitmap(coord_t x, coord_t y, const T *bmp, coord_t srcx, } template void BitmapBuffer::drawBitmap( - coord_t, coord_t, BitmapBufferBase const *, coord_t, + coord_t, coord_t, const BitmapBufferBase *, coord_t, coord_t, coord_t, coord_t, float); template void BitmapBuffer::drawBitmap(coord_t, coord_t, const BitmapBuffer *, coord_t, coord_t, coord_t, coord_t, float); -template void BitmapBuffer::drawBitmap(coord_t, coord_t, const RLEBitmap *, - coord_t, coord_t, coord_t, coord_t, - float); template void BitmapBuffer::drawScaledBitmap(const T *bitmap, coord_t x, coord_t y, @@ -1269,7 +1266,7 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) return bitmap; } -BitmapBuffer * BitmapBuffer::load8bitMask(const uint8_t * lbm) +BitmapBuffer* BitmapBuffer::load8bitMask(const uint8_t * lbm) { BitmapBuffer * bitmap = new BitmapBuffer(BMP_RGB565,lbm[0],lbm[1]); if (bitmap) { @@ -1283,6 +1280,21 @@ BitmapBuffer * BitmapBuffer::load8bitMask(const uint8_t * lbm) return bitmap; } +BitmapBuffer* BitmapBuffer::load8bitMaskLZ4(const uint8_t* compressed_data) +{ + LZ4Mask lz4_m(compressed_data); + + BitmapBuffer *bitmap = + new BitmapBuffer(BMP_RGB565, lz4_m.width(), lz4_m.height()); + if (bitmap) { + pixel_t *p = bitmap->getData(); + const uint8_t *src = lz4_m.getData(); + for (int i = bitmap->width() * bitmap->height(); i > 0; i--) + *((uint8_t *)p++) = (*(src++) >> 4); + } + return bitmap; +} + BitmapBuffer * BitmapBuffer::invertMask() const { BitmapBuffer * result = new BitmapBuffer(format, width(), height()); @@ -1730,3 +1742,55 @@ uint8_t * BitmapBuffer::loadFont(const uint8_t * lbm, int len, int& w, int& h) return font; } + +#include "../thirdparty/lz4/lz4.h" + +LZ4Bitmap::LZ4Bitmap(uint8_t format, const uint8_t* compressed_data) : + BitmapBuffer(format, 0, 0, nullptr) +{ + const uint16_t* hdr = (const uint16_t*)compressed_data; + _width = hdr[0]; + _height = hdr[1]; + + size_t len = *(uint32_t*)&hdr[2]; + + // skip 8 bytes header + compressed_data += 8; + + uint32_t pixels = _width * _height; + data = (uint16_t*)malloc(align32(pixels * sizeof(uint16_t))); + + LZ4_decompress_safe((const char *)compressed_data, (char *)data, len, + pixels * sizeof(uint16_t)); + data_end = data + pixels; +} + +LZ4Bitmap::~LZ4Bitmap() +{ + free(data); +} + +LZ4Mask::LZ4Mask(const uint8_t* compressed_data) : + BitmapBufferBase(BMP_8BIT, 0, 0, nullptr) +{ + const uint16_t* hdr = (const uint16_t*)compressed_data; + _width = hdr[0]; + _height = hdr[1]; + + size_t len = *(uint32_t*)&hdr[2]; + + // skip 8 bytes header + compressed_data += 8; + + uint32_t pixels = _width * _height; + data = (uint8_t*)malloc(align32(pixels)); + + LZ4_decompress_safe((const char *)compressed_data, (char *)data, len, + pixels); + data_end = data + pixels; +} + +LZ4Mask::~LZ4Mask() +{ + free(data); +} diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index ce3f05fc0..5d11c0678 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -48,6 +48,7 @@ constexpr uint8_t STASHED = 0x33; enum BitmapFormats { + BMP_8BIT, BMP_RGB565, BMP_ARGB4444 }; @@ -313,6 +314,7 @@ class BitmapBuffer: public BitmapBufferBase static BitmapBuffer * loadMask(const char * filename); static BitmapBuffer * load8bitMask(const uint8_t * lbm); + static BitmapBuffer * load8bitMaskLZ4(const uint8_t * compressed_data); static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background); static BitmapBuffer * load8bitMaskOnBackground(const uint8_t * lbm, LcdFlags foreground, LcdFlags background); @@ -447,6 +449,20 @@ class BitmapBuffer: public BitmapBufferBase bool liangBarskyClipper(coord_t& x1, coord_t& y1, coord_t& x2, coord_t& y2); }; +class LZ4Bitmap : public BitmapBuffer +{ + public: + LZ4Bitmap(uint8_t format, const uint8_t* compressed_data); + ~LZ4Bitmap(); +}; + +class LZ4Mask : public BitmapBufferBase +{ + public: + LZ4Mask(const uint8_t* compressed_data); + ~LZ4Mask(); +}; + // Back buffer to draw extern BitmapBuffer * lcd; diff --git a/radio/src/thirdparty/libopenui/thirdparty/lz4/LICENSE b/radio/src/thirdparty/libopenui/thirdparty/lz4/LICENSE new file mode 100644 index 000000000..74c2cdd7d --- /dev/null +++ b/radio/src/thirdparty/libopenui/thirdparty/lz4/LICENSE @@ -0,0 +1,24 @@ +LZ4 Library +Copyright (c) 2011-2016, Yann Collet +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/radio/src/thirdparty/libopenui/thirdparty/lz4/README.md b/radio/src/thirdparty/libopenui/thirdparty/lz4/README.md new file mode 100644 index 000000000..e2af868ff --- /dev/null +++ b/radio/src/thirdparty/libopenui/thirdparty/lz4/README.md @@ -0,0 +1,137 @@ +LZ4 - Library Files +================================ + +The `/lib` directory contains many files, but depending on project's objectives, +not all of them are necessary. + +#### Minimal LZ4 build + +The minimum required is **`lz4.c`** and **`lz4.h`**, +which provides the fast compression and decompression algorithms. +They generate and decode data using the [LZ4 block format]. + + +#### High Compression variant + +For more compression ratio at the cost of compression speed, +the High Compression variant called **lz4hc** is available. +Add files **`lz4hc.c`** and **`lz4hc.h`**. +This variant also compresses data using the [LZ4 block format], +and depends on regular `lib/lz4.*` source files. + + +#### Frame support, for interoperability + +In order to produce compressed data compatible with `lz4` command line utility, +it's necessary to use the [official interoperable frame format]. +This format is generated and decoded automatically by the **lz4frame** library. +Its public API is described in `lib/lz4frame.h`. +In order to work properly, lz4frame needs all other modules present in `/lib`, +including, lz4 and lz4hc, and also **xxhash**. +So it's necessary to include all `*.c` and `*.h` files present in `/lib`. + + +#### Advanced / Experimental API + +Definitions which are not guaranteed to remain stable in future versions, +are protected behind macros, such as `LZ4_STATIC_LINKING_ONLY`. +As the name strongly implies, these definitions should only be invoked +in the context of static linking ***only***. +Otherwise, dependent application may fail on API or ABI break in the future. +The associated symbols are also not exposed by the dynamic library by default. +Should they be nonetheless needed, it's possible to force their publication +by using build macros `LZ4_PUBLISH_STATIC_FUNCTIONS` +and `LZ4F_PUBLISH_STATIC_FUNCTIONS`. + + +#### Build macros + +The following build macro can be selected to adjust source code behavior at compilation time : + +- `LZ4_FAST_DEC_LOOP` : this triggers a speed optimized decompression loop, more powerful on modern cpus. + This loop works great on `x86`, `x64` and `aarch64` cpus, and is automatically enabled for them. + It's also possible to enable or disable it manually, by passing `LZ4_FAST_DEC_LOOP=1` or `0` to the preprocessor. + For example, with `gcc` : `-DLZ4_FAST_DEC_LOOP=1`, + and with `make` : `CPPFLAGS+=-DLZ4_FAST_DEC_LOOP=1 make lz4`. + +- `LZ4_DISTANCE_MAX` : control the maximum offset that the compressor will allow. + Set to 65535 by default, which is the maximum value supported by lz4 format. + Reducing maximum distance will reduce opportunities for LZ4 to find matches, + hence will produce a worse compression ratio. + However, a smaller max distance can allow compatibility with specific decoders using limited memory budget. + This build macro only influences the compressed output of the compressor. + +- `LZ4_DISABLE_DEPRECATE_WARNINGS` : invoking a deprecated function will make the compiler generate a warning. + This is meant to invite users to update their source code. + Should this be a problem, it's generally possible to make the compiler ignore these warnings, + for example with `-Wno-deprecated-declarations` on `gcc`, + or `_CRT_SECURE_NO_WARNINGS` for Visual Studio. + This build macro offers another project-specific method + by defining `LZ4_DISABLE_DEPRECATE_WARNINGS` before including the LZ4 header files. + +- `LZ4_USER_MEMORY_FUNCTIONS` : replace calls to 's `malloc`, `calloc` and `free` + by user-defined functions, which must be called `LZ4_malloc()`, `LZ4_calloc()` and `LZ4_free()`. + User functions must be available at link time. + +- `LZ4_FORCE_SW_BITCOUNT` : by default, the compression algorithm tries to determine lengths + by using bitcount instructions, generally implemented as fast single instructions in many cpus. + In case the target cpus doesn't support it, or compiler intrinsic doesn't work, or feature bad performance, + it's possible to use an optimized software path instead. + This is achieved by setting this build macros . + In most cases, it's not expected to be necessary, + but it can be legitimately considered for less common platforms. + +- `LZ4_ALIGN_TEST` : alignment test ensures that the memory area + passed as argument to become a compression state is suitably aligned. + This test can be disabled if it proves flaky, by setting this value to 0. + + +#### Amalgamation + +lz4 source code can be amalgamated into a single file. +One can combine all source code into `lz4_all.c` by using following command: +``` +cat lz4.c lz4hc.c lz4frame.c > lz4_all.c +``` +(`cat` file order is important) then compile `lz4_all.c`. +All `*.h` files present in `/lib` remain necessary to compile `lz4_all.c`. + + +#### Windows : using MinGW+MSYS to create DLL + +DLL can be created using MinGW+MSYS with the `make liblz4` command. +This command creates `dll\liblz4.dll` and the import library `dll\liblz4.lib`. +To override the `dlltool` command when cross-compiling on Linux, just set the `DLLTOOL` variable. Example of cross compilation on Linux with mingw-w64 64 bits: +``` +make BUILD_STATIC=no CC=x86_64-w64-mingw32-gcc DLLTOOL=x86_64-w64-mingw32-dlltool OS=Windows_NT +``` +The import library is only required with Visual C++. +The header files `lz4.h`, `lz4hc.h`, `lz4frame.h` and the dynamic library +`dll\liblz4.dll` are required to compile a project using gcc/MinGW. +The dynamic library has to be added to linking options. +It means that if a project that uses LZ4 consists of a single `test-dll.c` +file it should be linked with `dll\liblz4.dll`. For example: +``` + $(CC) $(CFLAGS) -Iinclude/ test-dll.c -o test-dll dll\liblz4.dll +``` +The compiled executable will require LZ4 DLL which is available at `dll\liblz4.dll`. + + +#### Miscellaneous + +Other files present in the directory are not source code. They are : + + - `LICENSE` : contains the BSD license text + - `Makefile` : `make` script to compile and install lz4 library (static and dynamic) + - `liblz4.pc.in` : for `pkg-config` (used in `make install`) + - `README.md` : this file + +[official interoperable frame format]: ../doc/lz4_Frame_format.md +[LZ4 block format]: ../doc/lz4_Block_format.md + + +#### License + +All source material within __lib__ directory are BSD 2-Clause licensed. +See [LICENSE](LICENSE) for details. +The license is also reminded at the top of each source file. diff --git a/radio/src/thirdparty/libopenui/thirdparty/lz4/lz4.c b/radio/src/thirdparty/libopenui/thirdparty/lz4/lz4.c new file mode 100644 index 000000000..9f5e9bfa0 --- /dev/null +++ b/radio/src/thirdparty/libopenui/thirdparty/lz4/lz4.c @@ -0,0 +1,2495 @@ +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-present, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ + +/*-************************************ +* Tuning parameters +**************************************/ +/* + * LZ4_HEAPMODE : + * Select how default compression functions will allocate memory for their hash table, + * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#ifndef LZ4_HEAPMODE +# define LZ4_HEAPMODE 0 +#endif + +/* + * LZ4_ACCELERATION_DEFAULT : + * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 + */ +#define LZ4_ACCELERATION_DEFAULT 1 +/* + * LZ4_ACCELERATION_MAX : + * Any "acceleration" value higher than this threshold + * get treated as LZ4_ACCELERATION_MAX instead (fix #876) + */ +#define LZ4_ACCELERATION_MAX 65537 + + +/*-************************************ +* CPU Feature Detection +**************************************/ +/* LZ4_FORCE_MEMORY_ACCESS + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method is portable but violate C standard. + * It can generate buggy code on targets which assembly generation depends on alignment. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ +# if defined(__GNUC__) && \ + ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define LZ4_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) +# define LZ4_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/* + * LZ4_FORCE_SW_BITCOUNT + * Define this parameter if your target system or compiler does not support hardware bit count + */ +#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ +# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */ +# define LZ4_FORCE_SW_BITCOUNT +#endif + + + +/*-************************************ +* Dependency +**************************************/ +/* + * LZ4_SRC_INCLUDED: + * Amalgamation flag, whether lz4.c is included + */ +#ifndef LZ4_SRC_INCLUDED +# define LZ4_SRC_INCLUDED 1 +#endif + +#ifndef LZ4_STATIC_LINKING_ONLY +#define LZ4_STATIC_LINKING_ONLY +#endif + +#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS +#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ +#endif + +#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ +#include "lz4.h" +/* see also "memory routines" below */ + + +/*-************************************ +* Compiler Options +**************************************/ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ +# include /* only present in VS2005+ */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#endif /* _MSC_VER */ + +#ifndef LZ4_FORCE_INLINE +# ifdef _MSC_VER /* Visual Studio */ +# define LZ4_FORCE_INLINE static __forceinline +# else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define LZ4_FORCE_INLINE static inline +# endif +# else +# define LZ4_FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +# endif /* _MSC_VER */ +#endif /* LZ4_FORCE_INLINE */ + +/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE + * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, + * together with a simple 8-byte copy loop as a fall-back path. + * However, this optimization hurts the decompression speed by >30%, + * because the execution does not go to the optimized loop + * for typical compressible data, and all of the preamble checks + * before going to the fall-back path become useless overhead. + * This optimization happens only with the -O3 flag, and -O2 generates + * a simple 8-byte copy loop. + * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 + * functions are annotated with __attribute__((optimize("O2"))), + * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute + * of LZ4_wildCopy8 does not affect the compression speed. + */ +#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) +# define LZ4_FORCE_O2 __attribute__((optimize("O2"))) +# undef LZ4_FORCE_INLINE +# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline)) +#else +# define LZ4_FORCE_O2 +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) +# define expect(expr,value) (__builtin_expect ((expr),(value)) ) +#else +# define expect(expr,value) (expr) +#endif + +#ifndef likely +#define likely(expr) expect((expr) != 0, 1) +#endif +#ifndef unlikely +#define unlikely(expr) expect((expr) != 0, 0) +#endif + +/* Should the alignment test prove unreliable, for some reason, + * it can be disabled by setting LZ4_ALIGN_TEST to 0 */ +#ifndef LZ4_ALIGN_TEST /* can be externally provided */ +# define LZ4_ALIGN_TEST 1 +#endif + + +/*-************************************ +* Memory routines +**************************************/ +#ifdef LZ4_USER_MEMORY_FUNCTIONS +/* memory management functions can be customized by user project. + * Below functions must exist somewhere in the Project + * and be available at link time */ +void* LZ4_malloc(size_t s); +void* LZ4_calloc(size_t n, size_t s); +void LZ4_free(void* p); +# define ALLOC(s) LZ4_malloc(s) +# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s) +# define FREEMEM(p) LZ4_free(p) +#else +# include /* malloc, calloc, free */ +# define ALLOC(s) malloc(s) +# define ALLOC_AND_ZERO(s) calloc(1,s) +# define FREEMEM(p) free(p) +#endif + +#include /* memset, memcpy */ +#define MEM_INIT(p,v,s) memset((p),(v),(s)) + + +/*-************************************ +* Common Constants +**************************************/ +#define MINMATCH 4 + +#define WILDCOPYLENGTH 8 +#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ +#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ +#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ +#define FASTLOOP_SAFE_DISTANCE 64 +static const int LZ4_minLength = (MFLIMIT+1); + +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define LZ4_DISTANCE_ABSOLUTE_MAX 65535 +#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ +# error "LZ4_DISTANCE_MAX is too big : must be <= 65535" +#endif + +#define ML_BITS 4 +#define ML_MASK ((1U<=1) +# include +#else +# ifndef assert +# define assert(condition) ((void)0) +# endif +#endif + +#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) +# include + static int g_debuglog_enable = 1; +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ + fprintf(stderr, __FILE__ ": "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } +#else +# define DEBUGLOG(l, ...) {} /* disabled */ +#endif + +static int LZ4_isAligned(const void* ptr, size_t alignment) +{ + return ((size_t)ptr & (alignment -1)) == 0; +} + + +/*-************************************ +* Types +**************************************/ +#include +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; + typedef uintptr_t uptrval; +#else +# if UINT_MAX != 4294967295UL +# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4" +# endif + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; + typedef size_t uptrval; /* generally true, except OpenVMS-64 */ +#endif + +#if defined(__x86_64__) + typedef U64 reg_t; /* 64-bits in x32 mode */ +#else + typedef size_t reg_t; /* 32-bits in x32 mode */ +#endif + +typedef enum { + notLimited = 0, + limitedOutput = 1, + fillOutput = 2 +} limitedOutput_directive; + + +/*-************************************ +* Reading and writing into memory +**************************************/ + +/** + * LZ4 relies on memcpy with a constant size being inlined. In freestanding + * environments, the compiler can't assume the implementation of memcpy() is + * standard compliant, so it can't apply its specialized memcpy() inlining + * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze + * memcpy() as if it were standard compliant, so it can inline it in freestanding + * environments. This is needed when decompressing the Linux Kernel, for example. + */ +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) +#else +#define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) +#endif + +static unsigned LZ4_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} + + +#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) +/* lie to the compiler about data alignment; use with caution */ + +static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } +static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } +static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } + +static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } +static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } + +#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign; + +static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } +static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } +static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; } + +static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } +static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } + +#else /* safe and portable access using memcpy() */ + +static U16 LZ4_read16(const void* memPtr) +{ + U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static U32 LZ4_read32(const void* memPtr) +{ + U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static reg_t LZ4_read_ARCH(const void* memPtr) +{ + reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static void LZ4_write16(void* memPtr, U16 value) +{ + LZ4_memcpy(memPtr, &value, sizeof(value)); +} + +static void LZ4_write32(void* memPtr, U32 value) +{ + LZ4_memcpy(memPtr, &value, sizeof(value)); +} + +#endif /* LZ4_FORCE_MEMORY_ACCESS */ + + +static U16 LZ4_readLE16(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read16(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + return (U16)((U16)p[0] + (p[1]<<8)); + } +} + +static void LZ4_writeLE16(void* memPtr, U16 value) +{ + if (LZ4_isLittleEndian()) { + LZ4_write16(memPtr, value); + } else { + BYTE* p = (BYTE*)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ +LZ4_FORCE_INLINE +void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ +LZ4_FORCE_INLINE void +LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH + * - there is at least 8 bytes available to write after dstEnd */ +LZ4_FORCE_INLINE void +LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) +{ + BYTE v[8]; + + assert(dstEnd >= dstPtr + MINMATCH); + + switch(offset) { + case 1: + MEM_INIT(v, *srcPtr, 8); + break; + case 2: + LZ4_memcpy(v, srcPtr, 2); + LZ4_memcpy(&v[2], srcPtr, 2); + LZ4_memcpy(&v[4], v, 4); + break; + case 4: + LZ4_memcpy(v, srcPtr, 4); + LZ4_memcpy(&v[4], srcPtr, 4); + break; + default: + LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); + return; + } + + LZ4_memcpy(dstPtr, v, 8); + dstPtr += 8; + while (dstPtr < dstEnd) { + LZ4_memcpy(dstPtr, v, 8); + dstPtr += 8; + } +} +#endif + + +/*-************************************ +* Common functions +**************************************/ +static unsigned LZ4_NbCommonBytes (reg_t val) +{ + assert(val != 0); + if (LZ4_isLittleEndian()) { + if (sizeof(val) == 8) { +# if defined(_MSC_VER) && (_MSC_VER >= 1800) && defined(_M_AMD64) && !defined(LZ4_FORCE_SW_BITCOUNT) + /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ + return (unsigned)_tzcnt_u64(val) >> 3; +# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanForward64(&r, (U64)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctzll((U64)val) >> 3; +# else + const U64 m = 0x0101010101010101ULL; + val ^= val - 1; + return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56); +# endif + } else /* 32 bits */ { +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward(&r, (U32)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctz((U32)val) >> 3; +# else + const U32 m = 0x01010101; + return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; +# endif + } + } else /* Big Endian CPU */ { + if (sizeof(val)==8) { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clzll((U64)val) >> 3; +# else +#if 1 + /* this method is probably faster, + * but adds a 128 bytes lookup table */ + static const unsigned char ctz7_tab[128] = { + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + }; + U64 const mask = 0x0101010101010101ULL; + U64 const t = (((val >> 8) - mask) | val) & mask; + return ctz7_tab[(t * 0x0080402010080402ULL) >> 57]; +#else + /* this method doesn't consume memory space like the previous one, + * but it contains several branches, + * that may end up slowing execution */ + static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. + Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. + Note that this code path is never triggered in 32-bits mode. */ + unsigned r; + if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } + if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } + r += (!val); + return r; +#endif +# endif + } else /* 32 bits */ { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clz((U32)val) >> 3; +# else + val >>= 8; + val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | + (val + 0x00FF0000)) >> 24; + return (unsigned)val ^ 3; +# endif + } + } +} + + +#define STEPSIZE sizeof(reg_t) +LZ4_FORCE_INLINE +unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) +{ + const BYTE* const pStart = pIn; + + if (likely(pIn < pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { + pIn+=STEPSIZE; pMatch+=STEPSIZE; + } else { + return LZ4_NbCommonBytes(diff); + } } + + while (likely(pIn < pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } + pIn += LZ4_NbCommonBytes(diff); + return (unsigned)(pIn - pStart); + } + + if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } + if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } + if ((pIn compression run slower on incompressible data */ + + +/*-************************************ +* Local Structures and types +**************************************/ +typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; + +/** + * This enum distinguishes several different modes of accessing previous + * content in the stream. + * + * - noDict : There is no preceding content. + * - withPrefix64k : Table entries up to ctx->dictSize before the current blob + * blob being compressed are valid and refer to the preceding + * content (of length ctx->dictSize), which is available + * contiguously preceding in memory the content currently + * being compressed. + * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere + * else in memory, starting at ctx->dictionary with length + * ctx->dictSize. + * - usingDictCtx : Like usingExtDict, but everything concerning the preceding + * content is in a separate context, pointed to by + * ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table + * entries in the current context that refer to positions + * preceding the beginning of the current compression are + * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx + * ->dictSize describe the location and size of the preceding + * content, and matches are found by looking in the ctx + * ->dictCtx->hashTable. + */ +typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; +typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; + + +/*-************************************ +* Local Utils +**************************************/ +int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } +const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } +int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } +int LZ4_sizeofState(void) { return LZ4_STREAMSIZE; } + + +/*-************************************ +* Internal Definitions used in Tests +**************************************/ +#if defined (__cplusplus) +extern "C" { +#endif + +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize); + +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, + int compressedSize, int maxOutputSize, + const void* dictStart, size_t dictSize); + +#if defined (__cplusplus) +} +#endif + +/*-****************************** +* Compression functions +********************************/ +LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType) +{ + if (tableType == byU16) + return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); + else + return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); +} + +LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) +{ + const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; + if (LZ4_isLittleEndian()) { + const U64 prime5bytes = 889523592379ULL; + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + } else { + const U64 prime8bytes = 11400714785074694791ULL; + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); + } +} + +LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) +{ + if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); + return LZ4_hash4(LZ4_read32(p), tableType); +} + +LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) +{ + switch (tableType) + { + default: /* fallthrough */ + case clearedTable: { /* illegal! */ assert(0); return; } + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) +{ + switch (tableType) + { + default: /* fallthrough */ + case clearedTable: /* fallthrough */ + case byPtr: { /* illegal! */ assert(0); return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } + case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, + void* tableBase, tableType_t const tableType, + const BYTE* srcBase) +{ + switch (tableType) + { + case clearedTable: { /* illegal! */ assert(0); return; } + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + U32 const h = LZ4_hashPosition(p, tableType); + LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); +} + +/* LZ4_getIndexOnHash() : + * Index of match position registered in hash table. + * hash position must be calculated by using base+index, or dictBase+index. + * Assumption 1 : only valid if tableType == byU32 or byU16. + * Assumption 2 : h is presumed valid (within limits of hash table) + */ +LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) +{ + LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); + if (tableType == byU32) { + const U32* const hashTable = (const U32*) tableBase; + assert(h < (1U << (LZ4_MEMORY_USAGE-2))); + return hashTable[h]; + } + if (tableType == byU16) { + const U16* const hashTable = (const U16*) tableBase; + assert(h < (1U << (LZ4_MEMORY_USAGE-1))); + return hashTable[h]; + } + assert(0); return 0; /* forbidden case */ +} + +static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } + if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } + { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ +} + +LZ4_FORCE_INLINE const BYTE* +LZ4_getPosition(const BYTE* p, + const void* tableBase, tableType_t tableType, + const BYTE* srcBase) +{ + U32 const h = LZ4_hashPosition(p, tableType); + return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); +} + +LZ4_FORCE_INLINE void +LZ4_prepareTable(LZ4_stream_t_internal* const cctx, + const int inputSize, + const tableType_t tableType) { + /* If the table hasn't been used, it's guaranteed to be zeroed out, and is + * therefore safe to use no matter what mode we're in. Otherwise, we figure + * out if it's safe to leave as is or whether it needs to be reset. + */ + if ((tableType_t)cctx->tableType != clearedTable) { + assert(inputSize >= 0); + if ((tableType_t)cctx->tableType != tableType + || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) + || ((tableType == byU32) && cctx->currentOffset > 1 GB) + || tableType == byPtr + || inputSize >= 4 KB) + { + DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); + MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); + cctx->currentOffset = 0; + cctx->tableType = (U32)clearedTable; + } else { + DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); + } + } + + /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, is faster + * than compressing without a gap. However, compressing with + * currentOffset == 0 is faster still, so we preserve that case. + */ + if (cctx->currentOffset != 0 && tableType == byU32) { + DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); + cctx->currentOffset += 64 KB; + } + + /* Finally, clear history */ + cctx->dictCtx = NULL; + cctx->dictionary = NULL; + cctx->dictSize = 0; +} + +/** LZ4_compress_generic() : + * inlined, to ensure branches are decided at compilation time. + * Presumed already validated at this stage: + * - source != NULL + * - inputSize > 0 + */ +LZ4_FORCE_INLINE int LZ4_compress_generic_validated( + LZ4_stream_t_internal* const cctx, + const char* const source, + char* const dest, + const int inputSize, + int *inputConsumed, /* only written when outputDirective == fillOutput */ + const int maxOutputSize, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + int result; + const BYTE* ip = (const BYTE*) source; + + U32 const startIndex = cctx->currentOffset; + const BYTE* base = (const BYTE*) source - startIndex; + const BYTE* lowLimit; + + const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; + const BYTE* const dictionary = + dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; + const U32 dictSize = + dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; + const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ + + int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); + U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ + const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary; + const BYTE* anchor = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; + const BYTE* const matchlimit = iend - LASTLITERALS; + + /* the dictCtx currentOffset is indexed on the start of the dictionary, + * while a dictionary in the current context precedes the currentOffset */ + const BYTE* dictBase = !dictionary ? NULL : (dictDirective == usingDictCtx) ? + dictionary + dictSize - dictCtx->currentOffset : + dictionary + dictSize - startIndex; + + BYTE* op = (BYTE*) dest; + BYTE* const olimit = op + maxOutputSize; + + U32 offset = 0; + U32 forwardH; + + DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); + assert(ip != NULL); + /* If init conditions are not met, we don't have to mark stream + * as having dirty context, since no action was taken yet */ + if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ + if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ + if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ + assert(acceleration >= 1); + + lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); + + /* Update context state */ + if (dictDirective == usingDictCtx) { + /* Subsequent linked blocks can't use the dictionary. */ + /* Instead, they use the block we just compressed. */ + cctx->dictCtx = NULL; + cctx->dictSize = (U32)inputSize; + } else { + cctx->dictSize += (U32)inputSize; + } + cctx->currentOffset += (U32)inputSize; + cctx->tableType = (U32)tableType; + + if (inputSizehashTable, tableType, base); + ip++; forwardH = LZ4_hashPosition(ip, tableType); + + /* Main Loop */ + for ( ; ; ) { + const BYTE* match; + BYTE* token; + const BYTE* filledIp; + + /* Find a match */ + if (tableType == byPtr) { + const BYTE* forwardIp = ip; + int step = 1; + int searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; + assert(ip < mflimitPlusOne); + + match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); + + } while ( (match+LZ4_DISTANCE_MAX < ip) + || (LZ4_read32(match) != LZ4_read32(ip)) ); + + } else { /* byU32, byU16 */ + + const BYTE* forwardIp = ip; + int step = 1; + int searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + U32 const current = (U32)(forwardIp - base); + U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); + assert(matchIndex <= current); + assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; + assert(ip < mflimitPlusOne); + + if (dictDirective == usingDictCtx) { + if (matchIndex < startIndex) { + /* there was no match, try the dictionary */ + assert(tableType == byU32); + matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); + match = dictBase + matchIndex; + matchIndex += dictDelta; /* make dictCtx index comparable with current context */ + lowLimit = dictionary; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; + } + } else if (dictDirective==usingExtDict) { + if (matchIndex < startIndex) { + DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); + assert(startIndex - matchIndex >= MINMATCH); + match = dictBase + matchIndex; + lowLimit = dictionary; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; + } + } else { /* single continuous memory segment */ + match = base + matchIndex; + } + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); + + DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); + if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ + assert(matchIndex < current); + if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) + && (matchIndex+LZ4_DISTANCE_MAX < current)) { + continue; + } /* too far */ + assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ + + if (LZ4_read32(match) == LZ4_read32(ip)) { + if (maybe_extMem) offset = current - matchIndex; + break; /* match found */ + } + + } while(1); + } + + /* Catch up */ + filledIp = ip; + while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } + + /* Encode Literals */ + { unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ + (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + if ((outputDirective == fillOutput) && + (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { + op--; + goto _last_literals; + } + if (litLength >= RUN_MASK) { + int len = (int)(litLength - RUN_MASK); + *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength< olimit)) { + /* the match was too close to the end, rewind and go to last literals */ + op = token; + goto _last_literals; + } + + /* Encode Offset */ + if (maybe_extMem) { /* static test */ + DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); + assert(offset <= LZ4_DISTANCE_MAX && offset > 0); + LZ4_writeLE16(op, (U16)offset); op+=2; + } else { + DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); + assert(ip-match <= LZ4_DISTANCE_MAX); + LZ4_writeLE16(op, (U16)(ip - match)); op+=2; + } + + /* Encode MatchLength */ + { unsigned matchCode; + + if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) + && (lowLimit==dictionary) /* match within extDict */ ) { + const BYTE* limit = ip + (dictEnd-match); + assert(dictEnd > match); + if (limit > matchlimit) limit = matchlimit; + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); + ip += (size_t)matchCode + MINMATCH; + if (ip==limit) { + unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); + matchCode += more; + ip += more; + } + DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); + } else { + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); + ip += (size_t)matchCode + MINMATCH; + DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); + } + + if ((outputDirective) && /* Check output buffer overflow */ + (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { + if (outputDirective == fillOutput) { + /* Match description too long : reduce it */ + U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; + ip -= matchCode - newMatchCode; + assert(newMatchCode < matchCode); + matchCode = newMatchCode; + if (unlikely(ip <= filledIp)) { + /* We have already filled up to filledIp so if ip ends up less than filledIp + * we have positions in the hash table beyond the current position. This is + * a problem if we reuse the hash table. So we have to remove these positions + * from the hash table. + */ + const BYTE* ptr; + DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); + for (ptr = ip; ptr <= filledIp; ++ptr) { + U32 const h = LZ4_hashPosition(ptr, tableType); + LZ4_clearHash(h, cctx->hashTable, tableType); + } + } + } else { + assert(outputDirective == limitedOutput); + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + } + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LZ4_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*255) { + op+=4; + LZ4_write32(op, 0xFFFFFFFF); + matchCode -= 4*255; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else + *token += (BYTE)(matchCode); + } + /* Ensure we have enough space for the last literals. */ + assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); + + anchor = ip; + + /* Test end of chunk */ + if (ip >= mflimitPlusOne) break; + + /* Fill table */ + LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); + + /* Test next position */ + if (tableType == byPtr) { + + match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); + LZ4_putPosition(ip, cctx->hashTable, tableType, base); + if ( (match+LZ4_DISTANCE_MAX >= ip) + && (LZ4_read32(match) == LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + } else { /* byU32, byU16 */ + + U32 const h = LZ4_hashPosition(ip, tableType); + U32 const current = (U32)(ip-base); + U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); + assert(matchIndex < current); + if (dictDirective == usingDictCtx) { + if (matchIndex < startIndex) { + /* there was no match, try the dictionary */ + matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); + match = dictBase + matchIndex; + lowLimit = dictionary; /* required for match length counter */ + matchIndex += dictDelta; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; /* required for match length counter */ + } + } else if (dictDirective==usingExtDict) { + if (matchIndex < startIndex) { + match = dictBase + matchIndex; + lowLimit = dictionary; /* required for match length counter */ + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; /* required for match length counter */ + } + } else { /* single memory segment */ + match = base + matchIndex; + } + LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); + assert(matchIndex < current); + if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) + && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) + && (LZ4_read32(match) == LZ4_read32(ip)) ) { + token=op++; + *token=0; + if (maybe_extMem) offset = current - matchIndex; + DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", + (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); + goto _next_match; + } + } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRun = (size_t)(iend - anchor); + if ( (outputDirective) && /* Check output buffer overflow */ + (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { + if (outputDirective == fillOutput) { + /* adapt lastRun to fill 'dst' */ + assert(olimit >= op); + lastRun = (size_t)(olimit-op) - 1/*token*/; + lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/ + } else { + assert(outputDirective == limitedOutput); + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRun< 0); + DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result); + return result; +} + +/** LZ4_compress_generic() : + * inlined, to ensure branches are decided at compilation time; + * takes care of src == (NULL, 0) + * and forward the rest to LZ4_compress_generic_validated */ +LZ4_FORCE_INLINE int LZ4_compress_generic( + LZ4_stream_t_internal* const cctx, + const char* const src, + char* const dst, + const int srcSize, + int *inputConsumed, /* only written when outputDirective == fillOutput */ + const int dstCapacity, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i", + srcSize, dstCapacity); + + if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */ + if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */ + if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */ + DEBUGLOG(5, "Generating an empty block"); + assert(outputDirective == notLimited || dstCapacity >= 1); + assert(dst != NULL); + dst[0] = 0; + if (outputDirective == fillOutput) { + assert (inputConsumed != NULL); + *inputConsumed = 0; + } + return 1; + } + assert(src != NULL); + + return LZ4_compress_generic_validated(cctx, src, dst, srcSize, + inputConsumed, /* only written into if outputDirective == fillOutput */ + dstCapacity, outputDirective, + tableType, dictDirective, dictIssue, acceleration); +} + + +int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; + assert(ctx != NULL); + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + if (maxOutputSize >= LZ4_compressBound(inputSize)) { + if (inputSize < LZ4_64Klimit) { + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + if (inputSize < LZ4_64Klimit) { + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } +} + +/** + * LZ4_compress_fast_extState_fastReset() : + * A variant of LZ4_compress_fast_extState(). + * + * Using this variant avoids an expensive initialization step. It is only safe + * to call if the state buffer is known to be correctly initialized already + * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of + * "correctly initialized"). + */ +int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) +{ + LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + + if (dstCapacity >= LZ4_compressBound(srcSize)) { + if (srcSize < LZ4_64Klimit) { + const tableType_t tableType = byU16; + LZ4_prepareTable(ctx, srcSize, tableType); + if (ctx->currentOffset) { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration); + } else { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + LZ4_prepareTable(ctx, srcSize, tableType); + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + if (srcSize < LZ4_64Klimit) { + const tableType_t tableType = byU16; + LZ4_prepareTable(ctx, srcSize, tableType); + if (ctx->currentOffset) { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); + } else { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + LZ4_prepareTable(ctx, srcSize, tableType); + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } +} + + +int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + int result; +#if (LZ4_HEAPMODE) + LZ4_stream_t* ctxPtr = ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + if (ctxPtr == NULL) return 0; +#else + LZ4_stream_t ctx; + LZ4_stream_t* const ctxPtr = &ctx; +#endif + result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); + +#if (LZ4_HEAPMODE) + FREEMEM(ctxPtr); +#endif + return result; +} + + +int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) +{ + return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); +} + + +/* Note!: This function leaves the stream in an unclean/broken state! + * It is not safe to subsequently use the same state with a _fastReset() or + * _continue() call without resetting it. */ +static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ + void* const s = LZ4_initStream(state, sizeof (*state)); + assert(s != NULL); (void)s; + + if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); + } else { + if (*srcSizePtr < LZ4_64Klimit) { + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); + } else { + tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); + } } +} + + +int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ +#if (LZ4_HEAPMODE) + LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + if (ctx == NULL) return 0; +#else + LZ4_stream_t ctxBody; + LZ4_stream_t* ctx = &ctxBody; +#endif + + int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); + +#if (LZ4_HEAPMODE) + FREEMEM(ctx); +#endif + return result; +} + + + +/*-****************************** +* Streaming functions +********************************/ + +LZ4_stream_t* LZ4_createStream(void) +{ + LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); + LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ + DEBUGLOG(4, "LZ4_createStream %p", lz4s); + if (lz4s == NULL) return NULL; + LZ4_initStream(lz4s, sizeof(*lz4s)); + return lz4s; +} + +static size_t LZ4_stream_t_alignment(void) +{ +#if LZ4_ALIGN_TEST + typedef struct { char c; LZ4_stream_t t; } t_a; + return sizeof(t_a) - sizeof(LZ4_stream_t); +#else + return 1; /* effectively disabled */ +#endif +} + +LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) +{ + DEBUGLOG(5, "LZ4_initStream"); + if (buffer == NULL) { return NULL; } + if (size < sizeof(LZ4_stream_t)) { return NULL; } + if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; + MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal)); + return (LZ4_stream_t*)buffer; +} + +/* resetStream is now deprecated, + * prefer initStream() which is more general */ +void LZ4_resetStream (LZ4_stream_t* LZ4_stream) +{ + DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); + MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal)); +} + +void LZ4_resetStream_fast(LZ4_stream_t* ctx) { + LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); +} + +int LZ4_freeStream (LZ4_stream_t* LZ4_stream) +{ + if (!LZ4_stream) return 0; /* support free on NULL */ + DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); + FREEMEM(LZ4_stream); + return (0); +} + + +#define HASH_UNIT sizeof(reg_t) +int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; + const tableType_t tableType = byU32; + const BYTE* p = (const BYTE*)dictionary; + const BYTE* const dictEnd = p + dictSize; + const BYTE* base; + + DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); + + /* It's necessary to reset the context, + * and not just continue it with prepareTable() + * to avoid any risk of generating overflowing matchIndex + * when compressing using this dictionary */ + LZ4_resetStream(LZ4_dict); + + /* We always increment the offset by 64 KB, since, if the dict is longer, + * we truncate it to the last 64k, and if it's shorter, we still want to + * advance by a whole window length so we can provide the guarantee that + * there are only valid offsets in the window, which allows an optimization + * in LZ4_compress_fast_continue() where it uses noDictIssue even when the + * dictionary isn't a full 64k. */ + dict->currentOffset += 64 KB; + + if (dictSize < (int)HASH_UNIT) { + return 0; + } + + if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; + base = dictEnd - dict->currentOffset; + dict->dictionary = p; + dict->dictSize = (U32)(dictEnd - p); + dict->tableType = (U32)tableType; + + while (p <= dictEnd-HASH_UNIT) { + LZ4_putPosition(p, dict->hashTable, tableType, base); + p+=3; + } + + return (int)dict->dictSize; +} + +void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) { + const LZ4_stream_t_internal* dictCtx = dictionaryStream == NULL ? NULL : + &(dictionaryStream->internal_donotuse); + + DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", + workingStream, dictionaryStream, + dictCtx != NULL ? dictCtx->dictSize : 0); + + if (dictCtx != NULL) { + /* If the current offset is zero, we will never look in the + * external dictionary context, since there is no value a table + * entry can take that indicate a miss. In that case, we need + * to bump the offset to something non-zero. + */ + if (workingStream->internal_donotuse.currentOffset == 0) { + workingStream->internal_donotuse.currentOffset = 64 KB; + } + + /* Don't actually attach an empty dictionary. + */ + if (dictCtx->dictSize == 0) { + dictCtx = NULL; + } + } + workingStream->internal_donotuse.dictCtx = dictCtx; +} + + +static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) +{ + assert(nextSize >= 0); + if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ + /* rescale hash table */ + U32 const delta = LZ4_dict->currentOffset - 64 KB; + const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; + int i; + DEBUGLOG(4, "LZ4_renormDictT"); + for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; + else LZ4_dict->hashTable[i] -= delta; + } + LZ4_dict->currentOffset = 64 KB; + if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; + LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; + } +} + + +int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, + const char* source, char* dest, + int inputSize, int maxOutputSize, + int acceleration) +{ + const tableType_t tableType = byU32; + LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse; + const BYTE* dictEnd = streamPtr->dictionary + streamPtr->dictSize; + + DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i)", inputSize); + + LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */ + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + + /* invalidate tiny dictionaries */ + if ( (streamPtr->dictSize-1 < 4-1) /* intentional underflow */ + && (dictEnd != (const BYTE*)source) ) { + DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); + streamPtr->dictSize = 0; + streamPtr->dictionary = (const BYTE*)source; + dictEnd = (const BYTE*)source; + } + + /* Check overlapping input/dictionary space */ + { const BYTE* sourceEnd = (const BYTE*) source + inputSize; + if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) { + streamPtr->dictSize = (U32)(dictEnd - sourceEnd); + if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; + if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; + streamPtr->dictionary = dictEnd - streamPtr->dictSize; + } + } + + /* prefix mode : source data follows dictionary */ + if (dictEnd == (const BYTE*)source) { + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); + else + return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); + } + + /* external dictionary mode */ + { int result; + if (streamPtr->dictCtx) { + /* We depend here on the fact that dictCtx'es (produced by + * LZ4_loadDict) guarantee that their tables contain no references + * to offsets between dictCtx->currentOffset - 64 KB and + * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe + * to use noDictIssue even when the dict isn't a full 64 KB. + */ + if (inputSize > 4 KB) { + /* For compressing large blobs, it is faster to pay the setup + * cost to copy the dictionary's tables into the active context, + * so that the compression loop is only looking into one table. + */ + LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr)); + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); + } + } else { + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); + } + } + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)inputSize; + return result; + } +} + + +/* Hidden debug function, to force-test external dictionary mode */ +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) +{ + LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; + int result; + + LZ4_renormDictT(streamPtr, srcSize); + + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { + result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); + } + + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)srcSize; + + return result; +} + + +/*! LZ4_saveDict() : + * If previously compressed data block is not guaranteed to remain available at its memory location, + * save it into a safer place (char* safeBuffer). + * Note : you don't need to call LZ4_loadDict() afterwards, + * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue(). + * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. + */ +int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) +{ + LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; + const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; + + if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ + if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } + + if (safeBuffer == NULL) assert(dictSize == 0); + if (dictSize > 0) + memmove(safeBuffer, previousDictEnd - dictSize, dictSize); + + dict->dictionary = (const BYTE*)safeBuffer; + dict->dictSize = (U32)dictSize; + + return dictSize; +} + + + +/*-******************************* + * Decompression functions + ********************************/ + +typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; +typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; + +#undef MIN +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + +/* Read the variable-length literal or match length. + * + * ip - pointer to use as input. + * lencheck - end ip. Return an error if ip advances >= lencheck. + * loop_check - check ip >= lencheck in body of loop. Returns loop_error if so. + * initial_check - check ip >= lencheck before start of loop. Returns initial_error if so. + * error (output) - error code. Should be set to 0 before call. + */ +typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error; +LZ4_FORCE_INLINE unsigned +read_variable_length(const BYTE**ip, const BYTE* lencheck, + int loop_check, int initial_check, + variable_length_error* error) +{ + U32 length = 0; + U32 s; + if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ + *error = initial_error; + return length; + } + do { + s = **ip; + (*ip)++; + length += s; + if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ + *error = loop_error; + return length; + } + } while (s==255); + + return length; +} + +/*! LZ4_decompress_generic() : + * This generic decompression function covers all use cases. + * It shall be instantiated several times, using different sets of directives. + * Note that it is important for performance that this function really get inlined, + * in order to remove useless branches during compilation optimization. + */ +LZ4_FORCE_INLINE int +LZ4_decompress_generic( + const char* const src, + char* const dst, + int srcSize, + int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ + + endCondition_directive endOnInput, /* endOnOutputSize, endOnInputSize */ + earlyEnd_directive partialDecoding, /* full, partial */ + dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ + const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note : = 0 if noDict */ + ) +{ + if (src == NULL) { return -1; } + + { const BYTE* ip = (const BYTE*) src; + const BYTE* const iend = ip + srcSize; + + BYTE* op = (BYTE*) dst; + BYTE* const oend = op + outputSize; + BYTE* cpy; + + const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; + + const int safeDecode = (endOnInput==endOnInputSize); + const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); + + + /* Set up the "end" pointers for the shortcut. */ + const BYTE* const shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/; + const BYTE* const shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/; + + const BYTE* match; + size_t offset; + unsigned token; + size_t length; + + + DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); + + /* Special cases */ + assert(lowPrefix <= op); + if ((endOnInput) && (unlikely(outputSize==0))) { + /* Empty output buffer */ + if (partialDecoding) return 0; + return ((srcSize==1) && (*ip==0)) ? 0 : -1; + } + if ((!endOnInput) && (unlikely(outputSize==0))) { return (*ip==0 ? 1 : -1); } + if ((endOnInput) && unlikely(srcSize==0)) { return -1; } + + /* Currently the fast loop shows a regression on qualcomm arm chips. */ +#if LZ4_FAST_DEC_LOOP + if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { + DEBUGLOG(6, "skip fast decode loop"); + goto safe_decode; + } + + /* Fast loop : decode sequences as long as output < iend-FASTLOOP_SAFE_DISTANCE */ + while (1) { + /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ + assert(oend - op >= FASTLOOP_SAFE_DISTANCE); + if (endOnInput) { assert(ip < iend); } + token = *ip++; + length = token >> ML_BITS; /* literal length */ + + assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ + + /* decode literal length */ + if (length == RUN_MASK) { + variable_length_error error = ok; + length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); + if (error == initial_error) { goto _output_error; } + if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + + /* copy literals */ + cpy = op+length; + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); + if (endOnInput) { /* LZ4_decompress_safe() */ + if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } + LZ4_wildCopy32(op, ip, cpy); + } else { /* LZ4_decompress_fast() */ + if (cpy>oend-8) { goto safe_literal_copy; } + LZ4_wildCopy8(op, ip, cpy); /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : + * it doesn't know input length, and only relies on end-of-block properties */ + } + ip += length; op = cpy; + } else { + cpy = op+length; + if (endOnInput) { /* LZ4_decompress_safe() */ + DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); + /* We don't need to check oend, since we check it once for each loop below */ + if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } + /* Literals can only be 14, but hope compilers optimize if we copy by a register size */ + LZ4_memcpy(op, ip, 16); + } else { /* LZ4_decompress_fast() */ + /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : + * it doesn't know input length, and relies on end-of-block properties */ + LZ4_memcpy(op, ip, 8); + if (length > 8) { LZ4_memcpy(op+8, ip+8, 8); } + } + ip += length; op = cpy; + } + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + assert(match <= op); + + /* get matchlength */ + length = token & ML_MASK; + + if (length == ML_MASK) { + variable_length_error error = ok; + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ + length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); + if (error != ok) { goto _output_error; } + if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ + length += MINMATCH; + if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + goto safe_match_copy; + } + } else { + length += MINMATCH; + if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + goto safe_match_copy; + } + + /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */ + if ((dict == withPrefix64k) || (match >= lowPrefix)) { + if (offset >= 8) { + assert(match >= lowPrefix); + assert(match <= op); + assert(op + 18 <= oend); + + LZ4_memcpy(op, match, 8); + LZ4_memcpy(op+8, match+8, 8); + LZ4_memcpy(op+16, match+16, 2); + op += length; + continue; + } } } + + if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ + /* match starting within external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + if (unlikely(op+length > oend-LASTLITERALS)) { + if (partialDecoding) { + DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); + length = MIN(length, (size_t)(oend-op)); + } else { + goto _output_error; /* end-of-block condition violated */ + } } + + if (length <= (size_t)(lowPrefix-match)) { + /* match fits entirely within external dictionary : just copy */ + memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match stretches into both external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix - match); + size_t const restSize = length - copySize; + LZ4_memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) { *op++ = *copyFrom++; } + } else { + LZ4_memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + + /* copy match within block */ + cpy = op + length; + + assert((op <= oend) && (oend-op >= 32)); + if (unlikely(offset<16)) { + LZ4_memcpy_using_offset(op, match, cpy, offset); + } else { + LZ4_wildCopy32(op, match, cpy); + } + + op = cpy; /* wildcopy correction */ + } + safe_decode: +#endif + + /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ + while (1) { + token = *ip++; + length = token >> ML_BITS; /* literal length */ + + assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ + + /* A two-stage shortcut for the most common case: + * 1) If the literal length is 0..14, and there is enough space, + * enter the shortcut and copy 16 bytes on behalf of the literals + * (in the fast mode, only 8 bytes can be safely copied this way). + * 2) Further if the match length is 4..18, copy 18 bytes in a similar + * manner; but we ensure that there's enough space in the output for + * those 18 bytes earlier, upon entering the shortcut (in other words, + * there is a combined check for both stages). + */ + if ( (endOnInput ? length != RUN_MASK : length <= 8) + /* strictly "less than" on input, to re-enter the loop with at least one byte */ + && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) { + /* Copy the literals */ + LZ4_memcpy(op, ip, endOnInput ? 16 : 8); + op += length; ip += length; + + /* The second stage: prepare for match copying, decode full info. + * If it doesn't work out, the info won't be wasted. */ + length = token & ML_MASK; /* match length */ + offset = LZ4_readLE16(ip); ip += 2; + match = op - offset; + assert(match <= op); /* check overflow */ + + /* Do not deal with overlapping matches. */ + if ( (length != ML_MASK) + && (offset >= 8) + && (dict==withPrefix64k || match >= lowPrefix) ) { + /* Copy the match. */ + LZ4_memcpy(op + 0, match + 0, 8); + LZ4_memcpy(op + 8, match + 8, 8); + LZ4_memcpy(op +16, match +16, 2); + op += length + MINMATCH; + /* Both stages worked, load the next token. */ + continue; + } + + /* The second stage didn't work out, but the info is ready. + * Propel it right to the point of match copying. */ + goto _copy_match; + } + + /* decode literal length */ + if (length == RUN_MASK) { + variable_length_error error = ok; + length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); + if (error == initial_error) { goto _output_error; } + if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + } + + /* copy literals */ + cpy = op+length; +#if LZ4_FAST_DEC_LOOP + safe_literal_copy: +#endif + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); + if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) ) + || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) + { + /* We've either hit the input parsing restriction or the output parsing restriction. + * In the normal scenario, decoding a full block, it must be the last sequence, + * otherwise it's an error (invalid input or dimensions). + * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow. + */ + if (partialDecoding) { + /* Since we are partial decoding we may be in this block because of the output parsing + * restriction, which is not valid since the output buffer is allowed to be undersized. + */ + assert(endOnInput); + DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") + DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); + DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); + DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip)); + /* Finishing in the middle of a literals segment, + * due to lack of input. + */ + if (ip+length > iend) { + length = (size_t)(iend-ip); + cpy = op + length; + } + /* Finishing in the middle of a literals segment, + * due to lack of output space. + */ + if (cpy > oend) { + cpy = oend; + assert(op<=oend); + length = (size_t)(oend-op); + } + } else { + /* We must be on the last sequence because of the parsing limitations so check + * that we exactly regenerate the original size (must be exact when !endOnInput). + */ + if ((!endOnInput) && (cpy != oend)) { goto _output_error; } + /* We must be on the last sequence (or invalid) because of the parsing limitations + * so check that we exactly consume the input and don't overrun the output buffer. + */ + if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { + DEBUGLOG(6, "should have been last run of literals") + DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); + DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); + goto _output_error; + } + } + memmove(op, ip, length); /* supports overlapping memory regions; only matters for in-place decompression scenarios */ + ip += length; + op += length; + /* Necessarily EOF when !partialDecoding. + * When partialDecoding, it is EOF if we've either + * filled the output buffer or + * can't proceed with reading an offset for following match. + */ + if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) { + break; + } + } else { + LZ4_wildCopy8(op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */ + ip += length; op = cpy; + } + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + + /* get matchlength */ + length = token & ML_MASK; + + _copy_match: + if (length == ML_MASK) { + variable_length_error error = ok; + length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); + if (error != ok) goto _output_error; + if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ + } + length += MINMATCH; + +#if LZ4_FAST_DEC_LOOP + safe_match_copy: +#endif + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ + /* match starting within external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + if (unlikely(op+length > oend-LASTLITERALS)) { + if (partialDecoding) length = MIN(length, (size_t)(oend-op)); + else goto _output_error; /* doesn't respect parsing restriction */ + } + + if (length <= (size_t)(lowPrefix-match)) { + /* match fits entirely within external dictionary : just copy */ + memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match stretches into both external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix - match); + size_t const restSize = length - copySize; + LZ4_memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) *op++ = *copyFrom++; + } else { + LZ4_memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + assert(match >= lowPrefix); + + /* copy match within block */ + cpy = op + length; + + /* partialDecoding : may end anywhere within the block */ + assert(op<=oend); + if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { + size_t const mlen = MIN(length, (size_t)(oend-op)); + const BYTE* const matchEnd = match + mlen; + BYTE* const copyEnd = op + mlen; + if (matchEnd > op) { /* overlap copy */ + while (op < copyEnd) { *op++ = *match++; } + } else { + LZ4_memcpy(op, match, mlen); + } + op = copyEnd; + if (op == oend) { break; } + continue; + } + + if (unlikely(offset<8)) { + LZ4_write32(op, 0); /* silence msan warning when offset==0 */ + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += inc32table[offset]; + LZ4_memcpy(op+4, match, 4); + match -= dec64table[offset]; + } else { + LZ4_memcpy(op, match, 8); + match += 8; + } + op += 8; + + if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { + BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); + if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ + if (op < oCopyLimit) { + LZ4_wildCopy8(op, match, oCopyLimit); + match += oCopyLimit - op; + op = oCopyLimit; + } + while (op < cpy) { *op++ = *match++; } + } else { + LZ4_memcpy(op, match, 8); + if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } + } + op = cpy; /* wildcopy correction */ + } + + /* end of decoding */ + if (endOnInput) { + DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); + return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ + } else { + return (int) (((const char*)ip)-src); /* Nb of input bytes read */ + } + + /* Overflow error detected */ + _output_error: + return (int) (-(((const char*)ip)-src))-1; + } +} + + +/*===== Instantiate the API decoding functions. =====*/ + +LZ4_FORCE_O2 +int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, + endOnInputSize, decode_full_block, noDict, + (BYTE*)dest, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, + endOnInputSize, partial_decode, + noDict, (BYTE*)dst, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_fast(const char* source, char* dest, int originalSize) +{ + return LZ4_decompress_generic(source, dest, 0, originalSize, + endOnOutputSize, decode_full_block, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +/*===== Instantiate a few more decoding cases, used more than once. =====*/ + +LZ4_FORCE_O2 /* Exported, an obsolete API function. */ +int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + endOnInputSize, decode_full_block, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +/* Another obsolete API function, paired with the previous one. */ +int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) +{ + /* LZ4_decompress_fast doesn't validate match offsets, + * and thus serves well with any prefixed dictionary. */ + return LZ4_decompress_fast(source, dest, originalSize); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, + size_t prefixSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + endOnInputSize, decode_full_block, noDict, + (BYTE*)dest-prefixSize, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, + int compressedSize, int maxOutputSize, + const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + endOnInputSize, decode_full_block, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, + const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, 0, originalSize, + endOnOutputSize, decode_full_block, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +/* The "double dictionary" mode, for use with e.g. ring buffers: the first part + * of the dictionary is passed as prefix, and the second via dictStart + dictSize. + * These routines are used only once, in LZ4_decompress_*_continue(). + */ +LZ4_FORCE_INLINE +int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize, + size_t prefixSize, const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + endOnInputSize, decode_full_block, usingExtDict, + (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_INLINE +int LZ4_decompress_fast_doubleDict(const char* source, char* dest, int originalSize, + size_t prefixSize, const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, 0, originalSize, + endOnOutputSize, decode_full_block, usingExtDict, + (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); +} + +/*===== streaming decompression functions =====*/ + +LZ4_streamDecode_t* LZ4_createStreamDecode(void) +{ + LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); + LZ4_STATIC_ASSERT(LZ4_STREAMDECODESIZE >= sizeof(LZ4_streamDecode_t_internal)); /* A compilation error here means LZ4_STREAMDECODESIZE is not large enough */ + return lz4s; +} + +int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) +{ + if (LZ4_stream == NULL) { return 0; } /* support free on NULL */ + FREEMEM(LZ4_stream); + return 0; +} + +/*! LZ4_setStreamDecode() : + * Use this function to instruct where to find the dictionary. + * This function is not necessary if previous data is still available where it was decoded. + * Loading a size of 0 is allowed (same effect as no dictionary). + * @return : 1 if OK, 0 if error + */ +int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + lz4sd->prefixSize = (size_t) dictSize; + lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; + lz4sd->externalDict = NULL; + lz4sd->extDictSize = 0; + return 1; +} + +/*! LZ4_decoderRingBufferSize() : + * when setting a ring buffer for streaming decompression (optional scenario), + * provides the minimum size of this ring buffer + * to be compatible with any source respecting maxBlockSize condition. + * Note : in a ring buffer scenario, + * blocks are presumed decompressed next to each other. + * When not enough space remains for next block (remainingSize < maxBlockSize), + * decoding resumes from beginning of ring buffer. + * @return : minimum ring buffer size, + * or 0 if there is an error (invalid maxBlockSize). + */ +int LZ4_decoderRingBufferSize(int maxBlockSize) +{ + if (maxBlockSize < 0) return 0; + if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0; + if (maxBlockSize < 16) maxBlockSize = 16; + return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize); +} + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks must still be available at the memory position where they were decoded. + If it's not possible, save the relevant part of decoded data into a safe buffer, + and indicate where it stands using LZ4_setStreamDecode() +*/ +LZ4_FORCE_O2 +int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + int result; + + if (lz4sd->prefixSize == 0) { + /* The first call, no dictionary yet. */ + assert(lz4sd->extDictSize == 0); + result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } else if (lz4sd->prefixEnd == (BYTE*)dest) { + /* They're rolling the current segment. */ + if (lz4sd->prefixSize >= 64 KB - 1) + result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); + else if (lz4sd->extDictSize == 0) + result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, + lz4sd->prefixSize); + else + result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize, + lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += (size_t)result; + lz4sd->prefixEnd += result; + } else { + /* The buffer wraps around, or they're switching to another buffer. */ + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } + + return result; +} + +LZ4_FORCE_O2 +int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + int result; + assert(originalSize >= 0); + + if (lz4sd->prefixSize == 0) { + assert(lz4sd->extDictSize == 0); + result = LZ4_decompress_fast(source, dest, originalSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } else if (lz4sd->prefixEnd == (BYTE*)dest) { + if (lz4sd->prefixSize >= 64 KB - 1 || lz4sd->extDictSize == 0) + result = LZ4_decompress_fast(source, dest, originalSize); + else + result = LZ4_decompress_fast_doubleDict(source, dest, originalSize, + lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += (size_t)originalSize; + lz4sd->prefixEnd += originalSize; + } else { + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_fast_extDict(source, dest, originalSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } + + return result; +} + + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as "_continue" ones, + the dictionary must be explicitly provided within parameters +*/ + +int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); + if (dictStart+dictSize == dest) { + if (dictSize >= 64 KB - 1) { + return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); +} + +int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) +{ + if (dictSize==0 || dictStart+dictSize == dest) + return LZ4_decompress_fast(source, dest, originalSize); + assert(dictSize >= 0); + return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); +} + + +/*=************************************************* +* Obsolete Functions +***************************************************/ +/* obsolete compression functions */ +int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) +{ + return LZ4_compress_default(source, dest, inputSize, maxOutputSize); +} +int LZ4_compress(const char* src, char* dest, int srcSize) +{ + return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize)); +} +int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); +} +int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); +} +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity) +{ + return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1); +} +int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) +{ + return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); +} + +/* +These decompression functions are deprecated and should no longer be used. +They are only provided here for compatibility with older user programs. +- LZ4_uncompress is totally equivalent to LZ4_decompress_fast +- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe +*/ +int LZ4_uncompress (const char* source, char* dest, int outputSize) +{ + return LZ4_decompress_fast(source, dest, outputSize); +} +int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) +{ + return LZ4_decompress_safe(source, dest, isize, maxOutputSize); +} + +/* Obsolete Streaming functions */ + +int LZ4_sizeofStreamState(void) { return LZ4_STREAMSIZE; } + +int LZ4_resetStreamState(void* state, char* inputBuffer) +{ + (void)inputBuffer; + LZ4_resetStream((LZ4_stream_t*)state); + return 0; +} + +void* LZ4_create (char* inputBuffer) +{ + (void)inputBuffer; + return LZ4_createStream(); +} + +char* LZ4_slideInputBuffer (void* state) +{ + /* avoid const char * -> char * conversion warning */ + return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; +} + +#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/radio/src/thirdparty/libopenui/thirdparty/lz4/lz4.h b/radio/src/thirdparty/libopenui/thirdparty/lz4/lz4.h new file mode 100644 index 000000000..7ab1e483a --- /dev/null +++ b/radio/src/thirdparty/libopenui/thirdparty/lz4/lz4.h @@ -0,0 +1,774 @@ +/* + * LZ4 - Fast LZ compression algorithm + * Header File + * Copyright (C) 2011-present, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ +#if defined (__cplusplus) +extern "C" { +#endif + +#ifndef LZ4_H_2983827168210 +#define LZ4_H_2983827168210 + +/* --- Dependency --- */ +#include /* size_t */ + + +/** + Introduction + + LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, + scalable with multi-cores CPU. It features an extremely fast decoder, with speed in + multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. + + The LZ4 compression library provides in-memory compression and decompression functions. + It gives full buffer control to user. + Compression can be done in: + - a single step (described as Simple Functions) + - a single step, reusing a context (described in Advanced Functions) + - unbounded multiple steps (described as Streaming compression) + + lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). + Decompressing such a compressed block requires additional metadata. + Exact metadata depends on exact decompression function. + For the typical case of LZ4_decompress_safe(), + metadata includes block's compressed size, and maximum bound of decompressed size. + Each application is free to encode and pass such metadata in whichever way it wants. + + lz4.h only handle blocks, it can not generate Frames. + + Blocks are different from Frames (doc/lz4_Frame_format.md). + Frames bundle both blocks and metadata in a specified manner. + Embedding metadata is required for compressed data to be self-contained and portable. + Frame format is delivered through a companion API, declared in lz4frame.h. + The `lz4` CLI can only manage frames. +*/ + +/*^*************************************************************** +* Export parameters +*****************************************************************/ +/* +* LZ4_DLL_EXPORT : +* Enable exporting of functions when building a Windows DLL +* LZ4LIB_VISIBILITY : +* Control library symbols visibility. +*/ +#ifndef LZ4LIB_VISIBILITY +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) +# else +# define LZ4LIB_VISIBILITY +# endif +#endif +#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) +# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY +#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) +# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define LZ4LIB_API LZ4LIB_VISIBILITY +#endif + +/*------ Version ------*/ +#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ +#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 3 /* for tweaks, bug-fixes, or development */ + +#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) + +#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE +#define LZ4_QUOTE(str) #str +#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) +#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) + +LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */ +LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */ + + +/*-************************************ +* Tuning parameter +**************************************/ +/*! + * LZ4_MEMORY_USAGE : + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) + * Increasing memory usage improves compression ratio. + * Reduced memory usage may improve speed, thanks to better cache locality. + * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache + */ +#ifndef LZ4_MEMORY_USAGE +# define LZ4_MEMORY_USAGE 14 +#endif + + +/*-************************************ +* Simple Functions +**************************************/ +/*! LZ4_compress_default() : + * Compresses 'srcSize' bytes from buffer 'src' + * into already allocated 'dst' buffer of size 'dstCapacity'. + * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). + * It also runs faster, so it's a recommended setting. + * If the function cannot compress 'src' into a more limited 'dst' budget, + * compression stops *immediately*, and the function result is zero. + * In which case, 'dst' content is undefined (invalid). + * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. + * dstCapacity : size of buffer 'dst' (which must be already allocated) + * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) + * or 0 if compression fails + * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). + */ +LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); + +/*! LZ4_decompress_safe() : + * compressedSize : is the exact complete size of the compressed block. + * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. + * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) + * If destination buffer is not large enough, decoding will stop and output an error code (negative value). + * If the source stream is detected malformed, the function will stop decoding and return a negative result. + * Note 1 : This function is protected against malicious data packets : + * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, + * even if the compressed block is maliciously modified to order the decoder to do these actions. + * In such case, the decoder stops immediately, and considers the compressed block malformed. + * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. + * The implementation is free to send / store / derive this information in whichever way is most beneficial. + * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. + */ +LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); + + +/*-************************************ +* Advanced Functions +**************************************/ +#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) + +/*! LZ4_compressBound() : + Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) + This function is primarily useful for memory allocation purposes (destination buffer size). + Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). + Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) + inputSize : max supported value is LZ4_MAX_INPUT_SIZE + return : maximum output size in a "worst case" scenario + or 0, if input size is incorrect (too large or negative) +*/ +LZ4LIB_API int LZ4_compressBound(int inputSize); + +/*! LZ4_compress_fast() : + Same as LZ4_compress_default(), but allows selection of "acceleration" factor. + The larger the acceleration value, the faster the algorithm, but also the lesser the compression. + It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. + An acceleration value of "1" is the same as regular LZ4_compress_default() + Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c). + Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). +*/ +LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + + +/*! LZ4_compress_fast_extState() : + * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. + * Use LZ4_sizeofState() to know how much memory must be allocated, + * and allocate it on 8-bytes boundaries (using `malloc()` typically). + * Then, provide this buffer as `void* state` to compression function. + */ +LZ4LIB_API int LZ4_sizeofState(void); +LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + + +/*! LZ4_compress_destSize() : + * Reverse the logic : compresses as much data as possible from 'src' buffer + * into already allocated buffer 'dst', of size >= 'targetDestSize'. + * This function either compresses the entire 'src' content into 'dst' if it's large enough, + * or fill 'dst' buffer completely with as much data as possible from 'src'. + * note: acceleration parameter is fixed to "default". + * + * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. + * New value is necessarily <= input value. + * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) + * or 0 if compression fails. + * + * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+): + * the produced compressed content could, in specific circumstances, + * require to be decompressed into a destination buffer larger + * by at least 1 byte than the content to decompress. + * If an application uses `LZ4_compress_destSize()`, + * it's highly recommended to update liblz4 to v1.9.2 or better. + * If this can't be done or ensured, + * the receiving decompression function should provide + * a dstCapacity which is > decompressedSize, by at least 1 byte. + * See https://github.com/lz4/lz4/issues/859 for details + */ +LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); + + +/*! LZ4_decompress_safe_partial() : + * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', + * into destination buffer 'dst' of size 'dstCapacity'. + * Up to 'targetOutputSize' bytes will be decoded. + * The function stops decoding on reaching this objective. + * This can be useful to boost performance + * whenever only the beginning of a block is required. + * + * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) + * If source stream is detected malformed, function returns a negative result. + * + * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. + * + * Note 2 : targetOutputSize must be <= dstCapacity + * + * Note 3 : this function effectively stops decoding on reaching targetOutputSize, + * so dstCapacity is kind of redundant. + * This is because in older versions of this function, + * decoding operation would still write complete sequences. + * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, + * it could write more bytes, though only up to dstCapacity. + * Some "margin" used to be required for this operation to work properly. + * Thankfully, this is no longer necessary. + * The function nonetheless keeps the same signature, in an effort to preserve API compatibility. + * + * Note 4 : If srcSize is the exact size of the block, + * then targetOutputSize can be any value, + * including larger than the block's decompressed size. + * The function will, at most, generate block's decompressed size. + * + * Note 5 : If srcSize is _larger_ than block's compressed size, + * then targetOutputSize **MUST** be <= block's decompressed size. + * Otherwise, *silent corruption will occur*. + */ +LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); + + +/*-********************************************* +* Streaming Compression Functions +***********************************************/ +typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ + +LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); +LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); + +/*! LZ4_resetStream_fast() : v1.9.0+ + * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks + * (e.g., LZ4_compress_fast_continue()). + * + * An LZ4_stream_t must be initialized once before usage. + * This is automatically done when created by LZ4_createStream(). + * However, should the LZ4_stream_t be simply declared on stack (for example), + * it's necessary to initialize it first, using LZ4_initStream(). + * + * After init, start any new stream with LZ4_resetStream_fast(). + * A same LZ4_stream_t can be re-used multiple times consecutively + * and compress multiple streams, + * provided that it starts each new stream with LZ4_resetStream_fast(). + * + * LZ4_resetStream_fast() is much faster than LZ4_initStream(), + * but is not compatible with memory regions containing garbage data. + * + * Note: it's only useful to call LZ4_resetStream_fast() + * in the context of streaming compression. + * The *extState* functions perform their own resets. + * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. + */ +LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); + +/*! LZ4_loadDict() : + * Use this function to reference a static dictionary into LZ4_stream_t. + * The dictionary must remain available during compression. + * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. + * The same dictionary will have to be loaded on decompression side for successful decoding. + * Dictionary are useful for better compression of small data (KB range). + * While LZ4 accept any input as dictionary, + * results are generally better when using Zstandard's Dictionary Builder. + * Loading a size of 0 is allowed, and is the same as reset. + * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) + */ +LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + +/*! LZ4_compress_fast_continue() : + * Compress 'src' content using data from previously compressed blocks, for better compression ratio. + * 'dst' buffer must be already allocated. + * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. + * + * @return : size of compressed block + * or 0 if there is an error (typically, cannot fit into 'dst'). + * + * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. + * Each block has precise boundaries. + * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. + * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. + * + * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! + * + * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. + * Make sure that buffers are separated, by at least one byte. + * This construction ensures that each block only depends on previous block. + * + * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. + * + * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. + */ +LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_saveDict() : + * If last 64KB data cannot be guaranteed to remain available at its current memory location, + * save it into a safer place (char* safeBuffer). + * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), + * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. + * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. + */ +LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); + + +/*-********************************************** +* Streaming Decompression Functions +* Bufferless synchronous API +************************************************/ +typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ + +/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : + * creation / destruction of streaming decompression tracking context. + * A tracking context can be re-used multiple times. + */ +LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); +LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); + +/*! LZ4_setStreamDecode() : + * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. + * Use this function to start decompression of a new stream of blocks. + * A dictionary can optionally be set. Use NULL or size 0 for a reset order. + * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. + * @return : 1 if OK, 0 if error + */ +LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); + +/*! LZ4_decoderRingBufferSize() : v1.8.2+ + * Note : in a ring buffer scenario (optional), + * blocks are presumed decompressed next to each other + * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), + * at which stage it resumes from beginning of ring buffer. + * When setting such a ring buffer for streaming decompression, + * provides the minimum size of this ring buffer + * to be compatible with any source respecting maxBlockSize condition. + * @return : minimum ring buffer size, + * or 0 if there is an error (invalid maxBlockSize). + */ +LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); +#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ + +/*! LZ4_decompress_*_continue() : + * These decoding functions allow decompression of consecutive blocks in "streaming" mode. + * A block is an unsplittable entity, it must be presented entirely to a decompression function. + * Decompression functions only accepts one block at a time. + * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. + * If less than 64KB of data has been decoded, all the data must be present. + * + * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : + * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). + * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. + * In which case, encoding and decoding buffers do not need to be synchronized. + * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. + * - Synchronized mode : + * Decompression buffer size is _exactly_ the same as compression buffer size, + * and follows exactly same update rule (block boundaries at same positions), + * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), + * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). + * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. + * In which case, encoding and decoding buffers do not need to be synchronized, + * and encoding ring buffer can have any size, including small ones ( < 64 KB). + * + * Whenever these conditions are not possible, + * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, + * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. +*/ +LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity); + + +/*! LZ4_decompress_*_usingDict() : + * These decoding functions work the same as + * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() + * They are stand-alone, and don't need an LZ4_streamDecode_t structure. + * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. + * Performance tip : Decompression speed can be substantially increased + * when dst == dictStart + dictSize. + */ +LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize); + +#endif /* LZ4_H_2983827168210 */ + + +/*^************************************* + * !!!!!! STATIC LINKING ONLY !!!!!! + ***************************************/ + +/*-**************************************************************************** + * Experimental section + * + * Symbols declared in this section must be considered unstable. Their + * signatures or semantics may change, or they may be removed altogether in the + * future. They are therefore only safe to depend on when the caller is + * statically linked against the library. + * + * To protect against unsafe usage, not only are the declarations guarded, + * the definitions are hidden by default + * when building LZ4 as a shared/dynamic library. + * + * In order to access these declarations, + * define LZ4_STATIC_LINKING_ONLY in your application + * before including LZ4's headers. + * + * In order to make their implementations accessible dynamically, you must + * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. + ******************************************************************************/ + +#ifdef LZ4_STATIC_LINKING_ONLY + +#ifndef LZ4_STATIC_3504398509 +#define LZ4_STATIC_3504398509 + +#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS +#define LZ4LIB_STATIC_API LZ4LIB_API +#else +#define LZ4LIB_STATIC_API +#endif + + +/*! LZ4_compress_fast_extState_fastReset() : + * A variant of LZ4_compress_fast_extState(). + * + * Using this variant avoids an expensive initialization step. + * It is only safe to call if the state buffer is known to be correctly initialized already + * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). + * From a high level, the difference is that + * this function initializes the provided state with a call to something like LZ4_resetStream_fast() + * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). + */ +LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_attach_dictionary() : + * This is an experimental API that allows + * efficient use of a static dictionary many times. + * + * Rather than re-loading the dictionary buffer into a working context before + * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a + * working LZ4_stream_t, this function introduces a no-copy setup mechanism, + * in which the working stream references the dictionary stream in-place. + * + * Several assumptions are made about the state of the dictionary stream. + * Currently, only streams which have been prepared by LZ4_loadDict() should + * be expected to work. + * + * Alternatively, the provided dictionaryStream may be NULL, + * in which case any existing dictionary stream is unset. + * + * If a dictionary is provided, it replaces any pre-existing stream history. + * The dictionary contents are the only history that can be referenced and + * logically immediately precede the data compressed in the first subsequent + * compression call. + * + * The dictionary will only remain attached to the working stream through the + * first compression call, at the end of which it is cleared. The dictionary + * stream (and source buffer) must remain in-place / accessible / unchanged + * through the completion of the first compression call on the stream. + */ +LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream); + + +/*! In-place compression and decompression + * + * It's possible to have input and output sharing the same buffer, + * for highly contrained memory environments. + * In both cases, it requires input to lay at the end of the buffer, + * and decompression to start at beginning of the buffer. + * Buffer size must feature some margin, hence be larger than final size. + * + * |<------------------------buffer--------------------------------->| + * |<-----------compressed data--------->| + * |<-----------decompressed size------------------>| + * |<----margin---->| + * + * This technique is more useful for decompression, + * since decompressed size is typically larger, + * and margin is short. + * + * In-place decompression will work inside any buffer + * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). + * This presumes that decompressedSize > compressedSize. + * Otherwise, it means compression actually expanded data, + * and it would be more efficient to store such data with a flag indicating it's not compressed. + * This can happen when data is not compressible (already compressed, or encrypted). + * + * For in-place compression, margin is larger, as it must be able to cope with both + * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, + * and data expansion, which can happen when input is not compressible. + * As a consequence, buffer size requirements are much higher, + * and memory savings offered by in-place compression are more limited. + * + * There are ways to limit this cost for compression : + * - Reduce history size, by modifying LZ4_DISTANCE_MAX. + * Note that it is a compile-time constant, so all compressions will apply this limit. + * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, + * so it's a reasonable trick when inputs are known to be small. + * - Require the compressor to deliver a "maximum compressed size". + * This is the `dstCapacity` parameter in `LZ4_compress*()`. + * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, + * in which case, the return code will be 0 (zero). + * The caller must be ready for these cases to happen, + * and typically design a backup scheme to send data uncompressed. + * The combination of both techniques can significantly reduce + * the amount of margin required for in-place compression. + * + * In-place compression can work in any buffer + * which size is >= (maxCompressedSize) + * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. + * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, + * so it's possible to reduce memory requirements by playing with them. + */ + +#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) +#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ + +#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ +# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ +#endif + +#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ +#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ + +#endif /* LZ4_STATIC_3504398509 */ +#endif /* LZ4_STATIC_LINKING_ONLY */ + + + +#ifndef LZ4_H_98237428734687 +#define LZ4_H_98237428734687 + +/*-************************************************************ + * Private Definitions + ************************************************************** + * Do not use these definitions directly. + * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. + * Accessing members will expose user code to API and/or ABI break in future versions of the library. + **************************************************************/ +#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) +#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) +#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ + +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# include + typedef int8_t LZ4_i8; + typedef uint8_t LZ4_byte; + typedef uint16_t LZ4_u16; + typedef uint32_t LZ4_u32; +#else + typedef signed char LZ4_i8; + typedef unsigned char LZ4_byte; + typedef unsigned short LZ4_u16; + typedef unsigned int LZ4_u32; +#endif + +typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; +struct LZ4_stream_t_internal { + LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; + LZ4_u32 currentOffset; + LZ4_u32 tableType; + const LZ4_byte* dictionary; + const LZ4_stream_t_internal* dictCtx; + LZ4_u32 dictSize; +}; + +typedef struct { + const LZ4_byte* externalDict; + size_t extDictSize; + const LZ4_byte* prefixEnd; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + + +/*! LZ4_stream_t : + * Do not use below internal definitions directly ! + * Declare or allocate an LZ4_stream_t instead. + * LZ4_stream_t can also be created using LZ4_createStream(), which is recommended. + * The structure definition can be convenient for static allocation + * (on stack, or as part of larger structure). + * Init this structure with LZ4_initStream() before first use. + * note : only use this definition in association with static linking ! + * this definition is not API/ABI safe, and may change in future versions. + */ +#define LZ4_STREAMSIZE 16416 /* static size, for inter-version compatibility */ +#define LZ4_STREAMSIZE_VOIDP (LZ4_STREAMSIZE / sizeof(void*)) +union LZ4_stream_u { + void* table[LZ4_STREAMSIZE_VOIDP]; + LZ4_stream_t_internal internal_donotuse; +}; /* previously typedef'd to LZ4_stream_t */ + + +/*! LZ4_initStream() : v1.9.0+ + * An LZ4_stream_t structure must be initialized at least once. + * This is automatically done when invoking LZ4_createStream(), + * but it's not when the structure is simply declared on stack (for example). + * + * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. + * It can also initialize any arbitrary buffer of sufficient size, + * and will @return a pointer of proper type upon initialization. + * + * Note : initialization fails if size and alignment conditions are not respected. + * In which case, the function will @return NULL. + * Note2: An LZ4_stream_t structure guarantees correct alignment and size. + * Note3: Before v1.9.0, use LZ4_resetStream() instead + */ +LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); + + +/*! LZ4_streamDecode_t : + * information structure to track an LZ4 stream during decompression. + * init this structure using LZ4_setStreamDecode() before first use. + * note : only use in association with static linking ! + * this definition is not API/ABI safe, + * and may change in a future version ! + */ +#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ ) +#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) +union LZ4_streamDecode_u { + unsigned long long table[LZ4_STREAMDECODESIZE_U64]; + LZ4_streamDecode_t_internal internal_donotuse; +} ; /* previously typedef'd to LZ4_streamDecode_t */ + + + +/*-************************************ +* Obsolete Functions +**************************************/ + +/*! Deprecation warnings + * + * Deprecated functions make the compiler generate a warning when invoked. + * This is meant to invite users to update their source code. + * Should deprecation warnings be a problem, it is generally possible to disable them, + * typically with -Wno-deprecated-declarations for gcc + * or _CRT_SECURE_NO_WARNINGS in Visual. + * + * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS + * before including the header file. + */ +#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS +# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ +#else +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define LZ4_DEPRECATED(message) [[deprecated(message)]] +# elif defined(_MSC_VER) +# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45)) +# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31) +# define LZ4_DEPRECATED(message) __attribute__((deprecated)) +# else +# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler") +# define LZ4_DEPRECATED(message) /* disabled */ +# endif +#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ + +/*! Obsolete compression functions (since v1.7.3) */ +LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); +LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); + +/*! Obsolete decompression functions (since v1.8.0) */ +LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); +LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); + +/* Obsolete streaming functions (since v1.7.0) + * degraded functionality; do not use! + * + * In order to perform streaming compression, these functions depended on data + * that is no longer tracked in the state. They have been preserved as well as + * possible: using them will still produce a correct output. However, they don't + * actually retain any history between compression calls. The compression ratio + * achieved will therefore be no better than compressing each chunk + * independently. + */ +LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); +LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); +LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); +LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); + +/*! Obsolete streaming decoding functions (since v1.7.0) */ +LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); +LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); + +/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : + * These functions used to be faster than LZ4_decompress_safe(), + * but this is no longer the case. They are now slower. + * This is because LZ4_decompress_fast() doesn't know the input size, + * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. + * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. + * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. + * + * The last remaining LZ4_decompress_fast() specificity is that + * it can decompress a block without knowing its compressed size. + * Such functionality can be achieved in a more secure manner + * by employing LZ4_decompress_safe_partial(). + * + * Parameters: + * originalSize : is the uncompressed size to regenerate. + * `dst` must be already allocated, its size must be >= 'originalSize' bytes. + * @return : number of bytes read from source buffer (== compressed size). + * The function expects to finish at block's end exactly. + * If the source stream is detected malformed, the function stops decoding and returns a negative result. + * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. + * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. + * Also, since match offsets are not validated, match reads from 'src' may underflow too. + * These issues never happen if input (compressed) data is correct. + * But they may happen if input data is invalid (error or intentional tampering). + * As a consequence, use these functions in trusted environments with trusted data **only**. + */ +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") +LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") +LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") +LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); + +/*! LZ4_resetStream() : + * An LZ4_stream_t structure must be initialized at least once. + * This is done with LZ4_initStream(), or LZ4_resetStream(). + * Consider switching to LZ4_initStream(), + * invoking LZ4_resetStream() will trigger deprecation warnings in the future. + */ +LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); + + +#endif /* LZ4_H_98237428734687 */ + + +#if defined (__cplusplus) +} +#endif diff --git a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py index 778324fd4..c99167297 100755 --- a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py +++ b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py @@ -3,6 +3,8 @@ import argparse from PIL import Image +# LZ4 compression +import lz4.block class RawMixin: def encode_byte(self, byte): @@ -11,7 +13,6 @@ class RawMixin: def encode_end(self): pass - class RleMixin: RLE_BYTE = 0 RLE_SEQ = 1 @@ -51,6 +52,21 @@ class RleMixin: if self.state == self.RLE_SEQ: self.write(self.count) +class Lz4Mixin: + + def __init__(self): + self.buffer = [] + + def encode_byte(self,byte): + self.buffer.append(byte) + + def encode_end(self): + compressed_data = lz4.block.compress(bytes(self.buffer), compression=12, mode='high_compression', store_size=False) + data_len = len(compressed_data).to_bytes(4, byteorder='little') + for b in data_len: + self.write(b) + for b in compressed_data: + self.write(b) class ImageEncoder: def __init__(self, filename, size_format, reverse=False): @@ -59,6 +75,8 @@ class ImageEncoder: self.reverse = reverse def write(self, value): + if isinstance(value, bytes): + value = int.from_bytes(value, 'little') self.f.write("0x%02x," % value) def write_size(self, width, height): @@ -163,6 +181,7 @@ def main(): parser.add_argument('--format', action="store", help="Output format") parser.add_argument("--reverse", help="Invert pixels order", action="store_true") parser.add_argument("--rle", help="Enable RLE compression", action="store_true") + parser.add_argument("--lz4", help="Enable RLE compression", action="store_true") parser.add_argument("--rows", help="Image rows count (for 1bit format)", type=int, default=1) parser.add_argument("--size-format", help="Header image size format (1 or 2 bytes)", type=int, default=1) @@ -170,7 +189,14 @@ def main(): image = Image.open(args.input) output = args.output - encoder = ImageEncoder.create(output, args.size_format, args.reverse, RleMixin if args.rle else RawMixin) + + byte_encoder = RawMixin + if args.rle: + byte_encoder = RleMixin + if args.lz4: + byte_encoder = Lz4Mixin + + encoder = ImageEncoder.create(output, args.size_format, args.reverse, byte_encoder) if args.format == "1bit": encoder.encode_1bit(image, args.rows) From f07243d47f910c48b21a5b02ce3dc3ca585dd344 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 25 May 2022 07:46:50 +0200 Subject: [PATCH 624/714] theme: removed unused methods --- radio/src/thirdparty/libopenui/src/theme.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/theme.h b/radio/src/thirdparty/libopenui/src/theme.h index 0de92ac33..f1fa038eb 100644 --- a/radio/src/thirdparty/libopenui/src/theme.h +++ b/radio/src/thirdparty/libopenui/src/theme.h @@ -36,9 +36,6 @@ enum IconState { class Theme { public: - // virtual void drawMessageBox(const char * title, const char * text, const - // char * action, LcdFlags flags) const = 0; - virtual void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value, int total) const = 0; @@ -51,8 +48,6 @@ class Theme { } virtual void drawCheckBox(BitmapBuffer * dc, bool checked, coord_t x, coord_t y, bool focus = false) const = 0; - virtual void drawChoice(BitmapBuffer * dc, ChoiceBase * choice, const char * str) const = 0; - virtual void drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const = 0; virtual const BitmapBuffer * getIcon(uint8_t index, IconState state) const = 0; virtual const BitmapBuffer * getIconMask(uint8_t index) const = 0; From 4ffc94b88d7c6cba355e89240d469f0f8f46a7f1 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 26 May 2022 06:44:14 +0200 Subject: [PATCH 625/714] 8bit masks use uint16_t for width/height --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 5 +++-- radio/src/thirdparty/libopenui/tools/encode-bitmap.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 913104421..cafa1d0d9 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1268,10 +1268,11 @@ BitmapBuffer * BitmapBuffer::loadMask(const char * filename) BitmapBuffer* BitmapBuffer::load8bitMask(const uint8_t * lbm) { - BitmapBuffer * bitmap = new BitmapBuffer(BMP_RGB565,lbm[0],lbm[1]); + const uint16_t* hdr = (const uint16_t*)lbm; + BitmapBuffer * bitmap = new BitmapBuffer(BMP_RGB565,hdr[0],hdr[1]); if (bitmap) { pixel_t * p = bitmap->getPixelPtrAbs(0, 0); - const uint8_t * src = lbm + 2; + const uint8_t * src = lbm + 4; for (int i = bitmap->width() * bitmap->height(); i > 0; i--) { *((uint8_t *)p) = (*(src++) >> 4); MOVE_TO_NEXT_RIGHT_PIXEL(p); diff --git a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py index c99167297..bd2c7be90 100755 --- a/radio/src/thirdparty/libopenui/tools/encode-bitmap.py +++ b/radio/src/thirdparty/libopenui/tools/encode-bitmap.py @@ -134,7 +134,6 @@ class ImageEncoder: value = self.get_pixel(image, x, y) value = 0xFF - value self.encode_byte(value) - self.f.write("\n") self.encode_end() def encode_5_6_5(self, image): From 866bc46d4de65d84f49173d8acdd6467e3a5ec0b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 26 May 2022 08:54:20 +0200 Subject: [PATCH 626/714] fix(lvgl): apply fix to allow for nested input device polling --- radio/src/thirdparty/libopenui/thirdparty/lvgl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/thirdparty/lvgl b/radio/src/thirdparty/libopenui/thirdparty/lvgl index f3deb17e3..2d557d712 160000 --- a/radio/src/thirdparty/libopenui/thirdparty/lvgl +++ b/radio/src/thirdparty/libopenui/thirdparty/lvgl @@ -1 +1 @@ -Subproject commit f3deb17e3a4b62e2beb9c9e1faf5d864ce0ac337 +Subproject commit 2d557d712848696cf2d52a802ba041554edb2803 From 25715ed648c16506cb0e27db9c2f465a44780061 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 27 May 2022 12:38:59 +0200 Subject: [PATCH 627/714] Replaced focus handling with LVGL groups --- .../thirdparty/libopenui/src/CMakeLists.txt | 4 +- radio/src/thirdparty/libopenui/src/button.cpp | 57 +---- radio/src/thirdparty/libopenui/src/button.h | 16 +- .../libopenui/src/button_matrix.cpp | 86 ++----- .../thirdparty/libopenui/src/button_matrix.h | 3 +- .../src/thirdparty/libopenui/src/checkbox.cpp | 31 +-- radio/src/thirdparty/libopenui/src/checkbox.h | 60 ++--- radio/src/thirdparty/libopenui/src/choice.cpp | 29 +-- radio/src/thirdparty/libopenui/src/choice.h | 8 +- radio/src/thirdparty/libopenui/src/dialog.cpp | 21 +- radio/src/thirdparty/libopenui/src/dialog.h | 3 +- .../libopenui/src/expansion_panel.cpp | 76 ------ .../libopenui/src/expansion_panel.h | 99 -------- .../thirdparty/libopenui/src/filechoice.cpp | 27 +- .../src/thirdparty/libopenui/src/filechoice.h | 8 +- radio/src/thirdparty/libopenui/src/form.cpp | 237 ++---------------- radio/src/thirdparty/libopenui/src/form.h | 84 +------ .../libopenui/src/keyboard_base.cpp | 65 ++++- .../thirdparty/libopenui/src/keyboard_base.h | 35 +-- .../libopenui/src/keyboard_number.cpp | 114 +++++---- .../libopenui/src/keyboard_number.h | 37 +-- .../libopenui/src/keyboard_text.cpp | 11 + .../thirdparty/libopenui/src/keyboard_text.h | 34 +-- radio/src/thirdparty/libopenui/src/layer.cpp | 64 +++++ radio/src/thirdparty/libopenui/src/layer.h | 68 +---- .../thirdparty/libopenui/src/mainwindow.cpp | 19 +- radio/src/thirdparty/libopenui/src/menu.cpp | 67 ++--- radio/src/thirdparty/libopenui/src/menu.h | 18 +- .../thirdparty/libopenui/src/menutoolbar.h | 62 +++-- .../thirdparty/libopenui/src/modal_window.cpp | 19 +- .../thirdparty/libopenui/src/modal_window.h | 52 ++-- .../thirdparty/libopenui/src/numberedit.cpp | 73 ++---- .../src/thirdparty/libopenui/src/numberedit.h | 35 ++- radio/src/thirdparty/libopenui/src/table.cpp | 33 ++- radio/src/thirdparty/libopenui/src/table.h | 2 +- .../src/thirdparty/libopenui/src/textedit.cpp | 37 +-- radio/src/thirdparty/libopenui/src/textedit.h | 7 +- .../libopenui/src/widgets/simple_btn.c | 38 +++ .../libopenui/src/widgets/simple_btn.h | 36 +++ .../libopenui/src/widgets/window_base.c | 52 ++++ .../libopenui/src/widgets/window_base.h | 36 +++ radio/src/thirdparty/libopenui/src/window.cpp | 181 ++++--------- radio/src/thirdparty/libopenui/src/window.h | 52 ++-- .../src/thirdparty/libopenui/thirdparty/lvgl | 2 +- 44 files changed, 746 insertions(+), 1352 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/src/expansion_panel.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/expansion_panel.h create mode 100644 radio/src/thirdparty/libopenui/src/widgets/simple_btn.c create mode 100644 radio/src/thirdparty/libopenui/src/widgets/simple_btn.h create mode 100644 radio/src/thirdparty/libopenui/src/widgets/window_base.c create mode 100644 radio/src/thirdparty/libopenui/src/widgets/window_base.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 07c62cea4..13b688369 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -35,7 +35,9 @@ set(LIBOPENUI_SRC table.cpp modal_window.cpp dialog.cpp - expansion_panel.cpp + # LVGL widgets + widgets/window_base.c + widgets/simple_btn.c ) if(SOFTWARE_KEYBOARD) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index d88ea9456..d3d716136 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -22,14 +22,7 @@ #include "font.h" #include "theme.h" -static void lvglEvent(lv_event_t* e) -{ - Button* btn = (Button*)lv_event_get_user_data(e); - if(btn->deleted()) - return; - - btn->onEvent(e); -} +#include "widgets/simple_btn.h" static void update_checked_flag(lv_obj_t* obj, WindowFlags flags) { @@ -43,11 +36,10 @@ Button::Button(Window* parent, const rect_t& rect, std::function pressHandler, WindowFlags windowFlag, LcdFlags textFlags, LvglCreate objConstruct) : - FormField(parent, rect, windowFlag, textFlags, objConstruct), + FormField(parent, rect, windowFlag, textFlags, + objConstruct ? objConstruct : simple_btn_create), pressHandler(std::move(pressHandler)) { - lv_obj_add_event_cb(lvobj, lvglEvent, LV_EVENT_CLICKED, this); - if (windowFlag & BUTTON_CHECKED) lv_obj_add_state(lvobj, LV_STATE_CHECKED); } @@ -78,55 +70,20 @@ void Button::onPress() update_checked_flag(lvobj, windowFlags); } -#if defined(HARDWARE_KEYS) -void Button::onEvent(event_t event) +void Button::onClicked() { - auto indev_act = lv_indev_get_act(); - switch (event) { - case EVT_KEY_FIRST(KEY_ENTER): - lv_event_send(lvobj, LV_EVENT_PRESSED, indev_act); - break; - - case EVT_KEY_BREAK(KEY_ENTER): - lv_event_send(lvobj, LV_EVENT_RELEASED, indev_act); - lv_event_send(lvobj, LV_EVENT_CLICKED, indev_act); - break; - - default: - FormField::onEvent(event); - break; - } -} -#endif - -void Button::onEvent(lv_event_t* event) -{ - lv_event_code_t code = lv_event_get_code(event); - - TRACE_WINDOWS("%s received lvgl event code 0x%X", getWindowDebugString("Button").c_str(), code); - - if (enabled && code == LV_EVENT_CLICKED) { - if (!(windowFlags & NO_FOCUS)) { - setFocus(SET_FOCUS_DEFAULT); - } - onKeyPress(); - onPress(); - } + onPress(); } #if defined(HARDWARE_TOUCH) // this needs to stay so that viewmain doesnt get the touch event -bool Button::onTouchEnd(coord_t x, coord_t y) -{ - return true; -} +bool Button::onTouchEnd(coord_t x, coord_t y) { return true; } #endif void Button::checkEvents() { Window::checkEvents(); - if (checkHandler) - checkHandler(); + if (checkHandler) checkHandler(); } TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 17209f7f2..56319ff35 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -40,6 +40,10 @@ class Button: public FormField } #endif + virtual void onPress(); + void onClicked() override; + void checkEvents() override; + void check(bool checked = true); bool checked() const; @@ -53,22 +57,18 @@ class Button: public FormField checkHandler = std::move(handler); } -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - void onEvent(lv_event_t* e);// override; +// #if defined(HARDWARE_KEYS) +// void onEvent(event_t event) override; +// #endif + // void onEvent(lv_event_t* e);// override; #if defined(HARDWARE_TOUCH) bool onTouchEnd(coord_t x, coord_t y) override; #endif - void checkEvents() override; - protected: std::function pressHandler; std::function checkHandler; - - virtual void onPress(); }; diff --git a/radio/src/thirdparty/libopenui/src/button_matrix.cpp b/radio/src/thirdparty/libopenui/src/button_matrix.cpp index 5e774aa0b..fd4ea7fa7 100644 --- a/radio/src/thirdparty/libopenui/src/button_matrix.cpp +++ b/radio/src/thirdparty/libopenui/src/button_matrix.cpp @@ -28,38 +28,47 @@ static const char _map_end[] = ""; static void btn_matrix_event(lv_event_t *e) { - static lv_area_t draw_area; - lv_event_code_t code = lv_event_get_code(e); if (code == LV_EVENT_VALUE_CHANGED) { + lv_obj_t* obj = lv_event_get_target(e); auto btn_id = (uint32_t *)lv_event_get_param(e); auto btnm = (ButtonMatrix*)lv_event_get_user_data(e); - btnm->onPress((uint8_t)*btn_id); + bool edited = lv_obj_has_state(obj, LV_STATE_EDITED); + bool is_pointer = lv_indev_get_type(lv_indev_get_act()) == LV_INDEV_TYPE_POINTER; + if (edited || is_pointer) + btnm->onPress((uint8_t)*btn_id); } else if (code == LV_EVENT_DRAW_PART_BEGIN) { lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); - if (dsc->part != LV_PART_ITEMS) return; - - auto btnm = (ButtonMatrix*)lv_event_get_user_data(e); - if (btnm->isActive((uint8_t)dsc->id)) { - dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_ACTIVE); - dsc->label_dsc->color = makeLvColor(COLOR_THEME_PRIMARY2); - } else { - dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_PRIMARY2); - dsc->label_dsc->color = makeLvColor(COLOR_THEME_SECONDARY1); + if (dsc->part == LV_PART_ITEMS) { + auto btnm = (ButtonMatrix*)lv_event_get_user_data(e); + if (btnm->isActive((uint8_t)dsc->id)) { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_ACTIVE); + dsc->label_dsc->color = makeLvColor(COLOR_THEME_PRIMARY2); + } else { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_PRIMARY2); + dsc->label_dsc->color = makeLvColor(COLOR_THEME_SECONDARY1); + } + } else if (dsc->part == LV_PART_MAIN) { + lv_obj_t* obj = lv_event_get_target(e); + if (lv_obj_has_state(obj, LV_STATE_FOCUS_KEY) && + !lv_obj_has_state(obj, LV_STATE_EDITED)) { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_FOCUS); + dsc->rect_dsc->bg_opa = LV_OPA_20; + } } - - // backup draw area as it gets modified for the label - lv_area_copy(&draw_area, dsc->draw_area); } } ButtonMatrix::ButtonMatrix(Window* parent, const rect_t& r) : FormField(parent, r, 0, 0, lv_btnmatrix_create) { + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); + lv_obj_add_event_cb(lvobj, btn_matrix_event, LV_EVENT_ALL, this); } @@ -132,49 +141,8 @@ void ButtonMatrix::update() lv_btnmatrix_set_btn_ctrl_all(lvobj, ctrl); } -void ButtonMatrix::onEvent(event_t event) +void ButtonMatrix::onClicked() { - char c = 0; - lv_event_code_t code = LV_EVENT_ALL; - -#if defined (HARDWARE_KEYS) - switch (event) { - case EVT_KEY_FIRST(KEY_ENTER): - code = LV_EVENT_PRESSED; - break; - - case EVT_KEY_BREAK(KEY_ENTER): - code = LV_EVENT_RELEASED; - break; - - case EVT_ROTARY_LEFT: - if (lv_btnmatrix_get_selected_btn(lvobj) != 0) { - code = LV_EVENT_KEY; - c = LV_KEY_LEFT; - } - break; - case EVT_ROTARY_RIGHT: - if (lv_btnmatrix_get_selected_btn(lvobj) < btn_cnt - 1) { - code = LV_EVENT_KEY; - c = LV_KEY_RIGHT; - } - break; - } -#endif - - if ((code == LV_EVENT_KEY) && c != 0) lv_event_send(lvobj, code, &c); - else if (code != LV_EVENT_ALL) lv_event_send(lvobj, code, lv_indev_get_act()); - else FormField::onEvent(event); -} - -void ButtonMatrix::setFocus(uint8_t flag, Window* from) -{ - if (lv_btnmatrix_get_selected_btn(lvobj) == LV_BTNMATRIX_BTN_NONE) { - if (flag == SET_FOCUS_BACKWARD) - lv_btnmatrix_set_selected_btn(lvobj, btn_cnt - 1); - else - lv_btnmatrix_set_selected_btn(lvobj, 0); - } - - FormField::setFocus(flag, from); + lv_group_focus_obj(lvobj); + FormField::onClicked(); } diff --git a/radio/src/thirdparty/libopenui/src/button_matrix.h b/radio/src/thirdparty/libopenui/src/button_matrix.h index 2438b4b0d..2d84ebae7 100644 --- a/radio/src/thirdparty/libopenui/src/button_matrix.h +++ b/radio/src/thirdparty/libopenui/src/button_matrix.h @@ -41,6 +41,5 @@ class ButtonMatrix : public FormField void setText(uint8_t btn_id, const char* txt); void update(); - void onEvent(event_t event) override; - void setFocus(uint8_t flag, Window* from) override; + void onClicked() override; }; diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 457e6326a..8bee639ae 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -52,34 +52,7 @@ CheckBox::CheckBox(Window* parent, const rect_t& rect, LV_EVENT_VALUE_CHANGED, this); } -#if defined(HARDWARE_KEYS) -void CheckBox::onEvent(event_t event) +void CheckBox::onClicked() { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("CheckBox").c_str(), event); - - if (enabled && event == EVT_KEY_BREAK(KEY_ENTER)) { - onKeyPress(); - if (lv_obj_get_state(lvobj) & LV_STATE_CHECKED) - lv_obj_clear_state(lvobj, LV_STATE_CHECKED); - else - lv_obj_add_state(lvobj, LV_STATE_CHECKED); - - _setValue(lv_obj_get_state(lvobj) & LV_STATE_CHECKED); - } - else { - FormField::onEvent(event); - } + // prevent FormField::onClicked() } -#endif - -#if defined(HARDWARE_TOUCH) -bool CheckBox::onTouchEnd(coord_t x, coord_t y) -{ - if (!hasFocus()) { - setFocus(SET_FOCUS_DEFAULT); - } - - return true; -} -#endif - diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 235d08b9c..12c4210cb 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -22,52 +22,32 @@ #include #include "form.h" -class CheckBox : public FormField { - public: - CheckBox(Window * parent, const rect_t & rect, std::function getValue, std::function setValue, WindowFlags flags = 0); +class CheckBox : public FormField +{ + public: + CheckBox(Window* parent, const rect_t& rect, + std::function getValue, + std::function setValue, WindowFlags flags = 0); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "CheckBox"; - } + std::string getName() const override { return "CheckBox"; } #endif - const char * getLabel() const - { - return label.c_str(); - } + void onClicked() override; - void setLabel(std::string newLabel) - { - label = std::move(newLabel); - } + const char* getLabel() const { return label.c_str(); } + void setLabel(std::string newLabel) { label = std::move(newLabel); } -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif + uint8_t getValue() const { return _getValue(); } + void setValue(uint8_t value) { _setValue(value); } -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif + void setSetValueHandler(std::function handler) + { + _setValue = std::move(handler); + } - uint8_t getValue() const - { - return _getValue(); - } - - void setValue(uint8_t value) - { - _setValue(value); - } - - void setSetValueHandler(std::function handler) - { - _setValue = std::move(handler); - } - - protected: - std::string label; - std::function _getValue; - std::function _setValue; + protected: + std::string label; + std::function _getValue; + std::function _setValue; }; diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index bfcf73556..760ea2386 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -181,20 +181,11 @@ void Choice::setValues(const char * const values[]) } } -#if defined(HARDWARE_KEYS) -void Choice::onEvent(event_t event) +void Choice::onClicked() { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - onKeyPress(); - openMenu(); - } - else { - FormField::onEvent(event); - } + onKeyPress(); + openMenu(); } -#endif void Choice::openMenu() { @@ -247,17 +238,3 @@ void Choice::openMenu() setEditMode(false); }); } - -#if defined(HARDWARE_TOUCH) -bool Choice::onTouchEnd(coord_t, coord_t) -{ - if (enabled) { - if (!hasFocus()) { - setFocus(SET_FOCUS_DEFAULT); - } - onKeyPress(); - openMenu(); - } - return true; -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index c338f5707..14337ee62 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -81,13 +81,7 @@ class Choice: public ChoiceBase { } #endif -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif + void onClicked() override; void setBeforeDisplayMenuHandler(std::function handler) { diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 00f599ccd..066fffafd 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -21,14 +21,16 @@ #include "mainwindow.h" #include "theme.h" +static rect_t _get_form_rect(const rect_t& rect) +{ + return {0, POPUP_HEADER_HEIGHT, rect.w, + coord_t(rect.h - POPUP_HEADER_HEIGHT)}; +} + DialogWindowContent::DialogWindowContent(Dialog* parent, const rect_t& rect) : ModalWindowContent(parent, rect), - form(this, - { 0, POPUP_HEADER_HEIGHT, rect.w, - coord_t(rect.h - POPUP_HEADER_HEIGHT) }, - FORM_NO_BORDER | FORM_FORWARD_FOCUS) + form(this, _get_form_rect(rect), FORM_NO_BORDER) { - form.setFocus(SET_FOCUS_DEFAULT); } void DialogWindowContent::deleteLater(bool detach, bool trash) @@ -58,12 +60,7 @@ Dialog::Dialog(Window* parent, std::string title, const rect_t& rect) : if (!title.empty()) content->setTitle(std::move(title)); } -void Dialog::onEvent(event_t event) +void Dialog::onCancel() { -#if defined(HARDWARE_KEYS) - if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { - killEvents(event); - deleteLater(); - } -#endif + deleteLater(); } diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 3b00520b8..0678fc11b 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -48,6 +48,7 @@ class Dialog : public ModalWindow Dialog(Window* parent, std::string title, const rect_t& rect); protected: - void onEvent(event_t event) override; DialogWindowContent* content; + + void onCancel() override; }; diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp b/radio/src/thirdparty/libopenui/src/expansion_panel.cpp deleted file mode 100644 index 99a8c7e43..000000000 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "expansion_panel.h" - -void ExpansionPanel::updateHeight(bool move) -{ - coord_t newHeight = (isOpen ? header->height() + body->height() : header->height()); - if (move) { - parent->moveWindowsTop(bottom(), newHeight - height()); - } - setHeight(newHeight); -} - -void ExpansionPanel::setFocus(uint8_t flag, Window * from) -{ - if (!enabled || isOpen) - FormGroup::setFocus(flag, from); - else - header->setFocus(flag, from); -} - -ExpansionPanelHeader::ExpansionPanelHeader(ExpansionPanel * parent): - FormGroup(parent, {0, 0, parent->width(), parent->height()}, FORWARD_SCROLL) -{ -} - -void ExpansionPanelHeader::onEvent(event_t event) -{ - auto panel = static_cast(parent); - - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - panel->toggle(); - } - else if (event == EVT_ROTARY_RIGHT && !panel->isOpen) { - auto next = panel->getNextField(); - if (next) - next->setFocus(SET_FOCUS_FORWARD, this); - } - else if (event == EVT_ROTARY_LEFT) { - auto previous = panel->getPreviousField(); - if (previous) { - previous->setFocus(SET_FOCUS_BACKWARD, this); - } - } - else { - FormGroup::onEvent(event); - } -} - -#if defined(HARDWARE_TOUCH) -bool ExpansionPanelHeader::onTouchEnd(coord_t, coord_t) -{ - if (enabled) { - static_cast(parent)->toggle(); - setFocus(SET_FOCUS_DEFAULT); - } - return true; -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/expansion_panel.h b/radio/src/thirdparty/libopenui/src/expansion_panel.h deleted file mode 100644 index 9dd965ac5..000000000 --- a/radio/src/thirdparty/libopenui/src/expansion_panel.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#pragma once - -#include "form.h" - -class ExpansionPanel; - -class ExpansionPanelHeader: virtual public FormGroup -{ - public: - explicit ExpansionPanelHeader(ExpansionPanel * parent); - -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "ExpansionPanelHeader"; - } -#endif - - void onEvent(event_t event) override; - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t, coord_t) override; -#endif -}; - -class ExpansionPanel: public FormGroup -{ - friend class ExpansionPanelHeader; - - public: - ExpansionPanel(Window * parent, const rect_t & rect): - FormGroup(parent, rect, FORM_NO_BORDER | FORM_FORWARD_FOCUS | FORWARD_SCROLL) - { - } - -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "ExpansionPanel"; - } -#endif - - void toggle() - { - open(!isOpen); - } - - virtual void open(bool state = true) - { - isOpen = state; - updateHeight(); - } - - void setCloseAllowed(bool value = true) - { - header->enable(value); - } - - void enable(bool value = true) - { - FormGroup::enable(value); - header->enable(value); - } - - void disable() - { - open(false); - enable(false); - } - - void updateHeight(bool move = true); - - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override; - - FormGroup * body = nullptr; - - protected: - ExpansionPanelHeader * header = nullptr; - bool isOpen = false; -}; diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index 0e153fd49..eb6ef25c6 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -120,7 +120,7 @@ bool FileChoice::openMenu() menu->setCloseHandler([=]() { editMode = false; - setFocus(SET_FOCUS_DEFAULT); + // setFocus(SET_FOCUS_DEFAULT); }); return true; @@ -132,29 +132,8 @@ bool FileChoice::openMenu() return false; } -#if defined(HARDWARE_KEYS) -void FileChoice::onEvent(event_t event) +void FileChoice::onClicked() { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - if (openMenu()) { - editMode = true; - invalidate(); - } - } - else { - FormField::onEvent(event); - } -} -#endif - -#if defined(HARDWARE_TOUCH) -bool FileChoice::onTouchEnd(coord_t, coord_t) -{ - setFocus(SET_FOCUS_DEFAULT); - setEditMode(true); + onKeyPress(); openMenu(); - return true; } -#endif diff --git a/radio/src/thirdparty/libopenui/src/filechoice.h b/radio/src/thirdparty/libopenui/src/filechoice.h index f3003c4c5..e74527107 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.h +++ b/radio/src/thirdparty/libopenui/src/filechoice.h @@ -35,13 +35,7 @@ public: std::string getName() const override { return "FileChoice"; } #endif -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif + void onClicked() override; protected: std::string getLabelText() override; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 0c4bb998e..91b2659ee 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -21,71 +21,45 @@ #include "bitmapbuffer.h" #include "libopenui_config.h" +#include "widgets/window_base.h" + FormField::FormField(Window* parent, const rect_t& rect, WindowFlags windowFlags, LcdFlags textFlags, LvglCreate objConstruct) : Window(parent, rect, windowFlags, textFlags, objConstruct) { - if (!(windowFlags & NO_FOCUS) || (windowFlags & FORM_FORWARD_FOCUS)) { - auto cont = dynamic_cast(parent); - if (cont) { - cont->addField(this); - } - } } -#if defined(HARDWARE_KEYS) -void FormField::onEvent(event_t event) +void FormField::setEditMode(bool newEditMode) { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormField").c_str(), event); + editMode = newEditMode; - if (event == EVT_ROTARY_RIGHT/*EVT_KEY_BREAK(KEY_DOWN)*/) { - if (next) { - onKeyPress(); - next->setFocus(SET_FOCUS_FORWARD, this); - } + if (lvobj != nullptr) { + lv_group_t* grp = (lv_group_t*)lv_obj_get_group(lvobj); + if (grp != nullptr) lv_group_set_editing(grp, editMode); } - else if (event == EVT_ROTARY_LEFT/*EVT_KEY_BREAK(KEY_UP)*/) { - if (previous) { - onKeyPress(); - previous->setFocus(SET_FOCUS_BACKWARD, this); - } + + if (editMode && lvobj != nullptr) { + lv_obj_add_state(lvobj, LV_STATE_EDITED); + } else if (lvobj != nullptr) { + lv_obj_clear_state(lvobj, LV_STATE_EDITED); } - else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - onKeyPress(); - setEditMode(!editMode); - invalidate(); - } - else if (event == EVT_KEY_BREAK(KEY_EXIT) && editMode) { - onKeyPress(); + invalidate(); +} + +void FormField::onClicked() +{ + setEditMode(!editMode); + invalidate(); +} + +void FormField::onCancel() +{ + if (isEditMode()) { setEditMode(false); invalidate(); - } - else { - Window::onEvent(event); - } -} -#endif -extern lv_group_t* inputGroup; -void FormField::setFocus(uint8_t flag, Window * from) -{ - if (enabled && !lv_obj_has_flag(lvobj, LV_OBJ_FLAG_HIDDEN)) { - Window::setFocus(flag, from); - if(lvobj != nullptr) { - lv_group_focus_obj(lvobj); - } - } - else { - if (flag == SET_FOCUS_BACKWARD) { - if (previous) { - previous->setFocus(flag, this); - } - } - else { - if (next) { - next->setFocus(flag, this); - } - } + } else { + Window::onCancel(); } } @@ -126,35 +100,11 @@ void FormGroup::Line::addChild(Window* window) } } -void FormGroup::Line::addField(FormField *field) -{ - group->addField(field); -} - -void FormGroup::Line::removeField(FormField *field) -{ - auto form = static_cast(parent); - form->removeField(field); -} - FormGroup::FormGroup(Window* parent, const rect_t& rect, WindowFlags windowflags, LvglCreate objConstruct) : - FormField(parent, rect, windowflags, 0, objConstruct) + Window(parent, rect, windowflags, 0, objConstruct) { lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); - - - -} - -void FormGroup::clear() -{ - Window::clear(); - first = nullptr; - last = nullptr; - if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { - previous->setNextField(this); - } } void FormGroup::setFlexLayout(lv_flex_flow_t flow, lv_coord_t padding) @@ -175,136 +125,3 @@ FormGroup::Line* FormGroup::newLine(FlexGridLayout* layout) auto lv_line = window_create(lvobj); return new Line(this, lv_line, layout); } - -void FormGroup::removeField(FormField * field) -{ - FormField* prev = field->getPreviousField(); - FormField* next = field->getNextField(); - - if (prev) { prev->setNextField(next); } - if (next) { next->setPreviousField(prev); } - - if (first == field) { - if (prev && (prev != field)) first = prev; - else if (next && (next != field)) first = next; - else first = nullptr; - } - - if (last == field) { - if (next && (next != field)) last = next; - else if (prev && (prev != field)) last = prev; - else last = nullptr; - } -} - -void FormGroup::addField(FormField * field) -{ - if (field->getWindowFlags() & FORM_DETACHED) - return; - - if (last) - link(last, field); - last = field; - if (!first) - first = field; - - if (WRAP_FORM_FIELDS_WITHIN_PAGE) { - if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { - last->setNextField(this); - link(previous, first); - } - else { - link(last, first); - } - } - else { - if (windowFlags & FORM_FORWARD_FOCUS) { - last->setNextField(this); - if (previous) - link(previous, first); - } - } -} - -void FormGroup::setFocus(uint8_t flag, Window * from) -{ - TRACE_WINDOWS("%s setFocus(%d)", getWindowDebugString("FormGroup").c_str(), flag); - - if (windowFlags & FORM_FORWARD_FOCUS) { - switch (flag) { - case SET_FOCUS_BACKWARD: - if (from && from->isChild(first)) { - if (previous == this) { - last->setFocus(SET_FOCUS_BACKWARD, this); - } - else if (previous) { - previous->setFocus(SET_FOCUS_BACKWARD, this); - } - } - else { - if (last) { - last->setFocus(SET_FOCUS_BACKWARD, this); - } - else if (previous) { - previous->setFocus(SET_FOCUS_BACKWARD, this); - } - } - break; - - case SET_FOCUS_FIRST: - clearFocus(); - // no break; - - case SET_FOCUS_FORWARD: - if (from && from->isChild(this)) { - if (next == this) { - if (first) - first->setFocus(SET_FOCUS_FORWARD, this); - } - else if (next) { - next->setFocus(SET_FOCUS_FORWARD, this); - } - } - else { - if (first) { - first->setFocus(SET_FOCUS_FORWARD, this); - } - else if (next) { - next->setFocus(SET_FOCUS_FORWARD, this); - } - } - break; - - default: - if (from == previous) { - if (first) { - first->setFocus(SET_FOCUS_DEFAULT); - } - else { - clearFocus(); - focusWindow = this; - } - } - else if (next) { - next->setFocus(SET_FOCUS_FORWARD); - } - else { - clearFocus(); - focusWindow = this; - } - break; - } - } - else if (!(windowFlags & NO_FOCUS)) { - FormField::setFocus(flag, from); - } -} - -#if defined(HARDWARE_KEYS) -void FormGroup::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString("FormGroup").c_str(), event); - - FormField::onEvent(event); -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 8ecc1e617..71a09571e 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -36,9 +36,7 @@ class FormField : public Window virtual void changeEnd(bool forceChanged = false) { - if (changeHandler) { - changeHandler(); - } + if (changeHandler) { changeHandler(); } } void setChangeHandler(std::function handler) @@ -51,33 +49,10 @@ class FormField : public Window backgroundHandler = std::move(handler); } - inline void setNextField(FormField *field) { next = field; } - - inline void setPreviousField(FormField *field) { previous = field; } - - inline FormField *getPreviousField() { return previous; } - - inline FormField *getNextField() { return next; } - inline bool isEditMode() const { return editMode; } + virtual void setEditMode(bool newEditMode); - virtual void setEditMode(bool newEditMode) - { - editMode = newEditMode; - - // if (lvobj != nullptr) { - // lv_group_t *grp = (lv_group_t *)lv_obj_get_group(lvobj); - // if (grp != nullptr) lv_group_set_editing(grp, editMode); - // } - - if (editMode && lvobj != nullptr) { - lv_obj_add_state(lvobj, LV_STATE_EDITED); - } else if (lvobj != nullptr) { - lv_obj_clear_state(lvobj, LV_STATE_EDITED); - } - invalidate(); - } - + // TODO: replace with LVGL flags void enable(bool value = true) { enabled = value; @@ -87,45 +62,26 @@ class FormField : public Window bool isEnabled() const { return enabled; } void disable() { enable(false); } - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, - Window *from = nullptr) override; - void onFocusLost() override { setEditMode(false); Window::onFocusLost(); } - inline static void link(FormField *previous, FormField *next) - { - previous->setNextField(next); - next->setPreviousField(previous); - } - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - + void onClicked() override; + void onCancel() override; + protected: - FormField *next = nullptr; - FormField *previous = nullptr; bool editMode = false; bool enabled = true; std::function changeHandler = nullptr; std::function backgroundHandler = nullptr; }; -class FieldContainer +class FormGroup : public Window { public: - virtual void addField(FormField *field) = 0; - virtual void removeField(FormField *field) = 0; -}; - -class FormGroup : public FormField, public FieldContainer -{ - public: - class Line : public Window, public FieldContainer + class Line : public Window { FlexGridLayout* layout; FormGroup* group; @@ -138,8 +94,6 @@ class FormGroup : public FormField, public FieldContainer protected: void addChild(Window* window) override; - void addField(FormField *field) override; - void removeField(FormField *field) override; }; FormGroup(Window *parent, const rect_t &rect, WindowFlags windowflags = 0, @@ -149,30 +103,8 @@ class FormGroup : public FormField, public FieldContainer std::string getName() const override { return "FormGroup"; } #endif - void clear(); - void setFlexLayout(lv_flex_flow_t flow = LV_FLEX_FLOW_COLUMN, lv_coord_t padding = 0); Line* newLine(FlexGridLayout* layout = nullptr); - - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, - Window *from = nullptr) override; - - void addField(FormField *field) override; - void removeField(FormField *field) override; - - void setFirstField(FormField *field) { first = field; } - void setLastField(FormField *field) { last = field; } - FormField *getFirstField() const { return first; } - FormField *getLastField() const { return last; } - - - protected: - FormField *first = nullptr; - FormField *last = nullptr; - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif }; class FormWindow : public FormGroup diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 1ac4a3b24..3017c3208 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -17,6 +17,8 @@ * Lesser General Public License for more details. */ #include "keyboard_base.h" + +#include "form.h" #include "mainwindow.h" Keyboard * Keyboard::activeKeyboard = nullptr; @@ -24,28 +26,66 @@ Keyboard * Keyboard::activeKeyboard = nullptr; static void keyboard_event_cb(lv_event_t * e) { auto code = lv_event_get_code(e); - Keyboard *kb = (Keyboard *) lv_event_get_user_data(e); if (code == LV_EVENT_READY) { pushEvent(EVT_VIRTUAL_KEY('\n')); - kb->hide(); + Keyboard::hide(); + } else if (code == LV_EVENT_CANCEL) { + Keyboard::hide(); + } else if (code == LV_EVENT_KEY) { + int32_t c = *((int32_t *)lv_event_get_param(e)); + if (c == LV_KEY_ESC) { + Keyboard::hide(); + } + } +} + +static void field_focus_leave(lv_event_t* e) +{ + Keyboard::hide(); +} + +static void _assign_lv_group(lv_group_t* g) +{ + // associate it with all input devices + lv_indev_t* indev = lv_indev_get_next(NULL); + while (indev) { + lv_indev_set_group(indev, g); + indev = lv_indev_get_next(indev); } } Keyboard::Keyboard(coord_t height) : - FormWindow(MainWindow::instance(), {0, LCD_H - height, LCD_W, height}, OPAQUE) + Window(MainWindow::instance(), {0, LCD_H - height, LCD_W, height}, OPAQUE) { // set the background of the window and opacity to 100% lv_obj_set_parent(lvobj, lv_layer_top()); // the keyboard is always on top lv_obj_set_style_bg_color(lvobj, lv_color_make(0xE0, 0xE0, 0xE0), LV_PART_MAIN); lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); - keyboard = lv_keyboard_create(this->getLvObj()); + // use a separate group for the keyboard + group = lv_group_create(); + lv_group_set_editing(group, true); + + auto old_g = lv_group_get_default(); + lv_group_set_default(group); + + keyboard = lv_keyboard_create(lvobj); + lv_group_set_default(old_g); + lv_obj_add_event_cb(keyboard, keyboard_event_cb, LV_EVENT_ALL, this); + lv_obj_set_pos(keyboard, 0, 0); lv_obj_set_size(keyboard, LCD_W, height); + + // TODO: really needed ??? lv_obj_clear_flag(keyboard, LV_OBJ_FLAG_HIDDEN); } +Keyboard::~Keyboard() +{ + if (group) lv_group_del(group); +} + void Keyboard::clearField() { TRACE("CLEAR FIELD"); @@ -62,10 +102,16 @@ void Keyboard::clearField() } if (field) { + // restore field's group + auto obj = field->getLvObj(); + lv_obj_remove_event_cb(obj, field_focus_leave); + lv_group_t* g = (lv_group_t*)lv_obj_get_group(obj); + if (g) _assign_lv_group(g); + field->setEditMode(false); field->changeEnd(); field = nullptr; - } + } } void Keyboard::hide() @@ -81,7 +127,7 @@ bool Keyboard::attachKeyboard() { if (activeKeyboard) { if (activeKeyboard == this) return false; - activeKeyboard->clearField(); + hide(); } activeKeyboard = this; @@ -113,8 +159,11 @@ void Keyboard::setField(FormField* newField) invalidate(); newField->setEditMode(true); - lv_keyboard_set_textarea(keyboard, newField->getLvObj()); - + + lv_keyboard_set_textarea(keyboard, obj); + lv_obj_add_event_cb(obj, field_focus_leave, LV_EVENT_DEFOCUSED, nullptr); + _assign_lv_group(group); + field = newField; } } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 4ced4488c..8a599dc45 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -19,24 +19,29 @@ #pragma once -#include "form.h" +#include "window.h" -class Keyboard: public FormWindow +class FormField; + +class Keyboard : public Window { - public: - explicit Keyboard(coord_t height); - void clearField(); - static void hide(); + public: + explicit Keyboard(coord_t height); + ~Keyboard(); - protected: - static Keyboard * activeKeyboard; - lv_obj_t * keyboard = nullptr; + void clearField(); + static void hide(); - FormField *field = nullptr; - Window *fieldContainer = nullptr; - lv_coord_t scroll_pos = 0; + protected: + static Keyboard *activeKeyboard; - void setField(FormField *newField); - bool attachKeyboard(); + lv_group_t* group = nullptr; + lv_obj_t* keyboard = nullptr; + + FormField* field = nullptr; + Window* fieldContainer = nullptr; + lv_coord_t scroll_pos = 0; + + void setField(FormField* newField); + bool attachKeyboard(); }; - diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index b55c9f174..b23ef7f33 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -18,64 +18,63 @@ */ #include "keyboard_number.h" -#include "button.h" +#include "numberedit.h" constexpr coord_t KEYBOARD_HEIGHT = 90; NumberKeyboard * NumberKeyboard::_instance = nullptr; +static const char* number_kb_map[] = { + "<<", "-", "+", ">>", "\n", + "MIN", "DEF", "+/-", "MAX", "" +}; + +#define LV_KB_BTN(width) LV_BTNMATRIX_CTRL_POPOVER | width +#define LV_KB_CTRL(width) LV_KEYBOARD_CTRL_BTN_FLAGS | width + +static const lv_btnmatrix_ctrl_t number_kb_ctrl_map[] = { + LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), + LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_CTRL(4), LV_KB_BTN(4) +}; + +static void on_key(lv_event_t* e) +{ + lv_obj_t * obj = lv_event_get_target(e); + NumberEdit* edit = (NumberEdit*)lv_event_get_user_data(e); + if (!obj || !edit) return; + + uint16_t btn_id = lv_btnmatrix_get_selected_btn(obj); + if(btn_id == LV_BTNMATRIX_BTN_NONE) return; + + const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_btn(obj)); + if(txt == NULL) return; + + if (strcmp(txt, "<<") == 0) { + edit->onEvent(EVT_VIRTUAL_KEY_BACKWARD); + } else if (strcmp(txt, "-") == 0) { + edit->onEvent(EVT_VIRTUAL_KEY_MINUS); + } else if (strcmp(txt, "+") == 0) { + edit->onEvent(EVT_VIRTUAL_KEY_PLUS); + } else if (strcmp(txt, ">>") == 0) { + edit->onEvent(EVT_VIRTUAL_KEY_FORWARD); + } else if (strcmp(txt, "MIN") == 0) { + edit->onEvent(EVT_VIRTUAL_KEY_MIN); + } else if (strcmp(txt, "DEF") == 0) { + edit->onEvent(EVT_VIRTUAL_KEY_DEFAULT); + } else if (strcmp(txt, "MAX") == 0) { + edit->onEvent(EVT_VIRTUAL_KEY_MAX); + } else if (strcmp(txt, "+/-") == 0) { + edit->onEvent(EVT_VIRTUAL_KEY_SIGN); + } +} + NumberKeyboard::NumberKeyboard() : Keyboard(KEYBOARD_HEIGHT) { - // ToDo: for now we are still using the old keyboard - lv_obj_add_flag(keyboard, LV_OBJ_FLAG_HIDDEN); + // setup custom keyboard + lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_USER_1, + number_kb_map, number_kb_ctrl_map); - new TextButton(this, {LCD_W / 2 - 115, 10, 50, 30}, "<<", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_BACKWARD); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - - new TextButton(this, {LCD_W / 2 - 55, 10, 50, 30}, "-", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_MINUS); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - - new TextButton(this, {LCD_W / 2 + 5, 10, 50, 30}, "+", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_PLUS); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - - new TextButton(this, {LCD_W / 2 + 65, 10, 50, 30}, ">>", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_FORWARD); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - - new TextButton(this, {LCD_W / 2 - 115, 50, 50, 30}, "MIN", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_MIN); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - - new TextButton(this, {LCD_W / 2 + 65, 50, 50, 30}, "MAX", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_MAX); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - - new TextButton(this, { LCD_W/2 - 55, 50, 50, 30 }, "DEF", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_DEFAULT); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); - - new TextButton(this, { LCD_W/2 + 5, 50, 50, 30 }, "+/-", - [=]() -> uint8_t { - pushEvent(EVT_VIRTUAL_KEY_SIGN); - return 0; - }, BUTTON_BACKGROUND | OPAQUE | NO_FOCUS); + lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_USER_1); } NumberKeyboard::~NumberKeyboard() @@ -83,3 +82,18 @@ NumberKeyboard::~NumberKeyboard() _instance = nullptr; } +void NumberKeyboard::show(NumberEdit* field) +{ + if (!_instance) _instance = new NumberKeyboard(); + + lv_obj_clear_flag(_instance->lvobj, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + + _instance->setField(field); + + auto kb = _instance->keyboard; + lv_keyboard_set_textarea(kb, nullptr); + + lv_obj_remove_event_cb(kb, on_key); + lv_obj_add_event_cb(kb, on_key, LV_EVENT_VALUE_CHANGED, field); +} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h index bd1f764f5..859cd9f7a 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -21,37 +21,20 @@ #include "keyboard_base.h" -class NumberKeyboard : public Keyboard { - public: - NumberKeyboard(); +class NumberEdit; - ~NumberKeyboard() override; +class NumberKeyboard : public Keyboard +{ + public: + NumberKeyboard(); + ~NumberKeyboard() override; #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "NumberKeyboard"; - } + std::string getName() const override { return "NumberKeyboard"; } #endif - static void show(FormField * field) - { - if (!_instance) - _instance = new NumberKeyboard(); - // else - // lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + static void show(NumberEdit* field); - // lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_NUMBER); - - lv_obj_clear_flag(_instance->lvobj, LV_OBJ_FLAG_HIDDEN); - // lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); - lv_keyboard_set_mode(_instance->keyboard, LV_KEYBOARD_MODE_NUMBER); - lv_obj_move_foreground(_instance->getLvObj()); - - _instance->setField(field); - } - - protected: - static NumberKeyboard * _instance; + protected: + static NumberKeyboard* _instance; }; - diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp index a449a6cad..2c1b2569b 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.cpp @@ -39,3 +39,14 @@ void TextKeyboard::onEvent(event_t event) { } #endif + +void TextKeyboard::show(FormField* field) +{ + if (!_instance) _instance = new TextKeyboard(); + + lv_obj_clear_flag(_instance->lvobj, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); + lv_keyboard_set_mode(_instance->keyboard, LV_KEYBOARD_MODE_TEXT_LOWER); + + _instance->setField(field); +} diff --git a/radio/src/thirdparty/libopenui/src/keyboard_text.h b/radio/src/thirdparty/libopenui/src/keyboard_text.h index c4e4e8e50..375f0efc4 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_text.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_text.h @@ -36,36 +36,22 @@ #define KEYBOARD_ENTER_WIDTH 80 #define KEYBOARD_BITMAP_WIDTH 45 -class TextKeyboard : public Keyboard { - public: - TextKeyboard(); - - ~TextKeyboard() override; +class TextKeyboard : public Keyboard +{ + public: + TextKeyboard(); + ~TextKeyboard() override; #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "TextKeyboard"; - } + std::string getName() const override { return "TextKeyboard"; } #endif - static void show(FormField * field) - { - if (!_instance) - _instance = new TextKeyboard(); + static void show(FormField* field); - lv_obj_clear_flag(_instance->lvobj, LV_OBJ_FLAG_HIDDEN); - lv_obj_clear_flag(_instance->keyboard, LV_OBJ_FLAG_HIDDEN); - lv_keyboard_set_mode(_instance->keyboard, LV_KEYBOARD_MODE_TEXT_LOWER); - _instance->setField(field); - } - - protected: + protected: #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; + void onEvent(event_t event) override; #endif - static TextKeyboard * _instance; - // const char * const * layout; + static TextKeyboard* _instance; }; - diff --git a/radio/src/thirdparty/libopenui/src/layer.cpp b/radio/src/thirdparty/libopenui/src/layer.cpp index b40c72bad..98c441cac 100644 --- a/radio/src/thirdparty/libopenui/src/layer.cpp +++ b/radio/src/thirdparty/libopenui/src/layer.cpp @@ -20,3 +20,67 @@ #include "layer.h" std::list Layer::stack; + +Layer::Layer(Window* w, lv_group_t* g) : window(w), group(g) {} +Layer::~Layer() { lv_group_del(group); } + +static void _assign_lv_group(lv_group_t* g) +{ + lv_group_set_default(g); + + // associate it with all input devices + lv_indev_t* indev = lv_indev_get_next(NULL); + while (indev) { + lv_indev_set_group(indev, g); + indev = lv_indev_get_next(indev); + } +} + +void Layer::push(Window* w) +{ + // create a new group + auto g = lv_group_create(); + _assign_lv_group(g); + + // and store + stack.emplace_back(w, g); +} + +void Layer::pop(Window* w) +{ + if (stack.empty()) return; + + if (back() == w) { + stack.pop_back(); + } else { + for (auto layer = stack.crbegin(); layer != stack.crend(); layer++) { + if (layer->window == w) { + stack.erase(layer.base()); + return; + } + } + } + + if (!stack.empty()) { + lv_group_t* g = stack.back().group; + _assign_lv_group(g); + } else { + lv_group_set_default(NULL); + } +} + +Window* Layer::back() +{ + if (stack.empty()) return nullptr; + return stack.back().window; +} + +Window* Layer::getFirstOpaque() +{ + for (auto layer = stack.crbegin(); layer != stack.crend(); layer++) { + auto w = layer->window; + if (w && (w->getWindowFlags() & OPAQUE)) return w; + } + + return nullptr; +} diff --git a/radio/src/thirdparty/libopenui/src/layer.h b/radio/src/thirdparty/libopenui/src/layer.h index f66303ba9..873138855 100644 --- a/radio/src/thirdparty/libopenui/src/layer.h +++ b/radio/src/thirdparty/libopenui/src/layer.h @@ -20,69 +20,21 @@ #pragma once #include "window.h" -extern lv_obj_t * canvas; class Layer { - public: - explicit Layer(Window * main): - main(main) - { - } + static std::list stack; - Window * main; - Window * focus = nullptr; + Window* window; + lv_group_t* group; - static std::list stack; + public: + explicit Layer(Window* w, lv_group_t* g); + ~Layer(); - static void push(Window * window) - { + static void push(Window* window); + static void pop(Window* window); - if (!stack.empty()) { - Layer & parentLayer = stack.back(); - parentLayer.focus = Window::getFocus(); - } - - - stack.emplace_back(Layer(window)); - -#if defined(DEBUG_WINDOWS) - TRACE_WINDOWS("New layer added for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); - for (auto & layer: stack) { - TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); - } -#endif - } - - static void pop(Window * window) - { - if (stack.back().main == window) { - stack.pop_back(); - const auto & back = stack.back(); - if (back.focus) { - back.focus->setFocus(SET_FOCUS_DEFAULT); - } - } - else { - // TODO it would be better to do it in reverse order - for (auto it = stack.begin(); it != stack.end(); it++) { - if (it->main == window) { - stack.erase(it); - break; - } - } - } - -#if defined(DEBUG_WINDOWS) - TRACE_WINDOWS("Layer removed for %s (%d layers)", window->getWindowDebugString().c_str(), stack.size()); - for (auto & layer: stack) { - TRACE_WINDOWS(" - %s (focus=%s)", layer.main->getWindowDebugString().c_str(), layer.focus ? layer.focus->getWindowDebugString().c_str() : "---"); - } -#endif - } - - static Window* back() - { - return stack.back().main; - } + static Window* back(); + static Window* getFirstOpaque(); }; diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 3511d1d4f..8efe7a72e 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -36,7 +36,7 @@ MainWindow::MainWindow() : Window(nullptr, {0, 0, LCD_W, LCD_H}, OPAQUE), invalidatedRect(rect) { - Layer::push(this); + // Layer::push(this); } void MainWindow::emptyTrash() @@ -98,21 +98,10 @@ void MainWindow::run(bool trash) { auto start = ticksNow(); - // KLK: removed for now. This is now - // called from lvgl event processing when - // necessary - auto layer = Layer::stack.rbegin(); - while (layer != Layer::stack.rend()) { - if (layer->main && (layer->main->getWindowFlags() & OPAQUE)) { - layer->main->checkEvents(); - break; - } - ++layer; - } + auto opaque = Layer::getFirstOpaque(); + if (opaque) opaque->checkEvents(); - if (trash) { - emptyTrash(); - } + if (trash) emptyTrash(); auto delta = ticksNow() - start; if (delta > 10 * SYSTEM_TICKS_1MS) { diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index a880947b5..a97d5dc28 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -60,6 +60,9 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect): { setColumnCount(1); setColumnWidth(0, rect.w); + + lv_group_t* g = (lv_group_t*)lv_obj_get_group(lvobj); + if (g) { lv_group_set_editing(g, true); } } void MenuBody::addLine(const std::string &text, std::function onPress, @@ -176,49 +179,11 @@ void MenuBody::selectNext(MENU_DIRECTION direction) } -#if defined(HARDWARE_KEYS) void MenuBody::onEvent(event_t event) { - // TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - // if (event == EVT_ROTARY_RIGHT) { - // if (!lines.empty()) { - // selectNext(DIRECTION_UP); - // onKeyPress(); - // } - // } - // else if (event == EVT_ROTARY_LEFT) { - // if (!lines.empty()) { - // selectNext(DIRECTION_DOWN); - // onKeyPress(); - // } - // } - // else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - // if (!lines.empty()) { - // onKeyPress(); - // if (selectedIndex < 0) { - // setIndex(0); - // } - // else { - // Menu * menu = getParentMenu(); - // if (menu->multiple) { - // lines[selectedIndex].onPress(); - // menu->invalidate(); - // } - // else { - // lines[selectedIndex].onPress(); - // menu->deleteLater(); - // } - // } - // } - // } - // else +#if defined(HARDWARE_KEYS) if (event == EVT_KEY_BREAK(KEY_EXIT)) { - onKeyPress(); - if (onCancel) { - onCancel(); - } - Window::onEvent(event); + onCancel(); } else { TableField::onEvent(event); @@ -226,12 +191,18 @@ void MenuBody::onEvent(event_t event) } #endif +void MenuBody::onCancel() +{ + if(_onCancel) _onCancel(); + TableField::onCancel(); +} + MenuWindowContent::MenuWindowContent(Menu* parent) : ModalWindowContent(parent, {(LCD_W - MENUS_WIDTH) / 2, (LCD_H - MENUS_WIDTH) / 2, MENUS_WIDTH, 0}), body(this, {0, 0, width(), height()}) { - body.setFocus(SET_FOCUS_DEFAULT); + // body.setFocus(SET_FOCUS_DEFAULT); lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); } @@ -305,19 +276,17 @@ void Menu::removeLines() updatePosition(); } -#if defined(HARDWARE_KEYS) void Menu::onEvent(event_t event) { +#if defined(HARDWARE_KEYS) if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || event == EVT_KEY_BREAK(KEY_PGUP))) { toolbar->onEvent(event); } - else if (event == EVT_KEY_BREAK(KEY_EXIT)) { - deleteLater(); - } - else if (event == EVT_KEY_BREAK(KEY_ENTER) && !multiple) { - deleteLater(); - } -} #endif +} +void Menu::onCancel() +{ + deleteLater(); +} diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 36b7fc017..b765f681c 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -92,9 +92,8 @@ class MenuBody: public TableField return lines.size(); } -#if defined(HARDWARE_KEYS) void onEvent(event_t event) override; -#endif + void onCancel() override; void addLine(const std::string &text, std::function onPress, std::function isChecked); @@ -107,7 +106,7 @@ class MenuBody: public TableField void setCancelHandler(std::function handler) { - onCancel = std::move(handler); + _onCancel = std::move(handler); } coord_t getContentHeight(); @@ -124,7 +123,7 @@ class MenuBody: public TableField std::vector lines; int selectedIndex = 0; - std::function onCancel; + std::function _onCancel; inline Menu * getParentMenu(); }; @@ -175,10 +174,10 @@ class Menu: public ModalWindow waitHandler = std::move(handler); } - void setFocusBody(uint8_t flag = SET_FOCUS_DEFAULT) - { - content->body.setFocus(flag); - } + // void setFocusBody(uint8_t flag = SET_FOCUS_DEFAULT) + // { + // content->body.setFocus(flag); + // } void setToolbar(Window * window); @@ -210,9 +209,8 @@ class Menu: public ModalWindow content->body.setIndex(index); } -#if defined(HARDWARE_KEYS) void onEvent(event_t event) override; -#endif + void onCancel() override; void checkEvents() override { diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index b0d3d39c5..3e1236e06 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -28,11 +28,7 @@ int getFirstAvailable(int min, int max, std::functionisValueAvailable class MenuToolbarButton: public Button { public: MenuToolbarButton(FormGroup * window, const rect_t & rect, const char* picto): -#if defined(HARDWARE_TOUCH) Button(window, rect, nullptr), -#else - Button(window, rect, nullptr), -#endif picto(picto) { } @@ -64,36 +60,36 @@ class MenuToolbar: public FormGroup { #if defined(HARDWARE_KEYS) void onEvent(event_t event) override { - if (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || event == EVT_KEY_BREAK(KEY_PGUP)) { -#if defined(HARDWARE_TOUCH) - if (current != children.end()) { - static_cast(*current)->onTouchEnd(0,0); - } -#endif - current = IS_KEY_LONG(event) || event == EVT_KEY_BREAK(KEY_PGUP) ? - current == children.end() ? - --current: - current == children.begin() ? - children.end() : - --current : - current == children.end() ? - children.begin() : - ++current; +// if (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || event == EVT_KEY_BREAK(KEY_PGUP)) { +// #if defined(HARDWARE_TOUCH) +// if (current != children.end()) { +// static_cast(*current)->onTouchEnd(0,0); +// } +// #endif +// current = IS_KEY_LONG(event) || event == EVT_KEY_BREAK(KEY_PGUP) ? +// current == children.end() ? +// --current: +// current == children.begin() ? +// children.end() : +// --current : +// current == children.end() ? +// children.begin() : +// ++current; - if (IS_KEY_LONG(event)) - killEvents(event); +// if (IS_KEY_LONG(event)) +// killEvents(event); - if (current != children.end()) { - auto button = static_cast(*current); -#if defined(HARDWARE_TOUCH) - button->onTouchEnd(0,0); -#endif - scrollTo(button); - } - else { - lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); - } - } +// if (current != children.end()) { +// auto button = static_cast(*current); +// #if defined(HARDWARE_TOUCH) +// button->onTouchEnd(0,0); +// #endif +// scrollTo(button); +// } +// else { +// lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); +// } +// } } #endif @@ -139,7 +135,7 @@ class MenuToolbar: public FormGroup { } else { choice->fillMenu(menu, val); } - menu->setFocusBody(); + // menu->setFocusBody(); // set current for page key procesing current = std::find_if(children.begin(), children.end(), [button](Window *o) { return o == button; }); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index ab5875050..055c4ec0d 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -19,6 +19,7 @@ #include "modal_window.h" +#include "keyboard_base.h" #include "font.h" #include "layer.h" @@ -26,30 +27,25 @@ ModalWindow::ModalWindow(Window * parent, bool closeWhenClickOutside): Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}), closeWhenClickOutside(closeWhenClickOutside) { + Layer::push(this); lv_obj_set_style_bg_color(lvobj, lv_color_black(), LV_PART_MAIN); lv_obj_set_style_bg_opa(lvobj, LV_OPA_50, LV_PART_MAIN); - Layer::push(this); } void ModalWindow::deleteLater(bool detach, bool trash) { - if (_deleted) - return; - - Window::deleteLater(detach, trash); + if (_deleted) return; Layer::pop(this); + Window::deleteLater(detach, trash); } -#if defined(HARDWARE_TOUCH) -bool ModalWindow::onTouchEnd(coord_t x, coord_t y) +void ModalWindow::onClicked() { if (closeWhenClickOutside) { onKeyPress(); deleteLater(); } - return true; } -#endif ModalWindowContent::ModalWindowContent(ModalWindow* parent, const rect_t& rect) : @@ -57,6 +53,11 @@ ModalWindowContent::ModalWindowContent(ModalWindow* parent, { } +void ModalWindowContent::onClicked() +{ + Keyboard::hide(); +} + void ModalWindowContent::setTitle(const std::string& text) { if (!title) { diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 381c96ea7..09864cf87 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -21,49 +21,41 @@ #include "window.h" -class ModalWindow: public Window +class ModalWindow : public Window { - public: - explicit ModalWindow(Window * parent, bool closeWhenClickOutside = false); + public: + explicit ModalWindow(Window* parent, bool closeWhenClickOutside = false); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "ModalWindow"; - } + std::string getName() const override { return "ModalWindow"; } #endif - void deleteLater(bool detach = true, bool trash = true) override; // NOLINT(google-default-arguments) + void setCloseWhenClickOutside(bool value = true) + { + closeWhenClickOutside = value; + } -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif + void onClicked() override; + void deleteLater(bool detach = true, bool trash = true) override; - void setCloseWhenClickOutside(bool value = true) - { - closeWhenClickOutside = value; - } - - protected: - bool closeWhenClickOutside; + protected: + bool closeWhenClickOutside; }; -class ModalWindowContent: public Window +class ModalWindowContent : public Window { - public: - explicit ModalWindowContent(ModalWindow * parent, const rect_t & rect); + public: + explicit ModalWindowContent(ModalWindow* parent, const rect_t& rect); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "ModalWindowContent"; - } + std::string getName() const override { return "ModalWindowContent"; } #endif - void setTitle(const std::string& text); - std::string getTitle() const; + void setTitle(const std::string& text); + std::string getTitle() const; - protected: - lv_obj_t* title = nullptr; + void onClicked() override; + + protected: + lv_obj_t* title = nullptr; }; - diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 7d7e03ee2..10f2af558 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -26,32 +26,18 @@ static void numberedit_cb(lv_event_t* e) { - lv_event_code_t code = lv_event_get_code(e); - if (code == LV_EVENT_DELETE) return; - NumberEdit* numEdit = (NumberEdit*)lv_event_get_user_data(e); if (!numEdit || numEdit->deleted()) return; uint32_t key = lv_event_get_key(e); - lv_obj_t* obj = numEdit->getLvObj(); - - if (obj != nullptr && lv_obj_is_editable(obj)) { - lv_group_t* grp = (lv_group_t*)lv_obj_get_group(obj); - if (grp && lv_group_get_focused(grp) == obj) - numEdit->setEditMode(lv_group_get_editing(grp)); - - switch (key) { - case LV_KEY_LEFT: - // numEdit->onEvent(EVT_ROTARY_LEFT); - break; - case LV_KEY_RIGHT: - // numEdit->onEvent(EVT_ROTARY_RIGHT); - break; - } + switch (key) { + case LV_KEY_LEFT: + numEdit->onEvent(EVT_ROTARY_LEFT); + break; + case LV_KEY_RIGHT: + numEdit->onEvent(EVT_ROTARY_RIGHT); + break; } - - // TODO: this looks highly forbidden!!! - e->code = LV_EVENT_REFRESH; } // create a new class for number edit so we can set the sytles differently if needed. @@ -133,14 +119,6 @@ void NumberEdit::onEvent(event_t event) } #endif - case EVT_KEY_FIRST(KEY_EXIT): -#if defined(SOFTWARE_KEYBOARD) - Keyboard::hide(); - return; -#endif - break; - - #if defined(HARDWARE_TOUCH) case EVT_VIRTUAL_KEY_PLUS: setValue(getValue() + getStep()); @@ -180,35 +158,12 @@ void NumberEdit::onEvent(event_t event) FormField::onEvent(event); } -void NumberEdit::onFocusLost() +void NumberEdit::onClicked() { -#if defined(SOFTWARE_KEYBOARD) - Keyboard::hide(); -#endif - - FormField::onFocusLost(); + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + if(indev_type == LV_INDEV_TYPE_POINTER) { + NumberKeyboard::show(this); + } else { + FormField::onClicked(); + } } - - -#if defined(HARDWARE_TOUCH) -bool NumberEdit::onTouchEnd(coord_t, coord_t) -{ - if (!enabled) { - return true; - } - - if (hasFocus()) { - setEditMode(true); - } - else { - setFocus(SET_FOCUS_DEFAULT); - } - -#if defined(SOFTWARE_KEYBOARD) - NumberKeyboard::show(this); -#endif - - return true; -} -#endif - diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 0a5de69d0..2aa04ab5f 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -24,30 +24,29 @@ class NumberEdit : public BaseNumberEdit { - public: - NumberEdit(Window * parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0, LcdFlags textFlags = 0); + public: + NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, + std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0, LcdFlags textFlags = 0); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "NumberEdit(" + std::to_string(getValue()) + ")"; - } + std::string getName() const override + { + return "NumberEdit(" + std::to_string(getValue()) + ")"; + } #endif - void setAvailableHandler(std::function handler) - { - isValueAvailable = std::move(handler); - } + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } - void onEvent(event_t event) override; - void onFocusLost() override; + void onEvent(event_t event) override; + void onClicked() override; -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif - - protected: - std::function isValueAvailable; + protected: + std::function isValueAvailable; }; extern const lv_obj_class_t lv_numberedit_class; diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index d1b3a2188..2038b40ee 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -24,14 +24,21 @@ void TableField::clicked(lv_event_t *e) lv_obj_t* target = lv_event_get_target(e); if (!target) return; - uint16_t row; - uint16_t col; - lv_table_get_selected_cell(target, &row, &col); - TableField* tf = (TableField*)lv_obj_get_user_data(target); if (!tf) return; - tf->onPress(row, col); + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + if(indev_type == LV_INDEV_TYPE_ENCODER) { + // Encoder send VALUE_CHANGED when selection changes + tf->adjustScroll(); + } else { + // Otherwise it's a click + uint16_t row; + uint16_t col; + lv_table_get_selected_cell(target, &row, &col); + + tf->onPress(row, col); + } } void TableField::draw_begin(lv_event_t* e) @@ -73,7 +80,7 @@ void TableField::draw_end(lv_event_t* e) } TableField::TableField(Window* parent, const rect_t& rect, WindowFlags windowFlags) : - FormField(parent, rect, windowFlags, 0, lv_table_create) + Window(parent, rect, windowFlags, 0, lv_table_create) { lv_obj_add_event_cb(lvobj, TableField::clicked, LV_EVENT_VALUE_CHANGED, nullptr); lv_obj_add_event_cb(lvobj, TableField::draw_begin, LV_EVENT_DRAW_PART_BEGIN, nullptr); @@ -205,15 +212,15 @@ void TableField::onEvent(event_t event) onKeyPress(); selectNext(-1); } - else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - onKeyPress(); + // else if (event == EVT_KEY_BREAK(KEY_ENTER)) { + // onKeyPress(); - uint16_t row; - uint16_t col; - lv_table_get_selected_cell(lvobj, &row, &col); + // uint16_t row; + // uint16_t col; + // lv_table_get_selected_cell(lvobj, &row, &col); - onPress(row, col); - } + // onPress(row, col); + // } else { Window::onEvent(event); } diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 2d0b4e192..0feb7ac30 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -21,7 +21,7 @@ #include "form.h" -class TableField : public FormField +class TableField : public Window { public: TableField(Window* parent, const rect_t& rect, diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 8458fbbb7..6bd890942 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -55,7 +55,11 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_textarea_set_password_mode(lvobj, false); lv_textarea_set_one_line(lvobj, true); - lv_textarea_set_text(lvobj, value); + + // value may not be null-terminated + std::string txt(value, length); + lv_textarea_set_text(lvobj, txt.c_str()); + lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); @@ -145,16 +149,6 @@ void TextEdit::onEvent(event_t event) } break; - case EVT_KEY_BREAK(KEY_EXIT): - changeEnd(); - FormField::onEvent(event); - -#if defined(SOFTWARE_KEYBOARD) - Keyboard::hide(); -#endif - setFocus(SET_FOCUS_DEFAULT, this); - break; - case EVT_KEY_LONG(KEY_ENTER): { killEvents(event); @@ -219,33 +213,14 @@ void TextEdit::onEvent(event_t event) #endif } -#if defined(HARDWARE_TOUCH) -bool TextEdit::onTouchEnd(coord_t x, coord_t y) +void TextEdit::onClicked() { - if (!isEnabled()) { - return true; - } - - if (!hasFocus()) { - setFocus(SET_FOCUS_DEFAULT); - } - cursorPos = lv_textarea_get_cursor_pos(lvobj); - -#if defined(SOFTWARE_KEYBOARD) TextKeyboard::show(this); -#endif - - return true; } -#endif void TextEdit::onFocusLost() { -#if defined(SOFTWARE_KEYBOARD) - TextKeyboard::hide(); -#endif - changeEnd(); FormField::onFocusLost(); } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 9f9d74307..40f7ae963 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -47,10 +47,11 @@ class TextEdit : public FormField { } void onEvent(event_t event) override; + void onClicked() override; -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; -#endif +// #if defined(HARDWARE_TOUCH) +// bool onTouchEnd(coord_t x, coord_t y) override; +// #endif void onFocusLost() override; diff --git a/radio/src/thirdparty/libopenui/src/widgets/simple_btn.c b/radio/src/thirdparty/libopenui/src/widgets/simple_btn.c new file mode 100644 index 000000000..751864404 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/simple_btn.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "simple_btn.h" + +#define MY_CLASS &simple_btn_class + +const lv_obj_class_t simple_btn_class = { + .width_def = LV_DPI_DEF, + .height_def = LV_DPI_DEF, + .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, + .instance_size = sizeof(lv_btn_t), + .base_class = &lv_btn_class, +}; + +lv_obj_t* simple_btn_create(lv_obj_t* parent) +{ + LV_LOG_INFO("begin"); + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} diff --git a/radio/src/thirdparty/libopenui/src/widgets/simple_btn.h b/radio/src/thirdparty/libopenui/src/widgets/simple_btn.h new file mode 100644 index 000000000..fe0e31ff7 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/simple_btn.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef SIMPLE_BTN_H +#define SIMPLE_BTN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const lv_obj_class_t simple_btn_class; +lv_obj_t* simple_btn_create(lv_obj_t* parent); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/radio/src/thirdparty/libopenui/src/widgets/window_base.c b/radio/src/thirdparty/libopenui/src/widgets/window_base.c new file mode 100644 index 000000000..008f50204 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/window_base.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "window_base.h" + +void window_event_cb(lv_event_t* e); + +#define MY_CLASS &window_base_class + +static void window_base_event(const lv_obj_class_t* class_p, lv_event_t* e); + +const lv_obj_class_t window_base_class = { + .base_class = &lv_obj_class, + .event_cb = window_base_event, + .width_def = LV_DPI_DEF, + .height_def = LV_DPI_DEF, + .editable = LV_OBJ_CLASS_EDITABLE_FALSE, + .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE, + .instance_size = sizeof(lv_obj_t) +}; + +static void window_base_event(const lv_obj_class_t* class_p, lv_event_t* e) +{ + /* Call the ancestor's event handler */ + lv_res_t res = lv_obj_event_base(MY_CLASS, e); + if (res != LV_RES_OK) return; + + window_event_cb(e); +} + +lv_obj_t* window_create(lv_obj_t* parent) +{ + lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} diff --git a/radio/src/thirdparty/libopenui/src/widgets/window_base.h b/radio/src/thirdparty/libopenui/src/widgets/window_base.h new file mode 100644 index 000000000..fdbdfa9cf --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/window_base.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef WINDOW_BASE_H +#define WINDOW_BASE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const lv_obj_class_t window_base_class; +lv_obj_t* window_create(lv_obj_t* parent); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index fdbd2f4eb..0666ebd54 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -21,15 +21,17 @@ #include "touch.h" #include "mainwindow.h" -Window * Window::focusWindow = nullptr; +#include "widgets/window_base.h" + +// Window * Window::focusWindow = nullptr; std::list Window::trash; extern lv_obj_t *virtual_kb; static bool is_scrolling = false; -static bool inhibit_focus = false; +// static bool inhibit_focus = false; -static void window_event_cb(lv_event_t * e) +extern "C" void window_event_cb(lv_event_t * e) { lv_obj_t *target = lv_event_get_target(e); lv_event_code_t code = lv_event_get_code(e); @@ -87,11 +89,11 @@ static void window_event_cb(lv_event_t * e) return; } - // Exclude keyboard ? // if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD || // lv_indev_get_type(click_source) == LV_INDEV_TYPE_ENCODER) { // return; // } + lv_area_t obj_coords; lv_obj_get_coords(target, &obj_coords); @@ -122,7 +124,6 @@ static void window_event_cb(lv_event_t * e) } #endif else if (code == LV_EVENT_SCROLL) { - #if defined(DEBUG_WINDOWS) lv_coord_t scroll_y = lv_obj_get_scroll_y(target); lv_coord_t scroll_x = lv_obj_get_scroll_x(target); @@ -130,53 +131,15 @@ static void window_event_cb(lv_event_t * e) TRACE_WINDOWS("SCROLL[x=%d;y=%d] %s", scroll_x, scroll_y, window->getWindowDebugString().c_str()); #endif - - - } else if (code == LV_EVENT_FOCUSED) { - if (inhibit_focus) return; - - bool focused = ((Window *)target->user_data)->hasFocus(); - TRACE_WINDOWS("FOCUSED[%d] %s", focused, - window->getWindowDebugString().c_str()); - - if (!focused) { - SetFocusFlag flag = SET_FOCUS_DEFAULT; - lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); - if(indev_type == LV_INDEV_TYPE_POINTER) { - flag = SET_FOCUS_NO_SCROLL; - } - window->setFocus(flag); - } } -} - -static void window_base_event(const lv_obj_class_t* class_p, lv_event_t* e) -{ - /*Call the ancestor's event handler*/ - lv_res_t res = lv_obj_event_base(&window_base_class, e); - if(res != LV_RES_OK) return; - - window_event_cb(e); -} - -const lv_obj_class_t window_base_class = { - .base_class = &lv_obj_class, - .constructor_cb = nullptr, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = window_base_event, - .width_def = LV_DPI_DEF, - .height_def = LV_DPI_DEF, - .editable = LV_OBJ_CLASS_EDITABLE_FALSE, - .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE, - .instance_size = sizeof(lv_obj_t) -}; - -lv_obj_t* window_create(lv_obj_t* parent) -{ - lv_obj_t * obj = lv_obj_class_create_obj(&window_base_class, parent); - lv_obj_class_init_obj(obj); - return obj; + else if (code == LV_EVENT_CLICKED) { + TRACE("CLICKED[%p]", window); + window->onClicked(); + } + else if (code == LV_EVENT_CANCEL) { + TRACE("CANCEL[%p]", window); + window->onCancel(); + } } Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, @@ -189,7 +152,7 @@ Window::Window(Window *parent, const rect_t &rect, WindowFlags windowFlags, lv_obj_t* lv_parent = nullptr; if (parent) lv_parent = parent->lvobj; - if (objConstruct != nullptr) { + if (objConstruct != nullptr && objConstruct != window_create) { lvobj = objConstruct(lv_parent); lv_obj_add_event_cb(lvobj, window_event_cb, LV_EVENT_ALL, nullptr); } else { @@ -254,9 +217,6 @@ Window::Window(Window *parent, lv_obj_t* lvobj) Window::~Window() { TRACE_WINDOWS("Destroy %p %s", this, getWindowDebugString().c_str()); - if (focusWindow == this) { - focusWindow = nullptr; - } if (children.size() > 0) deleteChildren(); @@ -352,10 +312,6 @@ void Window::deleteLater(bool detach, bool trash) _deleted = true; - if (static_cast(focusWindow) == static_cast(this)) { - focusWindow = nullptr; - } - if (detach) this->detach(); else @@ -389,62 +345,17 @@ void Window::clear() void Window::deleteChildren() { // prevent LVGL refocus while mass-deleting - inhibit_focus = true; + // inhibit_focus = true; for (auto window: children) { window->deleteLater(false); } - inhibit_focus = false; + // inhibit_focus = false; children.clear(); } -void Window::clearFocus() -{ - if (focusWindow) { - focusWindow->onFocusLost(); - focusWindow = nullptr; - } -} - -void Window::setFocus(uint8_t flag, Window * from) -{ - if (deleted()) - return; - - TRACE_WINDOWS("%s setFocus()", getWindowDebugString().c_str()); - - if (focusWindow != this) { - // synchronize lvgl focused state with libopenui - if (lvobj != nullptr && - !lv_obj_has_state(lvobj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY)) { - lv_obj_add_state(lvobj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY); - if (focusWindow != nullptr) { - lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | - LV_STATE_FOCUS_KEY | - LV_STATE_EDITED); - } - } - - // scroll before calling focusHandler so that the window can adjust the - // scroll position if needed - if (flag != SET_FOCUS_NO_SCROLL) { - lv_obj_scroll_to_view_recursive(lvobj, LV_ANIM_OFF); - } - invalidate(); - - clearFocus(); - focusWindow = this; - if (focusHandler) { - focusHandler(true); - } - } -} - -void Window::scrollTo(Window * child) -{ -} - -void Window::scrollTo(const rect_t & rect) +bool Window::hasFocus() const { + return lvobj && lv_obj_has_state(lvobj, LV_STATE_FOCUS_KEY); } bool Window::hasOpaqueRect(const rect_t & testRect) const @@ -579,14 +490,6 @@ void Window::checkEvents() } } - if (this == Window::focusWindow) { - event_t event = getWindowEvent(); -// lvglPushEncoderEvent(event); - if (event) { - this->onEvent(event); - } - } - if (windowFlags & REFRESH_ALWAYS) { invalidate(); } @@ -595,46 +498,52 @@ void Window::checkEvents() void Window::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", Window::getWindowDebugString("Window").c_str(), event); - if (parent) { - parent->onEvent(event); - } + if (parent) { parent->onEvent(event); } } +void Window::onClicked() +{ + if (parent && !(windowFlags & OPAQUE)) { parent->onClicked(); } +} + +void Window::onCancel() +{ + if (parent) { parent->onCancel(); } +} + + #if defined(HARDWARE_TOUCH) bool Window::onTouchStart(coord_t x, coord_t y) { - if (parent) - return parent->onTouchStart(x, y); + if (parent && !(windowFlags & OPAQUE)) return parent->onTouchStart(x, y); return true; } bool Window::onTouchEnd(coord_t x, coord_t y) { - if (parent && !(windowFlags & OPAQUE)) - return parent->onTouchEnd(x, y); + if (parent && !(windowFlags & OPAQUE)) return parent->onTouchEnd(x, y); return true; } bool Window::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) { + // TODO: this is not used anymore: REMOVE + + // for (auto it = children.rbegin(); it != children.rend(); ++it) { + // auto child = *it; + // if (child->rect.contains(startX, startY)) { + // if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { + // return true; + // } + // } + // } - for (auto it = children.rbegin(); it != children.rend(); ++it) { - auto child = *it; - if (child->rect.contains(startX, startY)) { - if (child->onTouchSlide(x - child->rect.x, y - child->rect.y, startX - child->rect.x, startY - child->rect.y, slideX, slideY)) { - return true; - } - } - } + // return false; - return false; + return true; } #endif -void Window::adjustInnerHeight() -{ -} - coord_t Window::adjustHeight() { coord_t old = rect.h; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index deee76402..8cf5dec9f 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -26,10 +26,14 @@ #include #include #include + #include "bitmapbuffer.h" #include "libopenui_defines.h" #include "libopenui_helpers.h" #include "libopenui_config.h" + +#include "widgets/window_base.h" + #include "LvglWrapper.h" typedef uint32_t WindowFlags; @@ -51,19 +55,16 @@ constexpr WindowFlags PAINT_CHILDREN_FIRST = 1u << 6u; constexpr WindowFlags PUSH_FRONT = 1u << 7u; constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; -enum SetFocusFlag -{ - SET_FOCUS_DEFAULT = 0, - SET_FOCUS_FORWARD = 1, - SET_FOCUS_BACKWARD = 2, - SET_FOCUS_FIRST = 3, - SET_FOCUS_NO_SCROLL= 4 -}; +// enum SetFocusFlag +// { +// SET_FOCUS_DEFAULT = 0, +// SET_FOCUS_FORWARD = 1, +// SET_FOCUS_BACKWARD = 2, +// SET_FOCUS_FIRST = 3, +// SET_FOCUS_NO_SCROLL= 4 +// }; typedef lv_obj_t *(*LvglCreate)(lv_obj_t *); -extern const lv_obj_class_t window_base_class; - -lv_obj_t* window_create(lv_obj_t* parent); class Window { @@ -110,23 +111,7 @@ class Window void clear(); virtual void deleteLater(bool detach = true, bool trash = true); - bool hasFocus() const - { - return focusWindow == this; - } - - static Window * getFocus() - { - return focusWindow; - } - - void scrollTo(const rect_t & rect); - - void scrollTo(Window * child); - - static void clearFocus(); - - virtual void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr); + bool hasFocus() const; void setRect(rect_t value) { @@ -216,8 +201,8 @@ class Window } virtual void onEvent(event_t event); - - void adjustInnerHeight(); + virtual void onClicked(); + virtual void onCancel(); coord_t adjustHeight(); @@ -243,10 +228,8 @@ class Window return _deleted; } - virtual void paint(BitmapBuffer *) - { - } - + virtual void paint(BitmapBuffer *) {} + #if defined(HARDWARE_TOUCH) virtual bool onTouchStart(coord_t x, coord_t y); virtual bool onTouchEnd(coord_t x, coord_t y); @@ -255,7 +238,6 @@ class Window inline lv_obj_t *getLvObj() { return lvobj; } protected: - static Window* focusWindow; static std::list trash; rect_t rect; diff --git a/radio/src/thirdparty/libopenui/thirdparty/lvgl b/radio/src/thirdparty/libopenui/thirdparty/lvgl index 2d557d712..a7ec08238 160000 --- a/radio/src/thirdparty/libopenui/thirdparty/lvgl +++ b/radio/src/thirdparty/libopenui/thirdparty/lvgl @@ -1 +1 @@ -Subproject commit 2d557d712848696cf2d52a802ba041554edb2803 +Subproject commit a7ec08238759d446cf22197ae346ed1b995ae422 From 6177a944867dfd6348c5c0ec2034b984b366432a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 28 May 2022 19:20:53 +0200 Subject: [PATCH 628/714] choice toolbar: new implementation based on LVGL group --- radio/src/thirdparty/libopenui/src/choice.cpp | 114 +++++++------- radio/src/thirdparty/libopenui/src/choice.h | 57 +++---- radio/src/thirdparty/libopenui/src/menu.cpp | 5 +- radio/src/thirdparty/libopenui/src/menu.h | 10 +- .../thirdparty/libopenui/src/menutoolbar.cpp | 104 +++++++++++++ .../thirdparty/libopenui/src/menutoolbar.h | 147 +++--------------- 6 files changed, 213 insertions(+), 224 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 760ea2386..5edbfce6c 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -79,10 +79,10 @@ std::string Choice::getLabelText() { std::string text; - if (textHandler != nullptr) - text = textHandler(getValue()); - else if (getValue) { - int val = getValue(); + if (textHandler != nullptr) { + if (_getValue) { text = textHandler(_getValue()); } + } else if (_getValue) { + int val = _getValue(); val -= vmin; if (val >= 0 && val < (int)values.size()) { text = values[val]; @@ -92,25 +92,26 @@ std::string Choice::getLabelText() return text; } -Choice::Choice(Window* parent, const rect_t & rect, int vmin, int vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : - ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), - vmin(vmin), - vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) +Choice::Choice(Window* parent, const rect_t& rect, int vmin, int vmax, + std::function _getValue, + std::function _setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + vmin(vmin), + vmax(vmax), + _getValue(std::move(_getValue)), + _setValue(std::move(_setValue)) { lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); } Choice::Choice(Window* parent, const rect_t& rect, const char* const values[], - int vmin, int vmax, std::function getValue, - std::function setValue, WindowFlags windowFlags) : + int vmin, int vmax, std::function _getValue, + std::function _setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), vmin(vmin), vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) + _getValue(std::move(_getValue)), + _setValue(std::move(_setValue)) { setValues(values); lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); @@ -118,26 +119,26 @@ Choice::Choice(Window* parent, const rect_t& rect, const char* const values[], Choice::Choice(Window* parent, const rect_t& rect, std::vector values, int vmin, int vmax, - std::function getValue, std::function setValue, + std::function _getValue, std::function _setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), values(std::move(values)), vmin(vmin), vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) + _getValue(std::move(_getValue)), + _setValue(std::move(_setValue)) { lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); } Choice::Choice(Window* parent, const rect_t& rect, const char* values, int vmin, - int vmax, std::function getValue, - std::function setValue, WindowFlags windowFlags) : + int vmax, std::function _getValue, + std::function _setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), vmin(vmin), vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) + _getValue(std::move(_getValue)), + _setValue(std::move(_setValue)) { if (values) { uint8_t len = values[0]; @@ -181,60 +182,51 @@ void Choice::setValues(const char * const values[]) } } +void Choice::setValue(int val) +{ + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); + if (_setValue) _setValue(val); +} + void Choice::onClicked() { onKeyPress(); openMenu(); } +void Choice::fillMenu(Menu *menu, const FilterFct& filter) +{ + menu->removeLines(); + auto value = _getValue(); + + int count = 0; + for (int i = vmin; i <= vmax; ++i) { + if (filter && !filter(i)) continue; + if (isValueAvailable && !isValueAvailable(i)) continue; + if (textHandler) { + menu->addLine(textHandler(i), [=]() { setValue(i); }); + } else if (unsigned(i - vmin) < values.size()) { + menu->addLine(values[i - vmin], [=]() { setValue(i); }); + } else { + menu->addLine(std::to_string(i), [=]() { setValue(i); }); + } + if (value == i) { menu->select(count); } + ++count; + } +} + void Choice::openMenu() { setEditMode(true); // this needs to be done first before menu is created. auto menu = new Menu(this); - if (!menuTitle.empty()) - menu->setTitle(menuTitle); + if (!menuTitle.empty()) menu->setTitle(menuTitle); - auto value = getValue(); - int count = 0; - int current = -1; - - for (int i = vmin; i <= vmax; ++i) { - if (isValueAvailable && !isValueAvailable(i)) - continue; - if (textHandler) { - menu->addLine(textHandler(i), [=]() { - setValue(i); - lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); - }); - } - else if (unsigned(i - vmin) < values.size()) { - menu->addLine(values[i - vmin], [=]() { - setValue(i); - lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); - }); - } - else { - menu->addLine(std::to_string(i), [=]() { - setValue(i); - lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); - }); - } - if (value == i) { - current = count; - } - ++count; - } - - if (current >= 0) { - menu->select(current); - } + fillMenu(menu); if (beforeDisplayMenuHandler) { beforeDisplayMenuHandler(menu); } - menu->setCloseHandler([=]() { - setEditMode(false); - }); + menu->setCloseHandler([=]() { setEditMode(false); }); } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 14337ee62..d349a11f8 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -31,39 +31,39 @@ enum ChoiceType { class ChoiceBase : public FormField { - public: - ChoiceBase(Window* parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0); + public: + ChoiceBase(Window *parent, const rect_t &rect, + ChoiceType type = CHOICE_TYPE_DROPOWN, + WindowFlags windowFlags = 0); - inline ChoiceType getType() const - { - return type; - } + inline ChoiceType getType() const { return type; } + + protected: + ChoiceType type; + lv_obj_t* label; - protected: - friend void choice_changed_cb(lv_event_t *e); - ChoiceType type; - lv_obj_t *label; - virtual std::string getLabelText() { return ""; }; + friend void choice_changed_cb(lv_event_t *e); + virtual std::string getLabelText() { return ""; }; }; class Choice: public ChoiceBase { public: Choice(Window* parent, const rect_t &rect, int vmin, int vmax, - std::function getValue, - std::function setValue = nullptr, + std::function _getValue, + std::function _setValue = nullptr, WindowFlags windowFlags = 0); Choice(Window* parent, const rect_t &rect, std::vector values, int vmin, int vmax, - std::function getValue, - std::function setValue = nullptr, + std::function _getValue, + std::function _setValue = nullptr, WindowFlags windowFlags = 0); Choice(Window* parent, const rect_t &rect, const char *const values[], - int vmin, int vmax, std::function getValue, - std::function setValue = nullptr, + int vmin, int vmax, std::function _getValue, + std::function _setValue = nullptr, WindowFlags windowFlags = 0); Choice(Window* parent, const rect_t &rect, const char *values, int vmin, - int vmax, std::function getValue, - std::function setValue = nullptr, + int vmax, std::function _getValue, + std::function _setValue = nullptr, WindowFlags windowFlags = 0); void addValue(const char *value); @@ -90,12 +90,12 @@ class Choice: public ChoiceBase { void setSetValueHandler(std::function handler) { - setValue = std::move(handler); + _setValue = std::move(handler); } void setGetValueHandler(std::function handler) { - getValue = std::move(handler); + _getValue = std::move(handler); } void setAvailableHandler(std::function handler) @@ -135,10 +135,8 @@ class Choice: public ChoiceBase { return value; } - int getIntValue() const - { - return getValue(); - } + void setValue(int val); + int getIntValue() const { return _getValue(); } unsigned getValuesCount() const { @@ -169,16 +167,21 @@ class Choice: public ChoiceBase { } protected: + friend class MenuToolbar; + std::string getLabelText() override; std::vector values; int vmin = 0; int vmax = 0; std::string menuTitle; - std::function getValue; - std::function setValue; + std::function _getValue; + std::function _setValue; std::function isValueAvailable; std::function textHandler; std::function beforeDisplayMenuHandler; + typedef std::function FilterFct; + void fillMenu(Menu *menu, const FilterFct& filter = nullptr); + virtual void openMenu(); }; diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index a97d5dc28..2c17b86b1 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -279,8 +279,9 @@ void Menu::removeLines() void Menu::onEvent(event_t event) { #if defined(HARDWARE_KEYS) - if (toolbar && (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || - event == EVT_KEY_BREAK(KEY_PGUP))) { + if (toolbar && + (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || + event == EVT_KEY_BREAK(KEY_PGUP))) { toolbar->onEvent(event); } #endif diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index b765f681c..f838187b3 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -174,12 +174,7 @@ class Menu: public ModalWindow waitHandler = std::move(handler); } - // void setFocusBody(uint8_t flag = SET_FOCUS_DEFAULT) - // { - // content->body.setFocus(flag); - // } - - void setToolbar(Window * window); + void setToolbar(Window* window); void setTitle(std::string text); @@ -221,8 +216,7 @@ class Menu: public ModalWindow } protected: - friend void menuBodyEventCallback(lv_event_t *); - MenuWindowContent * content; + MenuWindowContent* content; bool multiple; Window * toolbar = nullptr; std::function waitHandler; diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index aff6fcb9e..536dd37e4 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -18,8 +18,17 @@ */ #include "menutoolbar.h" +#include "choice.h" #include "font.h" +MenuToolbarButton::MenuToolbarButton(Window* parent, const rect_t& rect, + const char* picto) : + Button(parent, rect, nullptr, 0, 0, window_create), picto(picto) +{ + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); +} + void MenuToolbarButton::paint(BitmapBuffer * dc) { if (checked()) { @@ -35,3 +44,98 @@ void MenuToolbarButton::paint(BitmapBuffer * dc) picto, CENTERED | COLOR_THEME_PRIMARY1); } } + +MenuToolbar::MenuToolbar(Choice* choice, Menu* menu) : + Window(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR), + choice(choice), + menu(menu), + group(lv_group_create()) +{ + lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); +} + +MenuToolbar::~MenuToolbar() +{ + lv_group_del(group); +} + +void MenuToolbar::onEvent(event_t event) +{ + lv_obj_t* obj = lv_group_get_focused(group); + if (obj) { + auto btn = (MenuToolbarButton*)lv_obj_get_user_data(obj); + if (btn) { btn->check(false); } + } + + if (event == EVT_KEY_BREAK(KEY_PGDN)) { + lv_group_focus_next(group); + } else if (event == EVT_KEY_LONG(KEY_PGDN) || + event == EVT_KEY_BREAK(KEY_PGUP)) { + lv_group_focus_prev(group); + } + + obj = lv_group_get_focused(group); + if (!obj) { + choice->fillMenu(menu); + } else { + auto btn = (MenuToolbarButton*)lv_obj_get_user_data(obj); + if (btn) btn->onClicked(); + } +} + +static rect_t _get_button_rect(size_t buttons) +{ + coord_t y = buttons * MENUS_TOOLBAR_BUTTON_WIDTH; + return {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}; +} + +bool MenuToolbar::filterMenu(MenuToolbarButton* btn, int16_t filtermin, + int16_t filtermax) +{ + btn->check(!btn->checked()); + + Choice::FilterFct filter = nullptr; + if (btn->checked()) { + filter = [=](int16_t index) { + return index >= filtermin && index <= filtermax; + }; + } + + choice->fillMenu(menu, filter); + return btn->checked(); +} + +typedef std::function IsValueAvailable; +static int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable) +{ + int retval = 0; + for (int i = min; i <= max; i++) { + if (isValueAvailable(i)) { + retval = i; + break; + } + } + return retval; +} + + +void MenuToolbar::addButton(const char* picto, int16_t filtermin, + int16_t filtermax) +{ + int vmin = choice->vmin; + int vmax = choice->vmax; + + if (vmin > filtermin || vmax < filtermin) return; + + if (choice->isValueAvailable && + getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == 0) + return; + + rect_t r = _get_button_rect(children.size()); + auto button = new MenuToolbarButton(this, r, picto); + + button->setPressHandler( + std::bind(&MenuToolbar::filterMenu, this, button, filtermin, filtermax)); + + lv_group_add_obj(group, button->getLvObj()); +} diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 3e1236e06..cbec1830d 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -21,140 +21,35 @@ #include "button.h" #include "menu.h" -#include -int getFirstAvailable(int min, int max, std::functionisValueAvailable); +class Choice; -class MenuToolbarButton: public Button { - public: - MenuToolbarButton(FormGroup * window, const rect_t & rect, const char* picto): - Button(window, rect, nullptr), - picto(picto) - { - } +class MenuToolbarButton : public Button +{ + public: + MenuToolbarButton(Window* parent, const rect_t& rect, const char* picto); + void paint(BitmapBuffer* dc) override; - void paint(BitmapBuffer * dc) override; - - protected: - const char* picto; + protected: + const char* picto; }; -template -class MenuToolbar: public FormGroup { - friend T; +class MenuToolbar : public Window +{ + // friend Menu; - public: - MenuToolbar(T * choice, Menu * menu): - FormGroup(menu, MENUS_TOOLBAR_RECT, NO_SCROLLBAR), - choice(choice), - menu(menu) - { - lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); - } + public: + MenuToolbar(Choice* choice, Menu* menu); + ~MenuToolbar(); - void paint(BitmapBuffer * dc) override - { -// dc->clear(COLOR_THEME_SECONDARY3); - } + void onEvent(event_t event) override; -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override - { -// if (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || event == EVT_KEY_BREAK(KEY_PGUP)) { -// #if defined(HARDWARE_TOUCH) -// if (current != children.end()) { -// static_cast(*current)->onTouchEnd(0,0); -// } -// #endif -// current = IS_KEY_LONG(event) || event == EVT_KEY_BREAK(KEY_PGUP) ? -// current == children.end() ? -// --current: -// current == children.begin() ? -// children.end() : -// --current : -// current == children.end() ? -// children.begin() : -// ++current; + protected: + Choice* choice; + Menu* menu; -// if (IS_KEY_LONG(event)) -// killEvents(event); + lv_group_t* group; -// if (current != children.end()) { -// auto button = static_cast(*current); -// #if defined(HARDWARE_TOUCH) -// button->onTouchEnd(0,0); -// #endif -// scrollTo(button); -// } -// else { -// lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); -// } -// } - } -#endif - -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override - { - Window::onTouchEnd(x, y); - return true; // = don't close the menu (inverted so that click outside the menu closes it) - } -#endif - - protected: - std::list::iterator current = children.end(); - T * choice; - Menu * menu; - coord_t y = 0; - std::vector menuButtons; - - void addButton(const char* picto, int16_t filtermin, int16_t filtermax) - { - int vmin = choice->vmin; - int vmax = choice->vmax; - - if (vmin > filtermin || vmax < filtermin) - return; - - if (choice->isValueAvailable && - getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == - 0) - return; - - auto button = new MenuToolbarButton( - this, {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}, - picto); - button->setPressHandler([=]() { - auto val = choice->getValue(); - button->check(!button->checked()); - - if (button->checked()) { - choice->fillMenu(menu, val, [=](int16_t index) { - return index >= filtermin && index <= filtermax; - }); - } else { - choice->fillMenu(menu, val); - } - // menu->setFocusBody(); - - // set current for page key procesing - current = std::find_if(children.begin(), children.end(), [button](Window *o) { return o == button; }); - - // clear all checked but the current button - for (auto b : menuButtons) { - if (b != button) - b->check(false); - } - - return button->checked(); - }); - - menuButtons.emplace_back(button); - - - y += MENUS_TOOLBAR_BUTTON_WIDTH; - - } + void addButton(const char* picto, int16_t filtermin, int16_t filtermax); + bool filterMenu(MenuToolbarButton* btn, int16_t filtermin, int16_t filtermax); }; - - From 85e9f1d7ca5f3e8353026cbaeb3eb1944efb2cf5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 29 May 2022 13:08:30 +0200 Subject: [PATCH 629/714] fix(slider): edit mode --- radio/src/thirdparty/libopenui/src/form.cpp | 19 ++++------ radio/src/thirdparty/libopenui/src/form.h | 11 +++--- radio/src/thirdparty/libopenui/src/slider.cpp | 38 ++----------------- radio/src/thirdparty/libopenui/src/slider.h | 13 ++----- 4 files changed, 18 insertions(+), 63 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 91b2659ee..d5dbfb9a7 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -49,8 +49,11 @@ void FormField::setEditMode(bool newEditMode) void FormField::onClicked() { - setEditMode(!editMode); - invalidate(); + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + if(indev_type != LV_INDEV_TYPE_POINTER) { + setEditMode(!editMode); + invalidate(); + } } void FormField::onCancel() @@ -63,16 +66,8 @@ void FormField::onCancel() } } -FormGroup::Line::Line(FormGroup* parent, lv_obj_t* obj, - FlexGridLayout* layout) : - Window(parent, obj), layout(layout), group(parent) -{ - construct(); -} - -FormGroup::Line::Line(Window* parent, lv_obj_t* obj, FlexGridLayout* layout, - FormGroup* group) : - Window(parent, obj), layout(layout), group(group) +FormGroup::Line::Line(Window* parent, lv_obj_t* obj, FlexGridLayout* layout) : + Window(parent, obj), layout(layout) { construct(); } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 71a09571e..cd61dfe2c 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -83,17 +83,16 @@ class FormGroup : public Window public: class Line : public Window { - FlexGridLayout* layout; - FormGroup* group; + FlexGridLayout *layout; + FormGroup *group; void construct(); - + public: - Line(FormGroup *parent, lv_obj_t *obj, FlexGridLayout* layout); - Line(Window *parent, lv_obj_t *obj, FlexGridLayout* layout, FormGroup* group); + Line(Window *parent, lv_obj_t *obj, FlexGridLayout *layout = nullptr); protected: - void addChild(Window* window) override; + void addChild(Window *window) override; }; FormGroup(Window *parent, const rect_t &rect, WindowFlags windowflags = 0, diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index 34807c0a4..fc59bca7b 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -22,19 +22,14 @@ #include "LvglWrapper.h" -static void slider_changed_cb(lv_event_t * e) +static void slider_changed_cb(lv_event_t* e) { - lv_obj_t * target = lv_event_get_target(e); + lv_obj_t* target = lv_event_get_target(e); auto code = lv_event_get_code(e); Slider* sl = (Slider*)lv_obj_get_user_data(target); if (code == LV_EVENT_VALUE_CHANGED) { - // if (!lv_slider_is_dragged(target)) { - if (sl != nullptr) - sl->setValue(lv_slider_get_value(target)); - // } - } else if (code == LV_EVENT_PRESSED) { - sl->setEditMode(true); + if (sl != nullptr) sl->setValue(lv_slider_get_value(target)); } } @@ -100,36 +95,9 @@ Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, lv_obj_add_style(lvobj, &style_edit, LV_PART_KNOB | LV_STATE_FOCUSED | LV_STATE_EDITED); } -void Slider::paint(BitmapBuffer * dc) -{ -} int Slider::value(coord_t x) const { return vmin + ((vmax - vmin) * x + (rect.w / 2)) / rect.w; } -#if defined(HARDWARE_KEYS) -void Slider::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); - - if (editMode) { - if (event == EVT_ROTARY_RIGHT) { - setValue(_getValue() + ROTARY_ENCODER_SPEED()); - lv_slider_set_value(lvobj, _getValue(), LV_ANIM_OFF); - onKeyPress(); - return; - } - else if (event == EVT_ROTARY_LEFT) { - setValue(_getValue() - ROTARY_ENCODER_SPEED()); - lv_slider_set_value(lvobj, _getValue(), LV_ANIM_OFF); - onKeyPress(); - return; - } else if (event == EVT_KEY_FIRST(KEY_EXIT)) - return; - } - - FormField::onEvent(event); -} -#endif diff --git a/radio/src/thirdparty/libopenui/src/slider.h b/radio/src/thirdparty/libopenui/src/slider.h index d289676d7..353c6f832 100644 --- a/radio/src/thirdparty/libopenui/src/slider.h +++ b/radio/src/thirdparty/libopenui/src/slider.h @@ -33,21 +33,14 @@ class Slider : public FormField void setValue(int value) { - if (_setValue != nullptr) - _setValue(limit(vmin, value, vmax)); + if (_setValue != nullptr) _setValue(limit(vmin, value, vmax)); } - void paint(BitmapBuffer* dc) override; - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif - protected: - int value(coord_t x) const; int vmin; int vmax; - bool sliding = false; std::function _getValue; std::function _setValue; + + int value(coord_t x) const; }; From c1e143c4fd2158f5badb626ba7eece3ee897a9d4 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 29 May 2022 18:37:10 +0200 Subject: [PATCH 630/714] attach windo focusHandler to LVGL events --- radio/src/thirdparty/libopenui/src/window.cpp | 6 ++++++ radio/src/thirdparty/libopenui/src/window.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0666ebd54..c1b5258f5 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -139,6 +139,12 @@ extern "C" void window_event_cb(lv_event_t * e) else if (code == LV_EVENT_CANCEL) { TRACE("CANCEL[%p]", window); window->onCancel(); + } else if (code == LV_EVENT_FOCUSED) { + if(window->focusHandler) + window->focusHandler(true); + } else if (code == LV_EVENT_DEFOCUSED) { + if(window->focusHandler) + window->focusHandler(false); } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 8cf5dec9f..0aafc982b 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -65,10 +65,13 @@ constexpr WindowFlags WINDOW_FLAGS_LAST = PUSH_FRONT; // }; typedef lv_obj_t *(*LvglCreate)(lv_obj_t *); +extern "C" void window_event_cb(lv_event_t * e); + class Window { friend class GridLayout; + friend void window_event_cb(lv_event_t * e); public: Window(Window *parent, const rect_t &rect, WindowFlags windowFlags = 0, From fd0881f19e4a74c249027ad7a69dbbc44c8bc978 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 30 May 2022 14:12:42 +0200 Subject: [PATCH 631/714] fix(table): focus border and click behavior --- radio/src/thirdparty/libopenui/src/table.cpp | 63 +++++++++++++++++--- radio/src/thirdparty/libopenui/src/table.h | 1 + 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 2038b40ee..76dfd3362 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -19,6 +19,44 @@ #include "table.h" +void TableField::event_cb(lv_event_t *e) +{ + lv_event_code_t code = lv_event_get_code(e); + if (code == LV_EVENT_VALUE_CHANGED) { + clicked(e); + } else if (code == LV_EVENT_DRAW_PART_BEGIN) { + draw_begin(e); + } else if (code == LV_EVENT_DRAW_PART_END) { + draw_end(e); + } + else if (code == LV_EVENT_DRAW_POST) { + + lv_obj_t * obj = lv_event_get_target(e); + bool has_focus = lv_obj_has_state(obj, LV_STATE_FOCUS_KEY); + bool is_edited = lv_group_get_editing((lv_group_t*)lv_obj_get_group(obj)); + + if (has_focus && !is_edited) { + lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); + + lv_draw_rect_dsc_t rect_dsc; + lv_draw_rect_dsc_init(&rect_dsc); + rect_dsc.bg_opa = LV_OPA_TRANSP; + rect_dsc.bg_img_opa = LV_OPA_TRANSP; + rect_dsc.outline_opa = LV_OPA_TRANSP; + rect_dsc.shadow_opa = LV_OPA_TRANSP; + + rect_dsc.border_color = makeLvColor(COLOR_THEME_FOCUS); + rect_dsc.border_opa = LV_OPA_100; + rect_dsc.border_width = lv_dpx(2); + + lv_area_t coords; + lv_area_copy(&coords, &obj->coords); + + lv_draw_rect(draw_ctx, &rect_dsc, &coords); + } + } +} + void TableField::clicked(lv_event_t *e) { lv_obj_t* target = lv_event_get_target(e); @@ -33,11 +71,24 @@ void TableField::clicked(lv_event_t *e) tf->adjustScroll(); } else { // Otherwise it's a click - uint16_t row; - uint16_t col; - lv_table_get_selected_cell(target, &row, &col); + if (lv_group_get_editing((lv_group_t*)lv_obj_get_group(target)) + || indev_type == LV_INDEV_TYPE_POINTER) { + uint16_t row; + uint16_t col; + lv_table_get_selected_cell(target, &row, &col); - tf->onPress(row, col); + tf->onPress(row, col); + } else { + tf->onClicked(); + } + + // Note: VALUE_CHANGED is generated on RELEASED + // + // It must be avoided that CLICKED be generated afterwards + // in case the object has been deleted meanwhile and to + // avoid onClicked() being called. + // + lv_indev_reset(lv_indev_get_act(), nullptr); } } @@ -82,9 +133,7 @@ void TableField::draw_end(lv_event_t* e) TableField::TableField(Window* parent, const rect_t& rect, WindowFlags windowFlags) : Window(parent, rect, windowFlags, 0, lv_table_create) { - lv_obj_add_event_cb(lvobj, TableField::clicked, LV_EVENT_VALUE_CHANGED, nullptr); - lv_obj_add_event_cb(lvobj, TableField::draw_begin, LV_EVENT_DRAW_PART_BEGIN, nullptr); - lv_obj_add_event_cb(lvobj, TableField::draw_end, LV_EVENT_DRAW_PART_END, nullptr); + lv_obj_add_event_cb(lvobj, TableField::event_cb, LV_EVENT_ALL, nullptr); } void TableField::setColumnCount(uint16_t cols) diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 0feb7ac30..142d6534e 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -51,6 +51,7 @@ class TableField : public Window virtual void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {} virtual void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {} + static void event_cb(lv_event_t *e); static void clicked(lv_event_t *e); static void draw_begin(lv_event_t *e); static void draw_end(lv_event_t *e); From 40233604cea1dce77a49d793ccd2c1a885e10376 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 31 May 2022 10:26:58 +0200 Subject: [PATCH 632/714] cleanup & formatting --- radio/src/thirdparty/libopenui/src/static.h | 30 ++++++++++----------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 1659dd7d4..4912b2e6d 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -130,27 +130,25 @@ class StaticBitmap: public Window bool scale = false; }; -class DynamicText: public StaticText +class DynamicText : public StaticText { - public: - DynamicText(Window * parent, const rect_t & rect, std::function textHandler, LcdFlags textFlags = 0): + public: + DynamicText(Window *parent, const rect_t &rect, + std::function textHandler, + LcdFlags textFlags = 0) : StaticText(parent, rect, "", 0, textFlags), textHandler(std::move(textHandler)) - { - } + { + } - void checkEvents() override - { - StaticText::checkEvents(); - std::string newText = textHandler(); - if (newText != text) { - setText(newText); - invalidate(); - } - } + void checkEvents() override + { + StaticText::checkEvents(); + setText(textHandler()); + } - protected: - std::function textHandler; + protected: + std::function textHandler; }; template From 469375786bdc2e6dd68a4f171d6be1c4df923326 Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Fri, 3 Jun 2022 05:30:39 -0400 Subject: [PATCH 633/714] if we are flex layout then increase padding. also add margin to label to give it some white space. --- radio/src/thirdparty/libopenui/src/choice.cpp | 9 +++++++++ radio/src/thirdparty/libopenui/src/choice.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 5edbfce6c..084a15d41 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -46,6 +46,13 @@ ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type, lv_obj_set_layout(lvobj, LV_LAYOUT_FLEX); lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW); + if (height() == 0) + { + lv_obj_set_style_pad_top(lvobj, 4, LV_PART_MAIN); + lv_obj_set_style_pad_bottom(lvobj, 4, LV_PART_MAIN); + lv_obj_set_style_radius(lvobj, 4, LV_PART_MAIN); + } + lv_obj_add_event_cb(lvobj, choice_changed_cb, LV_EVENT_VALUE_CHANGED, lvobj); label = lv_label_create(lvobj); @@ -68,6 +75,8 @@ ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type, lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); + lv_obj_set_style_pad_right(label, CHOICE_LABEL_MARGIN_RIGHT, LV_PART_MAIN); + // lv_obj_set // add the image lv_obj_t *img = lv_img_create(lvobj); diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index d349a11f8..584f87fba 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -24,6 +24,8 @@ class Menu; +constexpr int CHOICE_LABEL_MARGIN_RIGHT = 10; + enum ChoiceType { CHOICE_TYPE_DROPOWN, CHOICE_TYPE_FOLDER, From 862073d4c2a91a3c42149f8a618e05a80fca8226 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 2 Jun 2022 05:40:25 +0200 Subject: [PATCH 634/714] fix(focus): hasFocus() uses proper state --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index c1b5258f5..244800a5c 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -361,7 +361,7 @@ void Window::deleteChildren() bool Window::hasFocus() const { - return lvobj && lv_obj_has_state(lvobj, LV_STATE_FOCUS_KEY); + return lvobj && lv_obj_has_state(lvobj, LV_STATE_FOCUSED); } bool Window::hasOpaqueRect(const rect_t & testRect) const From fd3deb25710394b6f48b7ff2a6078a2e9895652c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 4 Jun 2022 12:53:55 +0200 Subject: [PATCH 635/714] fix(choice): send LV_EVENT_VALUE_CHANGED after value has been set --- radio/src/thirdparty/libopenui/src/choice.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 084a15d41..85b13299f 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -193,8 +193,10 @@ void Choice::setValues(const char * const values[]) void Choice::setValue(int val) { - lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); - if (_setValue) _setValue(val); + if (_setValue) { + _setValue(val); + lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); + } } void Choice::onClicked() From 1acea8fc8887b84c85db1c20cbe25e8e251c2285 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 4 Jun 2022 12:54:31 +0200 Subject: [PATCH 636/714] fix(flex grid): added padding between rows as well --- radio/src/thirdparty/libopenui/src/flexlayout.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h index dcc3abe70..e7d9b24c9 100644 --- a/radio/src/thirdparty/libopenui/src/flexlayout.h +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -46,6 +46,7 @@ class FlexGridLayout // padding lv_obj_set_style_pad_all(obj, padding, LV_PART_MAIN); lv_obj_set_style_pad_left(obj, padding + PAGE_PADDING, LV_PART_MAIN); + lv_obj_set_style_pad_row(obj, padding, LV_PART_MAIN); // layout lv_obj_set_layout(obj, LV_LAYOUT_GRID); From 34652c4afa47dc9333753163077810cb1cec328c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 5 Jun 2022 00:39:09 +0200 Subject: [PATCH 637/714] fix: draw non-null terminated strings --- .../src/thirdparty/libopenui/src/bitmapbuffer.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index cafa1d0d9..a95aea46a 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -18,6 +18,8 @@ */ #include +#include + #include "bitmapbuffer.h" #include "libopenui_depends.h" #include "libopenui_helpers.h" @@ -1064,10 +1066,15 @@ extern lv_color_t makeLvColor(uint32_t colorFlags); coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, uint8_t len, LcdFlags flags) -{ +{ if (!s) return x; MOVE_OFFSET(); + // LVGL does not handle non-null terminated strings + static char buffer[256]; + strncpy(buffer, s, len); + buffer[len] = '\0'; + int height = getFontHeight(flags); if (y + height <= ymin || y >= ymax) { @@ -1094,7 +1101,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, } lv_point_t p; - lv_txt_get_size(&p, s, font, label_draw_dsc.letter_space, + lv_txt_get_size(&p, buffer, font, label_draw_dsc.letter_space, label_draw_dsc.line_space, LV_COORD_MAX, 0); lv_coord_t lv_x = (lv_coord_t)x; @@ -1118,12 +1125,12 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, } if (draw_ctx) { - lv_draw_label(draw_ctx, &label_draw_dsc, &coords, s, nullptr); + lv_draw_label(draw_ctx, &label_draw_dsc, &coords, buffer, nullptr); } #if !defined(BOOT) else if (canvas) { lv_canvas_draw_text(canvas, coords.x1, coords.y1, coords.x2 - coords.x1 + 1, - &label_draw_dsc, s); + &label_draw_dsc, buffer); } #endif From 574d008b67cbd61632f6d04af956bcfdf4da8100 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 5 Jun 2022 10:26:38 +0200 Subject: [PATCH 638/714] Snap to begin / end of window when near --- radio/src/thirdparty/libopenui/src/window.cpp | 35 ++++++++----------- .../src/thirdparty/libopenui/thirdparty/lvgl | 2 +- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 244800a5c..376627632 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -23,29 +23,19 @@ #include "widgets/window_base.h" -// Window * Window::focusWindow = nullptr; std::list Window::trash; extern lv_obj_t *virtual_kb; -static bool is_scrolling = false; -// static bool inhibit_focus = false; - extern "C" void window_event_cb(lv_event_t * e) { lv_obj_t *target = lv_event_get_target(e); lv_event_code_t code = lv_event_get_code(e); - // we do this before looking for user data (libopenui ptr) for this, and in - // fact during delete operations the window will be deleted and you need - // to reset scrolling if you get a pressing as when you are deleting controls - // you get scroll begin events and no scroll end if (code == LV_EVENT_SCROLL_BEGIN) { TRACE("SCROLL_BEGIN"); - is_scrolling = true; - } else if (code == LV_EVENT_SCROLL_END || code == LV_EVENT_PRESSING) { + } else if (code == LV_EVENT_SCROLL_END) { TRACE("SCROLL_END"); - is_scrolling = false; } Window* window = (Window *)lv_obj_get_user_data(target); @@ -84,11 +74,6 @@ extern "C" void window_event_cb(lv_event_t * e) TRACE_WINDOWS("PRESSED: %s", window->getWindowDebugString().c_str()); lv_indev_t *click_source = (lv_indev_t *)lv_event_get_param(e); - if (click_source == NULL || is_scrolling) { - TRACE_WINDOWS("CLICK WHILE SCROLLING"); - return; - } - // if(lv_indev_get_type(click_source) == LV_INDEV_TYPE_KEYPAD || // lv_indev_get_type(click_source) == LV_INDEV_TYPE_ENCODER) { // return; @@ -124,13 +109,23 @@ extern "C" void window_event_cb(lv_event_t * e) } #endif else if (code == LV_EVENT_SCROLL) { -#if defined(DEBUG_WINDOWS) lv_coord_t scroll_y = lv_obj_get_scroll_y(target); lv_coord_t scroll_x = lv_obj_get_scroll_x(target); - TRACE_WINDOWS("SCROLL[x=%d;y=%d] %s", scroll_x, scroll_y, - window->getWindowDebugString().c_str()); -#endif + // exclude pointer based scrolling (only focus scrolling) + if (!lv_obj_is_scrolling(target)) { + lv_point_t* p = (lv_point_t*)lv_event_get_param(e); + lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(target); + + // TRACE("SCROLL[x=%d;y=%d;top=%d;bottom=%d]", p->x, p->y, + // scroll_y, scroll_bottom); + + if (scroll_y <= lv_dpx(45) && p->y > 0) { + lv_obj_scroll_by(target, 0, scroll_y, LV_ANIM_OFF); + } else if (scroll_bottom <= lv_dpx(16) && p->y < 0) { + lv_obj_scroll_by(target, 0, -scroll_bottom, LV_ANIM_OFF); + } + } } else if (code == LV_EVENT_CLICKED) { TRACE("CLICKED[%p]", window); diff --git a/radio/src/thirdparty/libopenui/thirdparty/lvgl b/radio/src/thirdparty/libopenui/thirdparty/lvgl index a7ec08238..9a414b1d4 160000 --- a/radio/src/thirdparty/libopenui/thirdparty/lvgl +++ b/radio/src/thirdparty/libopenui/thirdparty/lvgl @@ -1 +1 @@ -Subproject commit a7ec08238759d446cf22197ae346ed1b995ae422 +Subproject commit 9a414b1d48d2893133b6038ec80d59fb157aade4 From aca9bf420aedb4e349fed18458089208a9cb1c26 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 5 Jun 2022 13:05:19 +0200 Subject: [PATCH 639/714] Text & number edit use the same custom widget --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + .../thirdparty/libopenui/src/numberedit.cpp | 24 +--------- .../src/thirdparty/libopenui/src/textedit.cpp | 7 ++- .../libopenui/src/widgets/field_edit.c | 45 +++++++++++++++++++ .../libopenui/src/widgets/field_edit.h | 36 +++++++++++++++ .../libopenui/src/widgets/number_edit.c | 41 +++++++++++++++++ .../libopenui/src/widgets/number_edit.h | 36 +++++++++++++++ radio/src/thirdparty/libopenui/src/window.cpp | 8 ++-- 8 files changed, 170 insertions(+), 28 deletions(-) create mode 100644 radio/src/thirdparty/libopenui/src/widgets/field_edit.c create mode 100644 radio/src/thirdparty/libopenui/src/widgets/field_edit.h create mode 100644 radio/src/thirdparty/libopenui/src/widgets/number_edit.c create mode 100644 radio/src/thirdparty/libopenui/src/widgets/number_edit.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 13b688369..6fb2a9cb2 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -38,6 +38,7 @@ set(LIBOPENUI_SRC # LVGL widgets widgets/window_base.c widgets/simple_btn.c + widgets/field_edit.c ) if(SOFTWARE_KEYBOARD) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 10f2af558..9f4ba582f 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -18,6 +18,7 @@ */ #include "numberedit.h" +#include "widgets/field_edit.h" #if defined(SOFTWARE_KEYBOARD) #include "keyboard_number.h" @@ -40,34 +41,13 @@ static void numberedit_cb(lv_event_t* e) } } -// create a new class for number edit so we can set the sytles differently if needed. -const lv_obj_class_t lv_numberedit_class = { - .base_class = &lv_obj_class, - .constructor_cb = lv_textarea_class.constructor_cb, - .destructor_cb = lv_textarea_class.destructor_cb, - .user_data = nullptr, - .event_cb = lv_textarea_class.event_cb, - .width_def = LV_DPI_DEF / 2, - .height_def = LV_SIZE_CONTENT, - .editable = LV_OBJ_CLASS_EDITABLE_TRUE, - .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, - .instance_size = sizeof(lv_textarea_t) -}; - -lv_obj_t *lv_numberedit_create(lv_obj_t *parent) -{ - lv_obj_t * obj = lv_obj_class_create_obj(&lv_numberedit_class, parent); - lv_obj_class_init_obj(obj); - return obj; -} - NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags, LcdFlags textFlags) : BaseNumberEdit(parent, rect, vmin, vmax, std::move(getValue), std::move(setValue), windowFlags, textFlags, - lv_numberedit_create) + field_edit_create) { // properties lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 6bd890942..35d29c800 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -19,7 +19,10 @@ #include "textedit.h" #include "font.h" -#include "libopenui_config.h" + +#include "widgets/field_edit.h" + +// #include "libopenui_config.h" #if !defined(STR_EDIT) #define STR_EDIT "Edit" @@ -45,7 +48,7 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, uint8_t length, LcdFlags windowFlags, const char *_extra_chars) : FormField(parent, rect, windowFlags, 0, - lv_textarea_create), + field_edit_create), value(value), length(length) { diff --git a/radio/src/thirdparty/libopenui/src/widgets/field_edit.c b/radio/src/thirdparty/libopenui/src/widgets/field_edit.c new file mode 100644 index 000000000..6fb27d9fb --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/field_edit.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "field_edit.h" + +#define MY_CLASS &field_edit_class + +static void field_edit_event(const lv_obj_class_t* class_p, lv_event_t* e) +{ + /* Call the ancestor's event handler */ + lv_obj_event_base(MY_CLASS, e); +} + +const lv_obj_class_t field_edit_class = { + .base_class = &lv_textarea_class, + .event_cb = field_edit_event, + .width_def = LV_DPI_DEF / 2, + .height_def = LV_SIZE_CONTENT, + .editable = LV_OBJ_CLASS_EDITABLE_TRUE, + .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, + .instance_size = sizeof(lv_textarea_t) +}; + +lv_obj_t* field_edit_create(lv_obj_t* parent) +{ + lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} diff --git a/radio/src/thirdparty/libopenui/src/widgets/field_edit.h b/radio/src/thirdparty/libopenui/src/widgets/field_edit.h new file mode 100644 index 000000000..7cf6d1f1b --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/field_edit.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef FIELD_EDIT_H +#define FIELD_EDIT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const lv_obj_class_t field_edit_class; +lv_obj_t* field_edit_create(lv_obj_t* parent); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/radio/src/thirdparty/libopenui/src/widgets/number_edit.c b/radio/src/thirdparty/libopenui/src/widgets/number_edit.c new file mode 100644 index 000000000..19624a2fa --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/number_edit.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "field_edit.h" + +#define MY_CLASS &field_edit_class + +const lv_obj_class_t field_edit_class = { + .base_class = &lv_obj_class, + .constructor_cb = lv_textarea_class.constructor_cb, + .destructor_cb = lv_textarea_class.destructor_cb, + .event_cb = lv_textarea_class.event_cb, + .width_def = LV_DPI_DEF / 2, + .height_def = LV_SIZE_CONTENT, + .editable = LV_OBJ_CLASS_EDITABLE_TRUE, + .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, + .instance_size = sizeof(lv_textarea_t) +}; + +lv_obj_t* field_edit_create(lv_obj_t* parent) +{ + lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} diff --git a/radio/src/thirdparty/libopenui/src/widgets/number_edit.h b/radio/src/thirdparty/libopenui/src/widgets/number_edit.h new file mode 100644 index 000000000..7cf6d1f1b --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/number_edit.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) OpenTX + * + * Source: + * https://github.com/opentx/libopenui + * + * This file is a part of libopenui library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef FIELD_EDIT_H +#define FIELD_EDIT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const lv_obj_class_t field_edit_class; +lv_obj_t* field_edit_create(lv_obj_t* parent); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 376627632..928897c89 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -117,12 +117,12 @@ extern "C" void window_event_cb(lv_event_t * e) lv_point_t* p = (lv_point_t*)lv_event_get_param(e); lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(target); - // TRACE("SCROLL[x=%d;y=%d;top=%d;bottom=%d]", p->x, p->y, - // scroll_y, scroll_bottom); + TRACE("SCROLL[x=%d;y=%d;top=%d;bottom=%d]", p->x, p->y, + scroll_y, scroll_bottom); - if (scroll_y <= lv_dpx(45) && p->y > 0) { + if (scroll_y <= 45 && p->y > 0) { lv_obj_scroll_by(target, 0, scroll_y, LV_ANIM_OFF); - } else if (scroll_bottom <= lv_dpx(16) && p->y < 0) { + } else if (scroll_bottom <= 16 && p->y < 0) { lv_obj_scroll_by(target, 0, -scroll_bottom, LV_ANIM_OFF); } } From ee3f51330a834ef85fa1a8ba657cc714b0334ed5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 6 Jun 2022 07:39:02 +0200 Subject: [PATCH 640/714] cleanup --- radio/src/thirdparty/libopenui/src/mainwindow.cpp | 1 - radio/src/thirdparty/libopenui/src/numberedit.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index 8efe7a72e..a3ce2d0f9 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -36,7 +36,6 @@ MainWindow::MainWindow() : Window(nullptr, {0, 0, LCD_W, LCD_H}, OPAQUE), invalidatedRect(rect) { - // Layer::push(this); } void MainWindow::emptyTrash() diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 2aa04ab5f..830399687 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -48,5 +48,3 @@ class NumberEdit : public BaseNumberEdit protected: std::function isValueAvailable; }; - -extern const lv_obj_class_t lv_numberedit_class; From 1ab128e08432d40d3addc212bfac8be387da62e4 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Wed, 8 Jun 2022 21:59:29 +0200 Subject: [PATCH 641/714] let sliders and text always stretch to a grid cel, if used in a grid layout add padding on the right side in grid layouts --- radio/src/thirdparty/libopenui/src/flexlayout.h | 1 + radio/src/thirdparty/libopenui/src/slider.cpp | 3 +++ radio/src/thirdparty/libopenui/src/static.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h index e7d9b24c9..f3f4af356 100644 --- a/radio/src/thirdparty/libopenui/src/flexlayout.h +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -46,6 +46,7 @@ class FlexGridLayout // padding lv_obj_set_style_pad_all(obj, padding, LV_PART_MAIN); lv_obj_set_style_pad_left(obj, padding + PAGE_PADDING, LV_PART_MAIN); + lv_obj_set_style_pad_right(obj, padding + PAGE_PADDING, LV_PART_MAIN); lv_obj_set_style_pad_row(obj, padding, LV_PART_MAIN); // layout diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index fc59bca7b..9d4e2f887 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -93,6 +93,9 @@ Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, lv_style_init(&style_edit); lv_style_set_bg_color(&style_edit, makeLvColor(COLOR_THEME_EDIT)); lv_obj_add_style(lvobj, &style_edit, LV_PART_KNOB | LV_STATE_FOCUSED | LV_STATE_EDITED); + + // always fill a grid cell in x-direction + lv_obj_set_style_grid_cell_x_align(lvobj, LV_GRID_ALIGN_STRETCH, 0); } diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 7fd9b9813..01cadec64 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -41,6 +41,8 @@ StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); setBackgroundColor(COLOR_THEME_SECONDARY2); } + + lv_obj_set_style_grid_cell_x_align(lvobj, LV_GRID_ALIGN_STRETCH, 0); } #if defined(DEBUG_WINDOWS) From cbe237a945697fb58a244f8ac675f60d7aec965f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 9 Jun 2022 19:02:32 +0200 Subject: [PATCH 642/714] button: add long press --- radio/src/thirdparty/libopenui/src/button.cpp | 25 +++++-- radio/src/thirdparty/libopenui/src/button.h | 65 ++++++++++--------- 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index d3d716136..cf28843b4 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -40,12 +40,14 @@ Button::Button(Window* parent, const rect_t& rect, objConstruct ? objConstruct : simple_btn_create), pressHandler(std::move(pressHandler)) { + lv_obj_add_event_cb(lvobj, Button::long_pressed, LV_EVENT_LONG_PRESSED, nullptr); if (windowFlag & BUTTON_CHECKED) lv_obj_add_state(lvobj, LV_STATE_CHECKED); } void Button::check(bool checked) { + if (_deleted) return; if (checked != bool(windowFlags & BUTTON_CHECKED)) { windowFlags ^= BUTTON_CHECKED; update_checked_flag(lvobj, windowFlags); @@ -63,11 +65,16 @@ bool Button::checked() const void Button::onPress() { - bool check = (pressHandler && pressHandler()); - windowFlags = - check ? windowFlags | BUTTON_CHECKED : windowFlags & ~BUTTON_CHECKED; - if (lvobj == nullptr) return; - update_checked_flag(lvobj, windowFlags); + check(pressHandler && pressHandler()); +} + +void Button::onLongPress() +{ + if (longPressHandler) { + check(longPressHandler()); + lv_obj_clear_state(lvobj, LV_STATE_PRESSED); + lv_indev_wait_release(lv_indev_get_act()); + } } void Button::onClicked() @@ -86,6 +93,14 @@ void Button::checkEvents() if (checkHandler) checkHandler(); } +void Button::long_pressed(lv_event_t* e) +{ + auto obj = lv_event_get_target(e); + auto btn = (Button*)lv_obj_get_user_data(obj); + if (obj) btn->onLongPress(); +} + + TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, std::function pressHandler, WindowFlags windowFlags, LcdFlags textFlags) : diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 56319ff35..0a5aa103c 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -25,52 +25,53 @@ constexpr WindowFlags BUTTON_BACKGROUND = FORM_FLAGS_LAST << 1u; constexpr WindowFlags BUTTON_CHECKED = FORM_FLAGS_LAST << 2u; constexpr WindowFlags BUTTON_CHECKED_ON_FOCUS = FORM_FLAGS_LAST << 3u; -class Button: public FormField +class Button : public FormField { - public: - Button(Window* parent, const rect_t& rect, - std::function pressHandler = nullptr, - WindowFlags windowFlags = 0, LcdFlags textFlags = 0, - LvglCreate objConstruct = nullptr); + public: + Button(Window* parent, const rect_t& rect, + std::function pressHandler = nullptr, + WindowFlags windowFlags = 0, LcdFlags textFlags = 0, + LvglCreate objConstruct = nullptr); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "Button"; - } + std::string getName() const override { return "Button"; } #endif - virtual void onPress(); - void onClicked() override; - void checkEvents() override; + virtual void onPress(); + virtual void onLongPress(); - void check(bool checked = true); - bool checked() const; + void onClicked() override; + void checkEvents() override; - void setPressHandler(std::function handler) - { - pressHandler = std::move(handler); - } + void check(bool checked = true); + bool checked() const; - void setCheckHandler(std::function handler) - { - checkHandler = std::move(handler); - } + void setPressHandler(std::function handler) + { + pressHandler = std::move(handler); + } -// #if defined(HARDWARE_KEYS) -// void onEvent(event_t event) override; -// #endif - // void onEvent(lv_event_t* e);// override; + void setLongPressHandler(std::function handler) + { + longPressHandler = std::move(handler); + } + + void setCheckHandler(std::function handler) + { + checkHandler = std::move(handler); + } #if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; + bool onTouchEnd(coord_t x, coord_t y) override; #endif - protected: - std::function pressHandler; - std::function checkHandler; -}; + protected: + std::function pressHandler; + std::function longPressHandler; + std::function checkHandler; + static void long_pressed(lv_event_t* e); +}; class TextButton: public Button { From 8f63932e4343a5fc0f8ab2a5ec295f68fd65bb09 Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Fri, 10 Jun 2022 20:56:54 +0200 Subject: [PATCH 643/714] don't always stretch sliders --- radio/src/thirdparty/libopenui/src/slider.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/slider.cpp b/radio/src/thirdparty/libopenui/src/slider.cpp index 9d4e2f887..fc59bca7b 100644 --- a/radio/src/thirdparty/libopenui/src/slider.cpp +++ b/radio/src/thirdparty/libopenui/src/slider.cpp @@ -93,9 +93,6 @@ Slider::Slider(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, lv_style_init(&style_edit); lv_style_set_bg_color(&style_edit, makeLvColor(COLOR_THEME_EDIT)); lv_obj_add_style(lvobj, &style_edit, LV_PART_KNOB | LV_STATE_FOCUSED | LV_STATE_EDITED); - - // always fill a grid cell in x-direction - lv_obj_set_style_grid_cell_x_align(lvobj, LV_GRID_ALIGN_STRETCH, 0); } From a7ee9d3a812d33f1c95ae73d9ad3d0194649c139 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Jun 2022 09:13:36 +0200 Subject: [PATCH 644/714] button: remove LV_OBJ_FLAG_CHECKABLE on text buttons --- radio/src/thirdparty/libopenui/src/button.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index cf28843b4..366dcfe0d 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -41,8 +41,7 @@ Button::Button(Window* parent, const rect_t& rect, pressHandler(std::move(pressHandler)) { lv_obj_add_event_cb(lvobj, Button::long_pressed, LV_EVENT_LONG_PRESSED, nullptr); - if (windowFlag & BUTTON_CHECKED) - lv_obj_add_state(lvobj, LV_STATE_CHECKED); + update_checked_flag(lvobj, windowFlags); } void Button::check(bool checked) @@ -109,10 +108,7 @@ TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, text(std::move(text)) { setTextFlags(textFlags | COLOR_THEME_PRIMARY1); - lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CHECKABLE); - - if (windowFlags & BUTTON_CHECKED) - lv_obj_add_state(lvobj, LV_STATE_CHECKED); + update_checked_flag(lvobj, windowFlags); label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); From 4ecedab3c805cdce478e6730964680160f3ed870 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Jun 2022 09:29:00 +0200 Subject: [PATCH 645/714] form field: add SCROLL_ON_FOCUS Fixes issue with choices not triggering scroll on focus. --- radio/src/thirdparty/libopenui/src/form.cpp | 1 + radio/src/thirdparty/libopenui/src/form.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index d5dbfb9a7..fa74c251f 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -28,6 +28,7 @@ FormField::FormField(Window* parent, const rect_t& rect, LvglCreate objConstruct) : Window(parent, rect, windowFlags, textFlags, objConstruct) { + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); } void FormField::setEditMode(bool newEditMode) diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index cd61dfe2c..c6fd3066d 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -84,8 +84,6 @@ class FormGroup : public Window class Line : public Window { FlexGridLayout *layout; - FormGroup *group; - void construct(); public: From 78e067a7537c6769c0bae4c533cc495ef4605617 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Jun 2022 09:29:35 +0200 Subject: [PATCH 646/714] window: remove unused local variable --- radio/src/thirdparty/libopenui/src/window.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 928897c89..7b2c5c9b6 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -109,14 +109,13 @@ extern "C" void window_event_cb(lv_event_t * e) } #endif else if (code == LV_EVENT_SCROLL) { - lv_coord_t scroll_y = lv_obj_get_scroll_y(target); - lv_coord_t scroll_x = lv_obj_get_scroll_x(target); // exclude pointer based scrolling (only focus scrolling) if (!lv_obj_is_scrolling(target)) { lv_point_t* p = (lv_point_t*)lv_event_get_param(e); lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(target); + lv_coord_t scroll_y = lv_obj_get_scroll_y(target); TRACE("SCROLL[x=%d;y=%d;top=%d;bottom=%d]", p->x, p->y, scroll_y, scroll_bottom); From 390fed899780348b768541c57c5f9b58eb034852 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Jun 2022 11:05:05 +0200 Subject: [PATCH 647/714] Better dynamic number --- radio/src/thirdparty/libopenui/src/static.h | 56 +++++++++++++-------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 4912b2e6d..f53f76f56 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -152,34 +152,48 @@ class DynamicText : public StaticText }; template -class DynamicNumber: public Window +class DynamicNumber : public StaticText { - public: - DynamicNumber(Window * parent, const rect_t & rect, std::function numberHandler, LcdFlags textFlags = 0, const char * prefix = nullptr, const char * suffix = nullptr): - Window(parent, rect, 0, textFlags), + public: + DynamicNumber(Window *parent, const rect_t &rect, + std::function numberHandler, LcdFlags textFlags = 0, + const char *prefix = nullptr, const char *suffix = nullptr) : + StaticText(parent, rect, "", 0, textFlags), numberHandler(std::move(numberHandler)), prefix(prefix), suffix(suffix) - { - } + { + updateText(); + } - void paint(BitmapBuffer * dc) override - { - dc->drawNumber(0, FIELD_PADDING_TOP, value, textFlags, 0, prefix, suffix); + void checkEvents() override + { + T newValue = numberHandler(); + if (value != newValue) { + value = newValue; + updateText(); } + } - void checkEvents() override - { - T newValue = numberHandler(); - if (value != newValue) { - value = newValue; - invalidate(); + protected: + T value = 0; + std::function numberHandler; + const char *prefix; + const char *suffix; + + void updateText() + { + if (lvobj) { + const char *p = prefix ? prefix : ""; + const char *s = suffix ? suffix : ""; + if (textFlags & PREC1) { + lv_label_set_text_fmt(lvobj, "%s%d.%01d%s", p, value / 10, value % 10, s); + } else if (textFlags & PREC2) { + lv_label_set_text_fmt(lvobj, "%s%d.%02d%s", p, value / 100, + value % 100, s); + } else { + lv_label_set_text_fmt(lvobj, "%s%d%s", p, value, s); } } - - protected: - T value = 0; - std::function numberHandler; - const char * prefix; - const char * suffix; + } }; From e47b1ca24526ffee738b662d2501f99aec6a56c6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Jun 2022 11:27:27 +0200 Subject: [PATCH 648/714] Always include code for visual keyboards Used now on non-touch interfaces as well. --- .../src/thirdparty/libopenui/src/CMakeLists.txt | 16 +++------------- .../src/thirdparty/libopenui/src/numberedit.cpp | 11 +++-------- radio/src/thirdparty/libopenui/src/textedit.cpp | 6 ------ 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 6fb2a9cb2..f55e96b2e 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -6,10 +6,6 @@ if(HARWARE_TOUCH) add_definitions(-DHARDWARE_TOUCH) endif() -if(SOFTWARE_KEYBOARD) - add_definitions(-DSOFTWARE_KEYBOARD) -endif() - set(LIBOPENUI_SRC libopenui_file.cpp bitmapbuffer.cpp @@ -35,21 +31,15 @@ set(LIBOPENUI_SRC table.cpp modal_window.cpp dialog.cpp + keyboard_text.cpp + keyboard_number.cpp + keyboard_base.cpp # LVGL widgets widgets/window_base.c widgets/simple_btn.c widgets/field_edit.c ) -if(SOFTWARE_KEYBOARD) - set(LIBOPENUI_SRC - ${LIBOPENUI_SRC} - keyboard_text.cpp - keyboard_number.cpp - keyboard_base.cpp - ) -endif() - foreach(LIBOPENUI_FILE ${LIBOPENUI_SRC}) set(LIBOPENUI_SRC_FILES ${LIBOPENUI_SRC_FILES} ${LIBOPENUI_SRC_DIR}/src/${LIBOPENUI_FILE}) endforeach() diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 9f4ba582f..a10494777 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -19,11 +19,7 @@ #include "numberedit.h" #include "widgets/field_edit.h" - -#if defined(SOFTWARE_KEYBOARD) #include "keyboard_number.h" -#endif - static void numberedit_cb(lv_event_t* e) { @@ -99,7 +95,6 @@ void NumberEdit::onEvent(event_t event) } #endif -#if defined(HARDWARE_TOUCH) case EVT_VIRTUAL_KEY_PLUS: setValue(getValue() + getStep()); break; @@ -131,7 +126,6 @@ void NumberEdit::onEvent(event_t event) case EVT_VIRTUAL_KEY_SIGN: setValue(-getValue()); break; -#endif } } @@ -143,7 +137,8 @@ void NumberEdit::onClicked() lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); if(indev_type == LV_INDEV_TYPE_POINTER) { NumberKeyboard::show(this); - } else { - FormField::onClicked(); + return; } + + FormField::onClicked(); } diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 35d29c800..88c4f70f9 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -22,8 +22,6 @@ #include "widgets/field_edit.h" -// #include "libopenui_config.h" - #if !defined(STR_EDIT) #define STR_EDIT "Edit" #endif @@ -32,9 +30,7 @@ #define STR_CLEAR "Clear" #endif -#if defined(HARDWARE_TOUCH) #include "keyboard_text.h" -#endif #if defined(HARDWARE_KEYS) #include "menu.h" @@ -93,14 +89,12 @@ void TextEdit::onEvent(event_t event) { TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); -#if defined(HARDWARE_TOUCH) if (IS_VIRTUAL_KEY_EVENT(event)) { uint8_t c = event & 0xFF; if (c == (uint8_t)KEYBOARD_ENTER[0]) { changeEnd(); } } -#endif #if defined(HARDWARE_KEYS) if (editMode) { From 14fea89118a170174509f64e326c9177e7754fa0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Jun 2022 13:50:56 +0200 Subject: [PATCH 649/714] flex grid: add shortcut for Window --- radio/src/thirdparty/libopenui/src/flexlayout.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h index f3f4af356..abe39bcbb 100644 --- a/radio/src/thirdparty/libopenui/src/flexlayout.h +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -41,6 +41,8 @@ class FlexGridLayout { } + void apply(Window* w) { apply(w->getLvObj()); } + void apply(lv_obj_t* obj) { // padding @@ -56,6 +58,8 @@ class FlexGridLayout } } + void add(Window* w) { add(w->getLvObj()); } + void add(lv_obj_t* obj) { if (col_dsc && row_dsc) { lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_START, col_pos, col_span, From ae6f7b55d344ceebb028be91f705743874a3d047 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 11 Jun 2022 13:51:12 +0200 Subject: [PATCH 650/714] table field: add onSelected() --- radio/src/thirdparty/libopenui/src/table.cpp | 19 +++++++------------ radio/src/thirdparty/libopenui/src/table.h | 1 + 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 76dfd3362..d83488ae4 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -65,17 +65,21 @@ void TableField::clicked(lv_event_t *e) TableField* tf = (TableField*)lv_obj_get_user_data(target); if (!tf) return; + uint16_t row; + uint16_t col; + lv_table_get_selected_cell(target, &row, &col); + if (row == LV_TABLE_CELL_NONE || col == LV_TABLE_CELL_NONE) + return; + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); if(indev_type == LV_INDEV_TYPE_ENCODER) { // Encoder send VALUE_CHANGED when selection changes tf->adjustScroll(); + tf->onSelected(row, col); } else { // Otherwise it's a click if (lv_group_get_editing((lv_group_t*)lv_obj_get_group(target)) || indev_type == LV_INDEV_TYPE_POINTER) { - uint16_t row; - uint16_t col; - lv_table_get_selected_cell(target, &row, &col); tf->onPress(row, col); } else { @@ -261,15 +265,6 @@ void TableField::onEvent(event_t event) onKeyPress(); selectNext(-1); } - // else if (event == EVT_KEY_BREAK(KEY_ENTER)) { - // onKeyPress(); - - // uint16_t row; - // uint16_t col; - // lv_table_get_selected_cell(lvobj, &row, &col); - - // onPress(row, col); - // } else { Window::onEvent(event); } diff --git a/radio/src/thirdparty/libopenui/src/table.h b/radio/src/thirdparty/libopenui/src/table.h index 142d6534e..520915231 100644 --- a/radio/src/thirdparty/libopenui/src/table.h +++ b/radio/src/thirdparty/libopenui/src/table.h @@ -48,6 +48,7 @@ class TableField : public Window void selectNext(int16_t dir); void onEvent(event_t event) override; + virtual void onSelected(uint16_t row, uint16_t col) {} virtual void onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {} virtual void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {} From cda44291f7cf94d974da38a007cbea5d9c2901c6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 12 Jun 2022 11:30:56 +0200 Subject: [PATCH 651/714] base number edit: streamlined code a bit --- radio/src/thirdparty/libopenui/src/basenumberedit.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index d97bf5cee..edbc65a6b 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -88,12 +88,14 @@ class BaseNumberEdit: public FormField } } if (lvobj != nullptr) { + std::string str; if (displayFunction != nullptr) { - lv_textarea_set_text(lvobj, displayFunction(currentValue).c_str()); + str = displayFunction(currentValue); } else { - auto dispString = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(), suffix.c_str()); - lv_textarea_set_text(lvobj, dispString.c_str()); + str = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(), + suffix.c_str()); } + lv_textarea_set_text(lvobj, str.c_str()); } } From 41ab90ad3616ab8b87413ba0ebd25213b839ddd0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 12 Jun 2022 11:58:57 +0200 Subject: [PATCH 652/714] fix(dynamic numbers): precision flags --- radio/src/thirdparty/libopenui/src/static.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index f53f76f56..b408d18af 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -186,11 +186,10 @@ class DynamicNumber : public StaticText if (lvobj) { const char *p = prefix ? prefix : ""; const char *s = suffix ? suffix : ""; - if (textFlags & PREC1) { + if ((textFlags & PREC2) == PREC2) { + lv_label_set_text_fmt(lvobj, "%s%d.%02d%s", p, value / 100, value % 100, s); + } else if (textFlags & PREC1) { lv_label_set_text_fmt(lvobj, "%s%d.%01d%s", p, value / 10, value % 10, s); - } else if (textFlags & PREC2) { - lv_label_set_text_fmt(lvobj, "%s%d.%02d%s", p, value / 100, - value % 100, s); } else { lv_label_set_text_fmt(lvobj, "%s%d%s", p, value, s); } From 7679077632a8b8a01675b0611b97542eaac5aed0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 12 Jun 2022 19:19:35 +0200 Subject: [PATCH 653/714] window: allow for accessing LVGL obj / children in close handler --- radio/src/thirdparty/libopenui/src/window.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 7b2c5c9b6..ff8996ae7 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -321,16 +321,16 @@ void Window::deleteLater(bool detach, bool trash) Window::trash.push_back(this); } + if (closeHandler) { + closeHandler(); + } + deleteChildren(); if (lvobj != nullptr) { lv_obj_del(lvobj); lvobj = nullptr; } - - if (closeHandler) { - closeHandler(); - } } void Window::clear() From 6f15104bb47b2d03e651038966eff6bcaacd6b3d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 13 Jun 2022 09:07:28 +0200 Subject: [PATCH 654/714] main window: remove last rest of touch handling --- .../thirdparty/libopenui/src/mainwindow.cpp | 56 +++---------------- .../src/thirdparty/libopenui/src/mainwindow.h | 25 --------- 2 files changed, 9 insertions(+), 72 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.cpp b/radio/src/thirdparty/libopenui/src/mainwindow.cpp index a3ce2d0f9..604dac494 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.cpp +++ b/radio/src/thirdparty/libopenui/src/mainwindow.cpp @@ -22,73 +22,34 @@ #include "lvgl/lvgl.h" -#if defined(HARDWARE_TOUCH) -#include "touch.h" -#endif -#if defined(HARDWARE_TOUCH) -TouchState touchState; -#endif MainWindow * MainWindow::_instance = nullptr; MainWindow::MainWindow() : - Window(nullptr, {0, 0, LCD_W, LCD_H}, OPAQUE), - invalidatedRect(rect) + Window(nullptr, {0, 0, LCD_W, LCD_H}, OPAQUE), invalidatedRect(rect) { } void MainWindow::emptyTrash() { - for (auto window: trash) { + for (auto window : trash) { delete window; } trash.clear(); } -#if defined(HARDWARE_TOUCH) -void MainWindow::setTouchEnabled(bool enable) -{ - switch(touchEnableState) - { - case TouchOn: - if(!enable) - touchEnableState = TouchOff; - break; - case TouchOn_Touching: - if(!enable) - touchEnableState = TouchOnToOff_Touching; - break; - case TouchOnToOff_Touching: - if(enable) - touchEnableState = TouchOn_Touching; - break; - case TouchOff: - if(enable) - touchEnableState = TouchOn; - break; - case TouchOff_Touching: - if(enable) - touchEnableState = TouchOffToOn_Touching; - break; - case TouchOffToOn_Touching: - if(!enable) - touchEnableState = TouchOff_Touching; - break; - } -} -#endif -void MainWindow::invalidate(const rect_t & rect) +void MainWindow::invalidate(const rect_t& rect) { if (invalidatedRect.w) { auto left = limit(0, rect.left(), invalidatedRect.left()); auto right = limit(invalidatedRect.right(), rect.right(), LCD_W); auto top = limit(0, rect.top(), invalidatedRect.top()); - auto bottom = limit(rect.bottom(), invalidatedRect.bottom(), LCD_H); + auto bottom = + limit(rect.bottom(), invalidatedRect.bottom(), LCD_H); invalidatedRect = {left, top, right - left, bottom - top}; - } - else { + } else { invalidatedRect = rect; } } @@ -101,9 +62,10 @@ void MainWindow::run(bool trash) if (opaque) opaque->checkEvents(); if (trash) emptyTrash(); - + auto delta = ticksNow() - start; if (delta > 10 * SYSTEM_TICKS_1MS) { - TRACE_WINDOWS("MainWindow::run took %dms", (ticksNow() - start) / SYSTEM_TICKS_1MS); + TRACE_WINDOWS("MainWindow::run took %dms", + (ticksNow() - start) / SYSTEM_TICKS_1MS); } } diff --git a/radio/src/thirdparty/libopenui/src/mainwindow.h b/radio/src/thirdparty/libopenui/src/mainwindow.h index 045614379..58e0f1fe8 100644 --- a/radio/src/thirdparty/libopenui/src/mainwindow.h +++ b/radio/src/thirdparty/libopenui/src/mainwindow.h @@ -62,17 +62,6 @@ class MainWindow: public Window } #endif -#if defined(HARDWARE_TOUCH) - void setTouchEnabled(bool enable); - bool touchEventOccured() { - if(_touchEventOccured) - { - _touchEventOccured = false; - return true; - } - return false; - } -#endif void setActiveScreen() { lv_scr_load(lvobj); @@ -99,18 +88,4 @@ class MainWindow: public Window static void emptyTrash(); rect_t invalidatedRect; const char * shutdown = nullptr; -#if defined(HARDWARE_TOUCH) - bool lastTouchState = false; - bool _touchEventOccured = false; - - enum TouchEnableState { - TouchOn, - TouchOn_Touching, - TouchOnToOff_Touching, - TouchOff, - TouchOff_Touching, - TouchOffToOn_Touching - }; - TouchEnableState touchEnableState = TouchOn; -#endif }; From 015c4795ca92c801bc34503ab95eb2da028cbc0f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 12 Jun 2022 17:29:09 +0200 Subject: [PATCH 655/714] Padding adjustments & dialog autosize --- radio/src/thirdparty/libopenui/src/dialog.cpp | 24 +++++++++++++------ radio/src/thirdparty/libopenui/src/dialog.h | 2 ++ .../src/thirdparty/libopenui/src/flexlayout.h | 3 +-- radio/src/thirdparty/libopenui/src/form.cpp | 3 ++- radio/src/thirdparty/libopenui/src/form.h | 2 +- .../thirdparty/libopenui/src/modal_window.h | 2 +- radio/src/thirdparty/libopenui/src/window.cpp | 1 - 7 files changed, 24 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 066fffafd..21d1f143a 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -21,16 +21,26 @@ #include "mainwindow.h" #include "theme.h" -static rect_t _get_form_rect(const rect_t& rect) -{ - return {0, POPUP_HEADER_HEIGHT, rect.w, - coord_t(rect.h - POPUP_HEADER_HEIGHT)}; -} - DialogWindowContent::DialogWindowContent(Dialog* parent, const rect_t& rect) : ModalWindowContent(parent, rect), - form(this, _get_form_rect(rect), FORM_NO_BORDER) + form(this, rect_t{}) { + form.setFlexLayout(); + + auto form_obj = form.getLvObj(); + lv_obj_set_style_pad_all(form_obj, lv_dpx(8), 0); + + lv_coord_t max_height = LCD_H * 0.8; + lv_obj_set_style_max_height(form_obj, max_height, 0); +} + +void DialogWindowContent::setTitle(const std::string& text) +{ + ModalWindowContent::setTitle(text); + if (title) { + lv_coord_t title_h = lv_obj_get_height(title); + lv_obj_set_y(form.getLvObj(), title_h); + } } void DialogWindowContent::deleteLater(bool detach, bool trash) diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 0678fc11b..146554a95 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -31,6 +31,8 @@ class DialogWindowContent : public ModalWindowContent public: DialogWindowContent(Dialog* parent, const rect_t& rect); + void setTitle(const std::string& text) override; + void deleteLater(bool detach = true, bool trash = true) override; void updateSize() override; diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h index abe39bcbb..fe5f3ed59 100644 --- a/radio/src/thirdparty/libopenui/src/flexlayout.h +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -47,9 +47,8 @@ class FlexGridLayout // padding lv_obj_set_style_pad_all(obj, padding, LV_PART_MAIN); - lv_obj_set_style_pad_left(obj, padding + PAGE_PADDING, LV_PART_MAIN); - lv_obj_set_style_pad_right(obj, padding + PAGE_PADDING, LV_PART_MAIN); lv_obj_set_style_pad_row(obj, padding, LV_PART_MAIN); + lv_obj_set_style_pad_column(obj, padding, LV_PART_MAIN); // layout lv_obj_set_layout(obj, LV_LAYOUT_GRID); diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index fa74c251f..6fc96559e 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -115,9 +115,10 @@ void FormGroup::setFlexLayout(lv_flex_flow_t flow, lv_coord_t padding) lv_obj_set_height(lvobj, LV_SIZE_CONTENT); } -FormGroup::Line* FormGroup::newLine(FlexGridLayout* layout) +FormGroup::Line* FormGroup::newLine(FlexGridLayout* layout, lv_coord_t left_padding) { if (layout) layout->resetPos(); auto lv_line = window_create(lvobj); + if (left_padding) lv_obj_set_style_pad_left(lv_line, left_padding, 0); return new Line(this, lv_line, layout); } diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index c6fd3066d..13732a80f 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -101,7 +101,7 @@ class FormGroup : public Window #endif void setFlexLayout(lv_flex_flow_t flow = LV_FLEX_FLOW_COLUMN, lv_coord_t padding = 0); - Line* newLine(FlexGridLayout* layout = nullptr); + Line* newLine(FlexGridLayout* layout = nullptr, lv_coord_t left_padding = 0); }; class FormWindow : public FormGroup diff --git a/radio/src/thirdparty/libopenui/src/modal_window.h b/radio/src/thirdparty/libopenui/src/modal_window.h index 09864cf87..63487a2f0 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.h +++ b/radio/src/thirdparty/libopenui/src/modal_window.h @@ -51,7 +51,7 @@ class ModalWindowContent : public Window std::string getName() const override { return "ModalWindowContent"; } #endif - void setTitle(const std::string& text); + virtual void setTitle(const std::string& text); std::string getTitle() const; void onClicked() override; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index ff8996ae7..42f9fe1ca 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -480,7 +480,6 @@ void Window::bringToTop() if (lvobj) lv_obj_move_foreground(lvobj); } -void lvglPushEncoderEvent(event_t& evt); void Window::checkEvents() { auto copy = children; From 85cdf709a0457424a0361e2cb96afa0f0e4a3767 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 14 Jun 2022 09:39:56 +0200 Subject: [PATCH 656/714] window: add padding methods --- radio/src/thirdparty/libopenui/src/window.cpp | 35 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/window.h | 9 +++++ 2 files changed, 44 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 42f9fe1ca..4f1b04008 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -358,6 +358,41 @@ bool Window::hasFocus() const return lvobj && lv_obj_has_state(lvobj, LV_STATE_FOCUSED); } +void Window::padLeft(coord_t pad) +{ + lv_obj_set_style_pad_left(lvobj, pad, 0); +} + +void Window::padRight(coord_t pad) +{ + lv_obj_set_style_pad_right(lvobj, pad, 0); +} + +void Window::padTop(coord_t pad) +{ + lv_obj_set_style_pad_top(lvobj, pad, 0); +} + +void Window::padBottom(coord_t pad) +{ + lv_obj_set_style_pad_bottom(lvobj, pad, 0); +} + +void Window::padAll(coord_t pad) +{ + lv_obj_set_style_pad_all(lvobj, pad, 0); +} + +void Window::padRow(coord_t pad) +{ + lv_obj_set_style_pad_row(lvobj, pad, 0); +} + +void Window::padColumn(coord_t pad) +{ + lv_obj_set_style_pad_column(lvobj, pad, 0); +} + bool Window::hasOpaqueRect(const rect_t & testRect) const { if (!rect.contains(testRect)) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 0aafc982b..cf08e83e8 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -195,6 +195,15 @@ class Window return rect; } + void padLeft(coord_t pad); + void padRight(coord_t pad); + void padTop(coord_t pad); + void padBottom(coord_t pad); + void padAll(coord_t pad); + + void padRow(coord_t pad); + void padColumn(coord_t pad); + bool isChildVisible(const Window * window) const; bool isChildFullSize(const Window * window) const; From 732cb1ba19a98f2f5ff58edbfeba2a7f38df105c Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Fri, 17 Jun 2022 06:02:01 -0400 Subject: [PATCH 657/714] fix table behavor on touch release (Highlight) --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 + radio/src/thirdparty/libopenui/src/table.cpp | 3 +- .../libopenui/src/widgets/edgetx_table.c | 48 +++++++++++++++++++ .../libopenui/src/widgets/edgetx_table.h | 16 +++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c create mode 100644 radio/src/thirdparty/libopenui/src/widgets/edgetx_table.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index f55e96b2e..50d910e00 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -38,6 +38,7 @@ set(LIBOPENUI_SRC widgets/window_base.c widgets/simple_btn.c widgets/field_edit.c + widgets/edgetx_table.c ) foreach(LIBOPENUI_FILE ${LIBOPENUI_SRC}) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index d83488ae4..cf249a564 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -18,6 +18,7 @@ */ #include "table.h" +#include "widgets/edgetx_table.h" void TableField::event_cb(lv_event_t *e) { @@ -135,7 +136,7 @@ void TableField::draw_end(lv_event_t* e) } TableField::TableField(Window* parent, const rect_t& rect, WindowFlags windowFlags) : - Window(parent, rect, windowFlags, 0, lv_table_create) + Window(parent, rect, windowFlags, 0, edgetx_table_create) { lv_obj_add_event_cb(lvobj, TableField::event_cb, LV_EVENT_ALL, nullptr); } diff --git a/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c new file mode 100644 index 000000000..22040de91 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c @@ -0,0 +1,48 @@ + +#include "edgetx_table.h" + + +#define MY_CLASS &lv_edgetx_table_class + +static void edgetx_table_event(const lv_obj_class_t * class_p, lv_event_t * e); + +const lv_obj_class_t lv_edgetx_table_class = { + .constructor_cb = NULL, + .destructor_cb = NULL, + .event_cb = edgetx_table_event, + .width_def = LV_SIZE_CONTENT, + .height_def = LV_SIZE_CONTENT, + .base_class = &lv_table_class, + .editable = LV_OBJ_CLASS_EDITABLE_TRUE, + .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, + .instance_size = 0, +}; + + +lv_obj_t * edgetx_table_create(lv_obj_t * parent) +{ + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} + + +static void edgetx_table_event(const lv_obj_class_t * class_p, lv_event_t * e) +{ + lv_res_t res; + + lv_table_t * obj = (lv_table_t *) lv_event_get_target(e); + uint16_t col = obj->col_act; + uint16_t row = obj->row_act; + + /*Call the ancestor's event handler*/ + res = lv_obj_event_base(MY_CLASS, e); + if(res != LV_RES_OK) return; + + lv_event_code_t code = lv_event_get_code(e); + if (code == LV_EVENT_RELEASED) { + obj->col_act = col; + obj->row_act = row; + lv_obj_invalidate((lv_obj_t *)obj); + } +} diff --git a/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.h b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.h new file mode 100644 index 000000000..613753471 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.h @@ -0,0 +1,16 @@ +#ifndef EDGETX_TABLE_H +#define EDGETX_TABLE_H +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const lv_obj_class_t lv_edgetx_table_class; +lv_obj_t* edgetx_table_create(lv_obj_t* parent); + +#ifdef __cplusplus +}; +#endif + +#endif From 314e5dc53899980eac5970826d097b15075cd756 Mon Sep 17 00:00:00 2001 From: kevin koenig Date: Fri, 17 Jun 2022 15:23:42 -0400 Subject: [PATCH 658/714] rename table --- radio/src/thirdparty/libopenui/src/table.cpp | 2 +- .../thirdparty/libopenui/src/widgets/edgetx_table.c | 12 ++++++------ .../thirdparty/libopenui/src/widgets/edgetx_table.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index cf249a564..6d0f93f62 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -136,7 +136,7 @@ void TableField::draw_end(lv_event_t* e) } TableField::TableField(Window* parent, const rect_t& rect, WindowFlags windowFlags) : - Window(parent, rect, windowFlags, 0, edgetx_table_create) + Window(parent, rect, windowFlags, 0, table_create) { lv_obj_add_event_cb(lvobj, TableField::event_cb, LV_EVENT_ALL, nullptr); } diff --git a/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c index 22040de91..7afbfe62c 100644 --- a/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c +++ b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c @@ -2,14 +2,14 @@ #include "edgetx_table.h" -#define MY_CLASS &lv_edgetx_table_class +#define MY_CLASS &table_class -static void edgetx_table_event(const lv_obj_class_t * class_p, lv_event_t * e); +static void table_event(const lv_obj_class_t * class_p, lv_event_t * e); -const lv_obj_class_t lv_edgetx_table_class = { +const lv_obj_class_t table_class = { .constructor_cb = NULL, .destructor_cb = NULL, - .event_cb = edgetx_table_event, + .event_cb = table_event, .width_def = LV_SIZE_CONTENT, .height_def = LV_SIZE_CONTENT, .base_class = &lv_table_class, @@ -19,7 +19,7 @@ const lv_obj_class_t lv_edgetx_table_class = { }; -lv_obj_t * edgetx_table_create(lv_obj_t * parent) +lv_obj_t * table_create(lv_obj_t * parent) { lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); lv_obj_class_init_obj(obj); @@ -27,7 +27,7 @@ lv_obj_t * edgetx_table_create(lv_obj_t * parent) } -static void edgetx_table_event(const lv_obj_class_t * class_p, lv_event_t * e) +static void table_event(const lv_obj_class_t * class_p, lv_event_t * e) { lv_res_t res; diff --git a/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.h b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.h index 613753471..c1c7146e5 100644 --- a/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.h +++ b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.h @@ -6,8 +6,8 @@ extern "C" { #endif -extern const lv_obj_class_t lv_edgetx_table_class; -lv_obj_t* edgetx_table_create(lv_obj_t* parent); +extern const lv_obj_class_t table_class; +lv_obj_t* table_create(lv_obj_t* parent); #ifdef __cplusplus }; From e10545b53f88a2a83b055e1f8b9d752ee2834206 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 18 Jun 2022 09:23:17 +0200 Subject: [PATCH 659/714] flex grid layout: copy constructor --- radio/src/thirdparty/libopenui/src/flexlayout.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h index fe5f3ed59..0c36c125d 100644 --- a/radio/src/thirdparty/libopenui/src/flexlayout.h +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -41,6 +41,11 @@ class FlexGridLayout { } + FlexGridLayout(const FlexGridLayout& g) : + col_dsc(g.col_dsc), row_dsc(g.row_dsc), padding(g.padding) + { + } + void apply(Window* w) { apply(w->getLvObj()); } void apply(lv_obj_t* obj) { From ff6a0bca560e744e993a22fd41756509c25b17d3 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 19 Jun 2022 10:24:05 +0200 Subject: [PATCH 660/714] number edit: allow null _getValue --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index a10494777..521e7a75c 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -50,9 +50,11 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, lv_textarea_set_password_mode(lvobj, false); lv_textarea_set_one_line(lvobj, true); - auto value = _getValue(); - this->setValue(value); - lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this); + if (_getValue) { + auto value = _getValue(); + this->setValue(value); + lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this); + } } void NumberEdit::onEvent(event_t event) From 42b98362c3189efb350d4283cc2e5896309295b7 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 19 Jun 2022 10:24:30 +0200 Subject: [PATCH 661/714] checkbox: allow setting _getValue later --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 2 +- radio/src/thirdparty/libopenui/src/checkbox.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 8bee639ae..e2dc3dbd1 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -46,7 +46,7 @@ CheckBox::CheckBox(Window* parent, const rect_t& rect, lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_DISABLED), LV_PART_MAIN); lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_DISABLED), LV_STATE_FOCUSED); - if (_getValue()) lv_obj_add_state(lvobj, LV_STATE_CHECKED); + if (_getValue && _getValue()) lv_obj_add_state(lvobj, LV_STATE_CHECKED); lv_obj_add_event_cb(lvobj, checkbox_event_handler, LV_EVENT_VALUE_CHANGED, this); diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 12c4210cb..46b260890 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -46,6 +46,11 @@ class CheckBox : public FormField _setValue = std::move(handler); } + void setGetValueHandler(std::function handler) + { + _getValue = std::move(handler); + } + protected: std::string label; std::function _getValue; From 0d8628f0cee2863f0cc530b1d0537e10caa4eb56 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 19 Jun 2022 10:25:16 +0200 Subject: [PATCH 662/714] form line: add constructor without lvobj --- radio/src/thirdparty/libopenui/src/form.cpp | 6 ++++++ radio/src/thirdparty/libopenui/src/form.h | 1 + 2 files changed, 7 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 6fc96559e..ba4575df4 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -73,6 +73,12 @@ FormGroup::Line::Line(Window* parent, lv_obj_t* obj, FlexGridLayout* layout) : construct(); } +FormGroup::Line::Line(Window* parent, FlexGridLayout* layout) : + Window(parent, rect_t{}), layout(layout) +{ + construct(); +} + void FormGroup::Line::construct() { // forward scroll and focus diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 13732a80f..29ddf3e6a 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -88,6 +88,7 @@ class FormGroup : public Window public: Line(Window *parent, lv_obj_t *obj, FlexGridLayout *layout = nullptr); + Line(Window *parent, FlexGridLayout *layout = nullptr); protected: void addChild(Window *window) override; From 0255bd5f3cb1d6cbd2313ff10b2b6d3f744eb14f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 19 Jun 2022 10:25:50 +0200 Subject: [PATCH 663/714] base number edit: allow setting _getValue later and update from current value --- .../thirdparty/libopenui/src/basenumberedit.h | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index edbc65a6b..5de8eda7b 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -132,6 +132,11 @@ class BaseNumberEdit: public FormField _setValue = std::move(handler); } + void setGetValueHandler(std::function handler) + { + _getValue = std::move(handler); + } + int32_t getValue() const { return (editMode && !instantChange) ? currentValue : _getValue(); @@ -157,6 +162,24 @@ class BaseNumberEdit: public FormField setValue(currentValue); } + void update() + { + auto newValue = _getValue(); + if (newValue != currentValue) { + currentValue = newValue; + } + if (lvobj != nullptr) { + std::string str; + if (displayFunction != nullptr) { + str = displayFunction(currentValue); + } else { + str = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(), + suffix.c_str()); + } + lv_textarea_set_text(lvobj, str.c_str()); + } + } + virtual void paint(BitmapBuffer* dc) override {}; protected: From f4916a3c4f0e4ee5d0ff2ae807d698242132ddff Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 20 Jun 2022 08:03:23 +0200 Subject: [PATCH 664/714] FormGroup::Line: add setLayout() --- radio/src/thirdparty/libopenui/src/form.cpp | 6 ++++++ radio/src/thirdparty/libopenui/src/form.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index ba4575df4..2c8d1a01a 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -93,6 +93,12 @@ void FormGroup::Line::construct() lv_obj_set_width(lvobj, lv_pct(100)); } +void FormGroup::Line::setLayout(FlexGridLayout *l) +{ + layout = l; + if (layout) layout->apply(lvobj); +} + void FormGroup::Line::addChild(Window* window) { Window::addChild(window); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 29ddf3e6a..2a35e045a 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -90,6 +90,8 @@ class FormGroup : public Window Line(Window *parent, lv_obj_t *obj, FlexGridLayout *layout = nullptr); Line(Window *parent, FlexGridLayout *layout = nullptr); + void setLayout(FlexGridLayout *l); + protected: void addChild(Window *window) override; }; From 0d8b36a0cf3ee6bc0c4db523b781d796dd90483c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 20 Jun 2022 16:28:58 +0200 Subject: [PATCH 665/714] virtual BaseNumberEdit::update() --- radio/src/thirdparty/libopenui/src/basenumberedit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 5de8eda7b..976185d18 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -162,7 +162,7 @@ class BaseNumberEdit: public FormField setValue(currentValue); } - void update() + virtual void update() { auto newValue = _getValue(); if (newValue != currentValue) { From 6b1ca16c7f99e6f08d3bbe6c8821f6329e42ecb8 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 3 Jul 2022 07:16:46 +0200 Subject: [PATCH 666/714] progress: limit value to 100 --- radio/src/thirdparty/libopenui/src/progress.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/progress.cpp b/radio/src/thirdparty/libopenui/src/progress.cpp index d17cd9a07..4d572e83e 100644 --- a/radio/src/thirdparty/libopenui/src/progress.cpp +++ b/radio/src/thirdparty/libopenui/src/progress.cpp @@ -22,5 +22,6 @@ void Progress::paint(BitmapBuffer * dc) { - theme->drawProgressBar(dc, 0, 0, width(), height(), value, 100); + auto val = value > 100 ? 100 : value; + theme->drawProgressBar(dc, 0, 0, width(), height(), val, 100); } From 87ef19a9de5c9463ff9427568d2222076c8102f6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 4 Jul 2022 07:54:39 +0200 Subject: [PATCH 667/714] static: fix format warnings --- radio/src/thirdparty/libopenui/src/static.cpp | 72 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/static.h | 15 +--- 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 01cadec64..d64c6466a 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -74,3 +74,75 @@ void StaticText::setBackgroundColor(LcdFlags color) lv_color_t c = makeLvColor(bgColor); lv_obj_set_style_bg_color(lvobj, c, LV_PART_MAIN); } + +template <> +void DynamicNumber::updateText() +{ + if (lvobj) { + const char* p = prefix ? prefix : ""; + const char* s = suffix ? suffix : ""; + if ((textFlags & PREC2) == PREC2) { + lv_label_set_text_fmt(lvobj, "%s%" PRIu32 ".%02" PRIu32 "%s", p, + value / 100, value % 100, s); + } else if (textFlags & PREC1) { + lv_label_set_text_fmt(lvobj, "%s%" PRIu32 ".%01" PRIu32 "%s", p, + value / 10, value % 10, s); + } else { + lv_label_set_text_fmt(lvobj, "%s%" PRIu32 "%s", p, value, s); + } + } +} + +template <> +void DynamicNumber::updateText() +{ + if (lvobj) { + const char* p = prefix ? prefix : ""; + const char* s = suffix ? suffix : ""; + if ((textFlags & PREC2) == PREC2) { + lv_label_set_text_fmt(lvobj, "%s%" PRId32 ".%02" PRId32 "%s", p, + value / 100, value % 100, s); + } else if (textFlags & PREC1) { + lv_label_set_text_fmt(lvobj, "%s%" PRId32 ".%01" PRId32 "%s", p, + value / 10, value % 10, s); + } else { + lv_label_set_text_fmt(lvobj, "%s%" PRId32 "%s", p, value, s); + } + } +} + +template <> +void DynamicNumber::updateText() +{ + if (lvobj) { + const char* p = prefix ? prefix : ""; + const char* s = suffix ? suffix : ""; + if ((textFlags & PREC2) == PREC2) { + lv_label_set_text_fmt(lvobj, "%s%" PRIu16 ".%02" PRIu16 "%s", p, + value / 100, value % 100, s); + } else if (textFlags & PREC1) { + lv_label_set_text_fmt(lvobj, "%s%" PRIu16 ".%01" PRIu16 "%s", p, + value / 10, value % 10, s); + } else { + lv_label_set_text_fmt(lvobj, "%s%" PRIu16 "%s", p, value, s); + } + } +} + +template <> +void DynamicNumber::updateText() +{ + if (lvobj) { + const char* p = prefix ? prefix : ""; + const char* s = suffix ? suffix : ""; + if ((textFlags & PREC2) == PREC2) { + lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%02" PRId16 "%s", p, + value / 100, value % 100, s); + } else if (textFlags & PREC1) { + lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%01" PRId16 "%s", p, + value / 10, value % 10, s); + } else { + lv_label_set_text_fmt(lvobj, "%s%" PRId16 "%s", p, value, s); + } + } +} diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index b408d18af..18516fb34 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -181,18 +181,5 @@ class DynamicNumber : public StaticText const char *prefix; const char *suffix; - void updateText() - { - if (lvobj) { - const char *p = prefix ? prefix : ""; - const char *s = suffix ? suffix : ""; - if ((textFlags & PREC2) == PREC2) { - lv_label_set_text_fmt(lvobj, "%s%d.%02d%s", p, value / 100, value % 100, s); - } else if (textFlags & PREC1) { - lv_label_set_text_fmt(lvobj, "%s%d.%01d%s", p, value / 10, value % 10, s); - } else { - lv_label_set_text_fmt(lvobj, "%s%d%s", p, value, s); - } - } - } + void updateText(); }; From d76c6415c95b93def1e60e54af7384f16059663a Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 24 Jul 2022 16:26:58 +0200 Subject: [PATCH 668/714] fix compilation with -DDEBUG_WINDOWS --- radio/src/thirdparty/libopenui/src/dialog.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 21d1f143a..a42fb906f 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -57,7 +57,7 @@ void DialogWindowContent::updateSize() } #if defined(DEBUG_WINDOWS) -std::string DialogWindowContent::getName() const override +std::string DialogWindowContent::getName() const { return "DialogWindowContent"; } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 4f1b04008..74d8e5b6e 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -248,7 +248,7 @@ std::string Window::getIndentString() const return result; } -std::string Window::getWindowDebugString(const char *name = nullptr) const +std::string Window::getWindowDebugString(const char *name) const { return getName() + (name ? std::string(" [") + name + "] " : " ") + getRectString(); From 4d34becd2ca1d80368c637de02fc2bd44b88aa4d Mon Sep 17 00:00:00 2001 From: gagarinlg Date: Sun, 24 Jul 2022 16:41:29 +0200 Subject: [PATCH 669/714] fix: the check to prevent out of screenbuffer drawing did not work --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 5d11c0678..1172b18aa 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -403,7 +403,7 @@ class BitmapBuffer: public BitmapBufferBase inline void drawPixel(pixel_t * p, pixel_t value) { - if (data && (data <= p || p < data_end)) { + if (data && (data <= p && p < data_end)) { *p = value; } #if defined(DEBUG) From eb6f0e56cdfd6e8540d237a729e50f2b200f3f3e Mon Sep 17 00:00:00 2001 From: Cliff Date: Fri, 29 Jul 2022 20:53:46 -0600 Subject: [PATCH 670/714] Menu multiselect with checkmark (#54) * Menu multiselect with checkmark * missed brace --- radio/src/thirdparty/libopenui/src/menu.cpp | 45 ++++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 2c17b86b1..befce2fca 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -36,23 +36,36 @@ void MenuBody::onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* d void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) { lv_obj_t* icon = lines[row].getIcon(); - if (!icon) return; + if (icon) { + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + + lv_img_dsc_t* img = lv_canvas_get_img(icon); + lv_area_t coords; - lv_draw_img_dsc_t img_dsc; - lv_draw_img_dsc_init(&img_dsc); - - lv_img_dsc_t* img = lv_canvas_get_img(icon); - lv_area_t coords; + lv_coord_t area_h = lv_area_get_height(dsc->draw_area); - lv_coord_t area_h = lv_area_get_height(dsc->draw_area); + lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS); + coords.x1 = dsc->draw_area->x1 + cell_left; + coords.x2 = coords.x1 + img->header.w - 1; + coords.y1 = dsc->draw_area->y1 + (area_h - img->header.h) / 2; + coords.y2 = coords.y1 + img->header.h - 1; + + lv_draw_img(dsc->draw_ctx, &img_dsc, &coords, img); + } - lv_coord_t cell_left = lv_obj_get_style_pad_left(lvobj, LV_PART_ITEMS); - coords.x1 = dsc->draw_area->x1 + cell_left; - coords.x2 = coords.x1 + img->header.w - 1; - coords.y1 = dsc->draw_area->y1 + (area_h - img->header.h) / 2; - coords.y2 = coords.y1 + img->header.h - 1; - - lv_draw_img(dsc->draw_ctx, &img_dsc, &coords, img); + if(lines[row].isChecked != nullptr && + lines[row].isChecked()) { + lv_area_t coords; + lv_coord_t area_h = lv_area_get_height(dsc->draw_area); + lv_coord_t cell_right = lv_obj_get_style_pad_right(lvobj, LV_PART_ITEMS); + lv_coord_t font_h = getFontHeight(FONT(STD)); + coords.x1 = dsc->draw_area->x2 - cell_right - font_h; + coords.x2 = coords.x1 + font_h; + coords.y1 = dsc->draw_area->y1 + (area_h - font_h) / 2; + coords.y2 = coords.y1 + font_h - 1; + lv_draw_label(dsc->draw_ctx, dsc->label_dsc, &coords, LV_SYMBOL_OK, nullptr); + } } MenuBody::MenuBody(Window * parent, const rect_t & rect): @@ -109,8 +122,10 @@ void MenuBody::onPress(size_t index) if (menu->multiple) { if (selectedIndex == (int)index) lines[index].onPress(); - else + else { setIndex(index); + lines[index].onPress(); + } } else { // delete menu first to avoid // focus issues with onPress() From 9c4a0d78a953abfa9e470cef70bcdfb404be136a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 1 Aug 2022 11:22:00 +0200 Subject: [PATCH 671/714] number edit: install call back regardless of _getValue --- radio/src/thirdparty/libopenui/src/basenumberedit.h | 9 ++++++--- radio/src/thirdparty/libopenui/src/numberedit.cpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 976185d18..375b1ecb6 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -139,7 +139,9 @@ class BaseNumberEdit: public FormField int32_t getValue() const { - return (editMode && !instantChange) ? currentValue : _getValue(); + return (editMode && !instantChange) ? currentValue + : _getValue != nullptr ? _getValue() + : 0; } void setEditMode(bool newEditMode) override @@ -148,10 +150,10 @@ class BaseNumberEdit: public FormField FormField::setEditMode(newEditMode); if (!instantChange) { if (!previousEditMode && newEditMode) { - currentValue = _getValue(); + if (_getValue != nullptr) currentValue = _getValue(); } else if (previousEditMode && !newEditMode) { - _setValue(currentValue); + if (_setValue != nullptr) _setValue(currentValue); } } } @@ -164,6 +166,7 @@ class BaseNumberEdit: public FormField virtual void update() { + if (_getValue == nullptr) return; auto newValue = _getValue(); if (newValue != currentValue) { currentValue = newValue; diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 521e7a75c..d2f8e092c 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -49,11 +49,11 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_textarea_set_password_mode(lvobj, false); lv_textarea_set_one_line(lvobj, true); + lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this); if (_getValue) { auto value = _getValue(); this->setValue(value); - lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this); } } From 9767b4baf12ee2ffcbc0b251631af686e2f3494b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 6 Aug 2022 08:43:35 +0200 Subject: [PATCH 672/714] textedit: removed useless code and added update() --- .../src/thirdparty/libopenui/src/textedit.cpp | 218 +++++++++--------- radio/src/thirdparty/libopenui/src/textedit.h | 9 +- 2 files changed, 115 insertions(+), 112 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 88c4f70f9..d2a4e0ab9 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -55,18 +55,22 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_textarea_set_password_mode(lvobj, false); lv_textarea_set_one_line(lvobj, true); - // value may not be null-terminated - std::string txt(value, length); - lv_textarea_set_text(lvobj, txt.c_str()); - lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); if (width() == 0) { lv_obj_set_width(lvobj, LV_DPI_DEF); } + + update(); } +void TextEdit::update() +{ + // value may not be null-terminated + std::string txt(value, length); + lv_textarea_set_text(lvobj, txt.c_str()); +} void TextEdit::setCursorPos(int cursorPos) { @@ -91,123 +95,123 @@ void TextEdit::onEvent(event_t event) if (IS_VIRTUAL_KEY_EVENT(event)) { uint8_t c = event & 0xFF; - if (c == (uint8_t)KEYBOARD_ENTER[0]) { + if (c == LV_KEY_ENTER) { changeEnd(); } } -#if defined(HARDWARE_KEYS) - if (editMode) { - int c = value[cursorPos]; - int v = c; +// #if defined(HARDWARE_KEYS) +// if (editMode) { +// int c = value[cursorPos]; +// int v = c; - switch (event) { - case EVT_ROTARY_RIGHT: - for (int i = 0; i < ROTARY_ENCODER_SPEED(); i++) { - v = getNextChar(v); - } - break; +// switch (event) { +// case EVT_ROTARY_RIGHT: +// for (int i = 0; i < ROTARY_ENCODER_SPEED(); i++) { +// v = getNextChar(v); +// } +// break; - case EVT_ROTARY_LEFT: - for (int i = 0; i < ROTARY_ENCODER_SPEED(); i++) { - v = getPreviousChar(v); - } - break; +// case EVT_ROTARY_LEFT: +// for (int i = 0; i < ROTARY_ENCODER_SPEED(); i++) { +// v = getPreviousChar(v); +// } +// break; - case EVT_KEY_BREAK(KEY_LEFT): - if (cursorPos > 0) { - setCursorPos(cursorPos - 1); - } - break; +// case EVT_KEY_BREAK(KEY_LEFT): +// if (cursorPos > 0) { +// setCursorPos(cursorPos - 1); +// } +// break; - case EVT_KEY_BREAK(KEY_RIGHT): - if (cursorPos < length - 1 && value[cursorPos + 1] != '\0') { - setCursorPos(cursorPos + 1); - } - break; +// case EVT_KEY_BREAK(KEY_RIGHT): +// if (cursorPos < length - 1 && value[cursorPos + 1] != '\0') { +// setCursorPos(cursorPos + 1); +// } +// break; - case EVT_KEY_BREAK(KEY_ENTER): - if (cursorPos < length - 1) { - if (value[cursorPos] == '\0') { - value[cursorPos] = ' '; - changed = true; - } - cursorPos++; - if (value[cursorPos] == '\0') { - value[cursorPos] = ' '; - changed = true; - } - lv_textarea_set_text(lvobj, value); - setCursorPos(cursorPos); - } - else { - changeEnd(); - FormField::onEvent(event); - } - break; +// case EVT_KEY_BREAK(KEY_ENTER): +// if (cursorPos < length - 1) { +// if (value[cursorPos] == '\0') { +// value[cursorPos] = ' '; +// changed = true; +// } +// cursorPos++; +// if (value[cursorPos] == '\0') { +// value[cursorPos] = ' '; +// changed = true; +// } +// update(); +// setCursorPos(cursorPos); +// } +// else { +// changeEnd(); +// FormField::onEvent(event); +// } +// break; - case EVT_KEY_LONG(KEY_ENTER): - { - killEvents(event); - auto menu = new Menu(this); - menu->setTitle(STR_EDIT); -#if defined(CLIPBOARD) - menu->addLine(STR_COPY, [=] { - clipboard.write((uint8_t *)value, length, Clipboard::CONTENT_TEXT); - }); - if (clipboard.contentType == Clipboard::CONTENT_TEXT) { - menu->addLine(STR_PASTE, [=] { - clipboard.read((uint8_t *)value, length); - changeEnd(true); - }); - } -#endif - menu->addLine(STR_CLEAR, [=] { - memset(value, 0, length); - changeEnd(true); - }); - break; - } +// case EVT_KEY_LONG(KEY_ENTER): +// { +// killEvents(event); +// auto menu = new Menu(this); +// menu->setTitle(STR_EDIT); +// #if defined(CLIPBOARD) +// menu->addLine(STR_COPY, [=] { +// clipboard.write((uint8_t *)value, length, Clipboard::CONTENT_TEXT); +// }); +// if (clipboard.contentType == Clipboard::CONTENT_TEXT) { +// menu->addLine(STR_PASTE, [=] { +// clipboard.read((uint8_t *)value, length); +// changeEnd(true); +// }); +// } +// #endif +// menu->addLine(STR_CLEAR, [=] { +// memset(value, 0, length); +// changeEnd(true); +// }); +// break; +// } - case EVT_KEY_BREAK(KEY_UP): - v = toggleCase(v); - break; +// case EVT_KEY_BREAK(KEY_UP): +// v = toggleCase(v); +// break; - case EVT_KEY_LONG(KEY_LEFT): - case EVT_KEY_LONG(KEY_RIGHT): - v = toggleCase(v); - if (event == EVT_KEY_LONG(KEY_LEFT)) { - killEvents(KEY_LEFT); - } - break; +// case EVT_KEY_LONG(KEY_LEFT): +// case EVT_KEY_LONG(KEY_RIGHT): +// v = toggleCase(v); +// if (event == EVT_KEY_LONG(KEY_LEFT)) { +// killEvents(KEY_LEFT); +// } +// break; - case EVT_KEY_BREAK(KEY_PGDN): - if (cursorPos < length) { - memmove(&value[cursorPos], &value[cursorPos + 1], length - cursorPos - 1); - value[length - 1] = '\0'; - changed = true; - if (cursorPos > 0 && value[cursorPos] == '\0') { - cursorPos = cursorPos - 1; - } - lv_textarea_set_text(lvobj, value); - setCursorPos(cursorPos); - } - break; - } +// case EVT_KEY_BREAK(KEY_PGDN): +// if (cursorPos < length) { +// memmove(&value[cursorPos], &value[cursorPos + 1], length - cursorPos - 1); +// value[length - 1] = '\0'; +// changed = true; +// if (cursorPos > 0 && value[cursorPos] == '\0') { +// cursorPos = cursorPos - 1; +// } +// update(); +// setCursorPos(cursorPos); +// } +// break; +// } - if (c != v) { - // TRACE("value[%d] = %d", cursorPos, v); - value[cursorPos] = v; - lv_textarea_set_text(lvobj, value); - setCursorPos(cursorPos); - changed = true; - } - } - else { - FormField::onEvent(event); - setCursorPos(0); - } -#endif +// if (c != v) { +// // TRACE("value[%d] = %d", cursorPos, v); +// value[cursorPos] = v; +// update(); +// setCursorPos(cursorPos); +// changed = true; +// } +// } +// else { +// FormField::onEvent(event); +// setCursorPos(0); +// } +// #endif } void TextEdit::onClicked() diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 40f7ae963..4c7ecce12 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -27,7 +27,8 @@ class TextEdit : public FormField { friend class TextKeyboard; public: - TextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0, const char * _extra_chars = nullptr); + TextEdit(Window* parent, const rect_t& rect, char* value, uint8_t length, + LcdFlags windowFlags = 0, const char* _extra_chars = nullptr); #if defined(DEBUG_WINDOWS) std::string getName() const override @@ -46,13 +47,11 @@ class TextEdit : public FormField { return value; } + void update(); + void onEvent(event_t event) override; void onClicked() override; -// #if defined(HARDWARE_TOUCH) -// bool onTouchEnd(coord_t x, coord_t y) override; -// #endif - void onFocusLost() override; virtual void paint(BitmapBuffer* dc) override {}; From 7ab53f757f7f50ed8811dde2c9697bb2d092a6cf Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 8 Aug 2022 11:13:21 +0200 Subject: [PATCH 673/714] window: clear lvobj before deleting it --- .../src/thirdparty/libopenui/src/keyboard_base.cpp | 13 ++++++++++--- radio/src/thirdparty/libopenui/src/keyboard_base.h | 1 + radio/src/thirdparty/libopenui/src/window.cpp | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 3017c3208..38a2c2f0a 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -104,13 +104,19 @@ void Keyboard::clearField() if (field) { // restore field's group auto obj = field->getLvObj(); - lv_obj_remove_event_cb(obj, field_focus_leave); - lv_group_t* g = (lv_group_t*)lv_obj_get_group(obj); - if (g) _assign_lv_group(g); + if (obj) { + lv_obj_remove_event_cb(obj, field_focus_leave); + } field->setEditMode(false); field->changeEnd(); field = nullptr; + + if (fieldGroup) { + _assign_lv_group(fieldGroup); + lv_group_set_editing(fieldGroup, false); + fieldGroup = nullptr; + } } } @@ -165,6 +171,7 @@ void Keyboard::setField(FormField* newField) _assign_lv_group(group); field = newField; + fieldGroup = (lv_group_t*)lv_obj_get_group(obj); } } } diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.h b/radio/src/thirdparty/libopenui/src/keyboard_base.h index 8a599dc45..c67689106 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.h @@ -40,6 +40,7 @@ class Keyboard : public Window FormField* field = nullptr; Window* fieldContainer = nullptr; + lv_group_t* fieldGroup = nullptr; lv_coord_t scroll_pos = 0; void setField(FormField* newField); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 74d8e5b6e..119dff4d3 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -328,8 +328,9 @@ void Window::deleteLater(bool detach, bool trash) deleteChildren(); if (lvobj != nullptr) { - lv_obj_del(lvobj); + auto obj = lvobj; lvobj = nullptr; + lv_obj_del(obj); } } From 84f402569f3221df1da23bcd3c1111bc1244b226 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 8 Aug 2022 12:41:52 +0200 Subject: [PATCH 674/714] textedit: simplified implementation --- .../libopenui/src/keyboard_base.cpp | 1 - .../src/thirdparty/libopenui/src/textedit.cpp | 143 ++---------------- radio/src/thirdparty/libopenui/src/textedit.h | 124 +++------------ 3 files changed, 29 insertions(+), 239 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp index 38a2c2f0a..889ddb0d8 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_base.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_base.cpp @@ -27,7 +27,6 @@ static void keyboard_event_cb(lv_event_t * e) { auto code = lv_event_get_code(e); if (code == LV_EVENT_READY) { - pushEvent(EVT_VIRTUAL_KEY('\n')); Keyboard::hide(); } else if (code == LV_EVENT_CANCEL) { Keyboard::hide(); diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index d2a4e0ab9..279af7a1e 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -41,15 +41,12 @@ #endif TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, - uint8_t length, LcdFlags windowFlags, - const char *_extra_chars) : + uint8_t length, LcdFlags windowFlags) : FormField(parent, rect, windowFlags, 0, field_edit_create), value(value), length(length) { - extra_chars = (_extra_chars) ? _extra_chars : extra_chars_default; - // properties lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_textarea_set_password_mode(lvobj, false); @@ -72,13 +69,6 @@ void TextEdit::update() lv_textarea_set_text(lvobj, txt.c_str()); } -void TextEdit::setCursorPos(int cursorPos) -{ - this->cursorPos = cursorPos; - if (lvobj != nullptr) - lv_textarea_set_cursor_pos(lvobj, cursorPos); -} - void TextEdit::trim() { for (int i = length - 1; i >= 0; i--) { @@ -89,134 +79,25 @@ void TextEdit::trim() } } -void TextEdit::onEvent(event_t event) +void TextEdit::changeEnd(bool forceChanged) { - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), event); + if (lvobj == nullptr) return; - if (IS_VIRTUAL_KEY_EVENT(event)) { - uint8_t c = event & 0xFF; - if (c == LV_KEY_ENTER) { - changeEnd(); - } + bool changed = false; + auto text = lv_textarea_get_text(lvobj); + if (strncmp(value, text, length) != 0) { + changed = true; } -// #if defined(HARDWARE_KEYS) -// if (editMode) { -// int c = value[cursorPos]; -// int v = c; - -// switch (event) { -// case EVT_ROTARY_RIGHT: -// for (int i = 0; i < ROTARY_ENCODER_SPEED(); i++) { -// v = getNextChar(v); -// } -// break; - -// case EVT_ROTARY_LEFT: -// for (int i = 0; i < ROTARY_ENCODER_SPEED(); i++) { -// v = getPreviousChar(v); -// } -// break; - -// case EVT_KEY_BREAK(KEY_LEFT): -// if (cursorPos > 0) { -// setCursorPos(cursorPos - 1); -// } -// break; - -// case EVT_KEY_BREAK(KEY_RIGHT): -// if (cursorPos < length - 1 && value[cursorPos + 1] != '\0') { -// setCursorPos(cursorPos + 1); -// } -// break; - -// case EVT_KEY_BREAK(KEY_ENTER): -// if (cursorPos < length - 1) { -// if (value[cursorPos] == '\0') { -// value[cursorPos] = ' '; -// changed = true; -// } -// cursorPos++; -// if (value[cursorPos] == '\0') { -// value[cursorPos] = ' '; -// changed = true; -// } -// update(); -// setCursorPos(cursorPos); -// } -// else { -// changeEnd(); -// FormField::onEvent(event); -// } -// break; - -// case EVT_KEY_LONG(KEY_ENTER): -// { -// killEvents(event); -// auto menu = new Menu(this); -// menu->setTitle(STR_EDIT); -// #if defined(CLIPBOARD) -// menu->addLine(STR_COPY, [=] { -// clipboard.write((uint8_t *)value, length, Clipboard::CONTENT_TEXT); -// }); -// if (clipboard.contentType == Clipboard::CONTENT_TEXT) { -// menu->addLine(STR_PASTE, [=] { -// clipboard.read((uint8_t *)value, length); -// changeEnd(true); -// }); -// } -// #endif -// menu->addLine(STR_CLEAR, [=] { -// memset(value, 0, length); -// changeEnd(true); -// }); -// break; -// } - -// case EVT_KEY_BREAK(KEY_UP): -// v = toggleCase(v); -// break; - -// case EVT_KEY_LONG(KEY_LEFT): -// case EVT_KEY_LONG(KEY_RIGHT): -// v = toggleCase(v); -// if (event == EVT_KEY_LONG(KEY_LEFT)) { -// killEvents(KEY_LEFT); -// } -// break; - -// case EVT_KEY_BREAK(KEY_PGDN): -// if (cursorPos < length) { -// memmove(&value[cursorPos], &value[cursorPos + 1], length - cursorPos - 1); -// value[length - 1] = '\0'; -// changed = true; -// if (cursorPos > 0 && value[cursorPos] == '\0') { -// cursorPos = cursorPos - 1; -// } -// update(); -// setCursorPos(cursorPos); -// } -// break; -// } - -// if (c != v) { -// // TRACE("value[%d] = %d", cursorPos, v); -// value[cursorPos] = v; -// update(); -// setCursorPos(cursorPos); -// changed = true; -// } -// } -// else { -// FormField::onEvent(event); -// setCursorPos(0); -// } -// #endif + if (changed || forceChanged) { + strncpy(value, text, length); + trim(); + FormField::changeEnd(); + } } void TextEdit::onClicked() { - cursorPos = lv_textarea_get_cursor_pos(lvobj); TextKeyboard::show(this); } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 4c7ecce12..6a3876172 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -21,121 +21,31 @@ #include "form.h" -const char extra_chars_default[] = ":;<=>"; +class TextEdit : public FormField +{ -class TextEdit : public FormField { - friend class TextKeyboard; - - public: - TextEdit(Window* parent, const rect_t& rect, char* value, uint8_t length, - LcdFlags windowFlags = 0, const char* _extra_chars = nullptr); + public: + TextEdit(Window* parent, const rect_t& rect, char* value, uint8_t length, + LcdFlags windowFlags = 0); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "TextEdit"; - } + std::string getName() const override { return "TextEdit"; } #endif - uint8_t getMaxLength() const - { - return length; - } + uint8_t getMaxLength() const { return length; } + char* getData() const { return value; } - char * getData() const - { - return value; - } + void update(); - void update(); + protected: + static void event_cb(lv_event_t* e); - void onEvent(event_t event) override; - void onClicked() override; + char* value; + uint8_t length; - void onFocusLost() override; + void trim(); - virtual void paint(BitmapBuffer* dc) override {}; - - void setCursorPos(int cursorPos); - - protected: - char * value; - bool changed = false; - uint8_t length; - const char * extra_chars; - uint8_t cursorPos = 0; - - void trim(); - - void changeEnd(bool forceChanged = false) override - { - if (lvobj != nullptr) - strncpy(value, lv_textarea_get_text(lvobj), length); - - setCursorPos(0); - if (changed || forceChanged) { - changed = false; - trim(); - if (changeHandler) { - changeHandler(); - } - } - } - - uint8_t getNextChar(uint8_t c) - { - if (c == ' ' || c == '\0') - return 'A'; - else if (c >= 'A' && c < 'Z') - return c + 1; - else if (c == 'Z') - return 'a'; - else if (c >= 'a' && c < 'z') - return c + 1; - else if (c == 'z') - return '0'; - else if (c >= '0' && c < '9') - return c + 1; - else if (c == '9') - return extra_chars[0]; - else { - for (uint8_t n = 0; n < strlen(extra_chars) - 1; n++) - if (c == extra_chars[n]) return extra_chars[n + 1]; - return ' '; - } - } - - uint8_t getPreviousChar(uint8_t c) - { - if (c == ' ' || c == '\0') - return extra_chars[strlen(extra_chars) - 1]; - else if (c == 'A') - return ' '; - else if (c > 'A' && c <= 'Z') - return c - 1; - else if (c == 'a') - return 'Z'; - else if (c > 'a' && c <= 'z') - return c - 1; - else if (c == '0') - return 'z'; - else if (c > '0' && c <= '9') - return c - 1; - else { - for (uint8_t n = 1; n < strlen(extra_chars); n++) - if (c == extra_chars[n]) return extra_chars[n - 1]; - return '9'; - } - } - - static uint8_t toggleCase(uint8_t c) - { - if (c >= 'A' && c <= 'Z') - return c + 32; // tolower - else if (c >= 'a' && c <= 'z') - return c - 32; // toupper - else - return c; - } + void changeEnd(bool forceChanged = false) override; + void onClicked() override; + void onFocusLost() override; }; - From e227c18113a2c363eeb1fff62a1aea8561925bea Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 19 Aug 2022 05:30:53 +0200 Subject: [PATCH 675/714] Remove `onKeyPress()` and unused `getWindowsEvent()` * deleted onKeyPress() due to PR https://github.com/EdgeTX/edgetx/pull/2201 * deleted reference to getWindowEvent(), no longer used due to PR https://github.com/EdgeTX/edgetx/pull/2201 * cosmetics - remove commented out getWindowsEvent() --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 1 - radio/src/thirdparty/libopenui/src/choice.cpp | 1 - radio/src/thirdparty/libopenui/src/filechoice.cpp | 1 - radio/src/thirdparty/libopenui/src/libopenui_depends.h | 2 -- radio/src/thirdparty/libopenui/src/menu.cpp | 1 - radio/src/thirdparty/libopenui/src/modal_window.cpp | 1 - radio/src/thirdparty/libopenui/src/numberedit.cpp | 2 -- radio/src/thirdparty/libopenui/src/table.cpp | 2 -- 8 files changed, 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index e2dc3dbd1..0f3480945 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -26,7 +26,6 @@ static void checkbox_event_handler(lv_event_t* e) lv_obj_t* target = lv_event_get_target(e); CheckBox* cb = (CheckBox*)lv_obj_get_user_data(target); - onKeyPress(); if (cb) cb->setValue(lv_obj_get_state(target) & LV_STATE_CHECKED); } diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 85b13299f..6c72a91b7 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -201,7 +201,6 @@ void Choice::setValue(int val) void Choice::onClicked() { - onKeyPress(); openMenu(); } diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index eb6ef25c6..ea5506bf5 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -134,6 +134,5 @@ bool FileChoice::openMenu() void FileChoice::onClicked() { - onKeyPress(); openMenu(); } diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index 4a9c2800d..414fccf85 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -27,10 +27,8 @@ uint16_t* lcdGetScratchBuffer(); void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h); void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, uint16_t y, const uint8_t * src, uint16_t srcw, uint16_t srch, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h, uint16_t bg_color); -void onKeyPress(); void onKeyError(); void killEvents(event_t event); -event_t getWindowEvent(); // // These colors MUST be defined diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index befce2fca..52a627683 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -118,7 +118,6 @@ void MenuBody::onPress(size_t index) { Menu *menu = getParentMenu(); if (index < lines.size()) { - onKeyPress(); if (menu->multiple) { if (selectedIndex == (int)index) lines[index].onPress(); diff --git a/radio/src/thirdparty/libopenui/src/modal_window.cpp b/radio/src/thirdparty/libopenui/src/modal_window.cpp index 055c4ec0d..2640fea4c 100644 --- a/radio/src/thirdparty/libopenui/src/modal_window.cpp +++ b/radio/src/thirdparty/libopenui/src/modal_window.cpp @@ -42,7 +42,6 @@ void ModalWindow::deleteLater(bool detach, bool trash) void ModalWindow::onClicked() { if (closeWhenClickOutside) { - onKeyPress(); deleteLater(); } } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index d2f8e092c..7b1452af6 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -71,7 +71,6 @@ void NumberEdit::onEvent(event_t event) } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); if (value <= vmax) { setValue(value); - onKeyPress(); } else { setValue(vmax); @@ -87,7 +86,6 @@ void NumberEdit::onEvent(event_t event) } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); if (value >= vmin) { setValue(value); - onKeyPress(); } else { setValue(vmin); diff --git a/radio/src/thirdparty/libopenui/src/table.cpp b/radio/src/thirdparty/libopenui/src/table.cpp index 6d0f93f62..7d21c60f0 100644 --- a/radio/src/thirdparty/libopenui/src/table.cpp +++ b/radio/src/thirdparty/libopenui/src/table.cpp @@ -259,11 +259,9 @@ void TableField::selectNext(int16_t dir) void TableField::onEvent(event_t event) { if (event == EVT_ROTARY_RIGHT) { - onKeyPress(); selectNext(1); } else if (event == EVT_ROTARY_LEFT) { - onKeyPress(); selectNext(-1); } else { From 10e1982b2356d3a1ff35620f1c7dc03cf0d6d808 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 22 Aug 2022 09:04:27 +0200 Subject: [PATCH 676/714] Form field: re-implemented enable/disable --- radio/src/thirdparty/libopenui/src/form.cpp | 14 ++++++++++++++ radio/src/thirdparty/libopenui/src/form.h | 10 ++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 2c8d1a01a..0cc4af75e 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -48,6 +48,20 @@ void FormField::setEditMode(bool newEditMode) invalidate(); } +void FormField::enable(bool value) +{ + if (value) { + lv_obj_clear_state(lvobj, LV_STATE_DISABLED); + } else { + lv_obj_add_state(lvobj, LV_STATE_DISABLED); + } +} + +bool FormField::isEnabled() const +{ + return !lv_obj_has_state(lvobj, LV_STATE_DISABLED); +} + void FormField::onClicked() { lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 2a35e045a..c6d8fd3af 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -52,14 +52,8 @@ class FormField : public Window inline bool isEditMode() const { return editMode; } virtual void setEditMode(bool newEditMode); - // TODO: replace with LVGL flags - void enable(bool value = true) - { - enabled = value; - invalidate(); - } - - bool isEnabled() const { return enabled; } + bool isEnabled() const; + void enable(bool value = true); void disable() { enable(false); } void onFocusLost() override From 18563cbc54656bd59b87d839f679b05742170c39 Mon Sep 17 00:00:00 2001 From: Cliff Date: Wed, 24 Aug 2022 02:09:53 -0600 Subject: [PATCH 677/714] Check for invalid index in `MenuBody::onDrawEnd`(#57) Method can get called before lines has data. --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 52a627683..0d9098671 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -35,6 +35,8 @@ void MenuBody::onDrawBegin(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* d void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) { + if(row >= lines.size()) return; + lv_obj_t* icon = lines[row].getIcon(); if (icon) { lv_draw_img_dsc_t img_dsc; From 4b05632c844b3c83c5b12b3606fd30e29f99bd65 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 28 Aug 2022 10:36:03 +0200 Subject: [PATCH 678/714] Remove ColorEdit (#60) --- .../thirdparty/libopenui/src/CMakeLists.txt | 1 - .../thirdparty/libopenui/src/coloredit.cpp | 101 ------------------ .../src/thirdparty/libopenui/src/coloredit.h | 41 ------- 3 files changed, 143 deletions(-) delete mode 100644 radio/src/thirdparty/libopenui/src/coloredit.cpp delete mode 100644 radio/src/thirdparty/libopenui/src/coloredit.h diff --git a/radio/src/thirdparty/libopenui/src/CMakeLists.txt b/radio/src/thirdparty/libopenui/src/CMakeLists.txt index 50d910e00..2d4891572 100644 --- a/radio/src/thirdparty/libopenui/src/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/src/CMakeLists.txt @@ -26,7 +26,6 @@ set(LIBOPENUI_SRC filechoice.cpp numberedit.cpp textedit.cpp - coloredit.cpp progress.cpp table.cpp modal_window.cpp diff --git a/radio/src/thirdparty/libopenui/src/coloredit.cpp b/radio/src/thirdparty/libopenui/src/coloredit.cpp deleted file mode 100644 index 36be41dde..000000000 --- a/radio/src/thirdparty/libopenui/src/coloredit.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "coloredit.h" -#include "numberedit.h" -#include "bitfield.h" -#include "dialog.h" - -#define PICKER_SIZE lv_colorwheel_class.width_def - -struct ColorWheelPopup : public Dialog { - ColorWheelPopup(Window* parent, lv_color_t color, - std::function setValue) : - Dialog(parent, std::string(), - rect_t{0, 0, PICKER_SIZE, - PICKER_SIZE + POPUP_HEADER_HEIGHT}) - { - auto lv_content = content->getLvObj(); - auto form = content->form.getLvObj(); - lv_obj_set_size(form, PICKER_SIZE, PICKER_SIZE); - - lv_obj_center(lv_content); - lv_obj_center(form); - - auto cw = lv_colorwheel_create(form, true); - lv_colorwheel_set_rgb(cw, color); - - auto wheel_size = 3 * PICKER_SIZE / 4; - lv_obj_set_style_arc_width(cw, wheel_size / 5, 0); - lv_obj_set_size(cw, wheel_size, wheel_size); - lv_obj_center(cw); - - content->updateSize(); - - setCloseWhenClickOutside(true); - setCloseHandler([=]() { - lv_color_t lv_color = lv_colorwheel_get_rgb(cw); - setValue(lv_color); - }); - } -}; - -static void colorwheel_popup(lv_event_t* e) -{ - auto edit = (ColorEdit*)lv_event_get_user_data(e); - if (edit) { - new ColorWheelPopup(edit, edit->getColor(), - [=](lv_color_t c) { edit->setColor(c); }); - } -} - -// based on LVGL default switch size -constexpr lv_coord_t COLOR_PAD_WIDTH = (4 * LV_DPI_DEF) / 10; -constexpr lv_coord_t COLOR_PAD_HEIGHT = (4 * LV_DPI_DEF) / 17; - -ColorEdit::ColorEdit(Window* parent, const rect_t& rect, - std::function getValue, - std::function setValue) : - FormField(parent, rect, 0, 0, lv_btn_create), - setValue(std::move(setValue)) -{ - auto value = getValue(); - color = lv_color_make(GET_RED(value), GET_GREEN(value), GET_BLUE(value)); - - lv_obj_set_style_bg_color(lvobj, color, LV_PART_MAIN); - lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); - - lv_obj_set_style_bg_color(lvobj, color, LV_STATE_FOCUS_KEY); - lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_STATE_FOCUS_KEY); - - lv_obj_set_size(lvobj, COLOR_PAD_WIDTH, COLOR_PAD_HEIGHT); - lv_obj_add_event_cb(lvobj, colorwheel_popup, LV_EVENT_CLICKED, this); -} - -void ColorEdit::setColor(lv_color_t c) -{ - color = c; - setValue(RGB_JOIN(c.ch.red, c.ch.green, c.ch.blue)); - - lv_obj_set_style_bg_color(lvobj, color, LV_PART_MAIN); - lv_obj_set_style_bg_color(lvobj, color, LV_STATE_FOCUS_KEY); - - // TRACE("### color: %d %d %d ###", c.ch.red, c.ch.green, c.ch.blue); - invalidate(); -} diff --git a/radio/src/thirdparty/libopenui/src/coloredit.h b/radio/src/thirdparty/libopenui/src/coloredit.h deleted file mode 100644 index 601fa490d..000000000 --- a/radio/src/thirdparty/libopenui/src/coloredit.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Source: - * https://github.com/opentx/libopenui - * - * This file is a part of libopenui library. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#pragma once - -#include "form.h" - -class ColorEdit : public FormField -{ - lv_color_t color; - std::function setValue; - -public: - - ColorEdit(Window* parent, const rect_t& rect, - std::function getValue, - std::function setValue = nullptr); - - void setColor(lv_color_t c); - lv_color_t getColor() const { return color; } - -#if defined(DEBUG_WINDOWS) - std::string getName() const override { return "ColorEdit"; } -#endif -}; From 199975b95cdaebb0039699ed023f0217dc1436ec Mon Sep 17 00:00:00 2001 From: David PHAM-VAN <1387855+DavBfr@users.noreply.github.com> Date: Mon, 5 Sep 2022 05:30:26 -0300 Subject: [PATCH 679/714] Add BitmapBuffer::to8bitMask (#63) --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 26 +++++++++++++++++++ .../thirdparty/libopenui/src/bitmapbuffer.h | 4 ++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index a95aea46a..bc1c5493e 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -917,6 +917,32 @@ void BitmapBuffer::drawAnnulusSector(coord_t x, coord_t y, coord_t internalRadiu } } +uint8_t * BitmapBuffer::to8bitMask(size_t* size) const { + *size = width() * height()+4; + uint8_t* mask = (uint8_t*)malloc(*size); + ((uint16_t*)mask)[0] = width(); + ((uint16_t*)mask)[1] = height(); + const pixel_t * p = getPixelPtrAbs(0, 0); + if (getFormat() == BMP_ARGB4444) { + auto ofs = 4u; + for (int i = width() * height(); i > 0; i--) { + ARGB_SPLIT(*p, a __attribute__((unused)), r, g, b); + auto v = ((uint32_t)r + (uint32_t)g + (uint32_t)b) / 3; + mask[ofs++] = ((OPACITY_MAX - v) << 4); + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } else { // BMP_RGB565 + auto ofs = 4u; + for (int i = width() * height(); i > 0; i--) { + auto v = ((GET_RED(*p) >> 1) + (GET_GREEN(*p) >> 2) + (GET_BLUE(*p) >> 1)) / 3; + mask[ofs++] = ((OPACITY_MAX - v) << 4); + MOVE_TO_NEXT_RIGHT_PIXEL(p); + } + } + + return mask; +} + void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offsetX, coord_t width) { if (!mask) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 1172b18aa..ccceec5f5 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -320,7 +320,9 @@ class BitmapBuffer: public BitmapBufferBase static BitmapBuffer * load8bitMaskOnBackground(const uint8_t * lbm, LcdFlags foreground, LcdFlags background); static uint8_t * loadFont(const uint8_t * lbm, int len, int& w, int& h); - + + uint8_t * to8bitMask(size_t* size) const; + void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, LcdFlags flags, coord_t offsetX = 0, coord_t width = 0); void drawMask(coord_t x, coord_t y, const BitmapBuffer * mask, const BitmapBuffer * srcBitmap, coord_t offsetX = 0, coord_t offsetY = 0, coord_t width = 0, coord_t height = 0); From 5174b58fb9d446aa4b172087202a71cdcc41812c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 5 Sep 2022 10:30:52 +0200 Subject: [PATCH 680/714] bitmap buffer: allow for forcing a destination pixel format (#61) Otherwise, 32bit images are converted automatically to ARGB4444, and 24bit image are converted to RGB565, thus leading to EdgeTX/edgetx#2261. --- .../thirdparty/libopenui/src/bitmapbuffer.cpp | 27 ++++++++++--------- .../thirdparty/libopenui/src/bitmapbuffer.h | 10 ++++--- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index bc1c5493e..7b0e97c03 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1257,14 +1257,14 @@ void drawSolidRect(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h //} // -BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename) +BitmapBuffer * BitmapBuffer::loadBitmap(const char * filename, BitmapFormats fmt) { //TRACE(" BitmapBuffer::loadBitmap(%s)", filename); const char * ext = getFileExtension(filename); if (ext && !strcmp(ext, ".bmp")) return load_bmp(filename); else - return load_stb(filename); + return load_stb(filename, fmt); } BitmapBuffer * BitmapBuffer::loadRamBitmap(const uint8_t * buffer, int len) @@ -1679,7 +1679,7 @@ const stbi_io_callbacks stbCallbacks = { stbc_eof }; -BitmapBuffer * BitmapBuffer::load_stb(const char * filename) +BitmapBuffer * BitmapBuffer::load_stb(const char * filename, BitmapFormats fmt) { //TRACE(" BitmapBuffer::load_stb(%s)", filename); @@ -1708,7 +1708,7 @@ BitmapBuffer * BitmapBuffer::load_stb(const char * filename) } //TRACE(" BitmapBuffer::load_stb()----Info File %s, %d, %d, %d/%d", filename, x, y, nn, n); - BitmapBuffer * bmp = convert_stb_bitmap(img, w, h, n); + BitmapBuffer * bmp = convert_stb_bitmap(img, w, h, n, fmt); stbi_image_free(img); return bmp; } @@ -1727,22 +1727,24 @@ BitmapBuffer * BitmapBuffer::load_stb_buffer(const uint8_t * buffer, int len) return bmp; } -BitmapBuffer * BitmapBuffer::convert_stb_bitmap(uint8_t * img, int w, int h, int n) +BitmapBuffer * BitmapBuffer::convert_stb_bitmap(uint8_t * img, int w, int h, int n, + BitmapFormats fmt) { // convert to RGB565 or ARGB4444 format - //TRACE(" BitmapBuffer::convert_stb_bitmap(%d)", n); - BitmapBuffer * bmp = new BitmapBuffer(n == 4 ? BMP_ARGB4444 : BMP_RGB565, w, h); + BitmapFormats dst_fmt = fmt; + if (dst_fmt == BMP_INVALID) { + dst_fmt = (n == 4 ? BMP_ARGB4444 : BMP_RGB565); + } + + BitmapBuffer * bmp = new BitmapBuffer(dst_fmt, w, h); if (bmp == nullptr) { TRACE("convert_stn_bitmap: malloc failed"); return nullptr; } -#if 0 // use Stb's "stbi__vertically_flip_on_load" instead of this? - DMABitmapConvert(bmp->data, img, w, h, n == 4 ? DMA2D_ARGB4444 : DMA2D_RGB565); -#else pixel_t * dest = bmp->getPixelPtrAbs(0, 0); const uint8_t * p = img; - if (n == 4) { + if (dst_fmt == BMP_ARGB4444) { for (int row = 0; row < h; ++row) { for (int col = 0; col < w; ++col) { *dest = ARGB(p[3], p[0], p[1], p[2]); @@ -1751,7 +1753,7 @@ BitmapBuffer * BitmapBuffer::convert_stb_bitmap(uint8_t * img, int w, int h, int } } } - else { // assume 3 bytes, packed in groups of 4, I guess + else { // assume 3 bytes, packed in groups of 4 for (int row = 0; row < h; ++row) { for (int col = 0; col < w; ++col) { *dest = RGB(p[0], p[1], p[2]); @@ -1760,7 +1762,6 @@ BitmapBuffer * BitmapBuffer::convert_stb_bitmap(uint8_t * img, int w, int h, int } } } -#endif return bmp; } diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index ccceec5f5..8a936dd6c 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -48,7 +48,8 @@ constexpr uint8_t STASHED = 0x33; enum BitmapFormats { - BMP_8BIT, + BMP_INVALID = -1, + BMP_8BIT = 0, BMP_RGB565, BMP_ARGB4444 }; @@ -309,7 +310,7 @@ class BitmapBuffer: public BitmapBufferBase void drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle); - static BitmapBuffer * loadBitmap(const char * filename); + static BitmapBuffer * loadBitmap(const char * filename, BitmapFormats fmt = BMP_INVALID); static BitmapBuffer * loadRamBitmap(const uint8_t * buffer, int len); static BitmapBuffer * loadMask(const char * filename); @@ -360,9 +361,10 @@ class BitmapBuffer: public BitmapBufferBase protected: static BitmapBuffer * load_bmp(const char * filename); - static BitmapBuffer * load_stb(const char * filename); + static BitmapBuffer * load_stb(const char * filename, BitmapFormats fmt = BMP_INVALID); static BitmapBuffer * load_stb_buffer(const uint8_t * buffer, int len); - static BitmapBuffer * convert_stb_bitmap(uint8_t * img, int w, int h, int n); + static BitmapBuffer * convert_stb_bitmap(uint8_t * img, int w, int h, int n, + BitmapFormats fmt = BMP_INVALID); inline bool applyClippingRect(coord_t & x, coord_t & y, coord_t & w, coord_t & h) const { From b4368fe120a2608cd6dbbe51a0dd708bf6b5faa9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 5 Sep 2022 10:31:10 +0200 Subject: [PATCH 681/714] checkbox: add update() method (#62) --- radio/src/thirdparty/libopenui/src/checkbox.cpp | 9 ++++++++- radio/src/thirdparty/libopenui/src/checkbox.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/checkbox.cpp b/radio/src/thirdparty/libopenui/src/checkbox.cpp index 0f3480945..708718c99 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.cpp +++ b/radio/src/thirdparty/libopenui/src/checkbox.cpp @@ -45,12 +45,19 @@ CheckBox::CheckBox(Window* parent, const rect_t& rect, lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_DISABLED), LV_PART_MAIN); lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_DISABLED), LV_STATE_FOCUSED); - if (_getValue && _getValue()) lv_obj_add_state(lvobj, LV_STATE_CHECKED); + update(); lv_obj_add_event_cb(lvobj, checkbox_event_handler, LV_EVENT_VALUE_CHANGED, this); } +void CheckBox::update() const +{ + if (!_getValue) return; + if (_getValue()) lv_obj_add_state(lvobj, LV_STATE_CHECKED); + else lv_obj_clear_state(lvobj, LV_STATE_CHECKED); +} + void CheckBox::onClicked() { // prevent FormField::onClicked() diff --git a/radio/src/thirdparty/libopenui/src/checkbox.h b/radio/src/thirdparty/libopenui/src/checkbox.h index 46b260890..7600c7225 100644 --- a/radio/src/thirdparty/libopenui/src/checkbox.h +++ b/radio/src/thirdparty/libopenui/src/checkbox.h @@ -51,6 +51,8 @@ class CheckBox : public FormField _getValue = std::move(handler); } + void update() const; + protected: std::string label; std::function _getValue; From c1f922ce1470557e73fee1517cb28f18d3f0b2bd Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 17 Sep 2022 09:03:55 +0200 Subject: [PATCH 682/714] chore(DynamicNumber): remove type argument warnings --- radio/src/thirdparty/libopenui/src/static.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index d64c6466a..59bd58bc4 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -119,10 +119,10 @@ void DynamicNumber::updateText() const char* s = suffix ? suffix : ""; if ((textFlags & PREC2) == PREC2) { lv_label_set_text_fmt(lvobj, "%s%" PRIu16 ".%02" PRIu16 "%s", p, - value / 100, value % 100, s); + (uint16_t)(value / 100), (uint16_t)(value % 100), s); } else if (textFlags & PREC1) { lv_label_set_text_fmt(lvobj, "%s%" PRIu16 ".%01" PRIu16 "%s", p, - value / 10, value % 10, s); + (uint16_t)(value / 10), (uint16_t)(value % 10), s); } else { lv_label_set_text_fmt(lvobj, "%s%" PRIu16 "%s", p, value, s); } @@ -137,10 +137,10 @@ void DynamicNumber::updateText() const char* s = suffix ? suffix : ""; if ((textFlags & PREC2) == PREC2) { lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%02" PRId16 "%s", p, - value / 100, value % 100, s); + (int16_t)(value / 100), (int16_t)(value % 100), s); } else if (textFlags & PREC1) { lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%01" PRId16 "%s", p, - value / 10, value % 10, s); + (int16_t)(value / 10), (int16_t)(value % 10), s); } else { lv_label_set_text_fmt(lvobj, "%s%" PRId16 "%s", p, value, s); } From 5b32c73690d0d9b7b83eab56c8be01fa9b2f8b6a Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Sun, 18 Sep 2022 11:21:24 +0200 Subject: [PATCH 683/714] fix: loading 32BPP BMPs --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 7b0e97c03..ecd5eb07f 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1542,11 +1542,11 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) return nullptr; } if (hasAlpha) { - *dst = ARGB(pixel & 0xff, (pixel >> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + *dst = ARGB((pixel >> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff, pixel & 0xff); } else { - if ((pixel & 0xff) == 0xff) { - *dst = RGB(pixel >> 24, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + if (((pixel >> 24) & 0xff) == 0xff) { + *dst = RGB(pixel >> 16, (pixel >> 8) & 0xff, pixel & 0xff); } else { hasAlpha = true; @@ -1555,7 +1555,7 @@ BitmapBuffer * BitmapBuffer::load_bmp(const char * filename) pixel_t tmp = *p; *p = ((tmp >> 1) & 0x0f) + (((tmp >> 7) & 0x0f) << 4) + (((tmp >> 12) & 0x0f) << 8); } - *dst = ARGB(pixel & 0xff, (pixel >> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff); + *dst = ARGB((pixel >> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff, pixel & 0xff); } } MOVE_TO_NEXT_RIGHT_PIXEL(dst); From 7682db2fb22a1d49debaa6a562b408f9fe61762b Mon Sep 17 00:00:00 2001 From: eshifri <46511024+eshifri@users.noreply.github.com> Date: Mon, 19 Sep 2022 20:29:55 -0700 Subject: [PATCH 684/714] fix: ChoiceEx long press support re-added LVGL style (#65) --- .../src/thirdparty/libopenui/src/choiceex.cpp | 93 +++---------------- radio/src/thirdparty/libopenui/src/choiceex.h | 53 +++++------ 2 files changed, 40 insertions(+), 106 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choiceex.cpp b/radio/src/thirdparty/libopenui/src/choiceex.cpp index f1986f904..bd7d7c113 100644 --- a/radio/src/thirdparty/libopenui/src/choiceex.cpp +++ b/radio/src/thirdparty/libopenui/src/choiceex.cpp @@ -20,18 +20,24 @@ */ #include "choiceex.h" -constexpr int LONG_PRESS_10MS = 40; - -extern inline tmr10ms_t getTicks() +static void localLongPressHandler(lv_event_t* e) { - return g_tmr10ms; + lv_eventData_t* ld = (lv_eventData_t*)lv_event_get_user_data(e); + ld->isLongPressed = true; + ld->lv_LongPressHandler(ld->userData); } - - -void ChoiceEx::setLongPressHandler(std::function handler) +void ChoiceEx::set_lv_LongPressHandler(lvHandler_t longPressHandler, void* data) { - longPressHandler = handler; + TRACE("longPressHandler=%p", longPressHandler); + + if (longPressHandler) { + longPressData.userData = data; + longPressData.lv_LongPressHandler = longPressHandler; + lv_obj_add_event_cb(lvobj, localLongPressHandler, LV_EVENT_LONG_PRESSED, + &longPressData); + lv_obj_add_event_cb(lvobj, ClickHandler, LV_EVENT_CLICKED, this); + } } ChoiceEx::ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, @@ -40,74 +46,5 @@ ChoiceEx::ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, WindowFlags windowFlags) : Choice(parent, rect, vmin, vmax, getValue, setValue, windowFlags) { -#if defined(HARDWARE_TOUCH) - duration10ms = 0; -#endif + longPressData.isLongPressed = false; } - -#if defined(HARDWARE_KEYS) -void ChoiceEx::onEvent(event_t event) -{ - if (event == EVT_KEY_LONG(KEY_ENTER)) { - if (longPressHandler) { - killEvents(event); - longPressHandler(event); - return; - } - } - - Choice::onEvent(event); -} -#endif - -#if defined(HARDWARE_TOUCH) -bool ChoiceEx::isLongPress() -{ - unsigned int curTimer = getTicks(); - return (!longPressed && duration10ms != 0 && curTimer - duration10ms > LONG_PRESS_10MS); -} - -void ChoiceEx::checkEvents(void) -{ - event_t event = getEvent(); - - if (isLongPress()) { - if (!longPressed && longPressHandler) { - longPressHandler(event); - killEvents(event); - duration10ms = 0; - longPressed = true; - return; - } - } - - if (hasFocus()) - onEvent(event); - else - pushEvent(event); -} - -bool ChoiceEx::onTouchStart(coord_t x, coord_t y) -{ - if (!longPressed && duration10ms == 0) { - duration10ms = getTicks(); - } - - return Choice::onTouchStart(x, y); -} - - -bool ChoiceEx::onTouchEnd(coord_t x, coord_t y) -{ - if (longPressed) { - longPressed = false; - return false; - } - - duration10ms = 0; - return Choice::onTouchEnd(x,y); -} -#endif - - - diff --git a/radio/src/thirdparty/libopenui/src/choiceex.h b/radio/src/thirdparty/libopenui/src/choiceex.h index 4d1348153..dcd21b750 100644 --- a/radio/src/thirdparty/libopenui/src/choiceex.h +++ b/radio/src/thirdparty/libopenui/src/choiceex.h @@ -22,41 +22,38 @@ #define _CHOICEEX_H_ #include "choice.h" +typedef void (*lvHandler_t)(void*); +typedef struct { + void* userData; + bool isLongPressed; + lvHandler_t lv_LongPressHandler; +} lv_eventData_t; + class ChoiceEx : public Choice { - public: - ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, int16_t vmax, - std::function getValue, - std::function setValue = nullptr, - WindowFlags windowFlags = 0); + public: + ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, int16_t vmax, + std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0); - void setLongPressHandler(std::function handler); + void set_lv_LongPressHandler(lvHandler_t longPressHandler, void* data); -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override; -#endif + lv_eventData_t longPressData; -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; - bool onTouchStart(coord_t x, coord_t y) override; - void checkEvents(void) override; - bool isLongPress(); -#endif + void onClicked() override + { + if (!longPressData.isLongPressed) Choice::onClicked(); + } -#if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "ChoceEx"; - } -#endif + protected: + std::function longPressHandler = nullptr; -protected: - std::function longPressHandler = nullptr; - -#if defined(HARDWARE_TOUCH) - uint32_t duration10ms; - bool longPressed = false; -#endif + static void ClickHandler(lv_event_t* e) + { + ChoiceEx* ch = (ChoiceEx*)lv_event_get_user_data(e); + if (ch) ch->longPressData.isLongPressed = false; + } }; #endif From 2cb1e6fce7fe834c75aae7cd80157f2dc77d6230 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 25 Sep 2022 09:09:53 +0200 Subject: [PATCH 685/714] dialog: prevent key events from being transmitted to parent --- radio/src/thirdparty/libopenui/src/dialog.cpp | 6 ++++++ radio/src/thirdparty/libopenui/src/dialog.h | 1 + 2 files changed, 7 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index a42fb906f..0ed6ca0b8 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -74,3 +74,9 @@ void Dialog::onCancel() { deleteLater(); } + +void Dialog::onEvent(event_t event) +{ + // block key events + (void)event; +} diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 146554a95..80a67bfe4 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -53,4 +53,5 @@ class Dialog : public ModalWindow DialogWindowContent* content; void onCancel() override; + void onEvent(event_t event) override; }; From 8ad593594b3a98b525f57c7ca52429ef035167f6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 26 Sep 2022 01:53:08 +0200 Subject: [PATCH 686/714] rotary encoder velocity: use a custom LVGL flag to signal support (#66) --- radio/src/thirdparty/libopenui/src/libopenui_defines.h | 1 + radio/src/thirdparty/libopenui/src/menu.cpp | 3 +++ radio/src/thirdparty/libopenui/src/numberedit.cpp | 3 +++ 3 files changed, 7 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 94ddacdd5..e81dad983 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -86,3 +86,4 @@ #define COLOR_VAL(flags) ((flags) >> 16u) #define COLOR_MASK(flags) ((flags) & 0xFFFF0000u) +#define LV_OBJ_FLAG_ENCODER_ACCEL LV_OBJ_FLAG_USER_1 diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 0d9098671..a8dadf402 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -73,6 +73,9 @@ void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc MenuBody::MenuBody(Window * parent, const rect_t & rect): TableField(parent, rect) { + // Allow encoder acceleration + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL); + setColumnCount(1); setColumnWidth(0, rect.w); diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 7b1452af6..0f4507c11 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -45,6 +45,9 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, std::move(setValue), windowFlags, textFlags, field_edit_create) { + // Allow encoder acceleration + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL); + // properties lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_textarea_set_password_mode(lvobj, false); From 01d7036e7827fe8590356e3535739e4353fc06b1 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 27 Sep 2022 04:11:45 +0200 Subject: [PATCH 687/714] TextButton: rely on LVGL theme for text color (#67) --- radio/src/thirdparty/libopenui/src/button.cpp | 6 ++---- radio/src/thirdparty/libopenui/src/button.h | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 366dcfe0d..438fc1cff 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -102,12 +102,10 @@ void Button::long_pressed(lv_event_t* e) TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, std::function pressHandler, - WindowFlags windowFlags, LcdFlags textFlags) : - Button(parent, rect, std::move(pressHandler), windowFlags, textFlags, - lv_btn_create), + WindowFlags windowFlags) : + Button(parent, rect, std::move(pressHandler), windowFlags, 0, lv_btn_create), text(std::move(text)) { - setTextFlags(textFlags | COLOR_THEME_PRIMARY1); update_checked_flag(lvobj, windowFlags); label = lv_label_create(lvobj); diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 0a5aa103c..89ae019fe 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -78,8 +78,7 @@ class TextButton: public Button public: TextButton(Window* parent, const rect_t& rect, std::string text, std::function pressHandler = nullptr, - WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE, - LcdFlags textFlags = 0); + WindowFlags windowFlags = BUTTON_BACKGROUND | OPAQUE); #if defined(DEBUG_WINDOWS) std::string getName() const override From 7ba7127699cd75138bcffa776347eda9196140c7 Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Sat, 1 Oct 2022 03:26:18 +0200 Subject: [PATCH 688/714] fix(gui): also horiz center pop up menus when they have a toolbar (#68) --- radio/src/thirdparty/libopenui/src/menu.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index a8dadf402..19d6adcdd 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -248,6 +248,7 @@ Menu::Menu(Window * parent, bool multiple): void Menu::setToolbar(Window * window) { toolbar = window; + updatePosition(); toolbar->setLeft(content->left() - toolbar->width()); toolbar->setTop(content->top()); toolbar->setHeight(content->height()); @@ -257,8 +258,10 @@ void Menu::updatePosition() { coord_t height = content->body.getContentHeight(); - if (!toolbar) { - // there is no navigation bar at the left, we may center the window on screen + if (toolbar) { + content->setLeft((LCD_W - content->width() + toolbar->width())/2); + } else { + content->setLeft((LCD_W - content->width())/2); auto headerHeight = content->getHeaderHeight(); auto bodyHeight = min(height, MENUS_MAX_HEIGHT - headerHeight); content->setTop((LCD_H - headerHeight - bodyHeight) / 2); From bb447ea49398af3c79c6c0fbbcc37044011cd5e9 Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Sat, 1 Oct 2022 03:27:06 +0200 Subject: [PATCH 689/714] fix: font height changes with LVGL through `getFontHeightCondensed` (#69) --- radio/src/thirdparty/libopenui/src/font.h | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/font.h b/radio/src/thirdparty/libopenui/src/font.h index 97e99346e..ca16383f5 100644 --- a/radio/src/thirdparty/libopenui/src/font.h +++ b/radio/src/thirdparty/libopenui/src/font.h @@ -24,5 +24,6 @@ const lv_font_t* getFont(LcdFlags flags); uint8_t getFontHeight(LcdFlags flags); +uint8_t getFontHeightCondensed(LcdFlags flags); int getTextWidth(const char * s, int len = 0, LcdFlags flags = 0); From fa59524ebdfdd89996eb45c86bd421cbe341dc1a Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Sat, 1 Oct 2022 13:23:18 +0200 Subject: [PATCH 690/714] fix(gui): Outline, more padding, wider buttons for menu toolbars (#70) --- radio/src/thirdparty/libopenui/src/menu.cpp | 15 +++++++++++-- radio/src/thirdparty/libopenui/src/menu.h | 2 ++ .../thirdparty/libopenui/src/menutoolbar.cpp | 21 +++++++++++-------- .../thirdparty/libopenui/src/menutoolbar.h | 2 ++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 19d6adcdd..d80c033f9 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -207,8 +207,8 @@ void MenuBody::onEvent(event_t event) else { TableField::onEvent(event); } -} #endif +} void MenuBody::onCancel() { @@ -254,12 +254,22 @@ void Menu::setToolbar(Window * window) toolbar->setHeight(content->height()); } +void Menu::setOutline(Window* obj) +{ + lv_obj_set_style_outline_width(obj->getLvObj(), 4, LV_PART_MAIN); + lv_obj_set_style_outline_color(obj->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN); + lv_obj_set_style_outline_opa(obj->getLvObj(), LV_OPA_100, LV_PART_MAIN); + lv_obj_set_style_outline_pad(obj->getLvObj(), 1, LV_PART_MAIN); + +} + void Menu::updatePosition() { coord_t height = content->body.getContentHeight(); if (toolbar) { content->setLeft((LCD_W - content->width() + toolbar->width())/2); + setOutline(toolbar); } else { content->setLeft((LCD_W - content->width())/2); auto headerHeight = content->getHeaderHeight(); @@ -267,8 +277,9 @@ void Menu::updatePosition() content->setTop((LCD_H - headerHeight - bodyHeight) / 2); content->setHeight(headerHeight + bodyHeight); content->body.setTop(headerHeight); - content->body.setHeight(bodyHeight); + content->body.setHeight(bodyHeight); } + setOutline(content); } void Menu::setTitle(std::string text) diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index f838187b3..c724f54fe 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -221,6 +221,8 @@ class Menu: public ModalWindow Window * toolbar = nullptr; std::function waitHandler; void updatePosition(); + + void setOutline(Window* obj); }; Menu * MenuBody::getParentMenu() diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 536dd37e4..c1320160d 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -31,17 +31,18 @@ MenuToolbarButton::MenuToolbarButton(Window* parent, const rect_t& rect, void MenuToolbarButton::paint(BitmapBuffer * dc) { + lv_coord_t width = lv_obj_get_content_width(lvobj); if (checked()) { dc->drawSolidFilledRect( MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_PADDING, - MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, + width - 2 * MENUS_TOOLBAR_BUTTON_PADDING, MENUS_TOOLBAR_BUTTON_WIDTH - 2 * MENUS_TOOLBAR_BUTTON_PADDING, COLOR_THEME_FOCUS); - dc->drawText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, - picto, CENTERED | COLOR_THEME_PRIMARY2); + dc->drawText(width / 2, (rect.h - getFontHeight(FONT(L))) / 2 + 2, + picto, FONT(L) | CENTERED | COLOR_THEME_PRIMARY2); } else { - dc->drawText(rect.w / 2, (rect.h - getFontHeight(FONT(STD))) / 2 + 1, - picto, CENTERED | COLOR_THEME_PRIMARY1); + dc->drawText(width / 2, (rect.h - getFontHeight(FONT(L))) / 2 + 2, + picto, FONT(L) | CENTERED | COLOR_THEME_PRIMARY1); } } @@ -83,10 +84,12 @@ void MenuToolbar::onEvent(event_t event) } } -static rect_t _get_button_rect(size_t buttons) +rect_t MenuToolbar::getButtonRect(size_t buttons) { - coord_t y = buttons * MENUS_TOOLBAR_BUTTON_WIDTH; - return {0, y, MENUS_TOOLBAR_BUTTON_WIDTH, MENUS_TOOLBAR_BUTTON_WIDTH}; + coord_t y = buttons * (MENUS_TOOLBAR_BUTTON_WIDTH + MENUS_TOOLBAR_BUTTON_PADDING); + coord_t width = std::max(MENUS_TOOLBAR_BUTTON_WIDTH, lv_obj_get_content_width(lvobj)); + return {0, y, LV_PCT(100), MENUS_TOOLBAR_BUTTON_WIDTH}; + } bool MenuToolbar::filterMenu(MenuToolbarButton* btn, int16_t filtermin, @@ -131,7 +134,7 @@ void MenuToolbar::addButton(const char* picto, int16_t filtermin, getFirstAvailable(filtermin, filtermax, choice->isValueAvailable) == 0) return; - rect_t r = _get_button_rect(children.size()); + rect_t r = getButtonRect(children.size()); auto button = new MenuToolbarButton(this, r, picto); button->setPressHandler( diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index cbec1830d..38ffda3a0 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -52,4 +52,6 @@ class MenuToolbar : public Window void addButton(const char* picto, int16_t filtermin, int16_t filtermax); bool filterMenu(MenuToolbarButton* btn, int16_t filtermin, int16_t filtermax); + + rect_t getButtonRect(size_t buttons); }; From d4661b66ea49bb6fae233384dff6aa412726cbe6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 2 Oct 2022 13:16:27 +0200 Subject: [PATCH 691/714] fix(MenuToolbar): resolve various focus/filtering issues This allows for de-selecting filters with keys. --- .../thirdparty/libopenui/src/menutoolbar.cpp | 26 ++++++++++++------- .../thirdparty/libopenui/src/menutoolbar.h | 2 ++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index c1320160d..cf31d3571 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -21,12 +21,21 @@ #include "choice.h" #include "font.h" +static void toolbar_btn_defocus(lv_event_t* event) +{ + auto obj = lv_event_get_target(event); + auto btn = (MenuToolbarButton*)lv_obj_get_user_data(obj); + btn->check(false); +} + MenuToolbarButton::MenuToolbarButton(Window* parent, const rect_t& rect, const char* picto) : Button(parent, rect, nullptr, 0, 0, window_create), picto(picto) { lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); + + lv_obj_add_event_cb(lvobj, toolbar_btn_defocus, LV_EVENT_DEFOCUSED, nullptr); } void MenuToolbarButton::paint(BitmapBuffer * dc) @@ -53,6 +62,7 @@ MenuToolbar::MenuToolbar(Choice* choice, Menu* menu) : group(lv_group_create()) { lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, LV_PART_MAIN); + lv_group_add_obj(group, lvobj); } MenuToolbar::~MenuToolbar() @@ -62,11 +72,6 @@ MenuToolbar::~MenuToolbar() void MenuToolbar::onEvent(event_t event) { - lv_obj_t* obj = lv_group_get_focused(group); - if (obj) { - auto btn = (MenuToolbarButton*)lv_obj_get_user_data(obj); - if (btn) { btn->check(false); } - } if (event == EVT_KEY_BREAK(KEY_PGDN)) { lv_group_focus_next(group); @@ -75,12 +80,11 @@ void MenuToolbar::onEvent(event_t event) lv_group_focus_prev(group); } - obj = lv_group_get_focused(group); + auto obj = lv_group_get_focused(group); if (!obj) { choice->fillMenu(menu); } else { - auto btn = (MenuToolbarButton*)lv_obj_get_user_data(obj); - if (btn) btn->onClicked(); + lv_event_send(obj, LV_EVENT_CLICKED, nullptr); } } @@ -121,7 +125,6 @@ static int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable return retval; } - void MenuToolbar::addButton(const char* picto, int16_t filtermin, int16_t filtermax) { @@ -142,3 +145,8 @@ void MenuToolbar::addButton(const char* picto, int16_t filtermin, lv_group_add_obj(group, button->getLvObj()); } + +void MenuToolbar::onClicked() +{ + choice->fillMenu(menu); +} diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 38ffda3a0..2255c22f5 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -54,4 +54,6 @@ class MenuToolbar : public Window bool filterMenu(MenuToolbarButton* btn, int16_t filtermin, int16_t filtermax); rect_t getButtonRect(size_t buttons); + + void onClicked() override; }; From ce9c5a302a71938751d5671555405d3228521e2c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 2 Oct 2022 13:17:12 +0200 Subject: [PATCH 692/714] fix(Menu): force into editing mode to fix behaviour with rotary encoder --- radio/src/thirdparty/libopenui/src/menu.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index d80c033f9..1b826b42a 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -70,6 +70,11 @@ void MenuBody::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc } } +static void _force_editing(lv_group_t* g) +{ + lv_group_set_editing(g, true); +} + MenuBody::MenuBody(Window * parent, const rect_t & rect): TableField(parent, rect) { @@ -80,7 +85,16 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect): setColumnWidth(0, rect.w); lv_group_t* g = (lv_group_t*)lv_obj_get_group(lvobj); - if (g) { lv_group_set_editing(g, true); } + if (g) { + setFocusHandler([=](bool focus) { + if (focus) { + lv_group_set_focus_cb(g, _force_editing); + } else { + lv_group_set_focus_cb(g, nullptr); + } + }); + lv_group_set_editing(g, true); + } } void MenuBody::addLine(const std::string &text, std::function onPress, @@ -112,6 +126,9 @@ void MenuBody::removeLines() { lines.clear(); setRowCount(0); + + // reset vertical scroll + lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); } coord_t MenuBody::getContentHeight() From d2676abc615ac7806c7f6eca98c68f53bdee575f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 2 Oct 2022 14:34:05 +0200 Subject: [PATCH 693/714] fix(Menu): properly reset selections when content is removed --- radio/src/thirdparty/libopenui/src/menu.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 1b826b42a..c5bd07f1d 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -126,6 +126,7 @@ void MenuBody::removeLines() { lines.clear(); setRowCount(0); + selectedIndex = 0; // reset vertical scroll lv_obj_scroll_to_y(lvobj, 0, LV_ANIM_OFF); From c41b1cb4580789fb43409e50a4e9190eebea8b32 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 2 Oct 2022 14:34:34 +0200 Subject: [PATCH 694/714] fix(Menu): handle negative selection index properly --- radio/src/thirdparty/libopenui/src/menu.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index c5bd07f1d..179f3473a 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -176,6 +176,12 @@ void MenuBody::setIndex(int index) lv_obj_invalidate(lvobj); lv_table_t* table = (lv_table_t*)lvobj; + + if (index < 0) { + table->row_act = LV_TABLE_CELL_NONE; + table->col_act = LV_TABLE_CELL_NONE; + return; + } table->row_act = index; table->col_act = 0; From 37159152b014964349accf4fb714f5653ea5a5b0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 2 Oct 2022 14:55:46 +0200 Subject: [PATCH 695/714] MenuToolbar: add resetFilter() --- radio/src/thirdparty/libopenui/src/menutoolbar.cpp | 10 ++++++++-- radio/src/thirdparty/libopenui/src/menutoolbar.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index cf31d3571..771f2c475 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -70,6 +70,14 @@ MenuToolbar::~MenuToolbar() lv_group_del(group); } +void MenuToolbar::resetFilter() +{ + if (lv_group_get_focused(group) != lvobj) { + lv_group_focus_obj(lvobj); + choice->fillMenu(menu); + } +} + void MenuToolbar::onEvent(event_t event) { @@ -91,9 +99,7 @@ void MenuToolbar::onEvent(event_t event) rect_t MenuToolbar::getButtonRect(size_t buttons) { coord_t y = buttons * (MENUS_TOOLBAR_BUTTON_WIDTH + MENUS_TOOLBAR_BUTTON_PADDING); - coord_t width = std::max(MENUS_TOOLBAR_BUTTON_WIDTH, lv_obj_get_content_width(lvobj)); return {0, y, LV_PCT(100), MENUS_TOOLBAR_BUTTON_WIDTH}; - } bool MenuToolbar::filterMenu(MenuToolbarButton* btn, int16_t filtermin, diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 2255c22f5..715cecc0f 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -42,6 +42,7 @@ class MenuToolbar : public Window MenuToolbar(Choice* choice, Menu* menu); ~MenuToolbar(); + void resetFilter(); void onEvent(event_t event) override; protected: From ba56990950798fb23706547d5d82eea4b9a71f15 Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Mon, 3 Oct 2022 04:58:25 +0200 Subject: [PATCH 696/714] fix(gui): partially drawn characters on top of bitmaps --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index ecd5eb07f..9c0faca28 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1155,6 +1155,7 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, } #if !defined(BOOT) else if (canvas) { + DMAWait(); lv_canvas_draw_text(canvas, coords.x1, coords.y1, coords.x2 - coords.x1 + 1, &label_draw_dsc, buffer); } From ca43d52960d55848e3fbd5fb5de2bc62849e1533 Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Thu, 6 Oct 2022 08:16:32 +0200 Subject: [PATCH 697/714] Add `setFont` and `setBackgroudOpacity` to StaticText (#73) --- radio/src/thirdparty/libopenui/src/static.cpp | 10 ++++++++++ radio/src/thirdparty/libopenui/src/static.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 59bd58bc4..919a3e403 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -75,6 +75,16 @@ void StaticText::setBackgroundColor(LcdFlags color) lv_obj_set_style_bg_color(lvobj, c, LV_PART_MAIN); } +void StaticText::setBackgroudOpacity(uint32_t opa) +{ + lv_obj_set_style_bg_opa(lvobj, opa, LV_PART_MAIN); +} + +void StaticText::setFont(LcdFlags font) +{ + lv_obj_set_style_text_font(lvobj, getFont(font), 0); +} + template <> void DynamicNumber::updateText() { diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 18516fb34..7a6c67ce7 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -36,6 +36,9 @@ class StaticText: public Window const std::string& getText() const; void setBackgroundColor(LcdFlags color); + void setBackgroudOpacity(uint32_t opa); + void setFont(LcdFlags font); + protected: std::string text; From 75087b2c1b11a81ed071494c341dd7a7f081fed7 Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Sat, 8 Oct 2022 05:26:58 +0200 Subject: [PATCH 698/714] fix(gui): reduce menu outline to two pixels (#74) --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 179f3473a..5e681357a 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -280,7 +280,7 @@ void Menu::setToolbar(Window * window) void Menu::setOutline(Window* obj) { - lv_obj_set_style_outline_width(obj->getLvObj(), 4, LV_PART_MAIN); + lv_obj_set_style_outline_width(obj->getLvObj(), 2, LV_PART_MAIN); lv_obj_set_style_outline_color(obj->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN); lv_obj_set_style_outline_opa(obj->getLvObj(), LV_OPA_100, LV_PART_MAIN); lv_obj_set_style_outline_pad(obj->getLvObj(), 1, LV_PART_MAIN); From df7d9a0fe0d8bff53b72418d768476b746060226 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Sun, 9 Oct 2022 14:40:11 +1000 Subject: [PATCH 699/714] fix(NumberEdit): do not call setValue() in constructor, use update() instead (#76) This avoids writing a value when it is not necessary. All that is needed is to update the internal value and set the field string. Co-authored-by: Raphael Coeffic --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 0f4507c11..e202e29ec 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -54,10 +54,7 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, lv_textarea_set_one_line(lvobj, true); lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this); - if (_getValue) { - auto value = _getValue(); - this->setValue(value); - } + update(); } void NumberEdit::onEvent(event_t event) From 8a4a1b1b38655f99762193d62e4f56fe13e3d7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20B=C3=A1ny=C3=A1sz?= <36520141+banyaszg@users.noreply.github.com> Date: Wed, 12 Oct 2022 05:05:18 +0200 Subject: [PATCH 700/714] Speed up the initial menu filling (#77) * Preset menu line counter. Disable update position during the fill. * Code formating * Redesign attempt with addLines() functions. * . * Menu::addLines fix for icons. * Redesign to clean up * Selection fix. Choice implementation. * One more redesign. --- radio/src/thirdparty/libopenui/src/choice.cpp | 11 +++-- .../thirdparty/libopenui/src/filechoice.cpp | 3 +- radio/src/thirdparty/libopenui/src/menu.cpp | 48 +++++++++++++++---- radio/src/thirdparty/libopenui/src/menu.h | 21 ++++++-- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 6c72a91b7..e5e24d317 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -210,19 +210,22 @@ void Choice::fillMenu(Menu *menu, const FilterFct& filter) auto value = _getValue(); int count = 0; + int selectedIx = -1; for (int i = vmin; i <= vmax; ++i) { if (filter && !filter(i)) continue; if (isValueAvailable && !isValueAvailable(i)) continue; if (textHandler) { - menu->addLine(textHandler(i), [=]() { setValue(i); }); + menu->addLineBuffered(textHandler(i), [=]() { setValue(i); }); } else if (unsigned(i - vmin) < values.size()) { - menu->addLine(values[i - vmin], [=]() { setValue(i); }); + menu->addLineBuffered(values[i - vmin], [=]() { setValue(i); }); } else { - menu->addLine(std::to_string(i), [=]() { setValue(i); }); + menu->addLineBuffered(std::to_string(i), [=]() { setValue(i); }); } - if (value == i) { menu->select(count); } + if (value == i) { selectedIx = count; } ++count; } + menu->updateLines(); + if (selectedIx >= 0) { menu->select(selectedIx); } } void Choice::openMenu() diff --git a/radio/src/thirdparty/libopenui/src/filechoice.cpp b/radio/src/thirdparty/libopenui/src/filechoice.cpp index ea5506bf5..223ea2614 100644 --- a/radio/src/thirdparty/libopenui/src/filechoice.cpp +++ b/radio/src/thirdparty/libopenui/src/filechoice.cpp @@ -101,7 +101,7 @@ bool FileChoice::openMenu() int current = -1; std::string value = getValue(); for (const auto &file : files) { - menu->addLine(file, [=]() { + menu->addLineBuffered(file, [=]() { setValue(file); lv_event_send(lvobj, LV_EVENT_VALUE_CHANGED, nullptr); }); @@ -113,6 +113,7 @@ bool FileChoice::openMenu() } ++count; } + menu->updateLines(); if (current >= 0) { menu->select(current); diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 5e681357a..48ed53f8e 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -98,28 +98,41 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect): } void MenuBody::addLine(const std::string &text, std::function onPress, - std::function isChecked) + std::function isChecked, bool update) { - lines.emplace_back(std::move(onPress), std::move(isChecked), nullptr); + lines.emplace_back(text, std::move(onPress), std::move(isChecked), nullptr); - auto idx = lines.size() - 1; - lv_table_set_cell_value(lvobj, idx, 0, text.c_str()); + if (update) { + auto idx = lines.size() - 1; + lv_table_set_cell_value(lvobj, idx, 0, text.c_str()); + } } void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, std::function onPress, - std::function isChecked) + std::function isChecked, + bool update) { lv_obj_t* canvas = lv_canvas_create(nullptr); - lines.emplace_back(std::move(onPress), std::move(isChecked), canvas); + lines.emplace_back(text, std::move(onPress), std::move(isChecked), canvas); lv_coord_t w = *((uint16_t *)icon_mask); lv_coord_t h = *(((uint16_t *)icon_mask)+1); void* buf = (void*)(icon_mask + 4); lv_canvas_set_buffer(canvas, buf, w, h, LV_IMG_CF_ALPHA_8BIT); - auto idx = lines.size() - 1; - lv_table_set_cell_value_fmt(lvobj, idx, 0, text.c_str()); + if (update) { + auto idx = lines.size() - 1; + lv_table_set_cell_value_fmt(lvobj, idx, 0, text.c_str()); + } +} + +void MenuBody::updateLines() +{ + setRowCount(lines.size()); + for(unsigned int idx = 0; idx < lines.size(); idx++) { + lv_table_set_cell_value_fmt(lvobj, idx, 0, lines[idx].text.c_str()); + } } void MenuBody::removeLines() @@ -327,6 +340,25 @@ void Menu::addLine(const uint8_t *icon_mask, const std::string &text, updatePosition(); } +void Menu::addLineBuffered(const std::string &text, std::function onPress, + std::function isChecked) +{ + content->body.addLine(text, std::move(onPress), std::move(isChecked), false); +} + +void Menu::addLineBuffered(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked) +{ + content->body.addLine(icon_mask, text, std::move(onPress), std::move(isChecked), false); +} + +void Menu::updateLines() +{ + content->body.updateLines(); + updatePosition(); +} + void Menu::removeLines() { content->body.removeLines(); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index c724f54fe..13a4c784a 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -53,9 +53,9 @@ class MenuBody: public TableField friend class MenuBody; public: - MenuLine(std::function onPress, std::function isChecked, + MenuLine(const std::string& text, std::function onPress, std::function isChecked, lv_obj_t *icon) : - onPress(std::move(onPress)), isChecked(std::move(isChecked)), icon(icon) + text(text), onPress(std::move(onPress)), isChecked(std::move(isChecked)), icon(icon) { } @@ -65,6 +65,7 @@ class MenuBody: public TableField lv_obj_t* getIcon() { return icon.get(); } protected: + std::string text; std::function onPress; std::function isChecked; std::unique_ptr icon; @@ -96,11 +97,14 @@ class MenuBody: public TableField void onCancel() override; void addLine(const std::string &text, std::function onPress, - std::function isChecked); + std::function isChecked, bool update = true); void addLine(const uint8_t *icon_mask, const std::string &text, std::function onPress, - std::function isChecked); + std::function isChecked, + bool update = true); + + void updateLines(); void removeLines(); @@ -185,6 +189,15 @@ class Menu: public ModalWindow std::function onPress, std::function isChecked = nullptr); + void addLineBuffered(const std::string &text, std::function onPress, + std::function isChecked = nullptr); + + void addLineBuffered(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked = nullptr); + + void updateLines(); + void addSeparator(); void removeLines(); From 4255642e7dad06774fdf40f0073aae565d7de673 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 13 Oct 2022 19:21:10 +0200 Subject: [PATCH 701/714] Fix menu toolbar PageUp/PageDown --- radio/src/thirdparty/libopenui/src/menu.cpp | 9 +++++++-- radio/src/thirdparty/libopenui/src/menutoolbar.cpp | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 48ed53f8e..5f508a3a5 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -369,8 +369,13 @@ void Menu::onEvent(event_t event) { #if defined(HARDWARE_KEYS) if (toolbar && - (event == EVT_KEY_BREAK(KEY_PGDN) || event == EVT_KEY_LONG(KEY_PGDN) || - event == EVT_KEY_BREAK(KEY_PGUP))) { + (event == EVT_KEY_BREAK(KEY_PGDN) || +#if defined(KEYS_GPIO_REG_PGUP) + event == EVT_KEY_BREAK(KEY_PGUP) +#else + event == EVT_KEY_LONG(KEY_PGDN) +#endif + )) { toolbar->onEvent(event); } #endif diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index 771f2c475..4df30752e 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -83,8 +83,13 @@ void MenuToolbar::onEvent(event_t event) if (event == EVT_KEY_BREAK(KEY_PGDN)) { lv_group_focus_next(group); - } else if (event == EVT_KEY_LONG(KEY_PGDN) || - event == EVT_KEY_BREAK(KEY_PGUP)) { + } +#if defined(KEYS_GPIO_REG_PGUP) + else if (event == EVT_KEY_BREAK(KEY_PGUP)) { +#else + else if (event == EVT_KEY_LONG(KEY_PGDN)) { + killEvents(event); +#endif lv_group_focus_prev(group); } From 4b85830ea242e13fffa67978d532d5710086ed3b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 19 Oct 2022 12:37:27 +0200 Subject: [PATCH 702/714] Improve rotary encoder velocity support (#78) --- .../thirdparty/libopenui/src/basenumberedit.h | 22 +++++++++++++++++++ .../libopenui/src/libopenui_depends.h | 3 +++ .../thirdparty/libopenui/src/numberedit.cpp | 12 ++++++---- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index 375b1ecb6..dd5cf2bcc 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -78,6 +78,26 @@ class BaseNumberEdit: public FormField return step; } + void setFastStep(int value) + { + fastStep = value; + } + + int32_t getFastStep() const + { + return fastStep; + } + + void setAccelFactor(int value) + { + accelFactor = value; + } + + int32_t getAccelFactor() const + { + return accelFactor; + } + void setValue(int value) { auto newValue = limit(vmin, value, vmax); @@ -190,6 +210,8 @@ class BaseNumberEdit: public FormField int vmin; int vmax; int step = 1; + int fastStep = 10; + int accelFactor = 4; int currentValue; bool instantChange = true; std::string prefix; diff --git a/radio/src/thirdparty/libopenui/src/libopenui_depends.h b/radio/src/thirdparty/libopenui/src/libopenui_depends.h index 414fccf85..279a4e070 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_depends.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_depends.h @@ -30,6 +30,9 @@ void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t void onKeyError(); void killEvents(event_t event); +// multiplication factor between 0 and 25 +int8_t rotaryEncoderGetAccel(); + // // These colors MUST be defined // diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index e202e29ec..bdd6f232c 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -66,8 +66,10 @@ void NumberEdit::onEvent(event_t event) #if defined(HARDWARE_KEYS) case EVT_ROTARY_RIGHT: { int value = getValue(); + auto step = getStep(); + step += (rotaryEncoderGetAccel() * getAccelFactor()) / 8; do { - value += ROTARY_ENCODER_SPEED() * step; + value += step; } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); if (value <= vmax) { setValue(value); @@ -81,8 +83,10 @@ void NumberEdit::onEvent(event_t event) case EVT_ROTARY_LEFT: { int value = getValue(); + auto step = getStep(); + step += (rotaryEncoderGetAccel() * getAccelFactor()) / 8; do { - value -= ROTARY_ENCODER_SPEED() * step; + value -= step; } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); if (value >= vmin) { setValue(value); @@ -104,11 +108,11 @@ void NumberEdit::onEvent(event_t event) break; case EVT_VIRTUAL_KEY_FORWARD: - setValue(getValue() + 10 * getStep()); + setValue(getValue() + getFastStep() * getStep()); break; case EVT_VIRTUAL_KEY_BACKWARD: - setValue(getValue() - 10 * getStep()); + setValue(getValue() - getFastStep() * getStep()); break; case EVT_VIRTUAL_KEY_DEFAULT: From fcb0e2cf5d42dcaeeb137d75ebce4be4a25e5707 Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Mon, 24 Oct 2022 03:48:00 +0200 Subject: [PATCH 703/714] fix(gui): missing % sign in choices (#79) --- radio/src/thirdparty/libopenui/src/menu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 5f508a3a5..fcb93e864 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -123,7 +123,7 @@ void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, if (update) { auto idx = lines.size() - 1; - lv_table_set_cell_value_fmt(lvobj, idx, 0, text.c_str()); + lv_table_set_cell_value(lvobj, idx, 0, text.c_str()); } } @@ -131,7 +131,7 @@ void MenuBody::updateLines() { setRowCount(lines.size()); for(unsigned int idx = 0; idx < lines.size(); idx++) { - lv_table_set_cell_value_fmt(lvobj, idx, 0, lines[idx].text.c_str()); + lv_table_set_cell_value(lvobj, idx, 0, lines[idx].text.c_str()); } } From a9b7f815f257cbcb87b50272bce169d97672332f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 31 Oct 2022 10:17:52 +0100 Subject: [PATCH 704/714] fix(NumberEdit): avoid calling setValue() to change the displayed string (#80) --- radio/src/thirdparty/libopenui/src/basenumberedit.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/basenumberedit.h b/radio/src/thirdparty/libopenui/src/basenumberedit.h index dd5cf2bcc..3ddb8fa5a 100644 --- a/radio/src/thirdparty/libopenui/src/basenumberedit.h +++ b/radio/src/thirdparty/libopenui/src/basenumberedit.h @@ -122,19 +122,19 @@ class BaseNumberEdit: public FormField void setPrefix(std::string value) { prefix = std::move(value); - setValue(currentValue); + update(); } void setSuffix(std::string value) { suffix = std::move(value); - setValue(currentValue); + update(); } void setZeroText(std::string value) { zeroText = std::move(value); - setValue(currentValue); + update(); } void enableInstantChange(bool value) @@ -181,7 +181,7 @@ class BaseNumberEdit: public FormField void setDisplayHandler(std::function function) { displayFunction = std::move(function); - setValue(currentValue); + update(); } virtual void update() From d174463d9707022e6bebf6ab2c95875f85cc9bb7 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 31 Oct 2022 12:09:51 +0100 Subject: [PATCH 705/714] fix(table): re-implement LV_EVENT_RELEASE handler (#81) Instead of calling `lv_table`'s handler, as this causes troubles when the selected cell is changed from within `LV_EVENT_VALUE_CHANGED`. Fixes EdgeTX/edgetx#2619 --- .../libopenui/src/widgets/edgetx_table.c | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c index 7afbfe62c..68488547f 100644 --- a/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c +++ b/radio/src/thirdparty/libopenui/src/widgets/edgetx_table.c @@ -30,19 +30,23 @@ lv_obj_t * table_create(lv_obj_t * parent) static void table_event(const lv_obj_class_t * class_p, lv_event_t * e) { lv_res_t res; - - lv_table_t * obj = (lv_table_t *) lv_event_get_target(e); - uint16_t col = obj->col_act; - uint16_t row = obj->row_act; - - /*Call the ancestor's event handler*/ - res = lv_obj_event_base(MY_CLASS, e); - if(res != LV_RES_OK) return; - lv_event_code_t code = lv_event_get_code(e); - if (code == LV_EVENT_RELEASED) { - obj->col_act = col; - obj->row_act = row; - lv_obj_invalidate((lv_obj_t *)obj); + if (code != LV_EVENT_RELEASED) { + /*Call the ancestor's event handler*/ + res = lv_obj_event_base(MY_CLASS, e); + if(res != LV_RES_OK) return; + } else { + lv_obj_t * obj = lv_event_get_target(e); + lv_table_t * table = (lv_table_t *)obj; + + /*From lv_table.c: handler for LV_EVENT_RELEASED*/ + lv_obj_invalidate(obj); + lv_indev_t* indev = lv_indev_get_act(); + lv_obj_t* scroll_obj = lv_indev_get_scroll_obj(indev); + if (table->col_act != LV_TABLE_CELL_NONE && + table->row_act != LV_TABLE_CELL_NONE && scroll_obj == NULL) { + res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL); + if (res != LV_RES_OK) return; + } } } From a08ccd00aaf53a9ee87f25386282e489301b393f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 4 Nov 2022 10:14:17 +0100 Subject: [PATCH 706/714] fix(drawSizedText): stop trying to be smart about text clipping (#82) --- radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp index 9c0faca28..742f3b5dd 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.cpp @@ -1101,13 +1101,6 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char *s, strncpy(buffer, s, len); buffer[len] = '\0'; - int height = getFontHeight(flags); - - if (y + height <= ymin || y >= ymax) { - RESTORE_OFFSET(); - return x; - } - coord_t pos = x; const coord_t orig_pos = pos; From 5a81a22514413a775e13cf5404a7a682e9089dab Mon Sep 17 00:00:00 2001 From: raphaelcoeffic Date: Tue, 15 Nov 2022 08:41:27 +0100 Subject: [PATCH 707/714] fix(window): fix crash in case LVGL parent is NULL --- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 119dff4d3..11fc67467 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -513,7 +513,7 @@ void Window::paintChildren(BitmapBuffer * dc, std::list::iterator it) void Window::bringToTop() { attach(parent); // does a detach + attach - if (lvobj) lv_obj_move_foreground(lvobj); + if (lvobj && lv_obj_get_parent(lvobj)) lv_obj_move_foreground(lvobj); } void Window::checkEvents() From d6185d924d04900b061167d3983ca2e2cbdfd791 Mon Sep 17 00:00:00 2001 From: eshifri <46511024+eshifri@users.noreply.github.com> Date: Tue, 17 Jan 2023 17:48:47 -0800 Subject: [PATCH 708/714] fix: Display of signed values (#83) * Fix display of the signed values with PREC1 and PREC2 * Use abs() for decimal digits output for negative numbers --- radio/src/thirdparty/libopenui/src/static.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 919a3e403..ddad3d980 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -110,11 +110,11 @@ void DynamicNumber::updateText() const char* p = prefix ? prefix : ""; const char* s = suffix ? suffix : ""; if ((textFlags & PREC2) == PREC2) { - lv_label_set_text_fmt(lvobj, "%s%" PRId32 ".%02" PRId32 "%s", p, - value / 100, value % 100, s); + lv_label_set_text_fmt(lvobj, "%s%" PRId32 ".%02" PRIu32 "%s", p, + value / 100, (uint32_t)abs(value % 100), s); } else if (textFlags & PREC1) { - lv_label_set_text_fmt(lvobj, "%s%" PRId32 ".%01" PRId32 "%s", p, - value / 10, value % 10, s); + lv_label_set_text_fmt(lvobj, "%s%" PRId32 ".%01" PRIu32 "%s", p, + value / 10, (uint32_t)abs(value % 10), s); } else { lv_label_set_text_fmt(lvobj, "%s%" PRId32 "%s", p, value, s); } @@ -146,11 +146,11 @@ void DynamicNumber::updateText() const char* p = prefix ? prefix : ""; const char* s = suffix ? suffix : ""; if ((textFlags & PREC2) == PREC2) { - lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%02" PRId16 "%s", p, - (int16_t)(value / 100), (int16_t)(value % 100), s); + lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%02" PRIu16 "%s", p, + (int16_t)(value / 100), (uint16_t)abs(value % 100), s); } else if (textFlags & PREC1) { - lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%01" PRId16 "%s", p, - (int16_t)(value / 10), (int16_t)(value % 10), s); + lv_label_set_text_fmt(lvobj, "%s%" PRId16 ".%01" PRIu16 "%s", p, + (int16_t)(value / 10), (uint16_t)abs(value % 10), s); } else { lv_label_set_text_fmt(lvobj, "%s%" PRId16 "%s", p, value, s); } From e67d1a789501697b2e2f663ead7e58385ee96cbb Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 18 Jan 2023 03:32:07 +0100 Subject: [PATCH 709/714] fix: strings constness (#84) --- radio/src/thirdparty/libopenui/src/bitmapbuffer.h | 2 +- radio/src/thirdparty/libopenui/src/libopenui_helpers.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h index 8a936dd6c..7a0f6bde6 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer.h +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer.h @@ -338,7 +338,7 @@ class BitmapBuffer: public BitmapBufferBase return drawSizedText(x, y, s, strlen(s), flags); } - coord_t drawTextAtIndex(coord_t x, coord_t y, const char** s, uint8_t idx, LcdFlags flags = 0) + coord_t drawTextAtIndex(coord_t x, coord_t y, const char *const *s, uint8_t idx, LcdFlags flags = 0) { return drawText(x, y, s[idx], flags); } diff --git a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h index be7a1ad96..035253912 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_helpers.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_helpers.h @@ -79,7 +79,7 @@ inline T sgn(T a) } // TODO duplicated code -inline std::string TEXT_AT_INDEX(const char** val, uint8_t idx) +inline std::string TEXT_AT_INDEX(const char *const *val, uint8_t idx) { return std::string(val[idx]); } From 3e49724e1121666098647605b63ff6ee735db499 Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 22 Mar 2023 20:42:13 +1100 Subject: [PATCH 710/714] Add scroll bar to menus where needed (#85) --- radio/src/thirdparty/libopenui/src/menu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index fcb93e864..5a7026b00 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -80,6 +80,8 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect): { // Allow encoder acceleration lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL); + // Add scroll bar if needed + lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_AUTO); setColumnCount(1); setColumnWidth(0, rect.w); From d56d641fb0e833e6afdde1eb1840b0dd514c50ed Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 22 Mar 2023 20:43:10 +1100 Subject: [PATCH 711/714] Set backSet background color on pop up dialog windows (#87) --- radio/src/thirdparty/libopenui/src/dialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 0ed6ca0b8..5f649a6c9 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -66,6 +66,8 @@ std::string DialogWindowContent::getName() const Dialog::Dialog(Window* parent, std::string title, const rect_t& rect) : ModalWindow(parent), content(new DialogWindowContent(this, rect)) { + lv_obj_set_style_bg_color(content->getLvObj(), makeLvColor(COLOR_THEME_SECONDARY3), 0); + lv_obj_set_style_bg_opa(content->getLvObj(), LV_OPA_100, LV_PART_MAIN); bringToTop(); if (!title.empty()) content->setTitle(std::move(title)); } From f84fbd01bf11c4ff167e8e64fdc3b8c174595dd7 Mon Sep 17 00:00:00 2001 From: Malte Langermann <54738901+gagarinlg@users.noreply.github.com> Date: Sun, 26 Mar 2023 10:16:28 +0200 Subject: [PATCH 712/714] chore(ui): improve static text and window base speed (#88) --- radio/src/thirdparty/libopenui/src/static.cpp | 7 +++++-- radio/src/thirdparty/libopenui/src/window.h | 7 ++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index ddad3d980..da6bd8e99 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -25,8 +25,7 @@ StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, Window(parent, rect, windowFlags, textFlags, lv_label_create), text(std::move(txt)) { - lv_label_set_text(lvobj, text.c_str()); - lv_obj_set_height(lvobj, LV_SIZE_CONTENT); + lv_obj_enable_style_refresh(false); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); lv_obj_set_style_text_font(lvobj, getFont(textFlags), LV_PART_MAIN); @@ -43,6 +42,10 @@ StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, } lv_obj_set_style_grid_cell_x_align(lvobj, LV_GRID_ALIGN_STRETCH, 0); + lv_label_set_text(lvobj, text.c_str()); + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } #if defined(DEBUG_WINDOWS) diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index cf08e83e8..605d7d51d 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -119,17 +119,17 @@ class Window void setRect(rect_t value) { rect = value; + lv_obj_enable_style_refresh(false); lv_obj_set_pos(lvobj, rect.x, rect.y); lv_obj_set_width(lvobj, rect.w); + lv_obj_enable_style_refresh(true); lv_obj_set_height(lvobj, rect.h); - invalidate(); } void setWidth(coord_t value) { rect.w = value; lv_obj_set_width(lvobj, rect.w); - invalidate(); } void setWindowCentered() @@ -143,21 +143,18 @@ class Window { rect.h = value; if (lvobj != nullptr) lv_obj_set_height(lvobj, rect.h); - invalidate(); } void setLeft(coord_t x) { rect.x = x; lv_obj_set_pos(lvobj, rect.x, rect.y); - invalidate(); } void setTop(coord_t y) { rect.y = y; lv_obj_set_pos(lvobj, rect.x, rect.y); - invalidate(); } coord_t left() const From 51ce75f10e947d15706bcec0f703949a77c5227f Mon Sep 17 00:00:00 2001 From: philmoz Date: Sun, 7 May 2023 11:25:50 +1000 Subject: [PATCH 713/714] Make the size and appearance of the number and text edit controls consistent with other UI elements. (#89) --- radio/src/thirdparty/libopenui/src/numberedit.cpp | 6 ++++++ radio/src/thirdparty/libopenui/src/textedit.cpp | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index bdd6f232c..d7ba3259d 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -54,6 +54,12 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, lv_textarea_set_one_line(lvobj, true); lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this); + setHeight(33); + padTop(5); + padRight(4); + lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_RIGHT, 0); + lv_obj_set_style_radius(lvobj, 4, 0); + update(); } diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 279af7a1e..a6ba34fc2 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -55,6 +55,11 @@ TextEdit::TextEdit(Window *parent, const rect_t &rect, char *value, lv_textarea_set_placeholder_text(lvobj, "---"); lv_textarea_set_max_length(lvobj, length); + setHeight(33); + padTop(5); + padLeft(4); + lv_obj_set_style_radius(lvobj, 4, 0); + if (width() == 0) { lv_obj_set_width(lvobj, LV_DPI_DEF); } From c6188af631a041bf7a1b7beeea5ab4d6f432d266 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 7 May 2023 06:05:50 +0200 Subject: [PATCH 714/714] Add setters for DynamicNumber prefix and suffix texts (#90) required by RX stat labeling on range check, rf tuning, low/critical warning pages --- radio/src/thirdparty/libopenui/src/static.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/radio/src/thirdparty/libopenui/src/static.h b/radio/src/thirdparty/libopenui/src/static.h index 7a6c67ce7..5e83c3777 100644 --- a/radio/src/thirdparty/libopenui/src/static.h +++ b/radio/src/thirdparty/libopenui/src/static.h @@ -178,6 +178,16 @@ class DynamicNumber : public StaticText } } + void setPrefix(const char * value) { + prefix = value; + updateText(); + } + + void setSuffix(const char * value) { + suffix = value; + updateText(); + } + protected: T value = 0; std::function numberHandler;