diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index fdb7dc9f4..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: ['PayPal.Me/opentx'] diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index c3483d818..4940fc9a8 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -1,6 +1,6 @@ --- name: Bug report -about: Create a report to help the OpenTX team to improve OpenTX +about: Create a report to help the EdgeTX team to improve EdgeTX --- *Before submitting, search the open issue if there is already a report of the bug* @@ -14,10 +14,6 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error **Expected behavior** A clear and concise description of what you expected to happen. @@ -26,6 +22,6 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **General information** - - OpenTX version (e.g. 2.1.23 or N123) - - Hardware (e.g. Frsky X9D+ or 9X Pro) - - OpenTX Companion (e.g. 2.1.23 on Ubuntu 16.04) + - EdgeTX version (e.g. 2.4.0 or nightly and #commit number) + - Hardware (e.g. Frsky X9D+ or RM TX12S) + diff --git a/.github/ISSUE_TEMPLATE/help.md b/.github/ISSUE_TEMPLATE/help.md index d50dfab6f..462ca6849 100644 --- a/.github/ISSUE_TEMPLATE/help.md +++ b/.github/ISSUE_TEMPLATE/help.md @@ -1,16 +1,13 @@ --- name: Help -about: Problems or question of using OpenTX +about: Problems or question of using EdgeTX --- -The issue tracker here is for devlopment of OpenTX. This is not a +The issue tracker here is for devlopment of EdgeTX. This is not a support site, only a bug tracking system. Please use support groups -like [rcgroups](https://rcgroups.com/) or -[openrcforum](https://www.openrcforums.com/forum/). See also our -[manual](https://opentx.gitbooks.io/manual-for-opentx-2-2/). +like [rcgroups](https://rcgroups.com/), [Discord](https://discord.gg/wF9wUKnZ6H) +or [Facebook](https://www.facebook.com/groups/edgetx). -For quick questions you can also join our general chat. - -Issues that fall into the category of asking for help, will be -generally closed as invalid without an answer. +Issues that fall into the category of asking for help may be re-directed +to the other support platforms or answered directly if time permits. diff --git a/.github/ISSUE_TEMPLATE/newfeature.md b/.github/ISSUE_TEMPLATE/newfeature.md index d5bf365fc..811b0cb21 100644 --- a/.github/ISSUE_TEMPLATE/newfeature.md +++ b/.github/ISSUE_TEMPLATE/newfeature.md @@ -1,9 +1,10 @@ --- name: Feature request -about: Suggest an idea for this project to make OpenTX even better +about: Suggest an idea to make EdgeTX even better! --- -*Before suggesting a new feature here, please search the open issue if there is already a request for the feature* +*Before suggesting a new feature here, please search the open issue if there is already a request for the feature. +You can add your comment there, or react so that we know know much interest there is in the feature request.* **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 76fb8130a..4bfc8a80c 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -6,11 +6,16 @@ on: - '2.4' paths-ignore: - '**.md' + - '.github/ISSUE_TEMPLATE/**' pull_request: branches: - '2.4' paths-ignore: - '**.md' + - '.github/ISSUE_TEMPLATE/**' + create: + tags: + - v* jobs: test: @@ -29,6 +34,7 @@ jobs: - X10 - X12S - X7 + - X9D - X9DP - X9DP2019 - X9LITE diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e3c663190..b4a213c74 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -28,6 +28,7 @@ jobs: - X10 - X12S - X7 + - X9D - X9DP - X9DP2019 - X9LITE diff --git a/CREDITS.txt b/CREDITS.txt deleted file mode 100644 index dc9dc238d..000000000 --- a/CREDITS.txt +++ /dev/null @@ -1,2269 +0,0 @@ -[Main developers] -Bertrand Songis (bsongis) -André Bernet (kilrah) -JC D. (3djc) -Michael Blandford -Damjan Adamic (projectkk2glider) -Franck Aguerre (jivarofad) -Romolo Manfredini (romoloman) -Martin Hotař (mhotar) -Maxim (Max) Paperno (mpaperno) -Arne Schwabe (schwabe) -Daniel Gorbea (dgatf) -Neil Horne (elecpower) - -[Translators] -[fr] Sebastien Charpentier (LapinFou) -[es] Daniel Gorbea (dgatf) - -[Other contributors] -Rob Thomson -Bryan J. Rentoul (gruvin) -Cameron Weeks (th9xer) -Andreas Weitl -Kjell Kernen -Gabriel Birkus -Jean-Pierre Parisy -Karl Szmutny -Michal Hlavinka -Pat Mackenzie -Michael Byrne - -[Companies and projects who have donated to OpenTX] -FrSky (monthly) -FlySky (monthly) -JumperRC (monthly) -RadioMaster (monthly) -HappyModel (monthly) -Aloft Hobbies -Jonathan Modellismo -WsTech -Aviomotive -BoltRC -Aerial Tech Hobbies -LitePilots -David Ankers (OpenPilot) -3D Robotics -RC Studio (monthly) -Craft & Theory (monthly) -OpenTX Manuals by Lothar Thole -Team BlackSheep -3DXR - -[People who have donated to OpenTX] -Mario Sette -Felice Baratto -Alessandro Mari -Gianpiero Spontina -Ingo Kuzniak -Hans-Åke Rahm -Tamas Varga -Thomas Gregg -Didier Christien -Alain Pinard -Sergio Melas -Paul Montenot -Chris Guy -Adrien Gravouille -Thomas Black -Marc Lucht -Zdenek Trojanek -Martin Hotar -Stefan Grundevik -Fabio Eboli -Federico Zotti -Albin Esbjörnsson -Kilian Hüttenhofer -Stephen Stough -Adilson Oliveira -Robert Saint Marie -Tad Leake -Andreas Weitl -Chan Lofland -Dirk Kasten -Denisov Alexander -Timo Varppee -Ondrej Hatapka -Valérie Billon -R. Scott Page -Johan Cronje -Renato Aranghelovici -Jeffrey Irvin -Kenneth Crump -Damjan Adamic -Martin Wilson -John Holdsworth -Daniel Schwartz -Dean Church (monthly) -Michael Halse -David Mark -Ted Moss -Jim Wagner -Nicola Porzi -Michael Halse -Chewytm -Espen Solbu -Mike Frizell -Bruce Jones -Stefano Airaghi -Dan Eeckhout -Alex Calderhead -Leonard MacKey -Heung Jin Choi -Mike Retterer -Keith Wood -Viljo Liukku -Eric Beier -Burt Pickard-Richardson -Helmut Renz -Rick Harms -Vincenzo Vavala -Patricia Martin -Harald Klein -Carl Petersen -Douglas Burkett -Clifford Evans -Teodor Yantchev -Jarad Berkman -Christian Schulz -Gregory J Shepherd -Michael Shellim -Ming Wang -Bernard Lukaschek -Nigel Chippindale -Mike Perkins -Ted Hammond -David Beanland -Bernd Koehler -Frédéric Duflos -Dave Fasken -Heinrich Illig -Michael Collins -Michael Manning -Craig Lorentz -André Lasseret -Felipe Alexandre de Oliveira -Brian Harris -Philippe Herbots -Rainer Nelte -Franck Bernhard -Erhard Werner -Stig Jøran Moen -Keith Hertzog -Christophe Dauvergne -Tauno Rautakorpi -Sven Assmus -Paul Dittman -Mike Matheny -Cheryl Wynne -Emay Toha -Don Nolder -André Lasseret -Filip Lowette -Arnaldo Gabella -Fiorenzo Agnelli -Michele Villa -Christophe Rodriguez -Larisa Filocamo -Hartmut Sahm -Åke Brodin -Ted Moss -Filippos Tigarakis -Chewytm -Eric Burdis -Nigel Chippindale -Michael Deasy -Stephen Stough -Kenneth Lilja -Robert Jero -Gary Bancroft -Robert Cotsford -NorthSide -George Constandinides -Robert Schonk Jr -Kevin Berkefeld -Peter Webster -Mike Matheny -Martin Sniedze -Fernando Rimoldi -Sidney Fisher -John Rodgers -Ed Pare -Thomas Kilduff -Robert Ferguson -Bryan Green -Kevin Berkefeld -Julien Michel -Krittikun Chiraphanda -Douglas Law -Alberto Scotti -Oliver Kunkel -D. Joseph Frazier -Heiko Polig -Andrew Fernie -Marco Lange -Nick McMillan -Michel Baily (monthly) -Patrick Mullen -Scott Campbell -Erich Roeder -Wayne Davy -Michael Fontenot -Willie Swanepoel -Satoru Sasaki -Tyler Darby -Nicholas Crego -Johan Moelich -Timo Esser -Brian Paddock -Jean Pierre Parisy -Norbert Martsch -Jean-Marie Guitard -Tilman Baumann -Fred Larssen -Ari Aalto -Robert Sams -Kirk Robertson -John Koontz -Jeffery Golias -Paul Robinson -Dave Matison -Mike Matheny -Pino Agostino -Graham Boucher -Nelson Noura -Joel Allamy -Philip David Martin -Sean Stephen -Bruce Mackay -Blake Spilman -Timothy Pelletier -Glen Roe (ShowMaster) -Jan Van Ooteghem -Lawrence Christensen -Leo Duflou -Bruce Wood -Arnd Feige -Tauno Rautakorpi -Steve Coley -Stefan Kiendl -Gregory Nuspel -Mitchell Brenner -Ronnnie DePrez -Ferenc Koller -Robert Sessler -Didier Lanot -Rodney Hunt -Steve Bernard -Paul Nazaroff -Gordon Ernenputsch -Ksamak -Marc Gittelman -David Poole -Ralph Sauer -Karl-Heinz Gansel -Michels Bernhard -Thomas Demmelbauer -Felice Baratto -Michael Halse -Horst Kampen -Ian Gough -Matthew Brett -Lexington S Morley -Clemens Drews -Axel Stöckle -Roberto Orsello -Richard Ruck -Hugh Caldwell -Christian Marauli -Albin Esbjörnsson -Per Esbjörnsson -Bernd Hohmann -Sławomir Juszczak -Stephen Henderson -David Morgan -Aloft Hobbies -Netanel Hobara -Cameron Galipo -Doug Schafer -Pietro Rossin -Earl Killingsworth -Victor B Wright -Terry McKinley -Марков Алексей -Offer Shmuely -Ian Lobley -Stéphane Fortas -Peter Mitrik -Thomas Majewski -ANF King -Henrik Hammarström -Kenneth Lilja -R Wells -Uwe Jordt -Harald Wilczak -Bernhard Lohmann -Ferenc Koller -Robert Ferguson -Daniel Byers -Tyler Darby -Miguel Villalpando -Kim Dong Hoon -Terry McKinley -Walter Bell -Clinton Linck -Josep Clodas Sola -Thierry Martin -Peter Mauro -Mitchell Brenner -Yak -Lluis Bullich Rañe -Steffen Rohr -Eric Jacquin -Harvest Lane -Ville Kuvaja -DK-System -Heinz Vogel -Heung Jin Choi -MMJS van der Helm -Dan Neelands -Yashiro Masayuki -Isaac Sloan -Harold Baird -Roy Bender -Bradley Murchie -Maximilian Süß -Dean Jansa -Ronald Donker -Paul Oldenkamp -Dietmar Drees -Bradley Murchie -Richard Fahie -James Kaufman -Henry Deucker -Christian Fuchs -Arold Reinders -Robert Miller -Mark Gaskievicz -Roland Müller -Frank Gewalt -Remi Viard -Reginald H Millsom -Pierluigi Sommaruga -A Harris -Stefan Grunenberg -Francisco Gutierrez Muñoz -Steve Coley -Sean Cull -Roberto Orsello -David Finger -Jean-Marie Oddo -Mike Matheny -Arron Bates -Tim Spurr -Daniel Morgan -Trent Ready -Mark Maskiell -Robert Crockford -Mark Illum -David McGuire -Martyn Coles -Jose Sobral -Heung Jin Choi -Tomasz Janiak -Fabien Gagné -Micha Ronen -Albert Blondin -Klaus Schuebel -Evening Shade Pottery -Thomas Kilduff -Sven Assmus -Markus Eberl -Nicholas Field -Harald Wilczak -John Grepps -Anthony Burbidge -Sherba Baine -Gordon Evans -Fred Figge -Uwe Kuehn -David Beatty -Aaron Stringer -Alan J Wasserman -Wolfgang Rauser -Samuel Cook -Friedrich Ipolt -Barrington Cole -Charles Lakins -Thos. O'Meara Photography -Adam Chevalier -Mike Mulligan -Serge Michaux -Robert Young -Wilco Hijink -Roy Nixon -Lexington S Morley -Daryl Dacko -Beat Zurflueh -Piotr Kundu -Kevin Berkefeld -Tomasz Klys -Mirco Didone' -Christoph Schroeter -Zdenek Trojanek -Bryan Anderson -Kamco Enterprises -Robert Bates -Robert Brown -Dennis Marinus -Ronald Van Putte -Thomas Kvitle Jenssen -Patrick Tuxbury -Istvan Magi -Sasaki Satoru -David Laburthe -Vit Jelinek -Pablo Gomez Martinez -Alan Thoren -Jack Clodfelter -William Simon -Peter Fithern -Thomas Svedman -TechFixPros -Rafael Eric Suarez Nordlow -Colin Rycroft -Emanuel Stassar -Michael Gregory -Robert Russell -Felix Kaiser -Joshua Bardwell -Davd Goodall -Leonard MacKey -Miles Kelly -Sergio Robledo -Mikael Wallbäck -Daniel Malcman -Paul Dittman -Attilio Rausse -Sidney Gates -Andrea Montefusco -Paul Luby -Donald L Webster -Kenneth Frehafer -Matthias Hase -Markus Wolf -Andrew Fazio -Stefan Künst -Stephen Adams -Tauno Rautakorpi -Frank Houvenaghel -George Neusse -Andreja Rusjakovski -Axel Stöckle -Erwin Störrle -Rodger Hopper -Fernando Cesar -Jonathan Gillis -DK-System -Lexington S Morley -Bernd Koehler -Davis RC -Pete Johnson -Patricia Martin -Florian Tschacher -Brian Spearing -Craig Hall -Joseph Case -Harald Klein -Robert Crockford -Chris Boultinghouse -Kitsew -Georgios Papadopoulos -Fourmost Products -Dennis Miller -Dave Matison -Maximilian Radig -Juan Rodriguez -Robert Sessler -John Murray -Martin Sniedze -David Poole -Manfred Delakowitz -Martijn Kruissen -Jörgen Norman -Nathan Kinney -John Godden -Larry Roper -Mike Frizell -Daniel Pospolita -Peter Herz -Don Mondrik -Paul Fram -Charles Knight -Matthew Adams -Kenneth Reed -Gary Wild -Christof Ramm -Bruno Bedard -Salih Samim Aydin -Alexander Pils -Andrew Scerbina -Donovan Bodishbaugh -Ray Dunn -Burt Pickard-Richardson -Robert Ferguson -CY Enterprise -Michel Stathakis Neto -Beat Zurflueh -Portstate Pty Ltd -Jens Hoffmann -Ricardo Santos -Jean-Francois Le Breton -Michael Deasy -Anindya Mukerjee -Emay Toha -Kamco Enterprises -Peter Mueller -Giovanni Trentini -Cecil Porter -Robert Canavan -Greg Higginbottom -Tim Walker -Spis Ltd/TurboNote -Patricia Cox -Matthew Lukes -Sabine Witte -Phillip Perry -Beat Zurflueh -Russell Stutzman -Dietmar Drees -Kenneth Ward -Jens Lochmüller -Anthony Witham -Peter Hauke -Arizona Action Photos -Francis F Dale -Willie Swanepoel -Edward Hauser -Adilson Oliveira -Joseph Ordille -Michael Shellim -Teddie Holmes -Alexandre Rault -Eric Albin -Lukas Kliment -Bernard Philippe -Bernard Delpy -Didier Klein -Arnaldo Guedes -Cyberauto Systems -John Alsbrooks -John Kearney -Christopher Borden -Marc Virgilio Ricci -Frederick Davie -Jürgen Enders -Terrence Pitts -Thomas Sandner -Marco Coltorti -Harald Wilczak -Rodney Lorimor -Richard Davis -Stephen Palmer -Michael Napolitan -Joshua Bardwell -Fred Dawson -Jim Wagner -Peter Johnson -Fredrik Lewerth -Arvin Stenersen -Daniel Morgan -Roy Nixon -Joachim Blunck -Karl-Heinz Gansel -Monrovia Mountain Enterprises -Alessandro Villa -Jan-Åke Nilsson -Matthias Rei -DVR Solutions BV -Bertrand Condevaux Lanloy -Henri Mirra -Aurélien Planiol -Kevin Caldwell -Andrew Garello -Tomasz Maziarz -Russell Flack -Fernando Rimoldi -Greg Reid -Dietmar Drees -Holger Landwehr -Arno Loehdorf -Boon Sripattha -Patrik Åkerfeldt -Alonzo Fretwell -Renjith Chandrasekharan -Ruben Gomez Hernandez -Michael Gardner -Judith Green -Timo Ruppell -John Lipscomb -Norbert Wenke -Erik Frieberg -Raimo Ruotsalainen -Brian Harris -Sebastian Blüml -Efisio Maxia -Jeff Theobald -Anthony Rizzo -Rodney Huddleston -Dietmar Drees -Jürgen Monden -David Morgan -Andrew Fernie -Donald Burke -Ronald Donker -Fredie Singh -Patrizio Simoncini -Harald Gabler -Michael Brown -Julien Gérard -Alexandre d'Alton -Peter Mergaerts -Jan Houwers -Christian Grandjean -Roger Bergevin -NorthSide -Sigmund Martinsen -Lionel Lafraise -Gisli Ofeigsson -The Gruvin9x Project -Manuel McLure -Fernando Rimoldi -Hiltenkamp Stephan -Sandeep Kishore Jain -Greg Parker -Norman Maaß -Adriano Del Ben -Justin Poole -Kenneth Harrison -Eugene LaFaille -John O'Shaughnessy -Uwe Jordt -Stuart Olson -Frédéric Devillard -Cheung Leo -Peter Webster -Jean-Claude Bachetta -Delfin Perena -Sae-wook Yoon -Focus on Excellence -Jeffery Golias -Nick Milton -Paul Zasso -Reidar Johannes Lillebotn -Handley William -Luke Williams -Michael Shellim -Brian Spearing -Derek Foster -Pasi Pitkänen -Lexington S Morley -Jeff Roberson -Volkov Victor -Bryan Anderson -Daniel Garber -Pavel Stein -RCdiy -Patrick Tuxbury -Mark Deverell -John Capell -Neil McVean -Matthew Elyash -Kerry Cochrell -Willem Aandewiel -Reg Bell -Jean Chenoz -Philippe Grand -Tauno Rautakorpi -Jeff Dillingham -Brad Kelley -Shaun Currier -Danilo Schiara -John Hennig -RK Custom Homes -Piet Teekens -Tommy Widenflycht -Johan Petrus Theophilus -Maria van Melis -Ralph Gustafson -Paul Luby -Andrew Duncan -Stephen Crowther -Edward Glover -Kelly Byrd -Brad Curtis -Wolfgang Kuhl -John Beidl -Roy Nixon -Glen Roe -CY Enterprise -Evening Shade Pottery -Gilles Dando -Patrick Richard -Justin Altemeier -Constant Dupuis -Tony Kenward -Pierre Rondel -Ernest Luguera Sanchez -Adam Brackney -Joe Baker -单 雷平 -Ben Fay -Chris Monk -Stan von Miller -Christian Krebs -Michel Motte -Graziano Rampinelli -Joe Baker -Jean-Philippe Laurent -Romain Rouzaud -Michel Garbarino -Henri Mirra -Otto Fröhlich -Thomas Cramer -Anthony Trombetta -Fernando Rimoldi -Dietmar Drees -Michal Kowalski -Ivan Correa -Modellbau -Massimo Luigi Stara -Graham Baldwin -Tomasz Rejman -Wolfgang Lang -John Greenwood -Till von Reumont -Paul Pereira Brunner -Donald L Singer -Junsun Lee -Guy Lamaison -Erwin Dulken -Mike H Dailey -Jon Cammarata -Martin Krueger -Adolfo Perogio -Sylvain Techer -Wes Warner -Kevin Caldwell -Ali Ersan Gur -Hans-Rudolf Riniker -Sébastien Boutard -Jean Chessé -Mike H Dailey -Don Polk -Roberto Orsello -Borovikov Dmitriy -John Lipscomb -Peter Glassey -Joseph Kramer -F.Javier Martinez Velasco -Claudio Piccinini -John Jackson -John Beech -Rene TraaJohn Jackson -Sebastien Charpentier (monthly) -Nigel Hendriks -Frédéric Devillard -Albert Herzog -Anindya Mukerjee -Kenny Stanberry -Tauno Rautakorpi -Mathias Krapp -Fernando Rimoldi -Wolfgang Rauser -Richard Shagam -Stephen Bailey -DK-System -Casa Kuxtal -Christopher Potts -Douglas George -Michael Banks -Jon Wiltshire -David Green -Guy Willems -Xavier Mestelan Pinon -Maik Gottschalk -Scott Sparks -John Dunning -RAD51 -Ron Powell -Alain Labonne -Carol Sills -Franck Aßmann -Larry Anderson -Mikael Wallbäck -Wes Warner -Christoffer H Henni -Donna Bayer -Garry Ogilvie -Karlheinz Sirsch -Fred Larssen -Rob Coppock -Franz Josef Hahn -Serge Ribault -Tanguy Le Moign -Barrett Powell -Patrice Lafeuille -Martin Blume -Marek Kopyto -Marco Lange -Gary Nix -Jack Oliphant -John Lever -Adolphe Montefiore -Steven De Weerdt -Håkan Berggen -Ayelet Egosi -Jean-Frederic Gaudreault -Jim Wagner -Lee Scott -Roger Keatley -Sherman Lovell -Hugh Caldwell -JB Fisher Company -Michels Bernhard -Pietro Rossin -Carsten Wache -Brock White -Steven Poretz -David Huelster -Gordon Stahl -John Mathison -Richard Duczmal -Gregory Barron -Marcel Fischer -Klas Mälman -Viljo Liukku -Eric Smith -Sven Assmus -Luke Mole -NorthSide -Hannes Mössler -James Kaufman -Lexington S Morley -Ralph Segar -Frédéric Devillard -Cory Theurer -Valentinos Zachariou -Garrett Paquette -Heung Jin Choi -Kerry Cochrell -Doug Miller -Andrzej Weydmann -Chair-man.nl -Eric Beier -Jay Warren -Marian Matej -Alastair Smith -Timothy Senecal -Dietmar Drees -Bruce Jones -Adil Busaidy -Herman Hofman -Jean Paul Michel -Peter McHenry -Julien lecoquierre -Jean-Marie Clay -Robert Nicoll -Dan Nixon -Ewald Moehring -Giovanni Carnesecchi -Paul Dumsday -Tadeusz Adamowski -Andrew Newton -Neil Horne -Kerstin Appelqvist -John Curran -Gerald Thiel -Jürg Bingisser -George Emer -Jerzy Manarczyk -Gerard Braakhuis -Nicolas Leblond -Burt Pickard-Richardson -Jiri Petrzelka -RK Custom Homes -David Semeria -Philip Lewis -William Kim -Dean Berg -Tim Spurr -Clifton Hipsher -Mike Frizell -Oliver Jacob -Pawel Stepien -Thomas Kilduff -Barry Fox -Anatoly Bakalenko -Bruce Anderson -Jeremy Cowgar -Edward Hauser -Stuart Brown -Anton Glock -Roger Ritchie -George Carnie -Philippe Stofleth -Tim Dzugan -Illia Izotov -Richard Miles -Attila Kapolnai -Gilles Dando -Denis Roussille -Olivier Anken -Michel Motte -David McDonald -Tauno Rautakorpi -Carsten Schlassa -Angel de la Dehesa Calles -Michel Garbarin -Algirdas Tamosiunas -Petr Kysela -Pascal Roux -Robert Whitfield -InTheTubeDeep -Jaromir Malek -Joe Street -Regin Poulsen -Hervé Laveyssiere -Walter Fowler -Richard Wagner -Kevin Hyman -Herald Reichel -Frederic Rouyre -David Libby -Hans Timmermans -Adrian De Zee -John Hanagan -Didier van Steensel -Julius Vicze -Cristiano Merighi -James Holley -Chris Merry -Jiri Fojtasek -Thierry Laligand -Hassouna Cherif -Fred Thijssen -Andrew Gardiner -Christian Niß -Douglas Penney -Starck Associates -Johannes Wollenweber -Fred Page -Jean Trouche -Patrick Blanc -Jean-Louis Tonnellier -Masoud -Mustafa Güven -William Waters -Joël Allamy -William Mckinstry -Jean-Jacques Pointel -Frédéric Rouyre -Jonathan Pruett -Jörg Zieren -Christopher Rhodes -Marco Strack -Stefano Boggia -Dwayne Myers -Stavros Papathanasiou -Roland Nittby -Courtney Puskas -NorthSide -Martin Polák -Ulf Reinhardt -Wolfgang Renisch -Trevor Hinze -Mohammad Zargari -Jean Pierre Bernard -Patrick Castellan -Udo Tödter -Marc J Merlin -Paul Chitonu -Wolfgang Rauser -Emmanuel Dallery -James Lyons -Tauno Rautakorpi -Gerald Peklar -Fabien Gagné -David Cox -Philip Cork -Kevin Wright -John Scott -Yves Berger -Otto Fröhlich -Kenneth Lilja -Benjamin Johnson -Brad Kelley -Michel Biancki -Lynn Laycock -Claude Godefroy -Gérald Joubert -Wayne J Podaima -Philippe Peyron -Julien Kieffer -Gregor Walter -Carlos Viegas -Roberto Orsello -Steve Murray -Marcel Schäfer -Sean Cull -Holger Bothmer -Michael Manning -Martin Risik -ZEN Computer Services -Egbert Stohlmann -Martin Peryea -Nathan James -Luis Suarez -Artur Uzieblo -InTheTubeDeep -Jaap Greve -Peter Glassey -David Reynolds -David Frazier -Donald L Singer -George Carnie -Susan Holbert -Richard Garrett -Paco Galván -Matthew Sylvester -Robert Bates -Hans-Peter Endres -Axel Stöckle -Leonard MacKey -Andrew Fernie -Mark McCoy -Damir Pooshs -Jeff Dillingham -Brent Hampton -Arkadiusz Milewski -Garry Ogilvie -Donald Porter -William Simon -Thomas Sandner -Pascal Pochard -Helmut Renz -T S Pung -Jorge Trinidad -Yeo Jonathan -David Olivier -Thierry Martin -Udo Nowakowski -Philippe Stofleth -Alain Labonne -Atam Gangaram Panday -Sajjad Rahman -Giovanni Carnesecchi -Lorenzo Frassy -Reinaldo Perez -Christian Aussillou -Christopher Potts -Eric Tremblet -Bernard Hunt -Evening Shade Pottery -Brian Cole -George McGinnis -Egid Schabl -Harold Henn -Chris VanCaeseele -Denis Novomlintsev -Pascal Amidey -Hermann Klampferer -Philippe Faure -Dean Berg -Sven Assmus -Gary Potter -Moritz Suerdieck -Eric Olivi -Jean-Jacques Pointel -Vianney Levesque -Fred Carlisle -Pierre Duvezin-Caubet -Pascal Dugué -Burt Pickard-Richardson -Robert Wilhelm -Thomas Kilduff -Hans-Rudolf Riniker -Arkadiusz Milewski -Kenneth Frehafer -Jens Ziegler -Peter Arnold -Jason Cole -Richard Vollkommer -Sherwood Drumheller -Jean-Christophe Gardaz -Igor Pongrac -James Mare -Kerry Cochrell -Florian Derriennic -HLL Vogels -Gordon Evans -Gaines Smith -Daniel Vogel -RCdiy -Joseph S Robinson -Jack Dunkley -Hannes Niethammer -Thierry Laligand -Pierre Duvezin-Caubet -Hannes Mössler -Petr Kysela -Calvin McCarthy -Piet Bomberg -Arne Johan Helle -Cees de Greef -Marc Gittelman -Scott Pritchett -Konstantinos Katsaklis -William Tortorello -Mark Maskiell -Gerald Peklar -Sidney Gates -Jörg Zander -Bryan Anderson -Stephane Monfette -Paul Poschen -Marco Lange -Peter Foto -Antonio Munuera -Vasil Petrov -Robert Wilhelm -Richard Hodgson -Manny Tau -Leonard MacKey -Johannes Wollenweber -Rodney Huddleston -Glen Roe -Chad Cranford -Tom Figge -Joerg-Dieter Rothfuchs -David Lorenzo -Chris Conroy -Fernando Wascheck -Martin Schoitsch -André Curci -Lukas Fischer -Larry Truesdale -Bernd Koehler -David Newton -Stephen Lesage -Mona Sturgis -Russell G Shanks -CY Enterprise -William Tortorello -André Lasseret -Lothar Thole -Stuart Olson -Paweł Stróż -Tim Buss -Christian Schaefer -Willyp -David Owens (monthly) -Willem Reij -Arthur Costerus -Martin Oldershaw -Robin Wienke -Jeffrey Johnson -Franz Josef Hahn -Burt Gray -Daniel Dupont -Felix Kaiser -Nicola Claudio Rossi -Gary Clayton -John Bitzer -Scott Campbell -Martin Polák -Jonathan Pruett -Charles Nowell -Randy Melton -Jo Jung -Chris Monk -Alvin Burke -Georges d'Haussy -Philippe Lhomme -Carsten Ullerup -Wolfgang Schneider-Reif -Anselm Schopplick -Georgios Papadopoulos -Thorsten Weber -Alexander Röhlich -Karl-Heinz Behn -Stefan Köhler -Jochen Kaphengst -Hans-Jüergen Duwe -Christoph Dalloz -Alexander Wimmer -Horst Efftinge -Nektarios Ntikmpasanis -Derek Gates -John Alsbrooks -Jürgen Deininger -Bruce Jones -Meinhard Schöll -Rolf Lemb -Casey Layton -Robert Bates -Udo Nowakowski -William Hallman -Paul Stein -Nurflügel & Modelltechnik -Gary Wild -James A Davey -Marco Giuseppe Colturi -Martin van Estrik -Vakhrushev Andrey -Jörgen Norman -Chen Zhongzhong -Eddie Ehlin -Evening Shade Pottery -Michel Vanaken -Forgues Research -Hike For Happiness -Vegar Elde -Anindya Mukerjee -Fabrice Debonnet -Darko Perković -Leonard MacKey -James A Davey -Matthias Häusel -Thierry Laligand -Pascal Gelis -Christelle Boudet -Damir Pooshs -Bertrand Brunner -Alexander Mathis -Jean-Luc Le Goff -Allan Abbott -Paul Elstob -Mark Levine -K L Wolfbrandt -Jeffrey Horton -Erlendur Konradsson -Dennis Miller -Шарлаимов Константин -Philippe Roussely -Michel Gutierrez -Joel Meziere -Widco Us -Marcos Guimaraes -Olivier Petri -Alfredo Mac Donald -Erich Strebel -Robert Goillandeau -Joel Lopez -Morten Myklebust -Ferenc Kunkli -Josh Kraker -Roger Bergevin -Geoff Sim -Arthur Reynolds -David Dalley -Colin Macdonald -Charles Wilson -Walter Daser -CG Tech Inc -Alexander Röhlich -Peter Goodrich -Randy Melton -Paul Pridday -Jörgen Norman -Peter Glassey -Carlos Vicente -Nael Bishtawi -Paul Dr. Willutzki -Heart of NM LLC -Ulf Hedlund -Meinolph Kaufmann -Bill Keenan -Ralf Kosel -Lukas Hucik -Uwe Jordt -Tuan Le -Burt Pickard-Richardson -Robert Rubio -Merv Dale -Michel Garbarino -Geoff Graham -Georges Pezet -Robert Wilhelm -Francis Herzog -Bo Hjeresen -Danny Koch -John Eppling -Thierry Malosse -Johann Antretter -Jan Kaiser -Rachel Berkhahn -Luca Massimiliano Comolli -Mario Wesierski -Evening Shade Pottery -Jean Desmet -Alexis Pons -Roger Bergevin -Eddie Ehlin -Bernard Avisse -Brent Warman -Tim Johnson -Reinhard Herden -Mark Hanson -Sander Helling -Anthony Hrabusicky -David Frazier -Alexander Orlov -Wes Warner -Samuel Grob -Wolfgang Kuhl -Stuart Olson -Michael Shellim -Jay Watkins -Juan Rodriguez -John Bitzer -Bryan Anderson -Claudio Piccinini -Patrick Breidenbach -Harald Puff -Mike Frizell -James A Davey -Ronald Fahlbusch -Piet Teekens -Jean Jacques Bultez -Paul Dittman -Philippe Faure -Jean-Jacques Pointel -Didier Christien -Huguette Viale -Marc Rivière -Cyril Maquaire -Nick Webb -Kristjan Crnoja -Gaines Smith -Tomasz Łapucha -Karl-Heinz Behn -Geir Atle Storhaug -Marco Lange -2 Brains Design -Philip Michels -Peter Helbling -Guy Lamaison -Michael Oeschger -Alistair Sutherland -Chris Hawley -Jeffrey Albers -Thomas Hollatz -Patrice Lafeuille -Gary Jones -Benoit Gruson -Petr Kysela -Sebastien Jehan -Jean Mathieu -Barry Andersen -James Vincent -Baker Hart -Thomas Voth -Michael Kiefer -Ronald Dalton -Thierry Laligand -Joel Lefebvre -Murray Rogers -Renato Elias -Richard Husta -Ian Ferreira -Zhang Tianyu -Josef de Beer -Ewald Moehring -Kevin Karamitros -Beat Zurflueh -Frank Morris -Edwin Butin Bik -Geir Atle Storhaug -Ted Hoenderdos -Sebastian Stefanitsch -Leif Pernstig -Michael Garofalo -Chris Sunman -Adam Kalus -Christoffer H Henni -Michael Stauning -Stuart Smith -Giuseppe Esposito -Stefano Boggia -Manuel Peon -Mark Bennett -Nicholas Platts -Carol B Meravi -Norman Hunt -Raymond Henderson -Christopher Marshall -Christopher Prinos -AntiSocial Engineering -Pierre Duvezin-Caubet -Hermann Haberer -Guy Poignonec -Robin Lauff -Renato Müller -Hannes Mössler -Cédric Lagoutte -Alexander Mathis -Barry Andersen -Philippe Glur -Felix Tang -Heiko Polig -Pierre Marty -Dirk Weiler -Eddie Ehlin -John Clarke -Laurent Thibaudet -James A Davey -Stuart Olson -claudio Piccinini -Kenneth Kriegel -Evening Shade Pottery -Raymond Henderson -Clifton Hipsher -François Carton -Olivier Portron -Didier Hochedez -Julien Gérard -Christian Syrbe -Juan Luis Guerrero Portero -Jonathan Badley -Ms Place -Heart of NM LLC -Daniele Gnesutta -Erik Peterson -SkyRaccoon -James Lummel -Jürgen Husske -Marko Domagoj Zic -Fabrice Debonnet -Ms Place -Jörg Hammer -Rapid Informatics LLC (monthly) -Renaud Perriguey -Edmond Dufresne -Jaroslav Gazik -Philippe Desrichard -Vincent Heron -Marcel Schäfer -Stefano Pascucci -Frédéric Bourgois -Stefan Hufnagl -Jean Philippe Bonacci -Jack Mitchell -Santiago Iglesias Garcia -Steve Kurywchak -Tore Skjefstad -Sergio Digiandco di Molino -Xavier Delepine -Miles Shearman -Bernard Teyssier -Entel Consulting BVBA -Stuart Olson -Maximilian Wechselberger -Kenneth Crump -Ken Elder -Darko Perković -Martin Krueger -Jack Lopes -Huguette Viale -Didier Christien -Pascal Roublot -Arnaud Sourisse -Aldo Midali -Sidney Gates -Serhiy Semenenko -Miguel Esquer Ramiro -Stefan Grunenberg -Ferenc Kunkli -Stefano Boggia -Lee Bennett -Morgantech -Marek Přikryl -Peter Scott -Ernst Camenzind -Steve Robbins -Jean Noel Berlan -Walter Szempruch -Petr Kysela -Gerhard Zdzuj -Joseph S Robinson -Zdenek Trojanek -Fred Page -Burt Pickard-Richardson -Eric Destrebecq -Derek Quigley -Lionel Lunan -Paul Dittman -Justin Delaire -Damir Pooshs -Alvin Burke -Pete Rathbun -Christophe Molliex -Ulf Hedlund -Damian Niegowski -Michae St Cyr -Ivan Pik -Rothermann, Kurt -Antinoro Ferdinando -Sam Day -Roman Rychtarik -Pierre Malriq -Jean Gaillard -Michel Garbarino -For Our Better World -Lothar Herrl -James Rhodes -Edward Glover -James Snodgrass -Dwayne Carden -Gerald Migely -Mark Schmidt -Hendrik Goessaert -Arturas Giedraitis -Paul Vernon -Rick Smith -Marcin Galczynski -Tom Knudtson -Sascha Heinrich -James Snodgrass -Alain Labonne -Darko Perković -Pierre Malriq -Donald Manson -Johannes Ferschl -Piotr Wysocki -Thomas Halenbeck (monthly) -Sjoerd Dost -Ralph Gustafson -Herbert Feix -Robert Costford -Taavi Tuisk -Carlos Quian -Noah Reinhart -Timothy Fry -Marc Stelzner -NXGRAPHICS -AIR-RC -Nicolas Deshaies -Jean-Francois Delhove -Hugo Daniel Farneti -RC electronic products -Adrian Andrei -Henk van de Kerkhof -Daniel Dubovski -Hans-Georg Böhm -Tiberiu Atudorei -Alessandro Apostoli (Yaapu!) -Kenneth Bonerigo -Frits van Voorst -Steven Dodge -Christian Buchinger -Patrick Blanc -Darrell Carew -Philippe Faure -Marco Lange -Aaron Tygart -Frantisek Vajbar -Mike Perkins -Knut Einar Kristensen -Georgios Papadopoulos -He Liu -Andrias Thomsen -Alain Azais -Alan Stuart -Fernando Rimoldi -James Snodgrass -Diego Mendoza Gonzalez -Dale Wheeler -Nico Wagner -James A Davey -Peter Glassey -Jeff Dillingham -Don Crowe -Nicola Claudio Rossi -Fabien Leroy -Tyler Nelson -Stuart Olson -Reinhard Plietsch -Manuel Spoerri -Paul Pearson -Thierry Laligand -Bruno Mexique -Uwe Jordt -Markus Goelzner -Jean-Claude Etiemble -Wojciech Winiarski -Michael Fleming -Russell G Shanks -Mike Sedgwick -William Goodrich -Marcel Schäfer -Joseph Santa Cruz -Gerald Roedel -James D McKeown -鄭 淳中 -Matthias Wuethrich -Michael Tessler -John Beech -Stefan Möst -Thomas McGraw -Bruno N Russo Jr -Daniel Eikenkötter -Guy Poignonec -Gérard Falaise -Jacques Loubier -Kelly Warner -James Cox -Sidney B Harden -CG Tech Inc. -Richard Gopstein -Francesco Ziche -Jean Paul Duroch -Romane Oughlissi -Alexander Maciol -Udo Kastenholz -Jacek Wielogórski -Siegfried Preißler -Bernard Mohamedaly -John North -Thomas Wormsley -Michael Schreiber -DTg Privat -Frédéric Devillard -Lauland Bourg -Jurij Peternel -Murray King -Horia Adrian Bucovanu -Freddy Prousteau -Wojciech Winiarski -James A Davey -James Donelson -David Jolly -Domingos S. Fonseca M. -Kenneth Frehafer -Christian Epple -David Williams -Michael Bernard -Michael Wagner -Carl Pennington -LTR Consulting -Norman Brisson -Darko Perković -Andreas Fischer -Pablo Peinado Abad -NthWave Web Works -Simon Brenner -Joseph Litko -Bradley Murchie -William Cessna -Justin Farnsworth -James Snodgrass -Peter Schneider -Norman Schenck -Boris Hemmer -Gilles Arbona -Patrice Germain -Mart Rogers -Stefan Möst -John Beech -John Rodgers -Stanton Sweet -Peter Suphan -FGS -Outward Ideas -Sylvie Le Madec -James Houck (monthly) -Frank Steinmetz -Gary Garland -Stefano Boggia -Jean-Jacques Pointel -Ferenc Kunkli -Sven Müller -Taavi Tuisk -Thomas Floyd -Matthew LePard -Peter Schneider -Michel Garbarino -Achim Stolze -Fernando Rimoldi -Bernard Delpy -Daniel Dubovsky -Jonathan Starch -Coolwind -Benoit Pichereau -Darko Perković -Burt Pickard-Richardson -Roland Nittby -Frank Barbas -Scott McCoy -Fabrice Debonnet -James Laver -Kevin Bowens -David Frech -Udo Kastenholz -Julio Margarido -Uwe Muendelein -James Jewitt -Daniel Ferreira -Michael Charlston -Leopoldo Cicero -Tom Van Driel -Karl Vetter -Johannes Janssen -François Blondé -Precision Aero -Thomas Håkansson -Gordon Evans -Benny Simonsen -George Kennedy -Stefano Boggia -Maksym Arshynkin -Tomas Sodergren -Jörg Lemnitz -TMac FPV -Josef Schaeffler -Mike Frizell -MeCodeGoodSomeday -Timothy Fry -John Beech -Wojciech Winiarski -Armin Kuepfer -Alain Bregand -James Thompson -Dautremépuis Benoit -Adriano Emanuele Figliola -Stefano Bortolotti -Gerald Zierer -Alessio Noto -Walter Fowler -Robert Black -Klaus Kernstock -Michael Wittich -AG-Software -Dietmar Drees -Winfried Goers -Steve Schoonover -N. Haasnoot (monthly) -Wolfram Bahmann -Florian Reck -Gary Bancroft -Karl-Heinz Behn -Stuart Godsmark -Josko Povse -David Reese -Paul Pearson -Stuart Olson -Pierre Connan -Henk Eeltink -Robert Miller -George Walker -Александр Щитов -Heiko Polig -Gary Wild -Peter Sarcher -Steve Schoonover -Francisco Orcera Exposito -Alan Powell -Martin Eriksson -Wayne J Podaima -Tom Wilkinson -Fred Ceratt -Stefan Grunenberg -Aram Parsamyan -Wolfgang Rauser -John Wall -Martin Haggas -Denis Kelly -James Houck -Brian Stephanik -William McNeice -Sven De Boeck -SB Europe Ltd -Evening Shade Pottery -D van Oostveen -Michael Grosso -Carlos Marcal -d@taweb -Justin Digby -Hermann Vocat -Michael Francini -Michael Chambers -Harold H Youngren -Yann Orselli -Dieter Schaad -Ernst Camenzind -Ciobanu Rivelino Costel -David Jolly -Aram Parsamyan -Stewart Davies -Karsten Schwarz -Robert Mckay -Michael Oeschger -Randy Ling -Jonathan Wise -David Gardner -Christophe Judas -Lachlan Peters -Patrick C Kutschke -Thomas Halenbeck -Barry Wright -Nicolas Ducher -Giampiero Sintoni -Sidney B Harden -John Moody -Sylvain Cubizolles -Adam Kouse -FCG Kas -Dennis Marinus -James Donelson -Stefan Hassler -Reinhard Prix -Jeffrey Johnson -Michael Conway -Ryan Buffington -Thomas Horns -Michael Carnes -William Hallman -James Snodgrass -Michal Mesaros -James Lindner -Laurence Gerrard -Nico Wagner -Christoph Rössner -Ruben Flores -Jim Cukla -Lee Taylor -Stefan Kuster -Barry Wright -Steven Gilley -Lloyd Evans -Nolan O'Hearne -Jeffrey James N Buffie -James Houck -Andreas Arlt -William Walker -Salvador Munoz Vinas -Chris Geiger Photography -John F Smith -Robert Zecha -Fernando Martins -Martin Brungard -Paul Witko -Michael Campbell -Stuart Posnett -PK Doddema -Leonard MacKey -Andrew Peterson -Dominik Sedlacek -Ronald Hellstern -Vicente Suarez -Patrick Geerts -Rossi Giampaolo -Jose Luis Franco Torres -Bart Sillen -Antonio Moreno -Carolina Carcione -Lonnie Misner -John Eppling -Phillip Elliott -Robin Bayer -Jurij Peternel -George Aandahl -Guy Dangerfield -Thomas Limpert -Ismael G Zapater -Alan Hall -Bradley Goodwin -Paul Dittman -John Johnson -Liam Mahoney -Joseph Carr -Fiorenzo Agnelli -Brian Ramsey -Angelo Movio -Weldon Parks -Simon Brink -Weldon Parks -Ralf Herzenbruch -Ashley Pearce -SneakIT -Soaring Stuff -Jörg Lemnitz -Johann Gross -Mark Gibson -Rendall Assoc -Cozyn Holdings Pty Ltd -Tim Karsten -Nick Hesson -Laurent Lapassade -Peter Deacon -Gilles Dando -Philippe Desrichard -Jón Haase -Michael Hansen -Sylvain Vanston -Helmut Nemecek -Alexander Mathis -Jay Watkins -Patrice Oudjaoudi -Didier Hochedez -Robert Hunter -Christophe Gilsoul -Lincoln Roberts -Johan Jurriens -Michael Ledward -Dave Waldesbuehl -Dale Rhoads -Denis Kelly -SBEurope LTD -Jean-Louis Mouche -Grant Mills -Fernando Rimoldi -Jim Cukla -Tomas Kirnak -Raymond Henderson -James Jewitt -Martin Sauter -Philip Hawkey -Lonnie Misner -Michael Hirschauer -Brian Bracken -Stuart Olson -Terry McKinley -Miklos Turcsin -Edward Skerness -Stefano Perinetti -Toni Marchante -Stuart Olson -Peter Campbell -Keith Wood -Leo Duflou -Michael Florey -Gerhard Weixelbaumer -Peter K Lehotsky -Elisha Jones -Joerg Sattler -paul42 -Pascal Heg -Bjorn Sorenssen -Chris Payne -Pascal Afflard -Robert Boboryko -Chris Whitehead -David Bradley -Ian Lever -Phillip Barber -Jan Bronee -N Haasnoot -Rick Smith -Randall Grant -Patrick Degouy -Piotr Zdanowski -David Gardner -TMac FPV -Bernard Thompson -Mark Sutton -Darren J Crane -Michael Keyes -Magnus Borg -David Lorentzen -Antony Smith -Aleksandr Sarkisov -Johannes Kuhne -Kathleen Cilluffo -David Hoffmann -Alan Porter -Arlé Mooldijk -Tenbones -Kim Bitsch Lauridsen -Arthur Costerus -Peter Harvey -John Simpson -Landy de los Santos -Graham Ridley -Schenk Peter -Joseph Rex -Richard Honeywell -Roy Nixon -Andrew Shewan -David Bradley -Alan Micklethwaite -Susan Siegel -Stephen Evans -William Hallman -Franck Michel -László Gajcsi (monthly) -Danny Koch -Robert Martin -Gavin Meerwald -Frédéric Maix -Frank Steinberg -Kevin Berkefeld -Marccos Colon -Kerry Reynolds -Kim Jeppesen -Arthur Peters -Andrew Lowes -Anthony Hallo -Bill Hill -Gerard Falaise -Kevin Raynor -Lorenzo gh -Jan Landeweer -David Bland -Manuel Spoerri -Jeff Jacobs -David Thorne -Maddox Philip -James Jewitt -Justin Digby -John Magnuson (monthly) -Holger Lippert -Hubert Huber -David Blum -Noam Kfir -Mats gårdstad friberg -Jesper Frickmann -Alan Bunker -Emanuel Kircher -Barry Andersen -Gabriel Formanek -Timothy Justiniano -Kelvin Garcia Urbina -Jan Lau -Choon Wei Jonathan Tan -Daniel Navas -Ray Moss -Markus Frielingsdorf -Michael Hirschauer -Jesse Frey -Philipp Koch -Robert Rubio -Dennis G Miller -Leonardo Garcia Osuna -Antonio Carrasco Pastor -Musa Pinar -Jens Moder -Josef de Beer -Chad Mofford -Pim Pouw -Kenth Jonsson -Gordon Westfall -Jean-Michel Saves -Roberto Orsello -Luboš Konvalinka -Lawrence Dorn -Michael Dittmann -Bernd Schmiedel -Raoul Piccioli -Uwe Probst -Glendon Satchell -Trevor Collins -Horst Grashauser -Miguel José Cárdenas Herrera -Mike Jennings -José Bernardo -Carlos Dangle -Gordon Evans -Pablo Peinado Abad -Carl Payne -Adam Sosnowski -Гасак Сергей -SaltCtyRacingFPV -Georg Bernhardt -James Mildenhall -Dave Plummer -Ivan Mokris -Patrick Daniels -Stuart Olson -Cory Znebel -Andrey Yarovoy -Christian Pons -Burt Pickard-Richardson -Michal Mesaros -Conrad Young -Martin Mathes -Jens Rainer Hansen -Mark Trible -Marc Dubois -Alan Wilkins -Daniel Hryciuk -Thomas Fox -Sidney B Harden -Augustas Sereika -David Belletete -Karl-Heinz Behn -Lena Hjort -Jan Langhorst -Alberto Scotti -John Rock -Adam Kouse -Ricardo Martins -Noel Irwin -Christoph Rausch -Anthony Hrabusicky -Sergio Tondini -Aldo Visentin -Greg Wilson -Mark Sutton -Lukas Kana -Peter Newman -Kevin Cave -Arold Reinders -Jason Plant -John Beech -Ian Parris -Danny Music -Eric Bissonnette -Riso LTD -William Booth -Ferenc Kunkli -Offer Shmuely -Troy Atneosen -Long Technologies -John Jaugilas -Jan-Hinrich Klee -Hans-Reinhard Mette -Alejandro Casals Oliver -David Homewood -John Dunning -Konrad Helbach -Bernard Delpy -Raymond Ellsworth -Bogdan Musial -Гасак Сергей -Philip Garcia -Helder Simoes -Thomas Blanchin -Jean-Luc Mesnier -Michael Bajer -David Homewood -Marc Sanders -Буров Александр -Rudy T'Jolleyn -Jay Watkins -Blaz Vodopivec -David De Salvo -Willem Verschoren -Dennis Kathrens -Alan Aucote -Richard Jerome Danstrom -David Garcia-Mendia -Mark Jones -John Zseleczky -Xiao Sun -Glenn Harvey -Albrecht Friebel -Gunter Klemke -Travis McCarthy -Franz Zier -Ian Contessa -Sean Sadler -Richard F Janczak -Mateusz Dziadosz -Thomas Merchant -M L Perkins -Stefano Boggia -Новоселов Юрий -James Rhodes -Peter Gaskill -Jacques Temey -Anthony Challis -Gerard Falaise -George McGinnis -Antoine Soulie -Tjeerd Jager -Bertold Van den Bergh -Stefanie Zerbe -James Mandelbaum -Javier J. Cordovez -Jess Barkley -Schneider Modell -Michael Hasselgaard -Thomas Young -Непочатов Алексей -Thomas Goebel -Joseph Macias -Paw Melin-Nielsen -Liam O'Brien -Ronald B Backman -Corvus Inspection Services Inc -Stuart Posnett -Clark Emerick -Helmut Sippel -Jhon Lennon Silva do Nascimento -Peter McCarthy -Bob Brown -Claude Lawyer -Peter Fithern -Michael Hoffman -Tim Smith -OpenSki Institute -Simon Melov -Richard Podolsky -James Wilson -John Norrbin -James Goins -Phillip Nguyen -Jeffrey M Engel -Richard Marotto -Greg Bell -Scott Geer -Thomas Abshier -Ernst Camenzind -Radomir Sterba -Ignacio Barrio -Bradley Murchie -Emmanuel Balintec -Rowell Torres -Christopher Kokis -Ken Bryan -Eric Yoder -Olav Roth -Rowell Torres -Christopher Kokis -Ken Bryan -Glenn Noonan -Michael Jakob -Dean Berg -David Hankins -Gary Cripps -Devin Crumb -Daniel Naude -James Miller -Pascal Cayol -eDefine -Mario Sette -Hans-Georg Böhm -Robert Oats -Helmut Suhrbier -Johannes Vorster -Deon du Rand -Stephen Hill-Harriss -Regi Pradeeswaran -Gary Busler -Stefano Boggia -Vernon Martin -Alexander W Wilson Jr -Adam Bowersock -Gary Burrows -Mark Gaskievicz -James Miller -Donald Taylor -Paul Makepeace -Thomas Fowler -Walter Szempruch -Charles Starck -Fabian Mächler -David Van Dyne -Karim el Mawardi diff --git a/README.md b/README.md index a2295b0f7..5d3851e01 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -## EdgeTX +# EdgeTX [![Commit Tests](https://github.com/EdgeTX/edgetx/actions/workflows/actions.yml/badge.svg)](https://github.com/EdgeTX/edgetx/actions/workflows/actions.yml) EdgeTX is the cutting edge version of OpenTX. Innovative ideas and cutting-edge features are developed and field-tested here, with an emphasis on new developments rather than maintenance and legacy. Stay tuned for more! + +## Acknowedgements +Some icon assets provided by [ICONS8](https://icons8.com). diff --git a/cmake/Bitmaps.cmake b/cmake/Bitmaps.cmake index 41a8589b6..f44e09517 100644 --- a/cmake/Bitmaps.cmake +++ b/cmake/Bitmaps.cmake @@ -14,6 +14,22 @@ macro(add_bitmaps_target targetname filter format args) add_custom_target(${targetname} DEPENDS ${bitmaps_files}) endmacro() +macro(add_png_target targetname filter) + set(bitmaps_files) + file(GLOB bitmaps ${filter}) + foreach(bitmap ${bitmaps}) + get_filename_component(target ${bitmap} NAME_WE) + set(target ${target}.lbm) + add_custom_command( + OUTPUT ${target} + COMMAND ${PYTHON_EXECUTABLE} ${UTILS_DIR}/bin2lbm.py ${bitmap} ${target} + DEPENDS ${bitmap} ${UTILS_DIR}/bin2lbm.py + ) + list(APPEND bitmaps_files ${target}) + endforeach() + add_custom_target(${targetname} DEPENDS ${bitmaps_files}) +endmacro() + macro(add_fonts_target targetname filter) set(fonts_files) file(GLOB fonts ${filter}) diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 4e8f11ca5..b022c3bcf 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -223,7 +223,6 @@ set(companion_SRCS burnconfigdialog.cpp multimodelprinter.cpp comparedialog.cpp - creditsdialog.cpp releasenotesdialog.cpp releasenotesfirmwaredialog.cpp customizesplashdialog.cpp @@ -257,7 +256,6 @@ set(companion_MOC_HDRS comparedialog.h printdialog.h logsdialog.h - creditsdialog.h releasenotesdialog.h releasenotesfirmwaredialog.h customizesplashdialog.h diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index c76418135..5c231c5a2 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -2,7 +2,6 @@ images/customize.png images/icon.png - ../../CREDITS.txt ../releasenotes.txt images/screenshot.png images/splash.png diff --git a/companion/src/creditsdialog.cpp b/companion/src/creditsdialog.cpp deleted file mode 100644 index 29b6b79bc..000000000 --- a/companion/src/creditsdialog.cpp +++ /dev/null @@ -1,135 +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 "creditsdialog.h" -#include "ui_htmldialog.h" -#include "helpers.h" -#include - -CreditsDialog::CreditsDialog(QWidget * parent): - QDialog(parent), - ui(new Ui::HtmlDialog) -{ - ui->setupUi(this); - - setWindowTitle(tr("OpenTX Contributors")); - setWindowIcon(CompanionIcon("contributors.png")); - - QString str = "" \ - "" \ - " " \ - "" - ""; - - foreach(CreditsSection section, readCredits()) { - str.append(formatTable(sectionTitle(section.title), section.names, 3)); - } - - str.append("" \ - " " \ - "
 
" + tr("Honors go to Rafal Tomczak (RadioClone), Thomas Husterer (th9x) and Erez Raviv (er9x and eePe)") + "
"); - -#if 0 - QFile blacklist(":/BLACKLIST.txt"); - if (blacklist.open(QIODevice::ReadOnly | QIODevice::Text)) { - QStringList names; - names << blacklist.readAll(); - str.append(formatTable(tr("OpenTX Blacklist"), names, 1)); - } -#endif - - str.append(""); - ui->textEditor->setHtml(str); - ui->textEditor->scroll(0, 0); - ui->textEditor->setOpenExternalLinks(true); -} - -CreditsDialog::~CreditsDialog() -{ - delete ui; -} - -QList CreditsDialog::readCredits() -{ - QFile credits(":/CREDITS.txt"); - QList result; - if (credits.open(QIODevice::ReadOnly | QIODevice::Text)) { - while (!credits.atEnd()) { - QByteArray line = credits.readLine().trimmed(); - if (line.size() >= 2) { - if (line.startsWith("[") && line.endsWith("]")) { - result.push_back(CreditsSection(line.mid(1, line.size() - 2))); - } else { - result.back().addName(line); - } - } - } - } - return result; -} - -QString CreditsDialog::sectionTitle(const QString & title) -{ - if (title == "Main developers") - return tr("Main developers"); - else if (title == "Translators") - return tr("Translators"); - else if (title == "Companies and projects who have donated to OpenTX") - return tr("Companies and projects who have donated to OpenTX"); - else if (title == "People who have donated to OpenTX") - return tr("People who have donated to OpenTX"); - else - return tr("Other contributors"); -} - -QString CreditsDialog::formatTable(const QString & title, const QStringList & names, int columns) -{ - const float cwidth = 100.0 / columns; - QString str = "" \ - " " \ - " " \ - "
 
" + title + "
"; - - str.append(""); - - int column = 0; - foreach(QString name, names) { - if (column == 0) { - str.append(""); - } - QString trclass = name.contains("monthly") ? "bold" : "normal"; - str.append(QString("").arg(cwidth).arg(trclass).arg(name.trimmed().replace("monthly", tr("monthly")))); - if (++column == columns) { - str.append(""); - column = 0; - } - } - - if (column != 0) { - str.append(""); - } - - str.append("
%3
"); - return str; -} diff --git a/companion/src/creditsdialog.h b/companion/src/creditsdialog.h deleted file mode 100644 index c1327aeea..000000000 --- a/companion/src/creditsdialog.h +++ /dev/null @@ -1,62 +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 _CREDITSDIALOG_H_ -#define _CREDITSDIALOG_H_ - -#include - -namespace Ui { - class HtmlDialog; -} - -class CreditsDialog : public QDialog -{ - Q_OBJECT - - class CreditsSection - { - public: - CreditsSection(const QString & title): - title(title) - { - } - - void addName(const QString & name) - { - names.push_back(name); - } - - QString title; - QStringList names; - }; - - public: - explicit CreditsDialog(QWidget * parent = 0); - ~CreditsDialog(); - - private: - Ui::HtmlDialog * ui; - QList readCredits(); - QString sectionTitle(const QString & title); - QString formatTable(const QString & title, const QStringList & names, int colums); -}; - -#endif // _CREDITSDIALOG_H_ diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 23c5f71b7..deb9652da 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -29,7 +29,6 @@ #include "downloaddialog.h" #include "printdialog.h" #include "version.h" -#include "creditsdialog.h" #include "releasenotesdialog.h" #include "releasenotesfirmwaredialog.h" #include "customizesplashdialog.h" @@ -868,13 +867,6 @@ void MainWindow::fwPrefs() dialog->deleteLater(); } -void MainWindow::contributors() -{ - CreditsDialog * dialog = new CreditsDialog(this); - dialog->exec(); - dialog->deleteLater(); -} - void MainWindow::sdsync() { // remember user-selectable options for duration of session TODO: save to settings @@ -1230,8 +1222,7 @@ void MainWindow::retranslateUi(bool showMsg) trAct(burnConfigAct, tr("Configure Communications..."), tr("Configure software for communicating with the Radio")); trAct(writeBUToRadioAct, tr("Write Backup to Radio"), tr("Write Backup from file to Radio")); trAct(readBUToFileAct, tr("Backup Radio to File"), tr("Save a complete backup file of all settings and model data in the Radio")); - trAct(contributorsAct, tr("Contributors..."), tr("A tribute to those who have contributed to OpenTX and Companion")); - + trAct(createProfileAct, tr("Add Radio Profile"), tr("Create a new Radio Settings Profile")); trAct(copyProfileAct, tr("Copy Current Radio Profile"), tr("Duplicate current Radio Settings Profile")); trAct(deleteProfileAct, tr("Delete Current Radio Profile..."), tr("Delete the current Radio Settings Profile")); @@ -1306,8 +1297,7 @@ void MainWindow::createActions() aboutAct = addAct("information.png", SLOT(about())); openDocURLAct = addAct("changelog.png", SLOT(openDocURL())); changelogAct = addAct("changelog.png", SLOT(changelog())); - contributorsAct = addAct("contributors.png", SLOT(contributors())); - + // these two get assigned menus in createMenus() recentFilesAct = addAct("recentdocument.png"); profilesMenuAct = addAct("profiles.png"); @@ -1315,7 +1305,6 @@ void MainWindow::createActions() exitAct->setMenuRole(QAction::QuitRole); aboutAct->setMenuRole(QAction::AboutRole); appPrefsAct->setMenuRole(QAction::PreferencesRole); - contributorsAct->setMenuRole(QAction::ApplicationSpecificRole); openDocURLAct->setMenuRole(QAction::ApplicationSpecificRole); checkForUpdatesAct->setMenuRole(QAction::ApplicationSpecificRole); changelogAct->setMenuRole(QAction::ApplicationSpecificRole); @@ -1402,8 +1391,7 @@ void MainWindow::createMenus() helpMenu->addSeparator(); helpMenu->addAction(changelogAct); helpMenu->addSeparator(); - helpMenu->addAction(contributorsAct); - + recentFilesMenu = new QMenu(this); recentFilesMenu->setToolTipsVisible(true); for ( int i = 0; i < g.historySize(); ++i) { diff --git a/companion/src/mainwindow.h b/companion/src/mainwindow.h index 0be249f04..78178d7f8 100644 --- a/companion/src/mainwindow.h +++ b/companion/src/mainwindow.h @@ -111,7 +111,6 @@ class MainWindow : public QMainWindow void readBackup(); void burnConfig(); void burnList(); - void contributors(); void sdsync(); void changelog(); void customizeSplash(); @@ -200,7 +199,6 @@ class MainWindow : public QMainWindow QAction *appPrefsAct; QAction *fwPrefsAct; QAction *checkForUpdatesAct; - QAction *contributorsAct; QAction *sdsyncAct; QAction *changelogAct; QAction *compareAct; diff --git a/fw.json b/fw.json index c6290644b..22102d1f2 100644 --- a/fw.json +++ b/fw.json @@ -2,12 +2,14 @@ "targets": [ ["TX16s", "tx16s-"], ["QX7", "x7-"], + ["X9D", "x9d-"], ["X9D Plus", "x9dp-"], ["X9D Plus 2019", "x9dp2019-"], ["T-Lite", "tlite-"], ["X-Lite", "xlite-"], ["X9 Lite", "x9lite-"], ["T16", "t16-"], + ["T18", "t18-"], ["TX12", "tx12-"], ["Horus X12s", "x12s-"], ["Horus X10", "x10-"], @@ -15,4 +17,4 @@ ["T8", "t8-"] ], "changelog": "- Major initial bug fixes" -} \ No newline at end of file +} diff --git a/radio/sdcard/horus/THEMES/EdgeTX/X10.bmp b/radio/sdcard/horus/THEMES/EdgeTX/X10.bmp deleted file mode 100755 index 24893bad0..000000000 Binary files a/radio/sdcard/horus/THEMES/EdgeTX/X10.bmp and /dev/null differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/X10S.bmp b/radio/sdcard/horus/THEMES/EdgeTX/X10S.bmp deleted file mode 100755 index e27932eef..000000000 Binary files a/radio/sdcard/horus/THEMES/EdgeTX/X10S.bmp and /dev/null differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/horus.bmp b/radio/sdcard/horus/THEMES/EdgeTX/horus.bmp deleted file mode 100644 index 98bc631cd..000000000 Binary files a/radio/sdcard/horus/THEMES/EdgeTX/horus.bmp and /dev/null differ diff --git a/radio/src/bitmaps/128x64/splash.png b/radio/src/bitmaps/128x64/splash.png index 64ebfb9d5..ebb8ecad7 100644 Binary files a/radio/src/bitmaps/128x64/splash.png and b/radio/src/bitmaps/128x64/splash.png differ diff --git a/radio/src/bitmaps/128x64/splash_frsky.png b/radio/src/bitmaps/128x64/splash_frsky.png deleted file mode 100644 index 84b3cf5e4..000000000 Binary files a/radio/src/bitmaps/128x64/splash_frsky.png and /dev/null differ diff --git a/radio/src/bitmaps/212x64/logo.png b/radio/src/bitmaps/212x64/logo.png index ae8b9e8e5..50ed47ed7 100644 Binary files a/radio/src/bitmaps/212x64/logo.png and b/radio/src/bitmaps/212x64/logo.png differ diff --git a/radio/src/bitmaps/212x64/splash.png b/radio/src/bitmaps/212x64/splash.png index 43cd387dc..9eac084cd 100644 Binary files a/radio/src/bitmaps/212x64/splash.png and b/radio/src/bitmaps/212x64/splash.png differ diff --git a/radio/src/bitmaps/212x64/splash_frsky.png b/radio/src/bitmaps/212x64/splash_frsky.png deleted file mode 100644 index 37c410f47..000000000 Binary files a/radio/src/bitmaps/212x64/splash_frsky.png and /dev/null differ diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index 2233da191..32e10bb5f 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -20,6 +20,8 @@ set(BITMAP_RLE_ARGS ${BITMAP_ARGS} --rle) set(MASK_RLE_ARGS ${MASK_ARGS} --rle) add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/bmp_*.png" "5/6/5" "${BITMAP_ARGS}") +add_png_target(${BITMAP_TARGET_PREFIX}_splash "${RADIO_SRC_DIR}/bitmaps/480x272/big_logo.png") + add_bitmaps_target(${BITMAP_TARGET_PREFIX}_calibration_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/calibration/bmp_*.png" "5/6/5" "${BITMAP_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_button_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/button/alpha_*.png" "4/4/4/4" "${BITMAP_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_alpha_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/alpha_*.png" "4/4/4/4" "${BITMAP_ARGS}") @@ -27,13 +29,16 @@ add_bitmaps_target(${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps "${RADIO_SR add_bitmaps_target(${BITMAP_TARGET_PREFIX}_masks ${RADIO_SRC_DIR}/bitmaps/480x272/mask_*.png 8bits "${MASK_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_slider_masks "${RADIO_SRC_DIR}/bitmaps/480x272/slider/*.png" 8bits "${MASK_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_layouts_masks "${RADIO_SRC_DIR}/gui/colorlcd/layouts/*.png" 8bits "${MASK_ARGS}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_bitmaps "${RADIO_SRC_DIR}/gui/colorlcd/themes/*.png" "5/6/5" "${BITMAPS_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_fonts ${RADIO_SRC_DIR}/fonts/truetype/*.png 8bits "${MASK_RLE_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_volume_masks ${RADIO_SRC_DIR}/bitmaps/480x272/volume/*.png 8bits "${MASK_ARGS}") + +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_masks "${RADIO_SRC_DIR}/bitmaps/480x272/default_theme/mask_*.png" 8bits "${BITMAPS_ARGS}") +add_png_target(${BITMAP_TARGET_PREFIX}_themes_alpha "${RADIO_SRC_DIR}/bitmaps/480x272/default_theme/alpha_*.png") + add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${RADIO_SRC_DIR}/bitmaps/480x272/bootloader/bmp_*.png "4/4/4/4" "${BITMAP_RLE_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_icons ${RADIO_SRC_DIR}/bitmaps/480x272/bootloader/icon_*.png 8bits "${MASK_ARGS}") add_dependencies(${BITMAP_TARGET_PREFIX}_bitmaps ${BITMAP_TARGET_PREFIX}_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_button_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_masks ${BITMAP_TARGET_PREFIX}_slider_masks ${BITMAP_TARGET_PREFIX}_layouts_masks - ${BITMAP_TARGET_PREFIX}_themes_bitmaps ${BITMAP_TARGET_PREFIX}_fonts ${BITMAP_TARGET_PREFIX}_volume_masks ${BITMAP_TARGET_PREFIX}_bootloader_bitmaps - ${BITMAP_TARGET_PREFIX}_bootloader_icons) + ${BITMAP_TARGET_PREFIX}_fonts ${BITMAP_TARGET_PREFIX}_themes_masks ${BITMAP_TARGET_PREFIX}_themes_alpha ${BITMAP_TARGET_PREFIX}_splash ${BITMAP_TARGET_PREFIX}_volume_masks + ${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${BITMAP_TARGET_PREFIX}_bootloader_icons ) diff --git a/radio/src/bitmaps/480x272/big_logo.png b/radio/src/bitmaps/480x272/big_logo.png new file mode 100644 index 000000000..9642b41d7 Binary files /dev/null and b/radio/src/bitmaps/480x272/big_logo.png differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/stick_background.png b/radio/src/bitmaps/480x272/default_theme/alpha_stick_background.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/stick_background.png rename to radio/src/bitmaps/480x272/default_theme/alpha_stick_background.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/stick_pointer.png b/radio/src/bitmaps/480x272/default_theme/alpha_stick_pointer.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/stick_pointer.png rename to radio/src/bitmaps/480x272/default_theme/alpha_stick_pointer.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/trackp_background.png b/radio/src/bitmaps/480x272/default_theme/alpha_trackp_background.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/trackp_background.png rename to radio/src/bitmaps/480x272/default_theme/alpha_trackp_background.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_asterisk.png b/radio/src/bitmaps/480x272/default_theme/mask_asterisk.png new file mode 100644 index 000000000..fd10af269 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_asterisk.png differ diff --git a/radio/src/bitmaps/480x272/default_theme/mask_back.png b/radio/src/bitmaps/480x272/default_theme/mask_back.png new file mode 100755 index 000000000..ffd31b820 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_back.png differ diff --git a/radio/src/bitmaps/480x272/default_theme/mask_busy.png b/radio/src/bitmaps/480x272/default_theme/mask_busy.png new file mode 100644 index 000000000..a2d870205 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_busy.png differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_currentmenu_bg.png b/radio/src/bitmaps/480x272/default_theme/mask_currentmenu_bg.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_currentmenu_bg.png rename to radio/src/bitmaps/480x272/default_theme/mask_currentmenu_bg.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_currentmenu_dot.png b/radio/src/bitmaps/480x272/default_theme/mask_currentmenu_dot.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_currentmenu_dot.png rename to radio/src/bitmaps/480x272/default_theme/mask_currentmenu_dot.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_currentmenu_shadow.png b/radio/src/bitmaps/480x272/default_theme/mask_currentmenu_shadow.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_currentmenu_shadow.png rename to radio/src/bitmaps/480x272/default_theme/mask_currentmenu_shadow.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_edgetx.png b/radio/src/bitmaps/480x272/default_theme/mask_edgetx.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_edgetx.png rename to radio/src/bitmaps/480x272/default_theme/mask_edgetx.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_error.png b/radio/src/bitmaps/480x272/default_theme/mask_error.png new file mode 100755 index 000000000..3bf350e55 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_error.png differ diff --git a/radio/src/bitmaps/480x272/default_theme/mask_iconback.png b/radio/src/bitmaps/480x272/default_theme/mask_iconback.png new file mode 100644 index 000000000..966f6b7f2 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_iconback.png differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_menu_model.png b/radio/src/bitmaps/480x272/default_theme/mask_menu_model.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_menu_model.png rename to radio/src/bitmaps/480x272/default_theme/mask_menu_model.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_menu_radio.png b/radio/src/bitmaps/480x272/default_theme/mask_menu_radio.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_menu_radio.png rename to radio/src/bitmaps/480x272/default_theme/mask_menu_radio.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_menu_stats.png b/radio/src/bitmaps/480x272/default_theme/mask_menu_stats.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_menu_stats.png rename to radio/src/bitmaps/480x272/default_theme/mask_menu_stats.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_menu_theme.png b/radio/src/bitmaps/480x272/default_theme/mask_menu_theme.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_menu_theme.png rename to radio/src/bitmaps/480x272/default_theme/mask_menu_theme.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_curves.png b/radio/src/bitmaps/480x272/default_theme/mask_model_curves.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_curves.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_curves.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_flight_modes.png b/radio/src/bitmaps/480x272/default_theme/mask_model_flight_modes.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_flight_modes.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_flight_modes.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_gvars.png b/radio/src/bitmaps/480x272/default_theme/mask_model_gvars.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_gvars.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_gvars.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_heli.png b/radio/src/bitmaps/480x272/default_theme/mask_model_heli.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_heli.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_heli.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_inputs.png b/radio/src/bitmaps/480x272/default_theme/mask_model_inputs.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_inputs.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_inputs.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_logical_switches.png b/radio/src/bitmaps/480x272/default_theme/mask_model_logical_switches.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_logical_switches.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_logical_switches.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_lua_scripts.png b/radio/src/bitmaps/480x272/default_theme/mask_model_lua_scripts.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_lua_scripts.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_lua_scripts.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_mixer.png b/radio/src/bitmaps/480x272/default_theme/mask_model_mixer.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_mixer.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_mixer.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_outputs.png b/radio/src/bitmaps/480x272/default_theme/mask_model_outputs.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_outputs.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_outputs.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_setup.png b/radio/src/bitmaps/480x272/default_theme/mask_model_setup.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_setup.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_setup.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_special_functions.png b/radio/src/bitmaps/480x272/default_theme/mask_model_special_functions.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_special_functions.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_special_functions.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_model_telemetry.png b/radio/src/bitmaps/480x272/default_theme/mask_model_telemetry.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_model_telemetry.png rename to radio/src/bitmaps/480x272/default_theme/mask_model_telemetry.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_modelname.png b/radio/src/bitmaps/480x272/default_theme/mask_modelname.png new file mode 100644 index 000000000..2edca73ea Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_modelname.png differ diff --git a/radio/src/bitmaps/480x272/default_theme/mask_modelqty.png b/radio/src/bitmaps/480x272/default_theme/mask_modelqty.png new file mode 100644 index 000000000..9a38e4f13 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_modelqty.png differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_channels1.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_channels1.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_channels1.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_channels1.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_channels2.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_channels2.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_channels2.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_channels2.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_channels3.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_channels3.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_channels3.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_channels3.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_channels4.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_channels4.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_channels4.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_channels4.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_gvars.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_gvars.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_gvars.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_gvars.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_inver.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_inver.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_inver.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_inver.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_lockch.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_lockch.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_lockch.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_lockch.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_logsw.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_logsw.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_logsw.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_logsw.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_mixes1.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_mixes1.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_mixes1.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_mixes1.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_mixes2.png b/radio/src/bitmaps/480x272/default_theme/mask_monitor_mixes2.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_monitor_mixes2.png rename to radio/src/bitmaps/480x272/default_theme/mask_monitor_mixes2.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_moveback.png b/radio/src/bitmaps/480x272/default_theme/mask_moveback.png new file mode 100644 index 000000000..63f56df92 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_moveback.png differ diff --git a/radio/src/bitmaps/480x272/default_theme/mask_moveico.png b/radio/src/bitmaps/480x272/default_theme/mask_moveico.png new file mode 100644 index 000000000..3662b9bb0 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_moveico.png differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_mplex_add.png b/radio/src/bitmaps/480x272/default_theme/mask_mplex_add.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_mplex_add.png rename to radio/src/bitmaps/480x272/default_theme/mask_mplex_add.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_mplex_multi.png b/radio/src/bitmaps/480x272/default_theme/mask_mplex_multi.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_mplex_multi.png rename to radio/src/bitmaps/480x272/default_theme/mask_mplex_multi.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_mplex_replace.png b/radio/src/bitmaps/480x272/default_theme/mask_mplex_replace.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_mplex_replace.png rename to radio/src/bitmaps/480x272/default_theme/mask_mplex_replace.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_next.png b/radio/src/bitmaps/480x272/default_theme/mask_next.png new file mode 100644 index 000000000..848019e8e Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_next.png differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_opentx.png b/radio/src/bitmaps/480x272/default_theme/mask_opentx.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_opentx.png rename to radio/src/bitmaps/480x272/default_theme/mask_opentx.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_opentx_testmode.png b/radio/src/bitmaps/480x272/default_theme/mask_opentx_testmode.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_opentx_testmode.png rename to radio/src/bitmaps/480x272/default_theme/mask_opentx_testmode.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_question.png b/radio/src/bitmaps/480x272/default_theme/mask_question.png new file mode 100644 index 000000000..66eb18762 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_question.png differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_calibration.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_calibration.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_calibration.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_calibration.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_global_functions.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_global_functions.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_global_functions.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_global_functions.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_hardware.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_hardware.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_hardware.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_hardware.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_sd_browser.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_sd_browser.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_sd_browser.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_sd_browser.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_setup.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_setup.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_setup.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_setup.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_tools.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_tools.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_tools.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_tools.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_trainer.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_trainer.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_trainer.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_trainer.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_version.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_version.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_version.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_version.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_mixer.png b/radio/src/bitmaps/480x272/default_theme/mask_sbar_mixer.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_mixer.png rename to radio/src/bitmaps/480x272/default_theme/mask_sbar_mixer.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_output.png b/radio/src/bitmaps/480x272/default_theme/mask_sbar_output.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_output.png rename to radio/src/bitmaps/480x272/default_theme/mask_sbar_output.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_to.png b/radio/src/bitmaps/480x272/default_theme/mask_sbar_to.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_to.png rename to radio/src/bitmaps/480x272/default_theme/mask_sbar_to.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_sdfree.png b/radio/src/bitmaps/480x272/default_theme/mask_sdfree.png new file mode 100644 index 000000000..f0ae81982 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_sdfree.png differ diff --git a/radio/src/bitmaps/480x272/default_theme/mask_shutdown.png b/radio/src/bitmaps/480x272/default_theme/mask_shutdown.png new file mode 100755 index 000000000..87704cc5f Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_shutdown.png differ diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_analogs.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_analogs.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_analogs.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_analogs.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_debug.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_debug.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_debug.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_debug.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_lua.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_lua.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_lua.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_lua.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_throttle_graph.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_throttle_graph.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_throttle_graph.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_throttle_graph.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_timers.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_timers.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_timers.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_timers.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_value.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_value.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_value.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_value.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_switches.png b/radio/src/bitmaps/480x272/default_theme/mask_switches.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_switches.png rename to radio/src/bitmaps/480x272/default_theme/mask_switches.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_curve.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_curve.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_curve.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_curve.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_delay.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_delay.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_delay.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_delay.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_delayslow.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_delayslow.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_delayslow.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_delayslow.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_fm.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_fm.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_fm.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_fm.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_label.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_label.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_label.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_label.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_slow.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_slow.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_slow.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_slow.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_switch.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_switch.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_switch.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_switch.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_add_view.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_add_view.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_add_view.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_add_view.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_setup.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_setup.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_setup.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_setup.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view1.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view1.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view1.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view1.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view2.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view2.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view2.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view2.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view3.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view3.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view3.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view3.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view4.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view4.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view4.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view4.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view5.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view5.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view5.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view5.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view6.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view6.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view6.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view6.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view7.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view7.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view7.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view7.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view8.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view8.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view8.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view8.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_topleft.png b/radio/src/bitmaps/480x272/default_theme/mask_topleft.png new file mode 100644 index 000000000..603bfe1cf Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_topleft.png differ diff --git a/radio/src/bitmaps/480x272/default_theme/thumb.png b/radio/src/bitmaps/480x272/default_theme/thumb.png new file mode 100644 index 000000000..908575505 Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/thumb.png differ diff --git a/radio/src/bitmaps/480x272/bmp_splash.png b/radio/src/bitmaps/480x272/splash.png similarity index 100% rename from radio/src/bitmaps/480x272/bmp_splash.png rename to radio/src/bitmaps/480x272/splash.png diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 26a4b56d5..86070e89b 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -881,6 +881,13 @@ enum BluetoothModes { #endif }; +enum UartSampleModes { + UART_SAMPLE_MODE_NORMAL = 0, + UART_SAMPLE_MODE_ONEBIT, + + UART_SAMPLE_MODE_MAX = UART_SAMPLE_MODE_ONEBIT +}; + // PXX2 constants #define PXX2_LEN_REGISTRATION_ID 8 #define PXX2_LEN_RX_NAME 8 diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 08afe8281..5c17f8723 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -705,7 +705,7 @@ PACK(struct TrainerData { #if defined(COLORLCD) #define SPLASH_MODE uint8_t splashSpares:3 #else - #define SPLASH_MODE uint8_t splashMode:1; uint8_t splashSpare:2 + #define SPLASH_MODE int8_t splashMode:3 #endif #if defined(PCBXLITES) @@ -842,6 +842,8 @@ PACK(struct RadioData { char ownerRegistrationID[PXX2_LEN_REGISTRATION_ID]; GYRO_FIELDS + + NOBACKUP(int8_t uartSampleMode:2); // See UartSampleModes }); #undef SWITCHES_WARNING_DATA @@ -948,22 +950,22 @@ static inline void check_struct() CHKSIZE(TrainerData, 16); #if defined(PCBXLITES) - CHKSIZE(RadioData, 860); + CHKSIZE(RadioData, 861); CHKSIZE(ModelData, 6157); #elif defined(PCBXLITE) - CHKSIZE(RadioData, 858); + CHKSIZE(RadioData, 859); CHKSIZE(ModelData, 6157); #elif defined(PCBX7) - CHKSIZE(RadioData, 864); + CHKSIZE(RadioData, 865); CHKSIZE(ModelData, 6157); #elif defined(PCBX9E) - CHKSIZE(RadioData, 960); + CHKSIZE(RadioData, 961); CHKSIZE(ModelData, 6614); #elif defined(PCBX9D) || defined(PCBX9DP) - CHKSIZE(RadioData, 898); + CHKSIZE(RadioData, 899); CHKSIZE(ModelData, 6604); #elif defined(PCBHORUS) - CHKSIZE(RadioData, 901); + CHKSIZE(RadioData, 902); CHKSIZE(ModelData, 11020); #endif diff --git a/radio/src/gui/128x64/gui.h b/radio/src/gui/128x64/gui.h index 59648a0f9..6675f252d 100644 --- a/radio/src/gui/128x64/gui.h +++ b/radio/src/gui/128x64/gui.h @@ -288,7 +288,6 @@ void drawCheckBox(coord_t x, coord_t y, uint8_t value, LcdFlags attr); extern const unsigned char sticks[] ; void drawSplash(); -void drawSecondSplash(); void drawScreenIndex(uint8_t index, uint8_t count, uint8_t attr); void drawStick(coord_t centrex, int16_t xval, int16_t yval); void drawPotsBars(); diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 12ddf963d..08f1072c1 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -1576,6 +1576,7 @@ void menuModelSetup(event_t event) if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { const char * title = getMultiOptionTitle(moduleIdx); + if (!title) break; lcdDrawText(INDENT_WIDTH, y, title); if (title == STR_MULTI_RFTUNE) { lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT); diff --git a/radio/src/gui/128x64/splash.cpp b/radio/src/gui/128x64/splash.cpp index d30fa7def..3f507ffbc 100644 --- a/radio/src/gui/128x64/splash.cpp +++ b/radio/src/gui/128x64/splash.cpp @@ -35,27 +35,13 @@ void drawSplash() #if MENUS_LOCK == 1 if (readonly == false) { - lcdDrawFilledRect((LCD_W-(sizeof(TR_UNLOCKED)-1)*FW)/2 - 9, 50, (sizeof(TR_UNLOCKED)-1)*FW+16, 11, SOLID, ERASE|ROUND); - lcdDrawText((LCD_W-(sizeof(TR_UNLOCKED)-1)*FW)/2 , 53, STR_UNLOCKED); + lcdDrawFilledRect((LCD_W - (sizeof(TR_UNLOCKED) - 1) * FW) / 2 - 9, 50, + (sizeof(TR_UNLOCKED) - 1) * FW + 16, 11, SOLID, + ERASE | ROUND); + lcdDrawText((LCD_W - (sizeof(TR_UNLOCKED) - 1) * FW) / 2, 53, STR_UNLOCKED); } #endif lcdRefresh(); } #endif - -#if defined(FRSKY_RELEASE) -const unsigned char splashdata2[] = { - 'S','F','S',0, - #include "bitmaps/128x64/splash_frsky.lbm" - 'S','F','E',0 }; - -const unsigned char * const splash2_lbm = splashdata2+4; - -void drawSecondSplash() -{ - lcdClear(); - lcdDraw1bitBitmap(0, 0, splash2_lbm, 0, 0); - lcdRefresh(); -} -#endif diff --git a/radio/src/gui/212x64/gui.h b/radio/src/gui/212x64/gui.h index c9006446b..f1e607646 100644 --- a/radio/src/gui/212x64/gui.h +++ b/radio/src/gui/212x64/gui.h @@ -64,7 +64,6 @@ extern uint8_t noHighlightCounter; void drawCheckBox(coord_t x, coord_t y, uint8_t value, LcdFlags attr); void drawSlider(coord_t x, coord_t y, uint8_t value, uint8_t max, uint8_t attr); void drawSplash(); -void drawSecondSplash(); void drawScreenIndex(uint8_t index, uint8_t count, uint8_t attr); void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uint16_t count, uint8_t visible); void drawGauge(coord_t x, coord_t y, coord_t w, coord_t h, int32_t val, int32_t max); diff --git a/radio/src/gui/212x64/splash.cpp b/radio/src/gui/212x64/splash.cpp index f9938c0c1..0de33f1d2 100644 --- a/radio/src/gui/212x64/splash.cpp +++ b/radio/src/gui/212x64/splash.cpp @@ -34,26 +34,13 @@ void drawSplash() #if MENUS_LOCK == 1 if (readonly == false) { - lcdDrawFilledRect((LCD_W-(sizeof(TR_UNLOCKED)-1)*FW)/2 - 9, 50, (sizeof(TR_UNLOCKED)-1)*FW+16, 11, SOLID, ERASE|ROUND); - lcdDrawText((LCD_W-(sizeof(TR_UNLOCKED)-1)*FW)/2 , 53, STR_UNLOCKED); + lcdDrawFilledRect((LCD_W - (sizeof(TR_UNLOCKED) - 1) * FW) / 2 - 9, 50, + (sizeof(TR_UNLOCKED) - 1) * FW + 16, 11, SOLID, + ERASE | ROUND); + lcdDrawText((LCD_W - (sizeof(TR_UNLOCKED) - 1) * FW) / 2, 53, STR_UNLOCKED); } #endif lcdRefresh(); } #endif - -#if defined(FRSKY_RELEASE) -const unsigned char splashdata2[] = { - 'S','F','S',0, - #include "bitmaps/212x64/splash_frsky.lbm" - 'S','F','E',0 }; -const unsigned char * const splash2_lbm = splashdata2+4; - -void drawSecondSplash() -{ - lcdClear(); - lcdDrawBitmap(0, 0, splash2_lbm); - lcdRefresh(); -} -#endif diff --git a/radio/src/gui/colorlcd/bitmaps.cpp b/radio/src/gui/colorlcd/bitmaps.cpp index c079a4225..1d8ed9670 100644 --- a/radio/src/gui/colorlcd/bitmaps.cpp +++ b/radio/src/gui/colorlcd/bitmaps.cpp @@ -195,8 +195,7 @@ const uint8_t LBM_SWIPE_RIGHT[] = { BitmapBuffer * calibStick = nullptr; BitmapBuffer * calibStickBackground = nullptr; BitmapBuffer * calibTrackpBackground = nullptr; -BitmapBuffer * calibRadioPict = nullptr; -BitmapBuffer * modelselIconBitmap = nullptr; +//BitmapBuffer * modelselIconBitmap = nullptr; BitmapBuffer * modelselSdFreeBitmap = nullptr; BitmapBuffer * modelselModelQtyBitmap = nullptr; BitmapBuffer * modelselModelNameBitmap = nullptr; diff --git a/radio/src/gui/colorlcd/bitmaps.h b/radio/src/gui/colorlcd/bitmaps.h index 3c6512153..60950a707 100644 --- a/radio/src/gui/colorlcd/bitmaps.h +++ b/radio/src/gui/colorlcd/bitmaps.h @@ -74,7 +74,7 @@ extern const uint8_t LBM_SWIPE_CIRCLE[]; extern const uint8_t LBM_SWIPE_LEFT[]; extern const uint8_t LBM_SWIPE_RIGHT[]; -extern BitmapBuffer * modelselIconBitmap; +//extern BitmapBuffer * modelselIconBitmap; extern BitmapBuffer * modelselSdFreeBitmap; extern BitmapBuffer * modelselModelQtyBitmap; extern BitmapBuffer * modelselModelNameBitmap; @@ -86,7 +86,6 @@ extern BitmapBuffer * modelselWizardBackground; extern BitmapBuffer * calibStick; extern BitmapBuffer * calibStickBackground; extern BitmapBuffer * calibTrackpBackground; -extern BitmapBuffer * calibRadioPict; // Channels monitor bitmaps extern BitmapBuffer * chanMonLockedBitmap; diff --git a/radio/src/gui/colorlcd/channel_bar.h b/radio/src/gui/colorlcd/channel_bar.h index 5c1afb1ad..c9df34879 100644 --- a/radio/src/gui/colorlcd/channel_bar.h +++ b/radio/src/gui/colorlcd/channel_bar.h @@ -58,18 +58,30 @@ class MixerChannelBar : public ChannelBar chanVal = limit(-VIEW_CHANNELS_LIMIT_PCT, chanVal, VIEW_CHANNELS_LIMIT_PCT); // Draw Background - dc->drawSolidFilledRect(0, 0, width(), height(), BARGRAPH_BGCOLOR); + dc->drawSolidFilledRect(0, 0, width(), height(), FIELD_BGCOLOR); // Draw mixer bar if (chanVal > 0) { - dc->drawSolidFilledRect(0 + width() / 2, 0, divRoundClosest(chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2), height(), BARGRAPH2_COLOR); - dc->drawNumber(width() / 2 - 10, 0, displayVal, FONT(XS) | DEFAULT_COLOR | RIGHT, 0, nullptr, "%"); - } - else if (chanVal < 0) { + + dc->drawSolidFilledRect( + 0 + width() / 2, 0, + divRoundClosest(chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2), + height(), FOCUS_BGCOLOR); + + dc->drawNumber(width() / 2 - 10, 0, displayVal, + FONT(XS) | DEFAULT_COLOR | RIGHT, 0, nullptr, "%"); + + } else if (chanVal < 0) { + const unsigned endpoint = width() / 2; - const unsigned size = divRoundClosest(-chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2); - dc->drawSolidFilledRect(endpoint - size, 0, size, height(), BARGRAPH2_COLOR); - dc->drawNumber(10 + width() / 2, 0, displayVal, FONT(XS) | DEFAULT_COLOR, 0, nullptr, "%"); + const unsigned size = + divRoundClosest(-chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2); + + dc->drawSolidFilledRect(endpoint - size, 0, size, height(), + FOCUS_BGCOLOR); + + dc->drawNumber(10 + width() / 2, 0, displayVal, + FONT(XS) | DEFAULT_COLOR, 0, nullptr, "%"); } // Draw middle bar @@ -100,36 +112,47 @@ class OutputChannelBar : public ChannelBar int chanVal = calcRESXto100(channelOutputs[channel]); int displayVal = chanVal; - chanVal = limit(-VIEW_CHANNELS_LIMIT_PCT, chanVal, VIEW_CHANNELS_LIMIT_PCT); + chanVal = limit(-VIEW_CHANNELS_LIMIT_PCT, chanVal, + VIEW_CHANNELS_LIMIT_PCT); // Draw Background - dc->drawSolidFilledRect(0, 0, width(), height(), BARGRAPH_BGCOLOR); + dc->drawSolidFilledRect(0, 0, width(), height(), FIELD_BGCOLOR); // Draw output bar if (chanVal > 0) { - dc->drawSolidFilledRect(width() / 2, 0, divRoundClosest(chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2), height(), BARGRAPH1_COLOR); - dc->drawNumber(width() / 2 - 10, 0, displayVal, FONT(XS) | DEFAULT_COLOR | RIGHT, 0, nullptr, "%"); - } - else if (chanVal < 0) { + + dc->drawSolidFilledRect( + width() / 2, 0, + divRoundClosest(chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2), + height(), HIGHLIGHT_COLOR); + + dc->drawNumber(width() / 2 - 10, 0, displayVal, + FONT(XS) | DEFAULT_COLOR | RIGHT, 0, nullptr, "%"); + + } else if (chanVal < 0) { unsigned endpoint = width() / 2; - unsigned size = divRoundClosest(-chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2); - dc->drawSolidFilledRect(endpoint - size, 0, size, height(), BARGRAPH1_COLOR); - dc->drawNumber(width() / 2 + 10, 0, displayVal, FONT(XS) | DEFAULT_COLOR, 0, nullptr, "%"); + unsigned size = + divRoundClosest(-chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2); + + dc->drawSolidFilledRect(endpoint - size, 0, size, height(), + HIGHLIGHT_COLOR); + + dc->drawNumber(width() / 2 + 10, 0, displayVal, + FONT(XS) | DEFAULT_COLOR, 0, nullptr, "%"); } // Draw middle bar dc->drawSolidVerticalLine(width() / 2, 0, height(), DEFAULT_COLOR); // Draw output limits bars - LimitData * ld = limitAddress(channel); + LimitData* ld = limitAddress(channel); if (ld && ld->revert) { - drawOutputBarLimits(dc, posOnBar(-100 - ld->max / 10), posOnBar(100 - ld->min / 10)); - //lcd->drawBitmap(x - X_OFFSET + 7, y + 25, chanMonInvertedBitmap); + drawOutputBarLimits(dc, posOnBar(-100 - ld->max / 10), + posOnBar(100 - ld->min / 10)); + } else if (ld) { + drawOutputBarLimits(dc, posOnBar(-100 + ld->min / 10), + posOnBar(100 + ld->max / 10)); } - else if (ld) { - drawOutputBarLimits(dc, posOnBar(-100 + ld->min / 10), posOnBar(100 + ld->max / 10)); - } - } inline unsigned posOnBar(int value_to100) @@ -193,13 +216,13 @@ class ComboChannelBar : public ChannelBar // Override icon #if defined(OVERRIDE_CHANNEL_FUNCTION) if (safetyCh[channel] != OVERRIDE_CHANNEL_UNDEFINED) - dc->drawBitmap(0, 1, chanMonLockedBitmap); + dc->drawMask(0, 1, chanMonLockedBitmap, DEFAULT_COLOR); #endif // Channel reverted icon LimitData * ld = limitAddress(channel); if (ld && ld->revert) { - lcd->drawBitmap(0, 20, chanMonInvertedBitmap); + lcd->drawMask(0, 20, chanMonInvertedBitmap, DEFAULT_COLOR); } } @@ -215,4 +238,4 @@ class ComboChannelBar : public ChannelBar protected: int value = 0; -}; \ No newline at end of file +}; diff --git a/radio/src/gui/colorlcd/colors.h b/radio/src/gui/colorlcd/colors.h index 8d0991676..93c83152c 100644 --- a/radio/src/gui/colorlcd/colors.h +++ b/radio/src/gui/colorlcd/colors.h @@ -27,50 +27,58 @@ // TODO common code, not in target enum LcdColorIndex { - DEFAULT_COLOR_INDEX, - DEFAULT_BGCOLOR_INDEX, - FOCUS_COLOR_INDEX, - FOCUS_BGCOLOR_INDEX, - TEXT_STATUSBAR_COLOR_INDEX, - LINE_COLOR_INDEX, - MENU_LINE_COLOR_INDEX = LINE_COLOR_INDEX, - CHECKBOX_COLOR_INDEX, - SCROLLBAR_COLOR_INDEX, - MENU_BGCOLOR_INDEX, - MENU_HIGHLIGHT_BGCOLOR_INDEX /*= MENU_BGCOLOR_INDEX*/, - MENU_COLOR_INDEX, - MENU_TITLE_DISABLE_COLOR_INDEX, - HEADER_COLOR_INDEX, ALARM_COLOR_INDEX, - HIGHLIGHT_COLOR_INDEX, - MENU_HIGHLIGHT_COLOR_INDEX = HIGHLIGHT_COLOR_INDEX, - TEXT_DISABLE_COLOR_INDEX, - DISABLE_COLOR_INDEX, - CURVE_COLOR_INDEX, - CURVE_AXIS_COLOR_INDEX, - CURVE_CURSOR_COLOR_INDEX, - HEADER_BGCOLOR_INDEX, - HEADER_ICON_BGCOLOR_INDEX, - HEADER_CURRENT_BGCOLOR_INDEX, - TITLE_BGCOLOR_INDEX, - TRIM_BGCOLOR_INDEX, - TRIM_SHADOW_COLOR_INDEX, - MAINVIEW_PANES_COLOR_INDEX, - MAINVIEW_GRAPHICS_COLOR_INDEX, - OVERLAY_COLOR_INDEX, - CUSTOM_COLOR_INDEX, BARGRAPH1_COLOR_INDEX, BARGRAPH2_COLOR_INDEX, BARGRAPH_BGCOLOR_INDEX, - SCROLLBOX_COLOR_INDEX, - TABLE_HEADER_BGCOLOR_INDEX, - TABLE_BGCOLOR_INDEX, BATTERY_CHARGE_COLOR_INDEX, + CHECKBOX_COLOR_INDEX, + CURVE_AXIS_COLOR_INDEX, + CURVE_COLOR_INDEX, + CURVE_CURSOR_COLOR_INDEX, + CUSTOM_COLOR_INDEX, + DEFAULT_BGCOLOR_INDEX, + DEFAULT_COLOR_INDEX, + DISABLE_COLOR_INDEX, + EDIT_MARKER_COLOR_INDEX, + FIELD_BGCOLOR_INDEX, + FIELD_FRAME_COLOR_INDEX, + FOCUS_BGCOLOR_INDEX, + FOCUS_COLOR_INDEX, + HEADER_BGCOLOR_INDEX, + HEADER_COLOR_INDEX, + HEADER_CURRENT_BGCOLOR_INDEX, + HEADER_ICON_BGCOLOR_INDEX, + HIGHLIGHT_COLOR_INDEX, + LINE_COLOR_INDEX, + MENU_HIGHLIGHT_BGCOLOR_INDEX, + MAINVIEW_GRAPHICS_COLOR_INDEX, + MAINVIEW_PANES_COLOR_INDEX, + MENU_BGCOLOR_INDEX, + MENU_COLOR_INDEX, + MENU_HIGHLIGHT_COLOR_INDEX, + MENU_LINE_COLOR_INDEX, + MENU_TITLE_DISABLE_COLOR_INDEX, + OVERLAY_COLOR_INDEX, + SCROLLBAR_COLOR_INDEX, + SCROLLBOX_COLOR_INDEX, + TABLE_BGCOLOR_INDEX, + TABLE_HEADER_BGCOLOR_INDEX, + TEXT_DISABLE_COLOR_INDEX, + TEXT_STATUSBAR_COLOR_INDEX, + TITLE_BGCOLOR_INDEX, + TRIM_BGCOLOR_INDEX, + TRIM_SHADOW_COLOR_INDEX, // this one MUST be last LCD_COLOR_COUNT, }; + +//#define MENU_HIGHLIGHT_COLOR_INDEX HIGHLIGHT_COLOR_INDEX +//#define MENU_LINE_COLOR_INDEX LINE_COLOR_INDEX + + // // Basic color definitions // @@ -116,6 +124,9 @@ inline void lcdSetColor(uint16_t color) #define DEFAULT_BGCOLOR COLOR(DEFAULT_BGCOLOR_INDEX) #define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) #define DISABLE_COLOR COLOR(DISABLE_COLOR_INDEX) +#define EDIT_MARKER_COLOR COLOR(EDIT_MARKER_COLOR_INDEX) +#define FIELD_BGCOLOR COLOR(FIELD_BGCOLOR_INDEX) +#define FIELD_FRAME_COLOR COLOR(FIELD_FRAME_COLOR_INDEX) #define FOCUS_BGCOLOR COLOR(FOCUS_BGCOLOR_INDEX) #define FOCUS_COLOR COLOR(FOCUS_COLOR_INDEX) #define HEADER_BGCOLOR COLOR(HEADER_BGCOLOR_INDEX) diff --git a/radio/src/gui/colorlcd/curve.cpp b/radio/src/gui/colorlcd/curve.cpp index ee62a79d0..ce796d63d 100644 --- a/radio/src/gui/colorlcd/curve.cpp +++ b/radio/src/gui/colorlcd/curve.cpp @@ -27,21 +27,20 @@ void Curve::drawBackground(BitmapBuffer * dc) { - lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); - dc->clear(CUSTOM_COLOR); + dc->clear(FIELD_BGCOLOR); // Axis - dc->drawSolidHorizontalLine(0, height()/2, width(), DISABLE_COLOR); - dc->drawSolidVerticalLine(width()/2, 0, height(), DISABLE_COLOR); + dc->drawSolidHorizontalLine(0, height()/2, width(), FIELD_FRAME_COLOR); + dc->drawSolidVerticalLine(width()/2, 0, height(), FIELD_FRAME_COLOR); // Extra lines - dc->drawVerticalLine(width()/4, 0, height(), STASHED, DISABLE_COLOR); - dc->drawVerticalLine(width()*3/4, 0, height(), STASHED, DISABLE_COLOR); - dc->drawHorizontalLine(0, height()/4, width(), STASHED, DISABLE_COLOR); - dc->drawHorizontalLine(0, height()*3/4, width(), STASHED, DISABLE_COLOR); + dc->drawVerticalLine(width()/4, 0, height(), STASHED, FIELD_FRAME_COLOR); + dc->drawVerticalLine(width()*3/4, 0, height(), STASHED, FIELD_FRAME_COLOR); + dc->drawHorizontalLine(0, height()/4, width(), STASHED, FIELD_FRAME_COLOR); + dc->drawHorizontalLine(0, height()*3/4, width(), STASHED, FIELD_FRAME_COLOR); // Outside border - // dc->drawSolidRect(0, 0, width(), height(), 1, DEFAULT_COLOR); + dc->drawSolidRect(0, 0, width(), height(), 1, FIELD_FRAME_COLOR); } coord_t Curve::getPointX(int x) const @@ -89,17 +88,17 @@ void Curve::drawPosition(BitmapBuffer * dc) coord_t y = getPointY(valueY); // the 2 lines - dc->drawSolidHorizontalLine(0, y, width(), CURVE_CURSOR_COLOR); - dc->drawSolidVerticalLine(x, 0, height(), CURVE_CURSOR_COLOR); + dc->drawSolidHorizontalLine(0, y, width(), HIGHLIGHT_COLOR); + dc->drawSolidVerticalLine(x, 0, height(), HIGHLIGHT_COLOR); // the point (white inside) - dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT, CURVE_CURSOR_COLOR); - dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT_CENTER, DEFAULT_BGCOLOR); + dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT, HIGHLIGHT_COLOR); + dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT_CENTER, FOCUS_COLOR); char coords[16]; strAppendSigned(strAppend(strAppendSigned(coords, calcRESXto100(valueX)), ","), calcRESXto100(valueY)); - dc->drawSolidFilledRect(10, 11, 1 + getTextWidth(coords, 0, FONT(XS)), 17, CURVE_CURSOR_COLOR); - dc->drawText(11, 10, coords, FONT(XS)|DEFAULT_BGCOLOR); + dc->drawSolidFilledRect(10, 11, 1 + getTextWidth(coords, 0, FONT(XS)), 17, HIGHLIGHT_COLOR); + dc->drawText(11, 10, coords, FONT(XS) | FOCUS_COLOR); } void Curve::drawPoint(BitmapBuffer * dc, const CurvePoint & point) diff --git a/radio/src/gui/colorlcd/curveedit.cpp b/radio/src/gui/colorlcd/curveedit.cpp index 9eb248083..9de50061f 100644 --- a/radio/src/gui/colorlcd/curveedit.cpp +++ b/radio/src/gui/colorlcd/curveedit.cpp @@ -42,48 +42,73 @@ void CurveDataEdit::update() grid.setMarginRight(parent->width() - rect.w + 5); coord_t boxWidth = rect.w / 5; - coord_t boxHeight = (rect.h - 6) / 3; + coord_t boxHeight = PAGE_LINE_HEIGHT; CurveHeader & curve = g_model.curves[index]; uint8_t curvePointsCount = 5 + curve.points; // Point number for (uint8_t i = 0; i < curvePointsCount; i++) { - new StaticText(this, {i * boxWidth, 10, boxWidth, boxHeight}, std::to_string(i + 1), 0, RIGHT | TEXT_DISABLE_COLOR); + auto txt = new StaticText( + this, + {i * boxWidth + PAGE_PADDING, 10, boxWidth - PAGE_PADDING, boxHeight}, + std::to_string(i + 1) + " ", 0, RIGHT | DEFAULT_COLOR); + txt->setBackgroundColor(FIELD_FRAME_COLOR); } - grid.spacer(rect.h / 3); + grid.spacer(boxHeight + 10 + PAGE_PADDING); // x value if (curve.type == CURVE_TYPE_CUSTOM) { // Adjustable points for custom curves for (uint8_t i = 0; i < curvePointsCount; i++) { - int8_t * points = curveAddress(index); - auto pointEdit = new NumberEdit(this, {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), 10 + boxHeight, coord_t(boxWidth - PAGE_LINE_SPACING), boxHeight - 12}, - i <= 1 ? -100 : points[curvePointsCount + i - 2], - i >= curvePointsCount - 2 ? 100 : points[curvePointsCount + i], - GET_VALUE(i == 0 ? -100 : i == curvePointsCount - 1 ? 100 : points[curvePointsCount + i - 1]), - [=](int32_t newValue) { - points[curvePointsCount + i - 1] = newValue; - SET_DIRTY(); - curveEdit->updatePreview(); - }, 0, RIGHT); + int8_t* points = curveAddress(index); + auto pointEdit = new NumberEdit( + this, + {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), + grid.getWindowHeight(), coord_t(boxWidth - PAGE_LINE_SPACING), + boxHeight}, + i <= 1 ? -100 : points[curvePointsCount + i - 2], + i >= curvePointsCount - 2 ? 100 : points[curvePointsCount + i], + GET_VALUE(i == 0 ? -100 + : i == curvePointsCount - 1 + ? 100 + : points[curvePointsCount + i - 1]), + [=](int32_t newValue) { + points[curvePointsCount + i - 1] = newValue; + SET_DIRTY(); + curveEdit->updatePreview(); + }, + 0, RIGHT); if (i == 0 || i == curvePointsCount - 1) { pointEdit->disable(); } } - } - else { + } else { for (uint8_t i = 0; i < curvePointsCount; i++) { - new StaticText(this, {i * boxWidth, 10 + boxHeight, boxWidth, boxHeight}, std::to_string(-100 + 200 * i / (5 + curve.points - 1)), 0, RIGHT | TEXT_DISABLE_COLOR); + new StaticText(this, {i * boxWidth, grid.getWindowHeight(), boxWidth, boxHeight}, + std::to_string(-100 + 200 * i / (5 + curve.points - 1)), 0, + RIGHT | DEFAULT_COLOR); } } + grid.spacer(boxHeight + PAGE_PADDING); // y value for (uint8_t i = 0; i < curvePointsCount; i++) { - int8_t * points = curveAddress(index); - new NumberEdit(this, {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), 10 + 2 * boxHeight, coord_t(boxWidth - PAGE_LINE_SPACING), boxHeight - 12}, -100, 100, - GET_VALUE(points[i]), [=](int32_t newValue) { points[i] = newValue; SET_DIRTY(); curveEdit->updatePreview(); }, 0, RIGHT); + int8_t* points = curveAddress(index); + new NumberEdit( + this, + {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), + grid.getWindowHeight(), + coord_t(boxWidth - PAGE_LINE_SPACING), + boxHeight}, + -100, 100, GET_VALUE(points[i]), + [=](int32_t newValue) { + points[i] = newValue; + SET_DIRTY(); + curveEdit->updatePreview(); + }, + 0, RIGHT); } setInnerWidth(curvePointsCount * boxWidth); @@ -92,8 +117,8 @@ void CurveDataEdit::update() void CurveDataEdit::paint(BitmapBuffer * dc) { dc->clear(DEFAULT_BGCOLOR); - dc->drawSolidHorizontalLine(0, rect.h / 3, getInnerWidth(), 0); - dc->drawSolidHorizontalLine(0, 2 * rect.h / 3, getInnerWidth(), 0); + // dc->drawSolidHorizontalLine(0, rect.h / 3, getInnerWidth(), 0); + // dc->drawSolidHorizontalLine(0, 2 * rect.h / 3, getInnerWidth(), 0); drawHorizontalScrollbar(dc); } @@ -125,35 +150,35 @@ void CurveEdit::updatePreview() invalidate(); } -#if defined(HARDWARE_TOUCH) -bool CurveEdit::onTouchEnd(coord_t x, coord_t y) -{ - if (!hasFocus()) { - setFocus(SET_FOCUS_DEFAULT); - } +// #if defined(HARDWARE_TOUCH) +// bool CurveEdit::onTouchEnd(coord_t x, coord_t y) +// { +// if (!hasFocus()) { +// setFocus(SET_FOCUS_DEFAULT); +// } - CurveKeyboard::show(this, isCustomCurve()); +// CurveKeyboard::show(this, isCustomCurve()); - CurveHeader & curve = g_model.curves[index]; - for (int i=0; i<5 + curve.points; i++) { - if (i != current) { - point_t point = getPoint(index, i); - if (abs(preview.getPointX(point.x) - x) <= 10 && abs(preview.getPointY(point.y) - y) <= 10) { - current = i; - updatePreview(); - break; - } - } - } +// CurveHeader & curve = g_model.curves[index]; +// for (int i=0; i<5 + curve.points; i++) { +// if (i != current) { +// point_t point = getPoint(index, i); +// if (abs(preview.getPointX(point.x) - x) <= 10 && abs(preview.getPointY(point.y) - y) <= 10) { +// current = i; +// updatePreview(); +// break; +// } +// } +// } - return true; -} +// return true; +// } -void CurveEdit::onFocusLost() -{ - CurveKeyboard::hide(); -} -#endif +// void CurveEdit::onFocusLost() +// { +// CurveKeyboard::hide(); +// } +// #endif void CurveEdit::next() { diff --git a/radio/src/gui/colorlcd/curveedit.h b/radio/src/gui/colorlcd/curveedit.h index 11efa8f8a..d76de9553 100644 --- a/radio/src/gui/colorlcd/curveedit.h +++ b/radio/src/gui/colorlcd/curveedit.h @@ -62,11 +62,10 @@ class CurveEdit: public FormField void onEvent(event_t event) override; -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; - - void onFocusLost() override; -#endif +// #if defined(HARDWARE_TOUCH) +// bool onTouchEnd(coord_t x, coord_t y) override; +// void onFocusLost() override; +// #endif protected: Curve preview; diff --git a/radio/src/gui/colorlcd/draw_functions.cpp b/radio/src/gui/colorlcd/draw_functions.cpp index 65f07c39b..0b2ca84f3 100644 --- a/radio/src/gui/colorlcd/draw_functions.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -74,68 +74,68 @@ void drawValueOrGVar(BitmapBuffer * dc, coord_t x, coord_t y, gvar_t value, gvar } } +constexpr LcdFlags splash_background_color = + COLOR2FLAGS(((0xC >> 3) << 11) | ((0x3F >> 2) << 5) | (0x66 >> 3)); + void drawSleepBitmap() { lcd->reset(); lcd->clear(); - const BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(OpenTxTheme::instance()->getFilePath("sleep.bmp")); + const BitmapBuffer* bitmap = OpenTxTheme::instance()->shutdown; if (bitmap) { - lcd->drawBitmap((LCD_W-bitmap->width())/2, (LCD_H-bitmap->height())/2, bitmap); - delete bitmap; + lcd->drawMask((LCD_W-bitmap->width())/2, (LCD_H-bitmap->height())/2, + bitmap, splash_background_color); } lcdRefresh(); } -// -// TODO: move shutdown animation to Theme -// #define SHUTDOWN_CIRCLE_DIAMETER 150 -void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char * message) + +void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, + const char* message) { if (totalDuration == 0) return; - static const BitmapBuffer* shutdown = nullptr; - if (!shutdown) { - shutdown = BitmapBuffer::loadBitmap( - OpenTxTheme::instance()->getFilePath("shutdown.bmp")); - } + static const BitmapBuffer * shutdown = OpenTxTheme::instance()->shutdown; lcd->reset(); - + lcd->clear(splash_background_color); + if (shutdown) { - OpenTxTheme::instance()->drawBackground(lcd); - lcd->drawBitmap((LCD_W - shutdown->width()) / 2, - (LCD_H - shutdown->height()) / 2, shutdown); + lcd->drawMask((LCD_W - shutdown->width()) / 2, + (LCD_H - shutdown->height()) / 2, shutdown, + COLOR2FLAGS(WHITE)); + int quarter = duration / (totalDuration / 5); if (quarter >= 1) lcd->drawBitmapPattern(LCD_W / 2, (LCD_H - SHUTDOWN_CIRCLE_DIAMETER) / 2, - LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, 0, + LBM_SHUTDOWN_CIRCLE, COLOR2FLAGS(WHITE), 0, SHUTDOWN_CIRCLE_DIAMETER / 2); if (quarter >= 2) lcd->drawBitmapPattern(LCD_W / 2, LCD_H / 2, LBM_SHUTDOWN_CIRCLE, - DEFAULT_COLOR, SHUTDOWN_CIRCLE_DIAMETER / 2, + COLOR2FLAGS(WHITE), SHUTDOWN_CIRCLE_DIAMETER / 2, SHUTDOWN_CIRCLE_DIAMETER / 2); if (quarter >= 3) lcd->drawBitmapPattern((LCD_W - SHUTDOWN_CIRCLE_DIAMETER) / 2, LCD_H / 2, - LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, + LBM_SHUTDOWN_CIRCLE, COLOR2FLAGS(WHITE), SHUTDOWN_CIRCLE_DIAMETER, SHUTDOWN_CIRCLE_DIAMETER / 2); if (quarter >= 4) lcd->drawBitmapPattern((LCD_W - SHUTDOWN_CIRCLE_DIAMETER) / 2, (LCD_H - SHUTDOWN_CIRCLE_DIAMETER) / 2, - LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, + LBM_SHUTDOWN_CIRCLE, COLOR2FLAGS(WHITE), SHUTDOWN_CIRCLE_DIAMETER * 3 / 2, SHUTDOWN_CIRCLE_DIAMETER / 2); } else { - lcd->clear(); + //lcd->clear(); int quarter = duration / (totalDuration / 5); for (int i = 1; i <= 4; i++) { if (quarter >= i) { lcd->drawSolidFilledRect(LCD_W / 2 - 70 + 24 * i, LCD_H / 2 - 10, 20, - 20, DEFAULT_BGCOLOR); + 20, COLOR2FLAGS(WHITE)); } } } @@ -317,19 +317,23 @@ coord_t drawGPSCoord(BitmapBuffer * dc, coord_t x, coord_t y, int32_t value, con } *tmp++ = direction[value>=0 ? 0 : 1]; *tmp = '\0'; - dc->drawText(x, y, s, flags); + x = dc->drawText(x, y, s, flags); return x; } void drawGPSPosition(BitmapBuffer * dc, coord_t x, coord_t y, int32_t longitude, int32_t latitude, LcdFlags flags) { - if (flags & EXPANDED) { - drawGPSCoord(dc, x, y, latitude, "NS", flags, true); - drawGPSCoord(dc, x, y + FH, longitude, "EW", flags, true); - } - else { - x = drawGPSCoord(dc, x, y, latitude, "NS", flags, false); - drawGPSCoord(dc, x + 5, y, longitude, "EW", flags, false); + if (flags & PREC1) { + drawGPSCoord(dc, x, y, latitude, "NS", flags, false); + drawGPSCoord(dc, x, y + FH, longitude, "EW", flags, false); + } else { + if (flags & RIGHT) { + x = drawGPSCoord(dc, x, y, longitude, "EW", flags, false); + drawGPSCoord(dc, x - 5, y, latitude, "NS", flags, false); + } else { + x = drawGPSCoord(dc, x, y, latitude, "NS", flags, false); + drawGPSCoord(dc, x + 5, y, longitude, "EW", flags, false); + } } } diff --git a/radio/src/gui/colorlcd/fab_button.h b/radio/src/gui/colorlcd/fab_button.h index 34ee06c19..98b51707c 100644 --- a/radio/src/gui/colorlcd/fab_button.h +++ b/radio/src/gui/colorlcd/fab_button.h @@ -31,7 +31,7 @@ class FabButton: public Button { #if defined(DEBUG_WINDOWS) std::string getName() const override { - return "FabButton(" + std::to_string(icon) + ")"; + return "FabButton"; } #endif diff --git a/radio/src/gui/colorlcd/fullscreen_dialog.cpp b/radio/src/gui/colorlcd/fullscreen_dialog.cpp index 73f139421..97a2f001b 100644 --- a/radio/src/gui/colorlcd/fullscreen_dialog.cpp +++ b/radio/src/gui/colorlcd/fullscreen_dialog.cpp @@ -54,16 +54,14 @@ void FullScreenDialog::paint(BitmapBuffer * dc) FOCUS_COLOR, OPACITY(8)); if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK) { - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, - OpenTxTheme::instance()->asterisk); - } - else if (type == WARNING_TYPE_INFO) { - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, - OpenTxTheme::instance()->busy); - } - else { // confirmation - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, - OpenTxTheme::instance()->question); + dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->error, ALARM_COLOR); + } else if (type == WARNING_TYPE_INFO) { + dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->busy, ALARM_COLOR); + } else { // confirmation + dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->error, ALARM_COLOR); } if (type == WARNING_TYPE_ALERT) { diff --git a/radio/src/gui/colorlcd/layout.cpp b/radio/src/gui/colorlcd/layout.cpp index 10cdf1a96..862168d1f 100644 --- a/radio/src/gui/colorlcd/layout.cpp +++ b/radio/src/gui/colorlcd/layout.cpp @@ -123,8 +123,6 @@ void loadCustomScreens() } auto topbar = viewMain->getTopbar(); - - // TODO: how to reload this guy? topbar->load(); viewMain->setCurrentMainView(0); diff --git a/radio/src/gui/colorlcd/layout.h b/radio/src/gui/colorlcd/layout.h index f611dc034..a1c31a9da 100644 --- a/radio/src/gui/colorlcd/layout.h +++ b/radio/src/gui/colorlcd/layout.h @@ -44,6 +44,7 @@ class LayoutFactory virtual const ZoneOption * getOptions() const = 0; virtual WidgetsContainer * create(LayoutPersistentData * persistentData) const = 0; virtual WidgetsContainer * load(LayoutPersistentData * persistentData) const = 0; + virtual void initPersistentData(LayoutPersistentData * persistentData) const = 0; protected: const char * id; diff --git a/radio/src/gui/colorlcd/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp index b88c97095..66bc3c186 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x4.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x4.cpp @@ -79,6 +79,25 @@ class Layout2x4: public Layout return zone; } + + void paint(BitmapBuffer * dc) override; }; +void Layout2x4::paint(BitmapBuffer* dc) +{ + Layout::paint(dc); + rect_t fullScreen = Layout::getMainZone(); + fullScreen.w /= 2; + if (getOptionValue(OPTION_PANEL1_BACKGROUND)->boolValue) { + dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, + fullScreen.h, COLOR2FLAGS(getOptionValue(OPTION_PANEL1_COLOR)->unsignedValue)); + } + + if (getOptionValue(OPTION_PANEL2_BACKGROUND)->boolValue) { + fullScreen.x += fullScreen.w; + dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, + fullScreen.h, COLOR2FLAGS(getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue)); + } +} + BaseLayoutFactory layout2x4("Layout2x4", "2 x 4", LBM_LAYOUT_2x4, OPTIONS_LAYOUT_2x4); diff --git a/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp b/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp index c3a93ca1b..6be71f9d1 100644 --- a/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp +++ b/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp @@ -89,7 +89,8 @@ void Layout::adjustLayout() (hasTopbar() ? 1 << 0 : 0) | (hasSliders() ? 1 << 1 : 0) | (hasTrims() ? 1 << 2 : 0) | - (hasFlightMode() ? 1 << 3 : 0); + (hasFlightMode() ? 1 << 3 : 0) | + (isMirrored() ? 1 << 4 : 0); if (checkSettings == decorationSettings) { // everything ok, exit! diff --git a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h index 30738471e..3a717a100 100644 --- a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h +++ b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h @@ -28,12 +28,12 @@ #include "widgets/widgets_container_impl.h" #include "layout.h" -#define LAYOUT_COMMON_OPTIONS \ - { STR_TOP_BAR, ZoneOption::Bool }, \ - { STR_FLIGHT_MODE, ZoneOption::Bool }, \ - { STR_SLIDERS, ZoneOption::Bool }, \ - { STR_TRIMS, ZoneOption::Bool }, \ - { STR_MIRROR, ZoneOption::Bool } +#define LAYOUT_COMMON_OPTIONS \ + {STR_TOP_BAR, ZoneOption::Bool, OPTION_VALUE_BOOL(true)}, \ + {STR_FLIGHT_MODE, ZoneOption::Bool, OPTION_VALUE_BOOL(true)}, \ + {STR_SLIDERS, ZoneOption::Bool, OPTION_VALUE_BOOL(true)}, \ + {STR_TRIMS, ZoneOption::Bool, OPTION_VALUE_BOOL(true)}, \ + {STR_MIRROR, ZoneOption::Bool, OPTION_VALUE_BOOL(false)} #define LAYOUT_OPTIONS_END \ { nullptr, ZoneOption::Bool } @@ -150,7 +150,7 @@ class BaseLayoutFactory: public LayoutFactory return layout; } - void initPersistentData(Layout::PersistentData * persistentData) const + void initPersistentData(Layout::PersistentData * persistentData) const override { memset(persistentData, 0, sizeof(Layout::PersistentData)); if (options) { diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp index 68d9e7378..7ba5bfb02 100644 --- a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp @@ -85,25 +85,26 @@ void TopbarImpl::paint(BitmapBuffer * dc) char str[10]; sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]); - dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, MENU_COLOR); - dc->drawText(DATETIME_MIDDLE, DATETIME_LINE1, str, FONT(XS) | CENTERED| MENU_COLOR); + //dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, MENU_COLOR); + dc->drawText(DATETIME_MIDDLE, DATETIME_LINE1, str, FONT(XS) | CENTERED| FOCUS_COLOR); getTimerString(str, getValue(MIXSRC_TX_TIME)); - dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | MENU_COLOR); + dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | FOCUS_COLOR); #if defined(INTERNAL_GPS) if (gpsData.fix) { char s[10]; sprintf(s, "%d", gpsData.numSat); - dc->drawText(LCD_W-148, 4, s, FONT(XS) | CENTERED | MENU_COLOR); + dc->drawText(LCD_W-148, 4, s, FONT(XS) | CENTERED | FOCUS_COLOR); } - dc->drawBitmapPattern(LCD_W-158, 22, LBM_TOPMENU_GPS, (gpsData.fix) ? MENU_COLOR : MENU_TITLE_DISABLE_COLOR); + dc->drawBitmapPattern(LCD_W - 158, 22, LBM_TOPMENU_GPS, + (gpsData.fix) ? FOCUS_COLOR : MENU_TITLE_DISABLE_COLOR); #endif // USB icon if (usbPlugged()) { - LcdFlags flags = MENU_COLOR; + LcdFlags flags = FOCUS_COLOR; if (getSelectedUsbMode() == USB_UNSELECTED_MODE) { flags = MENU_TITLE_DISABLE_COLOR; } @@ -112,7 +113,7 @@ void TopbarImpl::paint(BitmapBuffer * dc) } // Logs else if (isFunctionActive(FUNCTION_LOGS) && BLINK_ON_PHASE) { - dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, FOCUS_COLOR); } // RSSI @@ -120,42 +121,45 @@ void TopbarImpl::paint(BitmapBuffer * dc) const uint8_t rssiBarsHeight[] = {5, 10, 15, 21, 31}; for (unsigned int i = 0; i < DIM(rssiBarsHeight); i++) { uint8_t height = rssiBarsHeight[i]; - dc->drawSolidFilledRect(LCD_W - 90 + i * 6, 38 - height, 4, height, TELEMETRY_RSSI() >= rssiBarsValue[i] ? MENU_COLOR : MENU_TITLE_DISABLE_COLOR); + dc->drawSolidFilledRect(LCD_W - 90 + i * 6, 38 - height, 4, height, + TELEMETRY_RSSI() >= rssiBarsValue[i] + ? FOCUS_COLOR + : MENU_TITLE_DISABLE_COLOR); } #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) if (isModuleXJT(INTERNAL_MODULE) && isExternalAntennaEnabled()) { - dc->drawBitmapPattern(LCD_W-94, 4, LBM_TOPMENU_ANTENNA, MENU_COLOR); + dc->drawBitmapPattern(LCD_W-94, 4, LBM_TOPMENU_ANTENNA, FOCUS_COLOR); } #endif /* Audio volume */ dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_SCALE, MENU_TITLE_DISABLE_COLOR); if (requiredSpeakerVolume == 0 || g_eeGeneral.beepMode == e_mode_quiet) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_0, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_0, FOCUS_COLOR); else if (requiredSpeakerVolume < 7) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_1, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_1, FOCUS_COLOR); else if (requiredSpeakerVolume < 13) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_2, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_2, FOCUS_COLOR); else if (requiredSpeakerVolume < 19) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_3, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_3, FOCUS_COLOR); else - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_4, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_4, FOCUS_COLOR); /* Tx battery */ uint8_t bars = GET_TXBATT_BARS(5); #if defined(USB_CHARGER) if (usbChargerLed()) { - dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT_CHARGING, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT_CHARGING, FOCUS_COLOR); } else { - dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, FOCUS_COLOR); } #else - dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, FOCUS_COLOR); #endif for (unsigned int i = 0; i < 5; i++) { - dc->drawSolidFilledRect(LCD_W - 128 + 4 * i, 30, 2, 8, i >= bars ? MENU_TITLE_DISABLE_COLOR : MENU_COLOR); + dc->drawSolidFilledRect(LCD_W - 128 + 4 * i, 30, 2, 8, i >= bars ? MENU_TITLE_DISABLE_COLOR : FOCUS_COLOR); } #if 0 diff --git a/radio/src/gui/colorlcd/layouts/trims.cpp b/radio/src/gui/colorlcd/layouts/trims.cpp index 013ba9bc6..82dc4f9e3 100644 --- a/radio/src/gui/colorlcd/layouts/trims.cpp +++ b/radio/src/gui/colorlcd/layouts/trims.cpp @@ -44,19 +44,25 @@ void MainViewHorizontalTrim::paint(BitmapBuffer * dc) } // Trim line - lcdSetColor(GREY); // TODO add a color - dc->drawSolidFilledRect(TRIM_SQUARE_SIZE / 2, (height() - TRIM_LINE_WIDTH - 1) / 2, width() - TRIM_SQUARE_SIZE + 1, TRIM_LINE_WIDTH, CUSTOM_COLOR); + dc->drawSolidFilledRect( + TRIM_SQUARE_SIZE / 2, (height() - TRIM_LINE_WIDTH - 1) / 2, + width() - TRIM_SQUARE_SIZE + 1, TRIM_LINE_WIDTH, DEFAULT_COLOR); // Trim square - coord_t x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin); - drawTrimSquare(dc, x, 0, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ : TRIM_BGCOLOR); + coord_t x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value - trimMin), + trimMax - trimMin); + drawTrimSquare(dc, x, 0, + (value < TRIM_MIN || value > TRIM_MAX) + ? HIGHLIGHT_COLOR /* TODO add a color */ + : TRIM_BGCOLOR); // Trim value / small lines on the square if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS) { // TODO DISPLAY_TRIMS_CHANGE - dc->drawNumber(x + (TRIM_SQUARE_SIZE + 1) / 2, 3, divRoundClosest(value * 100, trimMax), FONT(XXS) | FOCUS_COLOR | CENTERED); - } - else { + dc->drawNumber(x + (TRIM_SQUARE_SIZE + 1) / 2, 3, + divRoundClosest(value * 100, trimMax), + FONT(XXS) | FOCUS_COLOR | CENTERED); + } else { if (value >= 0) { dc->drawSolidVerticalLine(x + 4, 3, 9, FOCUS_COLOR); } @@ -79,19 +85,26 @@ void MainViewVerticalTrim::paint(BitmapBuffer * dc) } // Trim line - lcdSetColor(GREY); // TODO add a color - dc->drawSolidFilledRect((width() - TRIM_LINE_WIDTH) / 2, TRIM_SQUARE_SIZE / 2, TRIM_LINE_WIDTH, height() - TRIM_SQUARE_SIZE + 1, CUSTOM_COLOR); + dc->drawSolidFilledRect((width() - TRIM_LINE_WIDTH) / 2, TRIM_SQUARE_SIZE / 2, + TRIM_LINE_WIDTH, height() - TRIM_SQUARE_SIZE + 1, + DEFAULT_COLOR); // Trim square - coord_t y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin); - drawTrimSquare(dc, 0, y, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ : TRIM_BGCOLOR); + coord_t y = height() - TRIM_SQUARE_SIZE - + divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value - trimMin), + trimMax - trimMin); + drawTrimSquare(dc, 0, y, + (value < TRIM_MIN || value > TRIM_MAX) + ? HIGHLIGHT_COLOR /* TODO add a color */ + : TRIM_BGCOLOR); // Trim value / small lines on the square if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS) { // TODO DISPLAY_TRIMS_CHANGE - dc->drawNumber((TRIM_SQUARE_SIZE + 1) / 2, y + 3, divRoundClosest(value * 100, trimMax), FONT(XXS) | FOCUS_COLOR | CENTERED); - } - else { + dc->drawNumber((TRIM_SQUARE_SIZE + 1) / 2, y + 3, + divRoundClosest(value * 100, trimMax), + FONT(XXS) | FOCUS_COLOR | CENTERED); + } else { if (value >= 0) { dc->drawSolidHorizontalLine(3, y + 4, 9, FOCUS_COLOR); } diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index 085bdf3b2..19af410a7 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -42,10 +42,16 @@ class CurveEditWindow : public Page void buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENUCURVE, 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUCURVE, 0, FOCUS_COLOR); char s[16]; strAppendStringWithIndex(s, STR_CV, index + 1); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, s, 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + s, 0, FOCUS_COLOR); } #if LCD_W > LCD_H @@ -230,8 +236,14 @@ class CurveButton : public Button { void paint(BitmapBuffer * dc) override { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); + // bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + } else { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } // curve characteristics if (isCurveUsed(index)) { @@ -301,7 +313,9 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) if (isCurveUsed(index)) { // Curve label - new StaticText(window, grid.getLabelSlot(), getCurveString(1 + index), BUTTON_BACKGROUND, CENTERED); + auto txt = + new StaticText(window, grid.getLabelSlot(), getCurveString(1 + index), + BUTTON_BACKGROUND, CENTERED); // Curve drawing Button * button = new CurveButton(window, grid.getFieldSlot(), index); @@ -323,16 +337,30 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); if (focusIndex == index) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); - } - else { + } else { auto button = new TextButton(window, grid.getLabelSlot(), getCurveString(1 + index)); + button->setPressHandler([=]() { Menu *menu = new Menu(window); menu->addLine(STR_EDIT, [=]() { diff --git a/radio/src/gui/colorlcd/model_curves.h b/radio/src/gui/colorlcd/model_curves.h index a747b50e6..52fe9b1f4 100644 --- a/radio/src/gui/colorlcd/model_curves.h +++ b/radio/src/gui/colorlcd/model_curves.h @@ -30,7 +30,7 @@ class ModelCurvesPage: public PageTab { virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/model_flightmodes.cpp b/radio/src/gui/colorlcd/model_flightmodes.cpp index 1298828d4..26bcfc789 100644 --- a/radio/src/gui/colorlcd/model_flightmodes.cpp +++ b/radio/src/gui/colorlcd/model_flightmodes.cpp @@ -58,6 +58,9 @@ class FlightModeGroup: public FormGroup if (index == getFlightMode()) { dc->drawSolidFilledRect(0, 0, width(), height(), HIGHLIGHT_COLOR); } + else { + dc->drawSolidFilledRect(0, 0, width(), height(), FIELD_BGCOLOR); + } FormGroup::paint(dc); } diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index e810d85ed..9b3cf42fa 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -64,9 +64,18 @@ void GVarButton::paint(BitmapBuffer * dc) currentFlightMode = getFlightMode(); gvarSum = 0; - dc->drawSolidFilledRect(0, 0, nameRectW, rect.h, CURVE_AXIS_COLOR); - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getGVarString(gvarIdx), 0); - dc->drawSizedText(x, y, gvar->name, LEN_GVAR_NAME, 0); + LcdFlags bgColor = FIELD_BGCOLOR; + LcdFlags textColor = 0; + if (hasFocus()) { + bgColor = FOCUS_BGCOLOR; + textColor = FOCUS_COLOR; + } + + dc->drawSolidFilledRect(0, 0, nameRectW, rect.h, bgColor); + dc->drawSolidFilledRect(nameRectW, 0, rect.w - nameRectW, rect.h, FIELD_BGCOLOR); + + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getGVarString(gvarIdx), textColor); + dc->drawSizedText(x, y, gvar->name, LEN_GVAR_NAME, textColor); // values are right aligned x += GVAR_NAME_SIZE; @@ -91,7 +100,8 @@ void GVarButton::paint(BitmapBuffer * dc) } if (!bgFilled) { - dc->drawSolidFilledRect(startX, y, width() - startX, PAGE_LINE_HEIGHT, CURVE_AXIS_COLOR); + dc->drawSolidFilledRect(startX, y, width() - startX, PAGE_LINE_HEIGHT, + FIELD_FRAME_COLOR); bgFilled = true; } @@ -116,7 +126,10 @@ void GVarButton::paint(BitmapBuffer * dc) } } - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? FOCUS_BGCOLOR : DISABLE_COLOR); + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FIELD_FRAME_COLOR); } void GVarButton::drawFlightMode(BitmapBuffer * dc, coord_t x, coord_t y, int fm, LcdFlags attr) @@ -131,17 +144,17 @@ void GVarRenderer::paint(BitmapBuffer * dc) lastFlightMode = getFlightMode(); FlightModeData * fmData = &g_model.flightModeData[lastFlightMode]; lastGVar = fmData->gvars[index]; - coord_t x = drawStringWithIndex(dc, 0, FIELD_PADDING_TOP, STR_GV, index + 1, MENU_COLOR, nullptr, "="); + coord_t x = drawStringWithIndex(dc, 0, FIELD_PADDING_TOP, STR_GV, index + 1, + FOCUS_COLOR, nullptr, "="); if (lastGVar > GVAR_MAX) { uint8_t fm = lastGVar - GVAR_MAX - 1; if (fm >= lastFlightMode) fm++; char label[16]; getFlightModeString(label, fm + 1); - dc->drawSizedText(x, FIELD_PADDING_TOP, label, strlen(label), MENU_COLOR); - } - else { - drawGVarValue(dc, x, FIELD_PADDING_TOP, index, lastGVar, MENU_COLOR); + dc->drawSizedText(x, FIELD_PADDING_TOP, label, strlen(label), FOCUS_COLOR); + } else { + drawGVarValue(dc, x, FIELD_PADDING_TOP, index, lastGVar, FOCUS_COLOR); } } @@ -167,8 +180,15 @@ bool GVarRenderer::isUpdated() void GVarEditWindow::buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_GLOBAL_VAR, 0, MENU_COLOR); - gVarInHeader = new GVarRenderer(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, index); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_GLOBAL_VAR, 0, FOCUS_COLOR); + gVarInHeader = + new GVarRenderer(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + index); } void GVarEditWindow::checkEvents() @@ -357,4 +377,4 @@ void ModelGVarsPage::build(FormWindow * window) grid.nextLine(button->height()); } window->setInnerHeight(grid.getWindowHeight()); -} \ No newline at end of file +} diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index 143b3cf30..815d709e6 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -91,94 +91,104 @@ void deleteExpo(uint8_t idx) storageDirty(EE_MODEL); } -class InputEditWindow: public Page +class InputEditWindow : public Page { - public: - InputEditWindow(int8_t input, uint8_t index): + public: + InputEditWindow(int8_t input, uint8_t index) : Page(ICON_MODEL_INPUTS), input(input), index(index), - preview(this, {INPUT_EDIT_CURVE_LEFT, INPUT_EDIT_CURVE_TOP, INPUT_EDIT_CURVE_WIDTH, INPUT_EDIT_CURVE_HEIGHT}, - [=](int x) -> int { - ExpoData * line = expoAddress(index); - int16_t anas[MAX_INPUTS] = {0}; - applyExpos(anas, e_perout_mode_inactive_flight_mode, line->srcRaw, x); - return anas[line->chn]; - }, - [=]() -> int { - return getValue(expoAddress(index)->srcRaw); - }) - { + preview( + this, + {INPUT_EDIT_CURVE_LEFT, INPUT_EDIT_CURVE_TOP, INPUT_EDIT_CURVE_WIDTH, + INPUT_EDIT_CURVE_HEIGHT}, + [=](int x) -> int { + ExpoData *line = expoAddress(index); + int16_t anas[MAX_INPUTS] = {0}; + applyExpos(anas, e_perout_mode_inactive_flight_mode, line->srcRaw, + x); + return anas[line->chn]; + }, + [=]() -> int { return getValue(expoAddress(index)->srcRaw); }) + { #if LCD_W > LCD_H - body.setWidth(LCD_W - 170); + body.setWidth(LCD_W - preview.width() - PAGE_PADDING); + body.setLeft(preview.width() + PAGE_PADDING); #else - body.setRect({0, INPUT_EDIT_CURVE_TOP + INPUT_EDIT_CURVE_HEIGHT, LCD_W, LCD_H - INPUT_EDIT_CURVE_TOP - INPUT_EDIT_CURVE_HEIGHT}); + body.setRect({0, INPUT_EDIT_CURVE_TOP + INPUT_EDIT_CURVE_HEIGHT, LCD_W, + LCD_H - INPUT_EDIT_CURVE_TOP - INPUT_EDIT_CURVE_HEIGHT}); #endif - buildBody(&body); - buildHeader(&header); - } + buildBody(&body); + buildHeader(&header); + } - void deleteLater(bool detach = true, bool trash = true) override - { - if (_deleted) - return; + void deleteLater(bool detach = true, bool trash = true) override + { + if (_deleted) return; - preview.deleteLater(true, false); + preview.deleteLater(true, false); - Page::deleteLater(detach, trash); - } + Page::deleteLater(detach, trash); + } - protected: - uint8_t input; - uint8_t index; - Curve preview; - Choice * trimChoice = nullptr; - FormGroup * curveParamField = nullptr; + protected: + uint8_t input; + uint8_t index; + Curve preview; + Choice *trimChoice = nullptr; + FormGroup *curveParamField = nullptr; - void buildHeader(Window * window) - { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENUINPUTS, 0, MENU_COLOR); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, getSourceString(MIXSRC_FIRST_INPUT + input), 0, MENU_COLOR); - } + void buildHeader(Window *window) + { + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUINPUTS, 0, FOCUS_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSourceString(MIXSRC_FIRST_INPUT + input), 0, FOCUS_COLOR); + } - // TODO share this code with MIXER - void updateCurveParamField(ExpoData * line) - { - curveParamField->clear(); + // TODO share this code with MIXER + void updateCurveParamField(ExpoData *line) + { + curveParamField->clear(); - const rect_t rect = {0, 0, curveParamField->width(), curveParamField->height()}; + const rect_t rect = {0, 0, curveParamField->width(), + curveParamField->height()}; - switch (line->curve.type) { - case CURVE_REF_DIFF: - case CURVE_REF_EXPO: - { - GVarNumberEdit * edit = new GVarNumberEdit(curveParamField, rect, -100, 100, GET_SET_DEFAULT(line->curve.value)); - edit->setSuffix("%"); - break; - } + switch (line->curve.type) { + case CURVE_REF_DIFF: + case CURVE_REF_EXPO: { + GVarNumberEdit *edit = + new GVarNumberEdit(curveParamField, rect, -100, 100, + GET_SET_DEFAULT(line->curve.value)); + edit->setSuffix("%"); + break; + } - case CURVE_REF_FUNC: - new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, GET_SET_DEFAULT(line->curve.value)); - break; + case CURVE_REF_FUNC: + new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, + GET_SET_DEFAULT(line->curve.value)); + break; - case CURVE_REF_CUSTOM: - { - auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, GET_SET_DEFAULT(line->curve.value)); - choice->setTextHandler([](int value) { - return getCurveString(value); - }); - break; - } + case CURVE_REF_CUSTOM: { + auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, + GET_SET_DEFAULT(line->curve.value)); + choice->setTextHandler([](int value) { return getCurveString(value); }); + break; } } + } - void buildBody(FormWindow * window) - { - FormGridLayout grid; - grid.setLabelWidth(INPUT_EDIT_LABELS_WIDTH); - grid.spacer(PAGE_PADDING); + void buildBody(FormWindow *window) + { + FormGridLayout grid; + grid.setLabelWidth(INPUT_EDIT_LABELS_WIDTH); + grid.spacer(PAGE_PADDING); - ExpoData * line = expoAddress(index) ; + ExpoData *line = expoAddress(index); #if LCD_W > LCD_H grid.setMarginRight(180); @@ -302,19 +312,23 @@ void CommonInputOrMixButton::checkEvents() Button::checkEvents(); } -void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, FlightModesType value) +void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, + FlightModesType value, + LcdFlags textColor) { - dc->drawMask(146, 2 + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupFlightmodeIcon, DEFAULT_COLOR); + dc->drawMask(146, 2 + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupFlightmodeIcon, textColor); coord_t x = 166; for (int i = 0; i < MAX_FLIGHT_MODES; i++) { char s[] = " "; s[0] = '0' + i; if (value & (1 << i)) { - dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, FONT(XS) | TEXT_DISABLE_COLOR); - } - else { - dc->drawSolidFilledRect(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, 8, 3, CHECKBOX_COLOR); - dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, FONT(XS)); + dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, + FONT(XS) | TEXT_DISABLE_COLOR); + } else { + dc->drawSolidFilledRect(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, 8, 3, + FOCUS_BGCOLOR); + dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, FONT(XS) | textColor); } x += 8; } @@ -322,56 +336,68 @@ void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, FlightModesType v void CommonInputOrMixButton::paint(BitmapBuffer * dc) { - if (active) - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, HIGHLIGHT_COLOR); + dc->drawSolidFilledRect(0, 0, width(), height(), + /*hasFocus() ? FOCUS_BGCOLOR :*/ FIELD_BGCOLOR); paintBody(dc); - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); + + if (!hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } -class InputLineButton : public CommonInputOrMixButton { - public: - InputLineButton(FormGroup * parent, const rect_t & rect, uint8_t index): +class InputLineButton : public CommonInputOrMixButton +{ + public: + InputLineButton(FormGroup *parent, const rect_t &rect, uint8_t index) : CommonInputOrMixButton(parent, rect, index) - { - const ExpoData & line = g_model.expoData[index]; - if (line.swtch || line.curve.value != 0 || line.flightModes) { - setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); - } + { + const ExpoData &line = g_model.expoData[index]; + if (line.swtch || line.curve.value != 0 || line.flightModes) { + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); + } + } + + bool isActive() const override { return isExpoActive(index); } + + void paintBody(BitmapBuffer *dc) override + { + const ExpoData &line = g_model.expoData[index]; + + LcdFlags textColor = DEFAULT_COLOR; + // if (hasFocus()) + // textColor = FOCUS_COLOR; + + // first line ... + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, + -100, 100, textColor); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); + + if (line.name[0]) { + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), + textColor); } - bool isActive() const override - { - return isExpoActive(index); + // second line ... + if (line.swtch) { + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupSwitchIcon, textColor); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + textColor); } - void paintBody(BitmapBuffer * dc) override - { - const ExpoData & line = g_model.expoData[index]; - - // first line ... - drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, -100, 100); - drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw); - - if (line.name[0]) { - dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); - dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name)); - } - - // second line ... - if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupSwitchIcon, DEFAULT_COLOR); - drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch); - } - - if (line.curve.value != 0 ) { - dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveIcon, DEFAULT_COLOR); - drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve); - } - - if (line.flightModes) { - drawFlightModes(dc, line.flightModes); - } + if (line.curve.value != 0) { + dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, + textColor); } + + if (line.flightModes) { + drawFlightModes(dc, line.flightModes, textColor); + } + } }; ModelInputsPage::ModelInputsPage(): @@ -396,27 +422,29 @@ void ModelInputsPage::editInput(FormWindow * window, uint8_t input, uint8_t inde }); } -void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) +void ModelInputsPage::build(FormWindow *window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); grid.setLabelWidth(66); int inputIndex = 0; - ExpoData * line = g_model.expoData; + ExpoData *line = g_model.expoData; for (uint8_t input = 0; input < MAX_INPUTS; input++) { if (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { - new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input), BUTTON_BACKGROUND, CENTERED); + coord_t h = grid.getWindowHeight(); + auto txt = new StaticText(window, grid.getLabelSlot(), + getSourceString(MIXSRC_FIRST_INPUT + input), + BUTTON_BACKGROUND, CENTERED); + while (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { - Button * button = new InputLineButton(window, grid.getFieldSlot(), inputIndex); - if (focusIndex == inputIndex) - button->setFocus(SET_FOCUS_DEFAULT); + Button *button = + new InputLineButton(window, grid.getFieldSlot(), inputIndex); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editInput(window, input, inputIndex); - }); + Menu *menu = new Menu(window); + menu->addLine(STR_EDIT, + [=]() { editInput(window, input, inputIndex); }); if (!reachExposLimit()) { menu->addLine(STR_INSERT_BEFORE, [=]() { insertExpo(inputIndex, input); @@ -434,7 +462,8 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) menu->addLine(STR_PASTE_BEFORE, [=]() { copyExpo(s_copySrcIdx, inputIndex, PASTE_BEFORE); if (s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } rebuild(window, inputIndex); @@ -442,10 +471,11 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) menu->addLine(STR_PASTE_AFTER, [=]() { copyExpo(s_copySrcIdx, inputIndex, PASTE_AFTER); if (s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } - rebuild(window, inputIndex+1); + rebuild(window, inputIndex + 1); }); } } @@ -459,22 +489,40 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + if (focus) button->bringToTop(); + }); - grid.spacer(button->height() - 2); - + if (focusIndex == inputIndex) { + button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); + } + grid.spacer(button->height() - 1); ++inputIndex; ++line; } + h = grid.getWindowHeight() - h + 1; + txt->setHeight(h); + grid.spacer(7); - } - else { - auto button = new TextButton(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input)); - if (focusIndex == inputIndex) - button->setFocus(SET_FOCUS_DEFAULT); + } else { + auto button = new TextButton(window, grid.getLabelSlot(), + getSourceString(MIXSRC_FIRST_INPUT + input)); + if (focusIndex == inputIndex) button->setFocus(SET_FOCUS_DEFAULT); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); - Menu * menu = new Menu(window); + Menu *menu = new Menu(window); menu->addLine(STR_EDIT, [=]() { insertExpo(inputIndex, input); editInput(window, input, inputIndex); @@ -484,8 +532,9 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) if (s_copyMode != 0) { menu->addLine(STR_PASTE, [=]() { copyExpo(s_copySrcIdx, inputIndex, input); - if(s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx >= inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + if (s_copyMode == MOVE_MODE) { + deleteExpo((s_copySrcIdx >= inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } rebuild(window, -1); @@ -500,7 +549,7 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) } } - Window * focus = Window::getFocus(); + Window *focus = Window::getFocus(); if (focus) { focus->bringToTop(); } diff --git a/radio/src/gui/colorlcd/model_inputs.h b/radio/src/gui/colorlcd/model_inputs.h index 0f6afbd93..9677c4916 100644 --- a/radio/src/gui/colorlcd/model_inputs.h +++ b/radio/src/gui/colorlcd/model_inputs.h @@ -56,7 +56,7 @@ class CommonInputOrMixButton : public Button { void checkEvents() override; - void drawFlightModes(BitmapBuffer *dc, FlightModesType value); + void drawFlightModes(BitmapBuffer *dc, FlightModesType value, LcdFlags textColor); void paint(BitmapBuffer * dc) override; diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index 4d3e6aa5e..16b6a6ec1 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -73,8 +73,15 @@ class LogicalSwitchEditPage: public Page void buildHeader(Window * window) { - new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT }, STR_MENULOGICALSWITCHES, 0, MENU_COLOR); - headerSwitchName = new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT }, getSwitchPositionName(SWSRC_SW1 + index), 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENULOGICALSWITCHES, 0, FOCUS_COLOR); + headerSwitchName = new StaticText( + window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSwitchPositionName(SWSRC_SW1 + index), 0, FOCUS_COLOR); } void updateLogicalSwitchOneWindow() @@ -161,7 +168,7 @@ class LogicalSwitchEditPage: public Page new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2); int16_t v2_min = 0, v2_max = 0; getMixSrcRange(cs->v1, v2_min, v2_max); - v2Edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MAX_LS_DELAY, GET_SET_DEFAULT(cs->v2)); + v2Edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), v2_min, v2_max, GET_SET_DEFAULT(cs->v2)); v2Edit->setDisplayHandler([=](BitmapBuffer * dc, LcdFlags flags, int32_t value) { drawSourceCustomValue(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, cs->v1, (cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(value) : value), flags); }); @@ -234,93 +241,97 @@ static constexpr coord_t col1 = 20; static constexpr coord_t col2 = (LCD_W - 100) / 3 + col1; static constexpr coord_t col3 = ((LCD_W - 100) / 3) * 2 + col1; -class LogicalSwitchButton : public Button { - public: - LogicalSwitchButton(FormGroup * parent, const rect_t & rect, int lsIndex): - Button(parent, rect), - lsIndex(lsIndex), - active(isActive()) - { - LogicalSwitchData * ls = lswAddress(lsIndex); - if (ls->andsw != SWSRC_NONE || ls->duration != 0 || ls->delay != 0) - setHeight(height() + 20); - if (lswFamily(ls->func) == LS_FAMILY_EDGE) - setHeight(height() + 20); +class LogicalSwitchButton : public Button +{ + public: + LogicalSwitchButton(FormGroup* parent, const rect_t& rect, int lsIndex) : + Button(parent, rect), lsIndex(lsIndex), active(isActive()) + { + LogicalSwitchData* ls = lswAddress(lsIndex); + if (ls->andsw != SWSRC_NONE || ls->duration != 0 || ls->delay != 0) + setHeight(height() + 20); + if (lswFamily(ls->func) == LS_FAMILY_EDGE) setHeight(height() + 20); + } + + bool isActive() const + { + return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + lsIndex); + } + + void checkEvents() override + { + if (active != isActive()) { + invalidate(); + active = !active; } - bool isActive() const - { - return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + lsIndex); + Button::checkEvents(); + } + + void paintLogicalSwitchLine(BitmapBuffer* dc) + { + LogicalSwitchData* ls = lswAddress(lsIndex); + uint8_t lsFamily = lswFamily(ls->func); + + // CSW func + dc->drawTextAtIndex(col1, line1, STR_VCSWFUNC, ls->func); + + // CSW params + if (lsFamily == LS_FAMILY_BOOL || lsFamily == LS_FAMILY_STICKY) { + drawSwitch(dc, col2, line1, ls->v1); + drawSwitch(dc, col3, line1, ls->v2); + } else if (lsFamily == LS_FAMILY_EDGE) { + drawSwitch(dc, col1, line2, ls->v1); + putsEdgeDelayParam(dc, col2, line2, ls); + } else if (lsFamily == LS_FAMILY_COMP) { + drawSource(dc, col2, line1, ls->v1, 0); + drawSource(dc, col3, line1, ls->v2, 0); + } else if (lsFamily == LS_FAMILY_TIMER) { + dc->drawNumber(col2, line1, lswTimerValue(ls->v1), LEFT | PREC1); + dc->drawNumber(col3, line1, lswTimerValue(ls->v2), LEFT | PREC1); + } else { + drawSource(dc, col2, line1, ls->v1, 0); + drawSourceCustomValue( + dc, col3, line1, ls->v1, + (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), 0); } - void checkEvents() override - { - if (active != isActive()) { - invalidate(); - active = !active; - } + // AND switch + drawSwitch(dc, col1, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->andsw, 0); - Button::checkEvents(); + // CSW duration + if (ls->duration > 0) { + dc->drawNumber(col2, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->duration, PREC1 | LEFT); } - void paintLogicalSwitchLine(BitmapBuffer * dc) - { - LogicalSwitchData * ls = lswAddress(lsIndex); - uint8_t lsFamily = lswFamily(ls->func); - - // CSW func - dc->drawTextAtIndex(col1, line1, STR_VCSWFUNC, ls->func); - - // CSW params - if (lsFamily == LS_FAMILY_BOOL || lsFamily == LS_FAMILY_STICKY) { - drawSwitch(dc, col2, line1, ls->v1); - drawSwitch(dc, col3, line1, ls->v2); - } - else if (lsFamily == LS_FAMILY_EDGE) { - drawSwitch(dc, col1, line2, ls->v1); - putsEdgeDelayParam(dc, col2, line2, ls); - } - else if (lsFamily == LS_FAMILY_COMP) { - drawSource(dc, col2, line1, ls->v1, 0); - drawSource(dc, col3, line1, ls->v2, 0); - } - else if (lsFamily == LS_FAMILY_TIMER) { - dc->drawNumber(col2, line1, lswTimerValue(ls->v1), LEFT|PREC1); - dc->drawNumber(col3, line1, lswTimerValue(ls->v2), LEFT|PREC1); - } - else { - drawSource(dc, col2, line1, ls->v1, 0); - drawSourceCustomValue(dc, col3, line1, ls->v1, (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), 0); - } - - // AND switch - drawSwitch(dc, col1, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->andsw, 0); - - // CSW duration - if (ls->duration > 0) { - dc->drawNumber(col2, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->duration, PREC1 | LEFT); - } - - // CSW delay - if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { - dc->drawNumber(col3, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->delay, PREC1 | LEFT); - } + // CSW delay + if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { + dc->drawNumber(col3, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->delay, PREC1 | LEFT); } + } - void paint(BitmapBuffer * dc) override - { - if (active) - dc->drawSolidFilledRect(2, 2, rect.w-4, rect.h-4, HIGHLIGHT_COLOR); + void paint(BitmapBuffer* dc) override + { + if (active) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, HIGHLIGHT_COLOR); + else + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - paintLogicalSwitchLine(dc); + paintLogicalSwitchLine(dc); - // The bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); - } + // The bounding rect + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } - protected: - uint8_t lsIndex; - bool active; + protected: + uint8_t lsIndex; + bool active; }; ModelLogicalSwitchesPage::ModelLogicalSwitchesPage(): @@ -344,17 +355,18 @@ void ModelLogicalSwitchesPage::editLogicalSwitch(FormWindow * window, uint8_t ls }); } -void ModelLogicalSwitchesPage::build(FormWindow * window, int8_t focusIndex) +void ModelLogicalSwitchesPage::build(FormWindow* window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); grid.setLabelWidth(66); for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { - LogicalSwitchData * ls = lswAddress(i); + LogicalSwitchData* ls = lswAddress(i); if (ls->func == LS_FUNC_NONE) { - auto button = new TextButton(window, grid.getLabelSlot(), getSwitchPositionName(SWSRC_SW1+i)); + auto button = new TextButton(window, grid.getLabelSlot(), + getSwitchPositionName(SWSRC_SW1 + i)); button->setPressHandler([=]() { if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) { Menu* menu = new Menu(window); @@ -370,40 +382,54 @@ void ModelLogicalSwitchesPage::build(FormWindow * window, int8_t focusIndex) return 0; }); grid.spacer(button->height() + 5); - } - else { - new StaticText(window, grid.getLabelSlot(), getSwitchPositionName(SWSRC_SW1 + i), BUTTON_BACKGROUND, CENTERED); + } else { + auto txt = new StaticText(window, grid.getLabelSlot(), + getSwitchPositionName(SWSRC_SW1 + i), + BUTTON_BACKGROUND, CENTERED); auto button = new LogicalSwitchButton(window, grid.getFieldSlot(), i); button->setPressHandler([=]() { - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editLogicalSwitch(window, i); + Menu* menu = new Menu(window); + menu->addLine(STR_EDIT, [=]() { editLogicalSwitch(window, i); }); + if (ls->func) + menu->addLine(STR_COPY, [=]() { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; + clipboard.data.csw = *ls; }); - if (ls->func) - menu->addLine(STR_COPY, [=]() { - clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; - clipboard.data.csw = *ls; - }); - if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) - menu->addLine(STR_PASTE, [=]() { - *ls = clipboard.data.csw; - storageDirty(EE_MODEL); - rebuild(window, i); - }); - if (ls->func || ls->v1 || ls->v2 || ls->delay || ls->duration || ls->andsw) - menu->addLine(STR_CLEAR, [=]() { - memset(ls, 0, sizeof(LogicalSwitchData)); - storageDirty(EE_MODEL); - rebuild(window, i); - }); - return 0; + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) + menu->addLine(STR_PASTE, [=]() { + *ls = clipboard.data.csw; + storageDirty(EE_MODEL); + rebuild(window, i); + }); + if (ls->func || ls->v1 || ls->v2 || ls->delay || ls->duration || + ls->andsw) + menu->addLine(STR_CLEAR, [=]() { + memset(ls, 0, sizeof(LogicalSwitchData)); + storageDirty(EE_MODEL); + rebuild(window, i); + }); + return 0; + }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); }); if (focusIndex == i) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } } diff --git a/radio/src/gui/colorlcd/model_logical_switches.h b/radio/src/gui/colorlcd/model_logical_switches.h index fc3a505bf..d211ead04 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.h +++ b/radio/src/gui/colorlcd/model_logical_switches.h @@ -29,7 +29,7 @@ public: virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index 08dc1d107..09804edf1 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -53,235 +53,253 @@ bool reachMixesLimit() return false; } -class MixEditWindow : public Page { - public: - MixEditWindow(int8_t channel, uint8_t mixIndex) : - Page(ICON_MODEL_MIXER), - channel(channel), - mixIndex(mixIndex) - { - buildBody(&body); - buildHeader(&header); +class MixEditWindow : public Page +{ + public: + MixEditWindow(int8_t channel, uint8_t mixIndex) : + Page(ICON_MODEL_MIXER), channel(channel), mixIndex(mixIndex) + { + buildBody(&body); + buildHeader(&header); + } + + protected: + uint8_t channel; + uint8_t mixIndex; + FormGroup *curveParamField = nullptr; + + void buildHeader(Window *window) + { + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MIXES, 0, FOCUS_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSourceString(MIXSRC_CH1 + channel), 0, FOCUS_COLOR); + } + + void buildBody(FormWindow *window) + { + FormGridLayout grid; + grid.spacer(8); + + MixData *mix = mixAddress(mixIndex); + + // Mix name + new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); + new RadioTextEdit(window, grid.getFieldSlot(), mix->name, + sizeof(mix->name)); + grid.nextLine(); + + // Source + new StaticText(window, grid.getLabelSlot(), STR_SOURCE); + new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST, + GET_SET_DEFAULT(mix->srcRaw)); + grid.nextLine(); + + // Weight + new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); + auto gvar = + new GVarNumberEdit(window, grid.getFieldSlot(), MIX_WEIGHT_MIN, + MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight)); + gvar->setSuffix("%"); + grid.nextLine(); + + // Offset + new StaticText(window, grid.getLabelSlot(), STR_OFFSET); + gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_OFFSET_MIN, + MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset)); + gvar->setSuffix("%"); + grid.nextLine(); + + // Trim + new StaticText(window, grid.getLabelSlot(), STR_TRIM); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim)); + grid.nextLine(); + + // Curve + new StaticText(&body, grid.getLabelSlot(), STR_CURVE); + new Choice(&body, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, + CURVE_REF_CUSTOM, GET_DEFAULT(mix->curve.type), + [=](int32_t newValue) { + mix->curve.type = newValue; + mix->curve.value = 0; + SET_DIRTY(); + updateCurveParamField(mix); + }); + curveParamField = + new FormGroup(&body, grid.getFieldSlot(2, 1), FORM_FORWARD_FOCUS); + updateCurveParamField(mix); + grid.nextLine(); + + // Flight modes + new StaticText(window, grid.getLabelSlot(), STR_FLMODE); + for (uint8_t i = 0; i < MAX_FLIGHT_MODES; i++) { + char fm[2] = {char('0' + i), '\0'}; + if (i > 0 && (i % 4) == 0) grid.nextLine(); + new TextButton( + window, grid.getFieldSlot(4, i % 4), fm, + [=]() -> uint8_t { + BFBIT_FLIP(mix->flightModes, bfBit(i)); + SET_DIRTY(); + return !(bfSingleBitGet(mix->flightModes, i)); + }, + OPAQUE | (bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED)); } + grid.nextLine(); - protected: - uint8_t channel; - uint8_t mixIndex; - FormGroup * curveParamField = nullptr; + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, + SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(mix->swtch)); + grid.nextLine(); - void buildHeader(Window * window) - { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MIXES, 0, MENU_COLOR); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); - } + // Warning + new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING); + auto edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3, + GET_SET_DEFAULT(mix->mixWarn)); + edit->setZeroText(STR_OFF); + grid.nextLine(); - void buildBody(FormWindow * window) - { - FormGridLayout grid; - grid.spacer(8); + // Multiplex + new StaticText(window, grid.getLabelSlot(), STR_MULTPX); + new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2, + GET_SET_DEFAULT(mix->mltpx)); + grid.nextLine(); - MixData * mix = mixAddress(mixIndex); + // Delay up + new StaticText(window, grid.getLabelSlot(), STR_DELAYUP); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->delayUp), + SET_VALUE(mix->delayUp, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Mix name - new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); - new RadioTextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name)); - grid.nextLine(); + // Delay down + new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->delayDown), + SET_VALUE(mix->delayDown, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Source - new StaticText(window, grid.getLabelSlot(), STR_SOURCE); - new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST, GET_SET_DEFAULT(mix->srcRaw)); - grid.nextLine(); + // Slow up + new StaticText(window, grid.getLabelSlot(), STR_SLOWUP); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->speedUp), + SET_VALUE(mix->speedUp, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Weight - new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); - auto gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight)); - gvar->setSuffix("%"); - grid.nextLine(); + // Slow down + new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->speedDown), + SET_VALUE(mix->speedDown, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Offset - new StaticText(window, grid.getLabelSlot(), STR_OFFSET); - gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_OFFSET_MIN, MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset)); - gvar->setSuffix("%"); - grid.nextLine(); + window->setInnerHeight(grid.getWindowHeight()); + } - // Trim - new StaticText(window, grid.getLabelSlot(), STR_TRIM); - new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim)); - grid.nextLine(); + // TODO share this code with INPUT + void updateCurveParamField(MixData *line) + { + curveParamField->clear(); - // Curve - new StaticText(&body, grid.getLabelSlot(), STR_CURVE); - new Choice(&body, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, CURVE_REF_CUSTOM, - GET_DEFAULT(mix->curve.type), - [=](int32_t newValue) { - mix->curve.type = newValue; - mix->curve.value = 0; - SET_DIRTY(); - updateCurveParamField(mix); - }); - curveParamField = new FormGroup(&body, grid.getFieldSlot(2, 1), FORM_FORWARD_FOCUS); - updateCurveParamField(mix); - grid.nextLine(); + const rect_t rect = {0, 0, curveParamField->width(), + curveParamField->height()}; - // Flight modes - new StaticText(window, grid.getLabelSlot(), STR_FLMODE); - for (uint8_t i = 0; i < MAX_FLIGHT_MODES; i++) { - char fm[2] = {char('0' + i), '\0'}; - if (i > 0 && (i % 4) == 0) - grid.nextLine(); - new TextButton(window, grid.getFieldSlot(4, i % 4), fm, - [=]() -> uint8_t { - BFBIT_FLIP(mix->flightModes, bfBit(i)); - SET_DIRTY(); - return !(bfSingleBitGet(mix->flightModes, i)); - }, - OPAQUE | (bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED)); + switch (line->curve.type) { + case CURVE_REF_DIFF: + case CURVE_REF_EXPO: { + GVarNumberEdit *edit = + new GVarNumberEdit(curveParamField, rect, -100, 100, + GET_SET_DEFAULT(line->curve.value)); + edit->setSuffix("%"); + break; } - grid.nextLine(); - // Switch - new StaticText(window, grid.getLabelSlot(), STR_SWITCH); - new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(mix->swtch)); - grid.nextLine(); + case CURVE_REF_FUNC: + new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, + GET_SET_DEFAULT(line->curve.value)); + break; - // Warning - new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING); - auto edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3, GET_SET_DEFAULT(mix->mixWarn)); - edit->setZeroText(STR_OFF); - grid.nextLine(); - - // Multiplex - new StaticText(window, grid.getLabelSlot(), STR_MULTPX); - new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2, GET_SET_DEFAULT(mix->mltpx)); - grid.nextLine(); - - // Delay up - new StaticText(window, grid.getLabelSlot(), STR_DELAYUP); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->delayUp), - SET_VALUE(mix->delayUp, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Delay down - new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->delayDown), - SET_VALUE(mix->delayDown, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Slow up - new StaticText(window, grid.getLabelSlot(), STR_SLOWUP); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->speedUp), - SET_VALUE(mix->speedUp, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Slow down - new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->speedDown), - SET_VALUE(mix->speedDown, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - window->setInnerHeight(grid.getWindowHeight()); - } - - // TODO share this code with INPUT - void updateCurveParamField(MixData * line) - { - curveParamField->clear(); - - const rect_t rect = {0, 0, curveParamField->width(), curveParamField->height()}; - - switch (line->curve.type) { - case CURVE_REF_DIFF: - case CURVE_REF_EXPO: - { - GVarNumberEdit * edit = new GVarNumberEdit(curveParamField, rect, -100, 100, GET_SET_DEFAULT(line->curve.value)); - edit->setSuffix("%"); - break; - } - - case CURVE_REF_FUNC: - new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, GET_SET_DEFAULT(line->curve.value)); - break; - - case CURVE_REF_CUSTOM: - { - auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, GET_SET_DEFAULT(line->curve.value)); - choice->setTextHandler([](int value) { - return getCurveString(value); - }); - break; - } + case CURVE_REF_CUSTOM: { + auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, + GET_SET_DEFAULT(line->curve.value)); + choice->setTextHandler([](int value) { return getCurveString(value); }); + break; } } + } }; -class MixLineButton : public CommonInputOrMixButton { - public: - MixLineButton(FormGroup * parent, const rect_t & rect, uint8_t index) : +class MixLineButton : public CommonInputOrMixButton +{ + public: + MixLineButton(FormGroup *parent, const rect_t &rect, uint8_t index) : CommonInputOrMixButton(parent, rect, index) - { - const MixData & mix = g_model.mixData[index]; - if (mix.swtch || mix.curve.value != 0 || mix.flightModes) { - setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); - } + { + const MixData &mix = g_model.mixData[index]; + if (mix.swtch || mix.curve.value != 0 || mix.flightModes) { + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); + } + } + + bool isActive() const override { return isMixActive(index); } + + void paintBody(BitmapBuffer *dc) override + { + const MixData &line = g_model.mixData[index]; + + LcdFlags textColor = DEFAULT_COLOR; + + // first line ... + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, + MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, textColor); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); + + if (line.name[0]) { + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), + textColor); } - bool isActive() const override - { - return isMixActive(index); + // second line ... + if (line.swtch) { + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupSwitchIcon, textColor); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + textColor); } - void paintBody(BitmapBuffer * dc) override - { - const MixData & line = g_model.mixData[index]; - - // first line ... - drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, MIX_WEIGHT_MIN, MIX_WEIGHT_MAX); - drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw); - - if (line.name[0]) { - dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); - dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name)); - } - - // second line ... - if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupSwitchIcon, DEFAULT_COLOR); - drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch); - } - - if (line.curve.value) { - dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveIcon, DEFAULT_COLOR); - drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, 0); - } - - if (line.flightModes) { - drawFlightModes(dc, line.flightModes); - } + if (line.curve.value) { + dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, + textColor); } + + if (line.flightModes) { + drawFlightModes(dc, line.flightModes, textColor); + } + } }; void insertMix(uint8_t idx, uint8_t channel) { pauseMixerCalculations(); - MixData * mix = mixAddress(idx); + MixData *mix = mixAddress(idx); memmove(mix + 1, mix, (MAX_MIXERS - (idx + 1)) * sizeof(MixData)); memclear(mix, sizeof(MixData)); mix->destCh = channel; mix->srcRaw = channel + 1; if (!isSourceAvailable(mix->srcRaw)) { - mix->srcRaw = (channel > 3 ? MIXSRC_Rud - 1 + channel : MIXSRC_Rud - 1 + channelOrder(channel)); + mix->srcRaw = (channel > 3 ? MIXSRC_Rud - 1 + channel + : MIXSRC_Rud - 1 + channelOrder(channel)); while (!isSourceAvailable(mix->srcRaw)) { mix->srcRaw += 1; } @@ -313,6 +331,21 @@ void ModelMixesPage::editMix(FormWindow * window, uint8_t channel, uint8_t mixIn }); } +class MixLineTitle : public StaticText +{ + public: + using StaticText::StaticText; + + void setTextFlags(LcdFlags flags) + { + textFlags = flags; + for (auto child : children) { + auto bitmap = dynamic_cast(child); + if (bitmap) bitmap->setMaskColor(flags & 0xFFFF0000); + } + } +}; + void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) { FormGridLayout grid; @@ -329,12 +362,15 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) MixData * mix = g_model.mixData; for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) { if (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { - new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch), BUTTON_BACKGROUND, CENTERED); + + coord_t h = grid.getWindowHeight(); + auto txt = new MixLineTitle(window, grid.getLabelSlot(), + getSourceString(MIXSRC_CH1 + ch), + BUTTON_BACKGROUND, CENTERED); + uint8_t count = 0; while (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { Button * button = new MixLineButton(window, grid.getFieldSlot(), mixIndex); - if (focusMixIndex == mixIndex) - button->setFocus(SET_FOCUS_DEFAULT); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); Menu * menu = new Menu(window); @@ -385,14 +421,42 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) }); if (count++ > 0) { - new StaticBitmap(window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, mixerMultiplexBitmap[mix->mltpx]); + new StaticBitmap( + txt, + {35 - txt->left(), + button->top() - txt->top() + (button->height() - 18) / 2, 25, + 17}, + mixerMultiplexBitmap[mix->mltpx], DEFAULT_COLOR); } - grid.spacer(button->height() - 2); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + if (focus) button->bringToTop(); + }); + + if (focusMixIndex == mixIndex) { + button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); + } + + + grid.spacer(button->height() - 1); ++mixIndex; ++mix; } + h = grid.getWindowHeight() - h + 1; + txt->setHeight(h); + grid.spacer(7); } else { @@ -448,10 +512,15 @@ void deleteMix(uint8_t idx) storageDirty(EE_MODEL); } +#if defined(LUA) +// This is necessary as the LUA API uses th old interface +// where insertMix() has only one param. The channel is +// passed through s_currCh void insertMix(uint8_t idx) { - insertMix(idx, s_currCh + 1); + insertMix(idx, s_currCh - 1); } +#endif void copyMix(uint8_t source, uint8_t dest, int8_t ch) { diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index aa381ad59..bbc42f58c 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -39,8 +39,14 @@ class OutputEditWindow : public Page { void buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENULIMITS, 0, MENU_COLOR); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENULIMITS, 0, FOCUS_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSourceString(MIXSRC_CH1 + channel), 0, FOCUS_COLOR); } void buildBody(FormWindow * window) @@ -120,28 +126,44 @@ class OutputLineButton : public Button { void paint(BitmapBuffer * dc) override { + LcdFlags textColor = DEFAULT_COLOR; + LcdFlags bgColor = FIELD_BGCOLOR; + + dc->drawSolidFilledRect(0, 0, width(), height(), bgColor); + // first line - dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, output->min - 1000, PREC1); - dc->drawNumber(68, FIELD_PADDING_TOP, output->max + 1000, PREC1); - dc->drawNumber(132, FIELD_PADDING_TOP, output->offset, PREC1); - dc->drawNumber(226, FIELD_PADDING_TOP, PPM_CENTER + output->ppmCenter, RIGHT); - dc->drawText(228, FIELD_PADDING_TOP, output->symetrical ? "=" : "\210"); + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, output->min - 1000, + PREC1 | textColor); + dc->drawNumber(68, FIELD_PADDING_TOP, output->max + 1000, PREC1 | textColor); + dc->drawNumber(132, FIELD_PADDING_TOP, output->offset, PREC1 | textColor); + dc->drawNumber(226, FIELD_PADDING_TOP, PPM_CENTER + output->ppmCenter, + RIGHT | textColor); + dc->drawText(228, FIELD_PADDING_TOP, output->symetrical ? "=" : "\210", + textColor); // second line if (output->revert) { - dc->drawTextAtIndex(4, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, STR_MMMINV, output->revert); + dc->drawTextAtIndex(4, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, STR_MMMINV, + output->revert, textColor); } if (output->curve) { - dc->drawMask(68, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveIcon, DEFAULT_COLOR); - dc->drawText(88, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, getCurveString(output->curve)); + dc->drawMask(68, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + dc->drawText(88, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + getCurveString(output->curve), textColor); } if (output->name[0]) { - dc->drawMask(146, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); - dc->drawSizedText(166, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, output->name, sizeof(output->name)); + dc->drawMask(146, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + output->name, sizeof(output->name), textColor); } // bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); } protected: @@ -171,7 +193,9 @@ void ModelOutputsPage::build(FormWindow * window, int8_t focusChannel) LimitData * output = limitAddress(ch); // Channel label - new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch), BUTTON_BACKGROUND, CENTERED); + auto txt = new StaticText(window, grid.getLabelSlot(), + getSourceString(MIXSRC_CH1 + ch), + BUTTON_BACKGROUND, CENTERED); // Channel settings Button * button = new OutputLineButton(window, grid.getFieldSlot(), output); @@ -203,11 +227,25 @@ void ModelOutputsPage::build(FormWindow * window, int8_t focusChannel) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); if (focusChannel == ch) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } diff --git a/radio/src/gui/colorlcd/model_outputs.h b/radio/src/gui/colorlcd/model_outputs.h index 2a5c61734..25671a4d7 100644 --- a/radio/src/gui/colorlcd/model_outputs.h +++ b/radio/src/gui/colorlcd/model_outputs.h @@ -29,7 +29,7 @@ class ModelOutputsPage: public PageTab { virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 2dbc75c93..04e08f621 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -24,9 +24,23 @@ #include "storage/modelslist.h" #include "libopenui.h" -#define CATEGORIES_WIDTH 120 -#define MODELS_LEFT 123 -#define MODELS_COLUMN_WIDTH 174 +#if LCD_W > LCD_H +constexpr int MODEL_CELLS_PER_LINE = 3; +#else +constexpr int MODEL_CELLS_PER_LINE = 2; +#endif + +constexpr coord_t MODEL_CELL_PADDING = 6; + +constexpr coord_t MODEL_SELECT_CELL_WIDTH = + (LCD_W - (MODEL_CELLS_PER_LINE + 1) * MODEL_CELL_PADDING) / + MODEL_CELLS_PER_LINE; + +constexpr coord_t MODEL_SELECT_CELL_HEIGHT = 92; + +constexpr coord_t MODEL_IMAGE_WIDTH = MODEL_SELECT_CELL_WIDTH; +constexpr coord_t MODEL_IMAGE_HEIGHT = 72; + enum ModelSelectMode { MODE_SELECT_MODEL, @@ -40,44 +54,6 @@ enum ModelDeleteMode { }; uint8_t selectMode, deleteMode; -//ModelsList modelslist; - -//ModelsCategory * currentCategory; -//int currentCategoryIndex; -//ModelCell * currentModel; - -/* -void drawCategory(BitmapBuffer * dc, coord_t y, const char * name, bool selected) -{ - if (selected) { - lcdDrawSolidFilledRect(1, y-INVERT_VERT_MARGIN, CATEGORIES_WIDTH-10, INVERT_LINE_HEIGHT+2, FOCUS_BGCOLOR); - lcdDrawText(6, y, name, DEFAULT_COLOR | INVERS); - } - else { - lcdDrawText(6, y, name, DEFAULT_COLOR); - } -} - -void drawModel(coord_t x, coord_t y, ModelCell * model, bool current, bool selected) -{ - lcd->drawBitmap(x+1, y+1, model->getBuffer()); - if (current) { - lcd->drawBitmapPattern(x+66, y+43, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); - } - if (selected) { - lcdDrawSolidRect(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2, 1, TITLE_BGCOLOR); - drawShadow(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2); - if (selectMode == MODE_MOVE_MODEL) { - lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->width(), y, modelselModelMoveBackground, TITLE_BGCOLOR); - lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->width()+12, y+5, modelselModelMoveIcon, TEXT_BGCOLOR); - } - } -} -*/ - -//uint16_t categoriesVerticalOffset = 0; -//uint16_t categoriesVerticalPosition = 0; -#define MODEL_INDEX() (menuVerticalPosition*2+menuHorizontalPosition) #if 0 void setCurrentModel(unsigned int index) @@ -104,308 +80,285 @@ void setCurrentCategory(unsigned int index) } #endif -class ModelButton: public Button { - public: - ModelButton(FormGroup * parent, const rect_t & rect, ModelCell * modelCell, Window * footer) : - Button(parent, rect), - modelCell(modelCell) - { - load(); +class ModelButton : public Button +{ + public: + ModelButton(FormGroup *parent, const rect_t &rect, ModelCell *modelCell) : + Button(parent, rect), modelCell(modelCell) + { + load(); + } + + ~ModelButton() + { + if (buffer) { delete buffer; } + } + + void load() + { + uint8_t version; + + PACK(struct { + ModelHeader header; + TimerData timers[MAX_TIMERS]; + }) + partialModel; + const char *error = nullptr; + + if (strncmp(modelCell->modelFilename, g_eeGeneral.currModelFilename, + LEN_MODEL_FILENAME) == 0) { + memcpy(&partialModel.header, &g_model.header, sizeof(partialModel)); + version = EEPROM_VER; + } else { + error = + readModel(modelCell->modelFilename, (uint8_t *)&partialModel.header, + sizeof(partialModel), &version); } - void load() - { - uint8_t version; - - PACK(struct { - ModelHeader header; - TimerData timers[MAX_TIMERS]; - }) partialModel; - const char * error = nullptr; - - if (strncmp(modelCell->modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0) { - memcpy(&partialModel.header, &g_model.header, sizeof(partialModel)); - } - else { - error = readModel(modelCell->modelFilename, (uint8_t *)&partialModel.header, sizeof(partialModel), &version); - // LEN_BITMAP_NAME has now 4 bytes more - if (version <= 218) { - memmove(partialModel.timers, &(partialModel.header.bitmap[10]), sizeof(TimerData)*MAX_TIMERS); - memclear(&(partialModel.header.bitmap[10]), 4); - } - } - -// if (modelCell->modelName[0] == '\0' && !error) -// setModelName(partialModel.header.name); // resets buffer!!! - - buffer = new BitmapBuffer(BMP_RGB565, MODELCELL_WIDTH, MODELCELL_HEIGHT); - if (buffer == nullptr) { - return; - } - buffer->clear(DEFAULT_BGCOLOR); - - if (error) { - buffer->drawText(5, 2, "(Invalid Model)", DEFAULT_COLOR); - buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, DEFAULT_COLOR); - } - else { - char timerName[LEN_TIMER_STRING]; - buffer->drawSizedText(5, 2, modelCell->modelName, LEN_MODEL_NAME, FONT(XS) | DEFAULT_COLOR); - getTimerString(timerName, 0); - for (auto & timer : partialModel.timers) { - if (timer.mode > 0 && timer.persistent) { - getTimerString(timerName, timer.value); - break; + if (!error) { + if (modelCell->modelName[0] == '\0' && + partialModel.header.name[0] != '\0') { + + if (version == 219) { + size_t len = sizeof(partialModel.header.name); + char* str = partialModel.header.name; + for (int i=0; i < len; i++) { + str[i] = zchar2char(str[i]); + } + // Trim string + while(len > 0 && str[len-1]) { + if (str[len - 1] != ' ' && str[len - 1] != '\0') break; + str[--len] = '\0'; } } - buffer->drawText(101, 40, timerName, DEFAULT_COLOR); - for (int i = 0; i < 4; i++) { - buffer->drawBitmapPattern(104+i*11, 25, LBM_SCORE0, TITLE_BGCOLOR); - } - GET_FILENAME(filename, BITMAPS_PATH, partialModel.header.bitmap, ""); - const BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(filename); - if (bitmap) { - buffer->drawScaledBitmap(bitmap, 5, 24, 56, 32); - delete bitmap; - } - else { - buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, DEFAULT_COLOR); - } - } - buffer->drawSolidHorizontalLine(5, 19, 143, LINE_COLOR); - } - - void paint(BitmapBuffer * dc) override - { - FormField::paint(dc); - dc->drawBitmap(10, 2, buffer); - if (modelCell == modelslist.getCurrentModel()) { - dc->drawBitmapPattern(112, 71, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); + modelCell->setModelName(partialModel.header.name); } } - const char * modelFilename() - { - return modelCell->modelFilename; + delete buffer; + buffer = new BitmapBuffer(BMP_RGB565, width(), height()); + if (buffer == nullptr) { + return; + } + buffer->clear(FIELD_BGCOLOR); + + if (error) { + buffer->drawText(width() / 2, 2, "(Invalid Model)", + DEFAULT_COLOR | CENTERED); + } else { + GET_FILENAME(filename, BITMAPS_PATH, partialModel.header.bitmap, ""); + const BitmapBuffer *bitmap = BitmapBuffer::loadBitmap(filename); + if (bitmap) { + buffer->drawScaledBitmap(bitmap, 0, 0, width(), height()); + delete bitmap; + } else { + buffer->drawText(width() / 2, 56, "(No Picture)", + FONT(XXS) | DEFAULT_COLOR | CENTERED); + } + } + } + + void paint(BitmapBuffer *dc) override + { + FormField::paint(dc); + + if (buffer) + dc->drawBitmap(0, 0, buffer); + + if (modelCell == modelslist.getCurrentModel()) { + dc->drawSolidFilledRect(0, 0, width(), 20, HIGHLIGHT_COLOR); + dc->drawSizedText(width() / 2, 2, modelCell->modelName, + LEN_MODEL_NAME, + DEFAULT_COLOR | CENTERED); + } else { + LcdFlags textColor; + // if (hasFocus()) { + // dc->drawFilledRect(0, 0, width(), 20, SOLID, FOCUS_BGCOLOR, 5); + // textColor = FOCUS_COLOR; + // } + // else { + dc->drawFilledRect(0, 0, width(), 20, SOLID, FIELD_BGCOLOR, 5); + textColor = DEFAULT_COLOR; + // } + + dc->drawSizedText(width() / 2, 2, modelCell->modelName, + LEN_MODEL_NAME, + textColor | CENTERED); } - protected: - ModelCell * modelCell; - BitmapBuffer * buffer = nullptr; + if (!hasFocus()) { + dc->drawSolidRect(0, 0, width(), height(), 1, FIELD_FRAME_COLOR); + } else { + dc->drawSolidRect(0, 0, width(), height(), 2, FOCUS_BGCOLOR); + } + } + + const char *modelFilename() { return modelCell->modelFilename; } + + protected: + ModelCell *modelCell; + BitmapBuffer *buffer = nullptr; }; -class ModelSelectFooter: public Window { - public: - explicit ModelSelectFooter(Window * parent): - Window(parent, {0, parent->height() - MODEL_SELECT_FOOTER_HEIGHT, LCD_W, MODEL_SELECT_FOOTER_HEIGHT}, OPAQUE) - { - } +class ModelCategoryPageBody : public FormWindow +{ + public: + ModelCategoryPageBody(FormWindow *parent, const rect_t &rect, + ModelsCategory *category) : + FormWindow(parent, rect, FORM_FORWARD_FOCUS), category(category) + { + update(); + } - void setCurrentModel(ModelCell * model) - { - currentModel = model; - invalidate(); - } + void update(int selected = 0) + { + clear(); - void paint(BitmapBuffer * dc) override - { - dc->drawSolidFilledRect(0, 0, width(), height(), DISABLE_COLOR); - uint32_t size = sdGetSize() / 100; - coord_t x = 7; - if (modelselSdFreeBitmap) { - dc->drawMask(7, 4, modelselSdFreeBitmap, DEFAULT_COLOR); - x += modelselSdFreeBitmap->width() + 3; - } - x = dc->drawNumber(x, 3, size, PREC1|FONT(XS), 0, nullptr, "GB"); - x += 20; - if (modelselModelQtyBitmap) { - dc->drawMask(x, 4, modelselModelQtyBitmap, DEFAULT_COLOR); - x += modelselModelQtyBitmap->width() + 3; - } - x = dc->drawNumber(x, 3, modelslist.getModelsCount(), FONT(XS)); - if (currentModel) { - x += 20; - if (modelselModelNameBitmap) { - dc->drawMask(x, 4, modelselModelNameBitmap, DEFAULT_COLOR); - x += modelselModelNameBitmap->width() + 3; - } - dc->drawText(x, 3, currentModel->modelFilename, FONT(XS) | DEFAULT_COLOR); - } - } + int index = 0; + coord_t y = MODEL_CELL_PADDING; + coord_t x = MODEL_CELL_PADDING; - protected: - ModelCell * currentModel = nullptr; -}; + for (auto &model : *category) { + auto button = new ModelButton( + this, {x, y, MODEL_SELECT_CELL_WIDTH, MODEL_SELECT_CELL_HEIGHT}, + model); + button->setPressHandler([=]() -> uint8_t { + if (button->hasFocus()) { + Menu *menu = new Menu(parent); + if (model != modelslist.getCurrentModel()) { + menu->addLine(STR_SELECT_MODEL, [=]() { + // we store the latest changes if any + storageFlushCurrentModel(); + storageCheck(true); + memcpy(g_eeGeneral.currModelFilename, model->modelFilename, + LEN_MODEL_FILENAME); + loadModel(g_eeGeneral.currModelFilename, false); + storageDirty(EE_GENERAL); + storageCheck(true); -#if defined(PCBX10) -constexpr coord_t MODEL_SELECT_CELL_WIDTH = (LCD_W - 3 * PAGE_PADDING) / 2; -#else -constexpr coord_t MODEL_SELECT_CELL_WIDTH = LCD_W - 2 * PAGE_PADDING; -#endif -constexpr coord_t MODEL_SELECT_CELL_HEIGHT = 94; - -class ModelCategoryPageBody: public FormWindow { - public: - ModelCategoryPageBody(FormWindow * parent, const rect_t & rect, ModelsCategory * category, ModelSelectFooter * footer): - FormWindow(parent, rect, FORM_FORWARD_FOCUS), - category(category), - footer(footer) - { - update(); - } - - void update(int selected = 0) - { - clear(); - - int index = 0; - coord_t y = PAGE_PADDING; - coord_t x = PAGE_PADDING; - for (auto & model: * category) { - auto button = new ModelButton(this, {x, y, MODEL_SELECT_CELL_WIDTH, MODEL_SELECT_CELL_HEIGHT}, model, nullptr); - - button->setFocusHandler([=](bool active) { - if (active) { - footer->setCurrentModel(model); + modelslist.setCurrentModel(model); + update(); // modelslist.getModelIndex(modelCell)); + }); } - }); - - button->setPressHandler([=]() -> uint8_t { - if (button->hasFocus()) { - Menu * menu = new Menu(parent); - if (model != modelslist.getCurrentModel()) { - menu->addLine(STR_SELECT_MODEL, [=]() { - // we store the latest changes if any - storageFlushCurrentModel(); - storageCheck(true); - memcpy(g_eeGeneral.currModelFilename, model->modelFilename, LEN_MODEL_FILENAME); - loadModel(g_eeGeneral.currModelFilename, false); - storageDirty(EE_GENERAL); - storageCheck(true); - - modelslist.setCurrentModel(model); - update(); // modelslist.getModelIndex(modelCell)); - }); - } - menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); - menu->addLine(STR_DUPLICATE_MODEL, [=]() { - char duplicatedFilename[LEN_MODEL_FILENAME + 1]; - memcpy(duplicatedFilename, model->modelFilename, sizeof(duplicatedFilename)); - if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, MODELS_PATH)) { - sdCopyFile(model->modelFilename, MODELS_PATH, duplicatedFilename, MODELS_PATH); - modelslist.addModel(category, duplicatedFilename); - update(index); - } - else { - POPUP_WARNING("Invalid File"); - } - }); - // menu->addLine(STR_MOVE_MODEL); - if (model != modelslist.getCurrentModel()) { - menu->addLine(STR_DELETE_MODEL, [=]() { - new ConfirmDialog(parent, STR_DELETE_MODEL, std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] { - modelslist.removeModel(category, model); - update(index > 0 ? index - 1 : 0); + menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); + menu->addLine(STR_DUPLICATE_MODEL, [=]() { + char duplicatedFilename[LEN_MODEL_FILENAME + 1]; + memcpy(duplicatedFilename, model->modelFilename, + sizeof(duplicatedFilename)); + if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, + MODELS_PATH)) { + sdCopyFile(model->modelFilename, MODELS_PATH, duplicatedFilename, + MODELS_PATH); + modelslist.addModel(category, duplicatedFilename); + update(index); + } else { + POPUP_WARNING("Invalid File"); + } + }); + // menu->addLine(STR_MOVE_MODEL); + if (model != modelslist.getCurrentModel()) { + menu->addLine(STR_DELETE_MODEL, [=]() { + new ConfirmDialog( + parent, STR_DELETE_MODEL, + std::string(model->modelName, sizeof(model->modelName)) + .c_str(), + [=] { + modelslist.removeModel(category, model); + update(index > 0 ? index - 1 : 0); }); - }); - } - } - else { - button->setFocus(SET_FOCUS_DEFAULT); - } - return 1; - }); - - if (selected == index) { + }); + } + } else { button->setFocus(SET_FOCUS_DEFAULT); - footer->setCurrentModel(model); } + return 1; + }); - index++; -#if LCD_W >= 480 - if (x == PAGE_PADDING) { - x = PAGE_PADDING + MODEL_SELECT_CELL_WIDTH + PAGE_PADDING; - } - else { - x = PAGE_PADDING; - y += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; - } + if (selected == index) { + button->setFocus(SET_FOCUS_DEFAULT); + } + + index++; + +#if LCD_W > LCD_H + if (index % MODEL_CELLS_PER_LINE == 0) { + x = MODEL_CELL_PADDING; + y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING; + } else { + x += MODEL_CELL_PADDING + MODEL_SELECT_CELL_WIDTH; + } #else - y += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; + y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING; #endif - } - - setInnerHeight(y); - - if (category->empty()) { - setFocus(); - } } + if (index % MODEL_CELLS_PER_LINE != 0) { + y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING; + } + setInnerHeight(y); + + if (category->empty()) { + setFocus(); + } + } + #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override - { - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - Menu * menu = new Menu(this); - menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); - //TODO: create category? - } - else { - FormWindow::onEvent(event); - } + void onEvent(event_t event) override + { + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + Menu *menu = new Menu(this); + menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); + // TODO: create category? + } else { + FormWindow::onEvent(event); } + } #endif - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override - { - if (category->empty()) { - // use Window::setFocus() to avoid forwarding focus to nowhere - // this crashes currently in libopenui - Window::setFocus(flag, from); - } - else { - FormWindow::setFocus(flag, from); - } + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, + Window *from = nullptr) override + { + if (category->empty()) { + // use Window::setFocus() to avoid forwarding focus to nowhere + // this crashes currently in libopenui + Window::setFocus(flag, from); + } else { + FormWindow::setFocus(flag, from); } + } - - protected: - ModelsCategory * category; - ModelSelectFooter * footer; - - std::function getCreateModelAction() - { - return [=]() { - storageCheck(true); - modelslist.setCurrentModel(modelslist.addModel(category, createModel())); -#if defined(LUA) - // chainMenu(menuModelWizard); -#endif - update(category->size() - 1); - }; - } + protected: + ModelsCategory *category; + std::function getCreateModelAction() + { + return [=]() { + storageCheck(true); + modelslist.setCurrentModel(modelslist.addModel(category, createModel())); + update(category->size() - 1); + }; + } }; -class ModelCategoryPage: public PageTab { - public: - explicit ModelCategoryPage(ModelsCategory * category) : - PageTab(category->name, ICON_MODEL), - category(category) - { - } +class ModelCategoryPage : public PageTab +{ + public: + explicit ModelCategoryPage(ModelsCategory *category) : + PageTab(category->name, ICON_MODEL), category(category) + { + } - protected: - ModelsCategory * category; + protected: + ModelsCategory *category; - void build(FormWindow * window) override - { - auto footer = new ModelSelectFooter(window); - new ModelCategoryPageBody(window, {0, 0, LCD_W, window->height() - MODEL_SELECT_FOOTER_HEIGHT}, category, footer); - } + void build(FormWindow *window) override + { + new ModelCategoryPageBody( + window, {0, 0, LCD_W, window->height()}, + category); + } }; - ModelSelectMenu::ModelSelectMenu(): TabsGroup(ICON_MODEL) { diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 208633f2c..b7817d2c7 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -34,7 +34,11 @@ std::string switchWarninglabel(swsrc_t index) CHAR_DOWN }; - return TEXT_AT_INDEX(STR_VSRCRAW, (index + MIXSRC_FIRST_SWITCH - MIXSRC_Rud + 1)) + std::string(&switchPositions[g_model.switchWarningState >> (3*index) & 0x07], 1); + return TEXT_AT_INDEX(STR_VSRCRAW, + (index + MIXSRC_FIRST_SWITCH - MIXSRC_Rud + 1)) + + std::string( + &switchPositions[g_model.switchWarningState >> (3 * index) & 0x07], + 1); } class ChannelFailsafeBargraph: public Window { @@ -80,7 +84,7 @@ class ChannelFailsafeBargraph: public Window { class FailSafeBody : public FormGroup { public: FailSafeBody(FormGroup * parent, const rect_t & rect, uint8_t moduleIdx) : - FormGroup(parent, rect), + FormGroup(parent, rect, FORM_FORWARD_FOCUS), moduleIdx(moduleIdx) { build(); @@ -106,16 +110,17 @@ class FailSafeBody : public FormGroup { new StaticText(this, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch)); // Channel numeric value - new NumberEdit(this, grid.getFieldSlot(3, 0), -lim, lim, - GET_DEFAULT(calcRESXto1000(g_model.failsafeChannels[ch])), + new NumberEdit(this, grid.getFieldSlot(8, 0), -lim, lim, + GET_DEFAULT(g_model.failsafeChannels[ch]), SET_VALUE(g_model.failsafeChannels[ch], newValue), - 0, PREC1); + 0, PREC1 | RIGHT); // Channel bargraph - new ChannelFailsafeBargraph(this, {150, grid.getWindowHeight(), 150, PAGE_LINE_HEIGHT}, moduleIdx, ch); + new ChannelFailsafeBargraph(this, {180, grid.getWindowHeight(), 150, PAGE_LINE_HEIGHT}, moduleIdx, ch); grid.nextLine(); } + grid.spacer(); auto out2fail = new TextButton(this, grid.getLineSlot(), STR_CHANNELS2FAILSAFE); out2fail->setPressHandler([=]() { setCustomFailsafe(moduleIdx); @@ -124,33 +129,24 @@ class FailSafeBody : public FormGroup { }); grid.nextLine(); - setInnerHeight(grid.getWindowHeight()); + setInnerHeight(grid.getWindowHeight() + PAGE_PADDING); } protected: uint8_t moduleIdx; }; -class FailSafeFooter : public Window { - public: - FailSafeFooter(Window * parent, const rect_t & rect) : - Window(parent, rect) - { - } - - void paint(BitmapBuffer * dc) override - { - } -}; - class FailSafePage : public Page { public: explicit FailSafePage(uint8_t moduleIdx) : Page(ICON_STATS_ANALOGS), moduleIdx(moduleIdx) { - new FailSafeBody(&body, {0, 0, LCD_W, height() - footerHeight}, moduleIdx); - new FailSafeFooter(&body, {0, height() - footerHeight, LCD_W, footerHeight}); + new FailSafeBody(&body, {0, 0, LCD_W, body.height()}, moduleIdx); + new StaticText(&header, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_FAILSAFESET, 0, FOCUS_COLOR); } protected: diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index ca24c6c99..e7c02f977 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -61,6 +61,12 @@ class SensorButton : public Button { void checkEvents() override { + uint32_t now = RTOS_GET_MS(); + if (now - lastRefresh >= 200) { + // update at least every 200ms + invalidate(); + } + TelemetryItem & telemetryItem = telemetryItems[index]; if (telemetryItem.isFresh()) { invalidate(); @@ -73,9 +79,10 @@ class SensorButton : public Button { { TelemetryItem &telemetryItem = telemetryItems[index]; - if (telemetryItem.isFresh()) { + if (telemetryItem.isAvailable()) dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, HIGHLIGHT_COLOR); - } + else + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, FIELD_BGCOLOR); dc->drawNumber(2, 1, number, LEFT, 0, nullptr, ":"); @@ -86,7 +93,7 @@ class SensorButton : public Button { drawSensorCustomValue(dc, SENSOR_COL2, line1, index, getValue(MIXSRC_FIRST_TELEM + 3 * index), LEFT | color); } else { - dc->drawText(SENSOR_COL2, line1, "---", CURVE_COLOR); + dc->drawText(SENSOR_COL2, line1, "---", DEFAULT_COLOR); } TelemetrySensor * sensor = & g_model.telemetrySensors[index]; @@ -96,12 +103,17 @@ class SensorButton : public Button { else if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) { dc->drawNumber(SENSOR_COL3, line1, sensor->instance, LEFT); } - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); + + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); } protected: uint8_t index; uint8_t number; + uint32_t lastRefresh = 0; }; class SensorEditWindow : public Page { diff --git a/radio/src/gui/colorlcd/page.cpp b/radio/src/gui/colorlcd/page.cpp index a042e9d80..91d7e3704 100644 --- a/radio/src/gui/colorlcd/page.cpp +++ b/radio/src/gui/colorlcd/page.cpp @@ -26,19 +26,20 @@ PageHeader::PageHeader(Page * parent, uint8_t icon): FormGroup(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, + , back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, [=]() -> uint8_t { parent->deleteLater(); return 0; - }, NO_FOCUS) + }, NO_FOCUS | FORM_NO_BORDER) #endif { } void PageHeader::paint(BitmapBuffer * dc) { - OpenTxTheme::instance()->drawMenuBackground(dc, getIcon(), ""); - dc->drawSolidFilledRect(MENU_HEADER_HEIGHT, 0, LCD_W - MENU_HEADER_HEIGHT, MENU_HEADER_HEIGHT, MENU_BGCOLOR); + OpenTxTheme::instance()->drawPageHeaderBackground(dc, getIcon(), ""); + dc->drawSolidFilledRect(MENU_HEADER_HEIGHT, 0, LCD_W - MENU_HEADER_HEIGHT, + MENU_HEADER_HEIGHT, HEADER_COLOR); } Page::Page(unsigned icon): diff --git a/radio/src/gui/colorlcd/page.h b/radio/src/gui/colorlcd/page.h index 212993575..6d1717a91 100644 --- a/radio/src/gui/colorlcd/page.h +++ b/radio/src/gui/colorlcd/page.h @@ -51,7 +51,7 @@ class PageHeader: public FormGroup protected: uint8_t icon; #if defined(HARDWARE_TOUCH) - IconButton back; + Button back; #endif }; @@ -84,7 +84,7 @@ class Page: public Window 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); + Window::onTouchSlide(x, y, startX, startY, 0/*slideX*/, slideY); return true; } #endif diff --git a/radio/src/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp index 2cd07317a..fcf41f723 100644 --- a/radio/src/gui/colorlcd/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -30,11 +30,14 @@ uint8_t menuCalibrationState; class StickCalibrationWindow: public Window { public: - StickCalibrationWindow(Window * parent, const rect_t & rect, uint8_t stickX, uint8_t stickY): - Window(parent, rect, REFRESH_ALWAYS), - stickX(stickX), - stickY(stickY) - { + StickCalibrationWindow(Window *parent, const rect_t &rect, uint8_t stickX, + uint8_t stickY) : + Window(parent, rect, REFRESH_ALWAYS), stickX(stickX), stickY(stickY) + { + setLeft(rect.x - calibStickBackground->width() / 2); + setTop(rect.y - calibStickBackground->height() / 2); + setWidth(calibStickBackground->width()); + setHeight(calibStickBackground->height()); } void paint(BitmapBuffer * dc) override @@ -42,7 +45,9 @@ class StickCalibrationWindow: public Window { dc->drawBitmap(0, 0, calibStickBackground); int16_t x = calibratedAnalogs[CONVERT_MODE(stickX)]; int16_t y = calibratedAnalogs[CONVERT_MODE(stickY)]; - dc->drawBitmap(width() / 2 - 9 + (bitmapSize / 2 * x) / RESX, height() / 2 - 9 - (bitmapSize / 2 * y) / RESX, calibStick); + dc->drawBitmap(width() / 2 - 9 + (bitmapSize / 2 * x) / RESX, + height() / 2 - 9 - (bitmapSize / 2 * y) / RESX, + calibStick); } protected: @@ -61,26 +66,31 @@ RadioCalibrationPage::RadioCalibrationPage(bool initial): void RadioCalibrationPage::buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENUCALIBRATION, 0, MENU_COLOR); - text = new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENUTOSTART, 0,MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUCALIBRATION, 0, FOCUS_COLOR); + + text = new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + STR_MENUTOSTART, 0, FOCUS_COLOR); } void RadioCalibrationPage::buildBody(FormWindow * window) { menuCalibrationState = CALIB_START; - if (calibRadioPict) { - // Background radio image -#if LCD_W > LCD_H - new StaticBitmap(window, {LCD_W / 2 - calibRadioPict->width() / 2, 5, calibRadioPict->height(), calibRadioPict->width()}, calibRadioPict); -#else - new StaticBitmap(window, {LCD_W / 2 - calibRadioPict->width() / 2, LCD_H / 2 - calibRadioPict->height() / 2, calibRadioPict->height(), calibRadioPict->width()}, calibRadioPict); -#endif - } - // The two sticks - new StickCalibrationWindow(window, {40, 20, 90, 90}, STICK1, STICK2); - new StickCalibrationWindow(window, {LCD_W - 130, 20, 90, 90}, STICK4, STICK3); + + //TODO: dynamic placing + new StickCalibrationWindow(window, + {window->width() / 3, window->height() / 2, 0, 0}, + STICK1, STICK2); + + new StickCalibrationWindow(window, + {(2 * window->width()) / 3, window->height() / 2, 0, 0}, + STICK4, STICK3); rect_t r = { 0, 0, window->width(), window->height() }; auto deco = new ViewMainDecoration(window, r); diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index 3e9227cb0..cd442c3c1 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -25,10 +25,85 @@ #include "io/multi_firmware_update.h" #include "io/bootloader_flash.h" #include "standalone_lua.h" +#include "sdcard.h" + +class FileNameEditWindow : public Page +{ + public: + FileNameEditWindow(const std::string iName) : + Page(ICON_RADIO_SD_MANAGER), name(std::move(iName)) + { + buildBody(&body); + buildHeader(&header); + }; + +#if defined(DEBUG_WINDOWS) + std::string getName() const override { return "FileNameEditWindow"; } +#endif + protected: + const std::string name; + + void buildHeader(Window *window) + { + new StaticText(window, {70, 4, 180, 30}, STR_RENAME_FILE, MENU_BGCOLOR); + } + + void buildBody(Window *window) + { + GridLayout grid(window); + grid.spacer(8); + uint8_t nameLength; + uint8_t extLength; + char extension[LEN_FILE_EXTENSION_MAX + 1]; + memset(extension, 0, sizeof(extension)); + const char *ext = + getFileExtension(name.data(), 0, 0, &nameLength, &extLength); + + if (extLength > LEN_FILE_EXTENSION_MAX) extLength = LEN_FILE_EXTENSION_MAX; + if (ext) strncpy(extension, ext, extLength); + + const uint8_t maxNameLength = SD_SCREEN_FILE_LENGTH - extLength; + nameLength -= extLength; + if (nameLength > maxNameLength) nameLength = maxNameLength; + memset(reusableBuffer.sdManager.originalName, 0, SD_SCREEN_FILE_LENGTH); + + strncpy(reusableBuffer.sdManager.originalName, name.c_str(), nameLength); + reusableBuffer.sdManager.originalName[nameLength] = '\0'; + + auto newFileName = new TextEdit( + window, grid.getSlot(), reusableBuffer.sdManager.originalName, + SD_SCREEN_FILE_LENGTH - extLength, LcdFlags(0)); + newFileName->setChangeHandler([=]() { + char *newValue = reusableBuffer.sdManager.originalName; + size_t totalSize = strlen(newValue); + char changedName[SD_SCREEN_FILE_LENGTH + 1]; + memset(changedName, 0, sizeof(changedName)); + strncpy(changedName, newValue, totalSize); + changedName[totalSize] = '\0'; + if (extLength) { + strncpy(changedName + totalSize, extension, extLength); + } + changedName[totalSize + extLength] = '\0'; + f_rename((const TCHAR *)name.c_str(), (const TCHAR *)changedName); + }); + }; +}; RadioSdManagerPage::RadioSdManagerPage() : PageTab(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, ICON_RADIO_SD_MANAGER) { + setOnSetVisibleHandler([]() { + TRACE("f_chdir(ROOT_PATH)"); + f_chdir(ROOT_PATH); + }); +} + +void RadioSdManagerPage::rebuild(FormWindow * window) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window); + window->setScrollPositionY(scrollPosition); } // TODO elsewhere @@ -38,12 +113,19 @@ extern bool compare_nocase(const std::string &first, const std::string &second); char * getFullPath(const std::string &filename) { static char full_path[FF_MAX_LFN + 1]; // TODO optimize that! - f_getcwd(full_path, FF_MAX_LFN); + f_getcwd((TCHAR*)full_path, FF_MAX_LFN); strcat(full_path, "/"); strcat(full_path, filename.c_str()); return full_path; } +char * getCurrentPath() +{ + static char path[FF_MAX_LFN + 1]; // TODO optimize that! + f_getcwd((TCHAR*)path, FF_MAX_LFN); + return path; +} + class FilePreview : public Window { public: @@ -138,6 +220,7 @@ void RadioSdManagerPage::build(FormWindow * window) std::list files; std::list directories; + std::string currentPath(getCurrentPath()); auto preview = new FilePreview(window, {LCD_W / 2 + 6, 0, LCD_W / 2 - 16, window->height()}); FRESULT res = f_opendir(&dir, "."); // Open the directory @@ -164,20 +247,23 @@ void RadioSdManagerPage::build(FormWindow * window) // sort directories and files directories.sort(compare_nocase); files.sort(compare_nocase); - + for (auto name: directories) { - new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { - f_chdir(name.data()); + auto b = new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { + std::string fullpath = currentPath + "/" + name; + f_chdir(fullpath.c_str()); window->clear(); build(window); return 0; - }, 0); + }, OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); grid.nextLine(); } for (auto name: files) { auto button = new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { auto menu = new Menu(window); + f_chdir(currentPath.c_str()); const char *ext = getFileExtension(name.data()); if (ext) { if (!strcasecmp(ext, SOUNDS_EXT)) { @@ -214,14 +300,14 @@ void RadioSdManagerPage::build(FormWindow * window) dialog->flash(getFullPath(name)); }); } - else if (isExtensionMatching(ext, BITMAPS_EXT)) { - // TODO - } - else if (!strcasecmp(ext, TEXT_EXT)) { - menu->addLine(STR_VIEW_TEXT, [=]() { - // TODO - }); - } + // else if (isExtensionMatching(ext, BITMAPS_EXT)) { + // // TODO + // } + // else if (!strcasecmp(ext, TEXT_EXT)) { + // menu->addLine(STR_VIEW_TEXT, [=]() { + // // TODO + // }); + // } if (!READ_ONLY() && !strcasecmp(ext, FIRMWARE_EXT)) { if (isBootloader(name.data())) { menu->addLine(STR_FLASH_BOOTLOADER, [=]() { @@ -256,8 +342,9 @@ void RadioSdManagerPage::build(FormWindow * window) } #if defined(LUA) else if (isExtensionMatching(ext, SCRIPTS_EXT)) { + std::string fullpath = currentPath + "/" + name; menu->addLine(STR_EXECUTE_FILE, [=]() { - luaExec(getFullPath(name)); + luaExec(fullpath.c_str()); //TODO: check 'luaState' StandaloneLuaWindow::instance()->attach(window); }); @@ -266,20 +353,35 @@ void RadioSdManagerPage::build(FormWindow * window) } if (!READ_ONLY()) { menu->addLine(STR_COPY_FILE, [=]() { - clipboard.type = CLIPBOARD_TYPE_SD_FILE; - f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN); - strncpy(clipboard.data.sd.filename, name.c_str(), CLIPBOARD_PATH_LEN - 1); + clipboard.type = CLIPBOARD_TYPE_SD_FILE; + f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN); + strncpy(clipboard.data.sd.filename, name.c_str(), + CLIPBOARD_PATH_LEN - 1); }); if (clipboard.type == CLIPBOARD_TYPE_SD_FILE) { menu->addLine(STR_PASTE, [=]() { - // TODO + static char lfn[FF_MAX_LFN + 1]; // TODO optimize that! + f_getcwd((TCHAR *)lfn, FF_MAX_LFN); + // prevent copying to the same directory + if (strcmp(clipboard.data.sd.directory, lfn)) { + sdCopyFile(clipboard.data.sd.filename, + clipboard.data.sd.directory, + clipboard.data.sd.filename, lfn); + clipboard.type = CLIPBOARD_TYPE_NONE; + } + rebuild(window); }); } menu->addLine(STR_RENAME_FILE, [=]() { - // TODO + auto few = new FileNameEditWindow(name); + few->setCloseHandler([=]() { + //window->clear(); + rebuild(window); + }); + // rebuild(window); }); }); menu->addLine(STR_DELETE_FILE, [=]() { - f_unlink(getFullPath(name)); + f_unlink((const TCHAR*)getFullPath(name)); // coord_t scrollPosition = window->getScrollPositionY(); window->clear(); build(window); @@ -287,7 +389,8 @@ void RadioSdManagerPage::build(FormWindow * window) }); } return 0; - }, 0); + }, OPAQUE); + button->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); button->setFocusHandler([=](bool active) { if (active) { preview->setFile(getFullPath(name)); diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index d3be72812..9f83dda45 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -112,19 +112,35 @@ void RadioToolsPage::rebuild(FormWindow * window) *ext = '\0'; label = getBasename(path); } - new StaticText(window, grid.getLabelSlot(), "lua", BUTTON_BACKGROUND, CENTERED); + auto txt = new StaticText(window, grid.getLabelSlot(), "lua", + BUTTON_BACKGROUND, CENTERED); std::string path_str(path); - new TextButton( + auto b = new TextButton( window, grid.getFieldSlot(1), label, [window, path_str]() -> uint8_t { - f_chdir("/SCRIPTS/TOOLS/"); - luaExec(path_str.c_str()); - // TODO: check 'luaState' + + char toolPath[FF_MAX_LFN + 1]; + strncpy(toolPath, path_str.c_str(), sizeof(toolPath)-1); + *((char *)getBasename(toolPath)-1) = '\0'; + f_chdir(toolPath); + StandaloneLuaWindow::instance()->attach(window); + luaExec(path_str.c_str()); return 0; }, - 0); + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } } @@ -133,63 +149,172 @@ void RadioToolsPage::rebuild(FormWindow * window) #if defined(INTERNAL_MODULE_PXX2) // PXX2 modules tools - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER)) { - new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, [=]() -> uint8_t { - new RadioSpectrumAnalyser(INTERNAL_MODULE); - return 0; - }, 0); + if (isPXX2ModuleOptionAvailable( + reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE] + .information.modelID, + MODULE_OPTION_SPECTRUM_ANALYSER)) { + auto txt = new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND, + CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, + [=]() -> uint8_t { + new RadioSpectrumAnalyser(INTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER)) { - new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_POWER_METER_INT, [=]() -> uint8_t { -// new RadioPowerMeter(INTERNAL_MODULE); - return 0; - }, 0); + if (isPXX2ModuleOptionAvailable( + reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE] + .information.modelID, + MODULE_OPTION_POWER_METER)) { + auto txt = new StaticText(window, grid.getLabelSlot(), "access", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_POWER_METER_INT, + [=]() -> uint8_t { + // new RadioPowerMeter(INTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } #endif #if defined(INTERNAL_MODULE_MULTI) - new StaticText(window, grid.getLabelSlot(), "multi", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, [=]() -> uint8_t { - new RadioSpectrumAnalyser(INTERNAL_MODULE); - return 0; - }, 0); - grid.nextLine(); + { + auto txt = new StaticText(window, grid.getLabelSlot(), "multi", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, + [=]() -> uint8_t { + new RadioSpectrumAnalyser(INTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); + grid.nextLine(); + } #endif #if defined(PXX2)|| defined(MULTIMODULE) - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER) || isModuleMultimodule(EXTERNAL_MODULE)) { - new StaticText(window, grid.getLabelSlot(), isModuleMultimodule(EXTERNAL_MODULE) ? "multi" : "access", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_EXT, [=]() -> uint8_t { - new RadioSpectrumAnalyser(EXTERNAL_MODULE); - return 0; - }, 0); + if (isPXX2ModuleOptionAvailable( + reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE] + .information.modelID, + MODULE_OPTION_SPECTRUM_ANALYSER) || + isModuleMultimodule(EXTERNAL_MODULE)) { + auto txt = new StaticText(window, grid.getLabelSlot(), + isModuleMultimodule(EXTERNAL_MODULE) ? "multi" : "access", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_EXT, + [=]() -> uint8_t { + new RadioSpectrumAnalyser(EXTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } #endif #if defined(PXX2) - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER)) { - new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_POWER_METER_EXT, [=]() -> uint8_t { -// new RadioPowerMeter(EXTERNAL_MODULE); - return 0; - }, 0); - +#if 0 // disabled Power Meter: not yet implemented + if (isPXX2ModuleOptionAvailable( + reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE] + .information.modelID, + MODULE_OPTION_POWER_METER)) { + auto txt = new StaticText(window, grid.getLabelSlot(), "access", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_POWER_METER_EXT, + [=]() -> uint8_t { + // new RadioPowerMeter(EXTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } #endif +#endif #if defined(GHOST) +#if 0 if (isModuleGhost(EXTERNAL_MODULE)) { - new StaticText(window, grid.getLabelSlot(), "ghost", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), "Ghost module config", [=]() -> uint8_t { - new RadioGhostModuleConfig(EXTERNAL_MODULE); - return 0; - }, 0); + auto txt = new StaticText(window, grid.getLabelSlot(), "ghost", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), "Ghost module config", + [=]() -> uint8_t { + new RadioGhostModuleConfig(EXTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } +#endif #endif window->setInnerHeight(grid.getWindowHeight()); diff --git a/radio/src/gui/colorlcd/screen_setup.cpp b/radio/src/gui/colorlcd/screen_setup.cpp index 70f316fbd..686d78fd4 100644 --- a/radio/src/gui/colorlcd/screen_setup.cpp +++ b/radio/src/gui/colorlcd/screen_setup.cpp @@ -209,14 +209,13 @@ class SetupWidgetsPage: public FormWindow bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override { - // prevent screen sliding when setting up widgets + FormWindow::onTouchSlide(x, y, startX, startY, slideX, slideY); return true; } bool onTouchEnd(coord_t x, coord_t y) override { - if (FormWindow::onTouchEnd(x, y)) return true; - + FormWindow::onTouchEnd(x, y); return true; } #endif diff --git a/radio/src/gui/colorlcd/sourcechoice.cpp b/radio/src/gui/colorlcd/sourcechoice.cpp index 53a4c23c1..68efe32d8 100644 --- a/radio/src/gui/colorlcd/sourcechoice.cpp +++ b/radio/src/gui/colorlcd/sourcechoice.cpp @@ -64,12 +64,15 @@ void SourceChoice::paint(BitmapBuffer * dc) if (editMode) textColor = FOCUS_COLOR; else if (hasFocus()) - textColor = FOCUS_BGCOLOR; - else if (value == 0) - textColor = DISABLE_COLOR; + textColor = FOCUS_COLOR; + // else if (value == 0) + // textColor = DISABLE_COLOR; else textColor = 0; + drawSource(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor); + dc->drawBitmapPattern(rect.w - 20, (rect.h - 11) / 2, LBM_DROPDOWN, + textColor); } // defined in gui/gui_common.cpp diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index a7092095a..3faf9d9ad 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -24,235 +24,387 @@ #define SET_DIRTY() storageDirty(functions == g_model.customFn ? EE_MODEL : EE_GENERAL) -class SpecialFunctionEditPage : public Page { - public: - SpecialFunctionEditPage(CustomFunctionData * functions, uint8_t index) : - Page(functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS : ICON_RADIO_GLOBAL_FUNCTIONS), +class SpecialFunctionEditPage : public Page +{ + public: + SpecialFunctionEditPage(CustomFunctionData *functions, uint8_t index) : + Page(functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS + : ICON_RADIO_GLOBAL_FUNCTIONS), functions(functions), index(index) - { - buildHeader(&header); - buildBody(&body); + { + buildHeader(&header); + buildBody(&body); + } + + protected: + CustomFunctionData *functions; + uint8_t index; + FormGroup *specialFunctionOneWindow = nullptr; + StaticText *headerSF = nullptr; + bool active = false; + + bool isActive() const + { + return ((functions == g_model.customFn + ? modelFunctionsContext.activeSwitches + : globalFunctionsContext.activeSwitches) & + ((MASK_CFN_TYPE)1 << index) + ? 1 + : 0); + } + + void checkEvents() override + { + Page::checkEvents(); + if (active != isActive()) { + invalidate(); + headerSF->setTextFlags(isActive() ? FONT(BOLD) | HIGHLIGHT_COLOR + : MENU_COLOR); + active = !active; } + } - protected: - CustomFunctionData * functions; - uint8_t index; - FormGroup * specialFunctionOneWindow = nullptr; - StaticText * headerSF = nullptr; - bool active = false; + void buildHeader(Window *window) + { + new StaticText( + window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, 20}, + functions == g_model.customFn ? STR_MENUCUSTOMFUNC + : STR_MENUSPECIALFUNCS, + 0, FOCUS_COLOR); + headerSF = new StaticText( + window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, 20}, + (functions == g_model.customFn ? "SF" : "GF") + std::to_string(index+1), + 0, FOCUS_COLOR); + } - bool isActive() const - { - return ((functions == g_model.customFn ? modelFunctionsContext.activeSwitches : globalFunctionsContext.activeSwitches) & ((MASK_CFN_TYPE)1 << index) ? 1 : 0); - } + void updateSpecialFunctionOneWindow() + { + FormGridLayout grid; + specialFunctionOneWindow->clear(); - void checkEvents() override - { - Page::checkEvents(); - if (active != isActive()) { - invalidate(); - headerSF->setTextFlags(isActive() ? FONT(BOLD) |HIGHLIGHT_COLOR : MENU_COLOR); - active = !active; - } - } + CustomFunctionData *cfn = &functions[index]; + uint8_t func = CFN_FUNC(cfn); - void buildHeader(Window * window) - { - new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, 20 }, functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, 0, MENU_COLOR); - headerSF = new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, 20 }, (functions == g_model.customFn ? "SF" : "GF" ) + std::to_string(index), 0, MENU_COLOR); - } - - void updateSpecialFunctionOneWindow() - { - FormGridLayout grid; - specialFunctionOneWindow->clear(); - - CustomFunctionData * cfn = &functions[index]; - uint8_t func = CFN_FUNC(cfn); - - // Func param - switch (func) { - case FUNC_OVERRIDE_CHANNEL: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_CH); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 1, MAX_OUTPUT_CHANNELS, GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1)); - grid.nextLine(); - - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_TRAINER: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - auto choice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0,NUM_STICKS + 1,GET_SET_DEFAULT(CFN_PARAM(cfn))); - choice->setTextHandler([=](int32_t value) { - if (value == 0) - return std::string(STR_STICKS); - else if (value == NUM_STICKS + 1) - return std::string(STR_CHANS); - else - return TEXT_AT_INDEX(STR_VSRCRAW, value);; - }); - grid.nextLine(); - break; - } - - case FUNC_RESET: - if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_RESET); - auto choice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, - FUNC_RESET_PARAM_FIRST_TELEM + lastUsedTelemetryIndex(), - GET_SET_DEFAULT(CFN_PARAM(cfn))); - choice->setAvailableHandler(isSourceAvailableInResetSpecialFunction); - choice->setTextHandler([=](int32_t value) { - if (value < FUNC_RESET_PARAM_FIRST_TELEM) - return TEXT_AT_INDEX(STR_VFSWRESET, value); - else - return std::string(g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM].label, TELEM_LABEL_LEN); - }); - grid.nextLine(); - } - break; - - case FUNC_VOLUME: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VOLUME); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_BACKLIGHT: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_SOUND: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new Choice(specialFunctionOneWindow, grid.getFieldSlot(), STR_FUNCSOUNDS, 0, - AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_TRACK: - case FUNC_BACKGND_MUSIC: - case FUNC_PLAY_SCRIPT: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new FileChoice(specialFunctionOneWindow, grid.getFieldSlot(), - func == FUNC_PLAY_SCRIPT ? SCRIPTS_FUNCS_PATH : std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) + - std::string(currentLanguagePack->id, 2), - func == FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, - sizeof(cfn->play.name), - [=]() { - return std::string(cfn->play.name, ZLEN(cfn->play.name)); - }, - [=](std::string newValue) { - strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); - SET_DIRTY(); - }, true); // strip extension - grid.nextLine(); - break; - - case FUNC_SET_TIMER: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_TIMER); - auto timerchoice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, TIMERS - 1, GET_SET_DEFAULT(CFN_TIMER_INDEX(cfn))); - timerchoice->setTextHandler([](int32_t value) { - return std::string(STR_TIMER) + std::to_string(value + 1); - }); - grid.nextLine(); - - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new TimeEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 9 * 60 * 60 - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - } - - case FUNC_SET_FAILSAFE: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_MODULE); - new Choice(specialFunctionOneWindow, grid.getFieldSlot(), "\004Int.Ext.", 0, NUM_MODULES - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_VALUE: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_HAPTIC: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 3, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_LOGS: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - auto edit = new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 255, GET_SET_DEFAULT(CFN_PARAM(cfn))); - edit->setDisplayHandler([=](BitmapBuffer * dc, LcdFlags flags, int32_t value) { - dc->drawNumber(2, 2, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); - }); - break; - } - } - - if (HAS_ENABLE_PARAM(func)) { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_ENABLE); - new CheckBox(specialFunctionOneWindow, grid.getFieldSlot(), GET_SET_DEFAULT(CFN_ACTIVE(cfn))); + // Func param + switch (func) { + case FUNC_OVERRIDE_CHANNEL: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_CH); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 1, + MAX_OUTPUT_CHANNELS, + GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1)); grid.nextLine(); - } - else if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ... - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_REPEAT); - auto repeat = new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(2, 1), -1, 60/CFN_PLAY_REPEAT_MUL, GET_SET_DEFAULT(CFN_PLAY_REPEAT(cfn))); - repeat->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), -100, 100, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_TRAINER: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + auto choice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + NUM_STICKS + 1, GET_SET_DEFAULT(CFN_CH_INDEX(cfn))); + choice->setTextHandler([=](int32_t value) { if (value == 0) - dc->drawText(3, 0, "1x", flags); - else if (value == CFN_PLAY_REPEAT_NOSTART) - dc->drawText(3, 0, "!1x", flags); + return std::string(STR_STICKS); + else if (value == NUM_STICKS + 1) + return std::string(STR_CHANS); else - dc->drawNumber(3, 0, value * CFN_PLAY_REPEAT_MUL, flags, 0, nullptr, "s"); + return TEXT_AT_INDEX(STR_VSRCRAW, value); + ; }); grid.nextLine(); + break; } - specialFunctionOneWindow->adjustHeight(); + case FUNC_RESET: + if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_RESET); + auto choice = new Choice( + specialFunctionOneWindow, grid.getFieldSlot(), 0, + FUNC_RESET_PARAM_FIRST_TELEM + lastUsedTelemetryIndex(), + GET_SET_DEFAULT(CFN_PARAM(cfn))); + choice->setAvailableHandler(isSourceAvailableInResetSpecialFunction); + choice->setTextHandler([=](int32_t value) { + if (value < FUNC_RESET_PARAM_FIRST_TELEM) + return TEXT_AT_INDEX(STR_VFSWRESET, value); + else + return std::string( + g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM] + .label, + TELEM_LABEL_LEN); + }); + grid.nextLine(); + } + break; + + case FUNC_VOLUME: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VOLUME); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_BACKLIGHT: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_SOUND: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), + STR_FUNCSOUNDS, 0, + AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_TRACK: + case FUNC_BACKGND_MUSIC: + case FUNC_PLAY_SCRIPT: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new FileChoice( + specialFunctionOneWindow, grid.getFieldSlot(), + func == FUNC_PLAY_SCRIPT + ? SCRIPTS_FUNCS_PATH + : std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) + + std::string(currentLanguagePack->id, 2), + func == FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, + sizeof(cfn->play.name), + [=]() { return std::string(cfn->play.name, ZLEN(cfn->play.name)); }, + [=](std::string newValue) { + strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); + SET_DIRTY(); + }, + true); // strip extension + grid.nextLine(); + break; + + case FUNC_SET_TIMER: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_TIMER); + auto timerchoice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + TIMERS - 1, GET_SET_DEFAULT(CFN_TIMER_INDEX(cfn))); + timerchoice->setTextHandler([](int32_t value) { + return std::string(STR_TIMER) + std::to_string(value + 1); + }); + grid.nextLine(); + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new TimeEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, + 9 * 60 * 60 - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + } + + case FUNC_SET_FAILSAFE: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_MODULE); + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), + "\004Int.Ext.", 0, NUM_MODULES - 1, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_VALUE: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_TELEM, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_HAPTIC: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 3, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_LOGS: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + auto edit = + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, + 255, GET_SET_DEFAULT(CFN_PARAM(cfn))); + edit->setDisplayHandler( + [=](BitmapBuffer *dc, LcdFlags flags, int32_t value) { + dc->drawNumber(2, 2, CFN_PARAM(cfn), PREC1, + sizeof(CFN_PARAM(cfn)), nullptr, "s"); + }); + break; + } + + case FUNC_ADJUST_GVAR: { + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_GLOBALVAR); + auto gvarchoice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MAX_GVARS - 1, GET_SET_DEFAULT(CFN_GVAR_INDEX(cfn))); + gvarchoice->setTextHandler([](int32_t value) { + return std::string(STR_GV) + std::to_string(value + 1); + }); + grid.nextLine(); + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_MODE); + auto modechoice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), + FUNC_ADJUST_GVAR_CONSTANT, FUNC_ADJUST_GVAR_INCDEC, + GET_DEFAULT(CFN_GVAR_MODE(cfn)), nullptr); + grid.nextLine(); + + modechoice->setTextHandler([](int32_t value) { + switch (value) { + case FUNC_ADJUST_GVAR_CONSTANT: + return std::string(STR_CONSTANT); + case FUNC_ADJUST_GVAR_SOURCE: + return std::string(STR_MIXSOURCE); + case FUNC_ADJUST_GVAR_GVAR: + return std::string(STR_GLOBALVAR); + case FUNC_ADJUST_GVAR_INCDEC: + return std::string(STR_INCDEC); + } + return std::string("---"); + }); + + modechoice->setSetValueHandler([=](int32_t newValue) { + CFN_GVAR_MODE(cfn) = newValue; + CFN_PARAM(cfn) = 0; + SET_DIRTY(); + updateSpecialFunctionOneWindow(); + }); + + switch (CFN_GVAR_MODE(cfn)) { + case FUNC_ADJUST_GVAR_CONSTANT: { + //TODO: label + int16_t val_min, val_max; + getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min, + val_max); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), + val_min, val_max, GET_SET_DEFAULT(CFN_PARAM(cfn))); + break; + } + case FUNC_ADJUST_GVAR_SOURCE: + //TODO: label + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), + 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + break; + case FUNC_ADJUST_GVAR_GVAR: { + auto gvarchoice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MAX_GVARS - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + gvarchoice->setTextHandler([](int32_t value) { + return std::string(STR_GV) + std::to_string(value + 1); + }); + gvarchoice->setAvailableHandler([=](int value) { + return CFN_GVAR_INDEX(cfn) != value; + }); + break; + } + case FUNC_ADJUST_GVAR_INCDEC: { + int16_t val_min, val_max; + getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min, val_max); + getGVarIncDecRange(val_min, val_max); + auto numedit = new NumberEdit(specialFunctionOneWindow, + grid.getFieldSlot(), val_min, val_max, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + numedit->setDisplayHandler( + [](BitmapBuffer *dc, LcdFlags flags, int value) { + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, abs(value), + flags, 0, value >= 0 ? "+= " : "-= ", nullptr); + }); + break; + } + } + grid.nextLine(); + } } - void buildBody(FormWindow * window) - { - // SF.one - FormGridLayout grid; - grid.spacer(PAGE_PADDING); - - CustomFunctionData * cfn = &functions[index]; - - // Switch - new StaticText(window, grid.getLabelSlot(), STR_SWITCH); - auto switchChoice = new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST, SWSRC_LAST, GET_SET_DEFAULT(CFN_SWITCH(cfn))); - switchChoice->setAvailableHandler([=](int value) { - return (functions == g_model.customFn ? isSwitchAvailable(value, ModelCustomFunctionsContext) - : isSwitchAvailable(value, - GeneralCustomFunctionsContext)); - }); + if (HAS_ENABLE_PARAM(func)) { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_ENABLE); + new CheckBox(specialFunctionOneWindow, grid.getFieldSlot(), + GET_SET_DEFAULT(CFN_ACTIVE(cfn))); grid.nextLine(); - - // Function - new StaticText(window, grid.getLabelSlot(), STR_FUNC); - auto functionChoice = new Choice(window, grid.getFieldSlot(), STR_VFSWFUNC, 0, FUNC_MAX - 1, GET_DEFAULT(CFN_FUNC(cfn))); - functionChoice->setSetValueHandler([=](int32_t newValue) { - CFN_FUNC(cfn) = newValue; - CFN_RESET(cfn); - SET_DIRTY(); - updateSpecialFunctionOneWindow(); - }); - functionChoice->setAvailableHandler([=](int value) { - return isAssignableFunctionAvailable(value, functions); - }); + } else if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ... + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_REPEAT); + auto repeat = new NumberEdit( + specialFunctionOneWindow, grid.getFieldSlot(2, 1), -1, + 60 / CFN_PLAY_REPEAT_MUL, GET_SET_DEFAULT(CFN_PLAY_REPEAT(cfn))); + repeat->setDisplayHandler( + [](BitmapBuffer *dc, LcdFlags flags, int32_t value) { + if (value == 0) + dc->drawText(3, 0, "1x", flags); + else if (value == CFN_PLAY_REPEAT_NOSTART) + dc->drawText(3, 0, "!1x", flags); + else + dc->drawNumber(3, 0, value * CFN_PLAY_REPEAT_MUL, flags, 0, + nullptr, "s"); + }); grid.nextLine(); + } - specialFunctionOneWindow = new FormGroup(window, {0, grid.getWindowHeight(), LCD_W, 0}, FORM_FORWARD_FOCUS); + specialFunctionOneWindow->adjustHeight(); + } + + void buildBody(FormWindow *window) + { + // SF.one + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + + CustomFunctionData *cfn = &functions[index]; + + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + auto switchChoice = + new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST, SWSRC_LAST, + GET_SET_DEFAULT(CFN_SWITCH(cfn))); + switchChoice->setAvailableHandler([=](int value) { + return (functions == g_model.customFn + ? isSwitchAvailable(value, ModelCustomFunctionsContext) + : isSwitchAvailable(value, GeneralCustomFunctionsContext)); + }); + grid.nextLine(); + + // Function + new StaticText(window, grid.getLabelSlot(), STR_FUNC); + auto functionChoice = + new Choice(window, grid.getFieldSlot(), STR_VFSWFUNC, 0, FUNC_MAX - 1, + GET_DEFAULT(CFN_FUNC(cfn))); + functionChoice->setSetValueHandler([=](int32_t newValue) { + CFN_FUNC(cfn) = newValue; + CFN_RESET(cfn); + SET_DIRTY(); updateSpecialFunctionOneWindow(); - grid.addWindow(specialFunctionOneWindow); - } + }); + functionChoice->setAvailableHandler([=](int value) { + return isAssignableFunctionAvailable(value, functions); + }); + grid.nextLine(); + + specialFunctionOneWindow = new FormGroup( + window, {0, grid.getWindowHeight(), LCD_W, 0}, FORM_FORWARD_FOCUS); + updateSpecialFunctionOneWindow(); + grid.addWindow(specialFunctionOneWindow); + } }; static constexpr coord_t line1 = FIELD_PADDING_TOP; @@ -261,161 +413,204 @@ static constexpr coord_t col1 = 20; static constexpr coord_t col2 = (LCD_W - 100) / 3 + col1; static constexpr coord_t col3 = ((LCD_W - 100) / 3) * 2 + col1 + 20; -class SpecialFunctionButton : public Button { - public: - SpecialFunctionButton(FormWindow * parent, const rect_t &rect, CustomFunctionData * functions, uint8_t index) : - Button(parent, rect), - functions(functions), - index(index) - { - const CustomFunctionData * cfn = &functions[index]; - uint8_t func = CFN_FUNC(cfn); - if (!cfn->isEmpty() && - (HAS_ENABLE_PARAM(func) || HAS_REPEAT_PARAM(func) || (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT))) { - setHeight(line2 + PAGE_LINE_HEIGHT); - } +class SpecialFunctionButton : public Button +{ + public: + SpecialFunctionButton(FormWindow *parent, const rect_t &rect, + CustomFunctionData *functions, uint8_t index) : + Button(parent, rect), functions(functions), index(index) + { + const CustomFunctionData *cfn = &functions[index]; + uint8_t func = CFN_FUNC(cfn); + if (!cfn->isEmpty() && + (HAS_ENABLE_PARAM(func) || HAS_REPEAT_PARAM(func) || + (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || + func == FUNC_PLAY_SCRIPT))) { + setHeight(line2 + PAGE_LINE_HEIGHT); } + } #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "SpecialFunctionButton"; - } + std::string getName() const override { return "SpecialFunctionButton"; } #endif - bool isActive() const - { - return ((functions == g_model.customFn ? modelFunctionsContext.activeSwitches : globalFunctionsContext.activeSwitches) & ((MASK_CFN_TYPE)1 << index) ? 1 : 0); + bool isActive() const + { + return ((functions == g_model.customFn + ? modelFunctionsContext.activeSwitches + : globalFunctionsContext.activeSwitches) & + ((MASK_CFN_TYPE)1 << index) + ? 1 + : 0); + } + + void checkEvents() override + { + Button::checkEvents(); + if (active != isActive()) { + invalidate(); + active = !active; } + } - void checkEvents() override - { - Button::checkEvents(); - if (active != isActive()) { - invalidate(); - active = !active; - } + void paintSpecialFunctionLine(BitmapBuffer *dc) + { + const CustomFunctionData *cfn = &functions[index]; + if (functions[index].func == FUNC_OVERRIDE_CHANNEL && + functions != g_model.customFn) { + functions[index].func = FUNC_OVERRIDE_CHANNEL + 1; } + uint8_t func = CFN_FUNC(cfn); - void paintSpecialFunctionLine(BitmapBuffer * dc) - { - const CustomFunctionData * cfn = &functions[index]; - if (functions[index].func == FUNC_OVERRIDE_CHANNEL && functions != g_model.customFn) { - functions[index].func = FUNC_OVERRIDE_CHANNEL + 1; + drawSwitch(dc, col1, line1, CFN_SWITCH(cfn), 0); + if (cfn->isEmpty()) return; + + dc->drawTextAtIndex(col2, line1, STR_VFSWFUNC, func, 0); + int16_t val_min = 0; + int16_t val_max = 255; + + switch (func) { + case FUNC_OVERRIDE_CHANNEL: + drawChn(dc, col1, line2, CFN_CH_INDEX(cfn) + 1, 0); + getMixSrcRange(MIXSRC_FIRST_CH, val_min, val_max); + dc->drawNumber(col2, line2, CFN_PARAM(cfn)); + break; + + case FUNC_TRAINER: { + std::string text; + int16_t value = CFN_CH_INDEX(cfn); + if (value == 0) + text = std::string(STR_STICKS); + else if (value == NUM_STICKS + 1) + text = std::string(STR_CHANS); + else + text = TEXT_AT_INDEX(STR_VSRCRAW, value); + dc->drawText(col1, line2, text.c_str(), 0); + break; } - uint8_t func = CFN_FUNC(cfn); + case FUNC_RESET: + if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { + dc->drawTextAtIndex(col1, line2, STR_VFSWRESET, CFN_PARAM(cfn)); + } else { + TelemetrySensor *sensor = + &g_model.telemetrySensors[CFN_PARAM(cfn) - + FUNC_RESET_PARAM_FIRST_TELEM]; + dc->drawSizedText(col1, line2, sensor->label, TELEM_LABEL_LEN); + } + break; - drawSwitch(dc, col1, line1, CFN_SWITCH(cfn), 0); - if (cfn->isEmpty()) - return; + case FUNC_VOLUME: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; - dc->drawTextAtIndex(col2, line1, STR_VFSWFUNC, func, 0); - int16_t val_min = 0; - int16_t val_max = 255; + case FUNC_BACKLIGHT: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; - switch (func) { - case FUNC_OVERRIDE_CHANNEL: - drawChn(dc, col1, line2, CFN_CH_INDEX(cfn) + 1, 0); - getMixSrcRange(MIXSRC_FIRST_CH, val_min, val_max); - dc->drawNumber(col2, line2, CFN_PARAM(cfn)); - break; + case FUNC_PLAY_SOUND: + dc->drawTextAtIndex(col1, line2, STR_FUNCSOUNDS, CFN_PARAM(cfn)); + break; - case FUNC_TRAINER: - drawSource(dc, col1, line2, CFN_CH_INDEX(cfn) == 0 ? 0 : MIXSRC_Rud + CFN_CH_INDEX(cfn) - 1); - break; + case FUNC_PLAY_TRACK: + case FUNC_BACKGND_MUSIC: + case FUNC_PLAY_SCRIPT: + if (ZEXIST(cfn->play.name)) + dc->drawSizedText(col1, line2, cfn->play.name, sizeof(cfn->play.name), + 0); + else + dc->drawTextAtIndex(col1, line2, STR_VCSWFUNC, 0, 0); + break; - case FUNC_RESET: - if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { - dc->drawTextAtIndex(col1, line2, STR_VFSWRESET, CFN_PARAM(cfn)); - } - else { - TelemetrySensor * sensor = &g_model.telemetrySensors[CFN_PARAM(cfn) - FUNC_RESET_PARAM_FIRST_TELEM]; - dc->drawSizedText(col1, line2, sensor->label, TELEM_LABEL_LEN); - } - break; + case FUNC_SET_TIMER: + drawStringWithIndex(dc, col1, line2, STR_TIMER, + CFN_TIMER_INDEX(cfn) + 1, 0); + break; - case FUNC_VOLUME: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; + case FUNC_SET_FAILSAFE: + dc->drawTextAtIndex(col1, line2, "\004Int.Ext.", CFN_PARAM(cfn), 0); + break; - case FUNC_BACKLIGHT: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; + case FUNC_PLAY_VALUE: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; - case FUNC_PLAY_SOUND: - dc->drawTextAtIndex(col1, line2, STR_FUNCSOUNDS, CFN_PARAM(cfn)); - break; + case FUNC_HAPTIC: + dc->drawNumber(col1, line2, CFN_PARAM(cfn), 0); + break; - case FUNC_PLAY_TRACK: - case FUNC_BACKGND_MUSIC: - case FUNC_PLAY_SCRIPT: - if (ZEXIST(cfn->play.name)) - dc->drawSizedText(col1, line2, cfn->play.name, sizeof(cfn->play.name), 0); - else - dc->drawTextAtIndex(col1, line2, STR_VCSWFUNC, 0, 0); - break; + case FUNC_LOGS: + dc->drawNumber(col3, line1, CFN_PARAM(cfn), PREC1, + sizeof(CFN_PARAM(cfn)), nullptr, "s"); + break; - case FUNC_SET_TIMER: - drawStringWithIndex(dc, col1, line2, STR_TIMER, CFN_TIMER_INDEX(cfn) + 1, 0); - break; - - case FUNC_SET_FAILSAFE: - dc->drawTextAtIndex(col1, line2, "\004Int.Ext.", CFN_PARAM(cfn), 0); - break; - - case FUNC_PLAY_VALUE: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_HAPTIC: + case FUNC_ADJUST_GVAR: + switch(CFN_GVAR_MODE(cfn)) { + case FUNC_ADJUST_GVAR_CONSTANT: dc->drawNumber(col1, line2, CFN_PARAM(cfn), 0); break; - - case FUNC_LOGS: - dc->drawNumber(col3, line1, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); + case FUNC_ADJUST_GVAR_SOURCE: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + case FUNC_ADJUST_GVAR_GVAR: + drawSource(dc, col1, line2, CFN_PARAM(cfn) + MIXSRC_FIRST_GVAR, 0); + break; + case FUNC_ADJUST_GVAR_INCDEC: { + int16_t value = CFN_PARAM(cfn); + std::string text(value >= 0 ? "+= " : "-= "); + text += std::to_string(abs(value)); + dc->drawText(col1, line2, text.c_str()); break; - } - if (HAS_ENABLE_PARAM(func)) { - theme->drawCheckBox(dc, CFN_ACTIVE(cfn), col3, line2); - } - else if (HAS_REPEAT_PARAM(func)) { - if (CFN_PLAY_REPEAT(cfn) == 0) { - dc->drawText(col3, line2, "1x", 0); } - else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { - dc->drawText(col3, line2, "!1x", 0); - } - else { - dc->drawNumber(col3 + 12, line2, CFN_PLAY_REPEAT(cfn) * CFN_PLAY_REPEAT_MUL, 0 | RIGHT, 0, nullptr, "s"); } + } + if (HAS_ENABLE_PARAM(func)) { + theme->drawCheckBox(dc, CFN_ACTIVE(cfn), col3, line2); + } else if (HAS_REPEAT_PARAM(func)) { + if (CFN_PLAY_REPEAT(cfn) == 0) { + dc->drawText(col3, line2, "1x", 0); + } else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { + dc->drawText(col3, line2, "!1x", 0); + } else { + dc->drawNumber(col3 + 12, line2, + CFN_PLAY_REPEAT(cfn) * CFN_PLAY_REPEAT_MUL, 0 | RIGHT, 0, + nullptr, "s"); } } + } - void paint(BitmapBuffer * dc) override - { - if (active) - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, HIGHLIGHT_COLOR); + void paint(BitmapBuffer *dc) override + { + if (active) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, HIGHLIGHT_COLOR); + else + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - paintSpecialFunctionLine(dc); + paintSpecialFunctionLine(dc); - // The bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); - } + // The bounding rect + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } - protected: - CustomFunctionData * functions; - uint8_t index; - bool active = false; + protected: + CustomFunctionData *functions; + uint8_t index; + bool active = false; }; -SpecialFunctionsPage::SpecialFunctionsPage(CustomFunctionData * functions) : - PageTab(functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, - functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS : ICON_RADIO_GLOBAL_FUNCTIONS), - functions(functions) +SpecialFunctionsPage::SpecialFunctionsPage(CustomFunctionData *functions) : + PageTab(functions == g_model.customFn ? STR_MENUCUSTOMFUNC + : STR_MENUSPECIALFUNCS, + functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS + : ICON_RADIO_GLOBAL_FUNCTIONS), + functions(functions) { } -void SpecialFunctionsPage::rebuild(FormWindow * window, int8_t focusSpecialFunctionIndex) +void SpecialFunctionsPage::rebuild(FormWindow *window, + int8_t focusSpecialFunctionIndex) { coord_t scrollPosition = window->getScrollPositionY(); window->clear(); @@ -423,15 +618,14 @@ void SpecialFunctionsPage::rebuild(FormWindow * window, int8_t focusSpecialFunct window->setScrollPositionY(scrollPosition); } -void SpecialFunctionsPage::editSpecialFunction(FormWindow * window, uint8_t index) +void SpecialFunctionsPage::editSpecialFunction(FormWindow *window, + uint8_t index) { auto editPage = new SpecialFunctionEditPage(functions, index); - editPage->setCloseHandler([=]() { - rebuild(window, index); - }); + editPage->setCloseHandler([=]() { rebuild(window, index); }); } -void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) +void SpecialFunctionsPage::build(FormWindow *window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); @@ -440,12 +634,11 @@ void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) Window::clearFocus(); char s[] = "SFxx"; - if (functions == g_eeGeneral.customFn) - s[0] = 'G'; + if (functions == g_eeGeneral.customFn) s[0] = 'G'; for (uint8_t i = 0; i < MAX_SPECIAL_FUNCTIONS; i++) { - CustomFunctionData * cfn = &functions[i]; - strAppendUnsigned(&s[2], i); + CustomFunctionData *cfn = &functions[i]; + strAppendUnsigned(&s[2], i+1); if (cfn->swtch == 0) { auto button = new TextButton(window, grid.getLabelSlot(), s); @@ -464,64 +657,82 @@ void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) return 0; }); grid.spacer(button->height() + 5); - } - else { - new StaticText(window, grid.getLabelSlot(), s, BUTTON_BACKGROUND, CENTERED); + } else { + auto txt = new StaticText(window, grid.getLabelSlot(), s, + BUTTON_BACKGROUND, CENTERED); - auto button = new SpecialFunctionButton(window, grid.getFieldSlot(), functions, i); + auto button = + new SpecialFunctionButton(window, grid.getFieldSlot(), functions, i); button->setPressHandler([=]() { - button->bringToTop(); - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editSpecialFunction(window, i); + button->bringToTop(); + Menu *menu = new Menu(window); + menu->addLine(STR_EDIT, [=]() { editSpecialFunction(window, i); }); + menu->addLine(STR_COPY, [=]() { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; + clipboard.data.cfn = *cfn; + }); + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) { + menu->addLine(STR_PASTE, [=]() { + *cfn = clipboard.data.cfn; + SET_DIRTY(); + rebuild(window, i); }); - menu->addLine(STR_COPY, [=]() { - clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; - clipboard.data.cfn = *cfn; + } + if (functions[MAX_SPECIAL_FUNCTIONS - 1].isEmpty()) { + menu->addLine(STR_INSERT, [=]() { + memmove( + cfn + 1, cfn, + (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); + memset(cfn, 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + rebuild(window, i); }); - if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) { - menu->addLine(STR_PASTE, [=]() { - *cfn = clipboard.data.cfn; - SET_DIRTY(); - rebuild(window, i); - }); - } - if (functions[MAX_SPECIAL_FUNCTIONS - 1].isEmpty()) { - menu->addLine(STR_INSERT, [=]() { - memmove(cfn + 1, cfn, (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); - memset(cfn, 0, sizeof(CustomFunctionData)); - SET_DIRTY(); - rebuild(window, i); - }); - } - menu->addLine(STR_CLEAR, [=]() { - memset(cfn, 0, sizeof(CustomFunctionData)); + } + menu->addLine(STR_CLEAR, [=]() { + memset(cfn, 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + }); + for (int j = i; j < MAX_SPECIAL_FUNCTIONS; j++) { + if (!functions[j].isEmpty()) { + menu->addLine(STR_DELETE, [=]() { + memmove( + cfn, cfn + 1, + (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); + memset(&functions[MAX_SPECIAL_FUNCTIONS - 1], 0, + sizeof(CustomFunctionData)); SET_DIRTY(); - }); - for (int j = i; j < MAX_SPECIAL_FUNCTIONS; j++) { - if (!functions[j].isEmpty()) { - menu->addLine(STR_DELETE, [=]() { - memmove(cfn, cfn + 1, (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); - memset(&functions[MAX_SPECIAL_FUNCTIONS - 1], 0, sizeof(CustomFunctionData)); - SET_DIRTY(); - rebuild(window, i); - }); - break; - } + rebuild(window, i); + }); + break; } - return 0; + } + return 0; + }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); }); if (focusIndex == i) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } } grid.nextLine(); -// window->setLastField(); + // window->setLastField(); window->setInnerHeight(grid.getWindowHeight()); } diff --git a/radio/src/gui/colorlcd/special_functions.h b/radio/src/gui/colorlcd/special_functions.h index 0fa96341c..eaf85ec9b 100644 --- a/radio/src/gui/colorlcd/special_functions.h +++ b/radio/src/gui/colorlcd/special_functions.h @@ -31,11 +31,7 @@ class SpecialFunctionsPage: public PageTab { void build(FormWindow * window) override { -#if defined(HARDWARE_TOUCH) - build(window, -1); -#else build(window, 0); -#endif } protected: diff --git a/radio/src/gui/colorlcd/splash.cpp b/radio/src/gui/colorlcd/splash.cpp index 1ac28e8b9..4c035b5d0 100644 --- a/radio/src/gui/colorlcd/splash.cpp +++ b/radio/src/gui/colorlcd/splash.cpp @@ -22,35 +22,38 @@ #if defined(SPLASH) -const uint8_t __bmp_splash[] __ALIGNED(4) { -#include "bmp_splash.lbm" +const uint8_t __bmp_splash[] { +#include "big_logo.lbm" }; -Bitmap BMP_SPLASH(BMP_RGB565, (const uint16_t *)__bmp_splash); void drawSplash() { - static bool loadImgFromSD = true; + constexpr LcdFlags splash_background_color = + COLOR2FLAGS(((0xC >> 3) << 11) | ((0x3F >> 2) << 5) | (0x66 >> 3)); + + static bool loadSplashImg = true; static BitmapBuffer * splashImg = nullptr; - if (loadImgFromSD && splashImg == nullptr) { + // try splash from SD card first + if (loadSplashImg && splashImg == nullptr) { if (!sdMounted()) sdInit(); splashImg = BitmapBuffer::loadBitmap(BITMAPS_PATH "/" SPLASH_FILE); - loadImgFromSD = false; + loadSplashImg = false; + + // otherwise load from FLASH + if (splashImg == nullptr) { + splashImg = BitmapBuffer::loadRamBitmap(__bmp_splash, sizeof(__bmp_splash)); + } } - lcd->clear(); + lcd->clear(splash_background_color); if (splashImg) { lcd->drawBitmap((LCD_W - splashImg->width())/2, (LCD_H - splashImg->height())/2, splashImg); } - else { - lcd->drawBitmap((LCD_W - BMP_SPLASH.width())/2, - (LCD_H - BMP_SPLASH.height())/2, - &BMP_SPLASH); - } - + lcdRefresh(); } #endif diff --git a/radio/src/gui/colorlcd/switchchoice.cpp b/radio/src/gui/colorlcd/switchchoice.cpp index 6f90e8878..9db366569 100644 --- a/radio/src/gui/colorlcd/switchchoice.cpp +++ b/radio/src/gui/colorlcd/switchchoice.cpp @@ -48,12 +48,15 @@ void SwitchChoice::paint(BitmapBuffer * dc) if (editMode) textColor = FOCUS_COLOR; else if (hasFocus()) - textColor = FOCUS_BGCOLOR; - else if (value == 0) - textColor = DISABLE_COLOR; + textColor = FOCUS_COLOR; + // else if (value == 0) + // textColor = DISABLE_COLOR; else - textColor = 0; + textColor = DEFAULT_COLOR; + drawSwitch(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor); + dc->drawBitmapPattern(rect.w - 20, (rect.h - 11) / 2, LBM_DROPDOWN, + textColor); } void SwitchChoice::fillMenu(Menu * menu, std::function filter) diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index 3484d1187..f3ea3163b 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -32,12 +32,12 @@ TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): FormGroup(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, + back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, [=]() -> uint8_t { parent->deleteLater(); ViewMain::instance()->setFocus((SET_FOCUS_DEFAULT)); return 1; - }, NO_FOCUS), + }, NO_FOCUS | FORM_NO_BORDER), #endif icon(icon), carousel(this, parent) @@ -46,12 +46,15 @@ TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): void TabsGroupHeader::paint(BitmapBuffer * dc) { - OpenTxTheme::instance()->drawMenuBackground(dc, icon, title); + OpenTxTheme::instance()->drawPageHeaderBackground(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) +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) { } @@ -62,7 +65,7 @@ void TabsCarousel::updateInnerWidth() void TabsCarousel::paint(BitmapBuffer * dc) { - OpenTxTheme::instance()->drawMenuHeader(dc, menu->tabs, currentIndex); + OpenTxTheme::instance()->drawPageHeader(dc, menu->tabs, currentIndex); } #if defined(HARDWARE_TOUCH) @@ -143,9 +146,10 @@ void TabsGroup::setVisibleTab(PageTab * tab) Keyboard::hide(); #endif currentTab = tab; + if (tab->onSetVisible) tab->onSetVisible(); tab->build(&body); - if (!focusWindow) - setFocus(SET_FOCUS_DEFAULT); + if (!focusWindow) setFocus(SET_FOCUS_DEFAULT); + header.setTitle(tab->title.c_str()); invalidate(); } diff --git a/radio/src/gui/colorlcd/tabsgroup.h b/radio/src/gui/colorlcd/tabsgroup.h index a985c111d..1618bf7a0 100644 --- a/radio/src/gui/colorlcd/tabsgroup.h +++ b/radio/src/gui/colorlcd/tabsgroup.h @@ -50,17 +50,9 @@ class PageTab { { } - void destroy() + void setOnSetVisibleHandler(std::function handler) { - if (onPageDestroyed) { - onPageDestroyed(); - } - delete this; - } - - void setOnPageDestroyedHandler(std::function handler) - { - onPageDestroyed = std::move(handler); + onSetVisible = std::move(handler); } void setTitle(std::string value) @@ -81,7 +73,7 @@ class PageTab { protected: std::string title; unsigned icon = 0; - std::function onPageDestroyed; + std::function onSetVisible; }; class TabsCarousel: public Window { @@ -155,7 +147,7 @@ class TabsGroupHeader: public FormGroup { protected: #if defined(HARDWARE_TOUCH) - IconButton back; + Button back; #endif uint8_t icon; TabsCarousel carousel; diff --git a/radio/src/gui/colorlcd/theme.cpp b/radio/src/gui/colorlcd/theme.cpp index 5954ca760..e3626dd6b 100644 --- a/radio/src/gui/colorlcd/theme.cpp +++ b/radio/src/gui/colorlcd/theme.cpp @@ -25,9 +25,9 @@ //#include "modal_window.h" extern OpenTxTheme * defaultTheme; -const BitmapBuffer * OpenTxTheme::asterisk = nullptr; -const BitmapBuffer * OpenTxTheme::question = nullptr; +const BitmapBuffer * OpenTxTheme::error = nullptr; const BitmapBuffer * OpenTxTheme::busy = nullptr; +const BitmapBuffer * OpenTxTheme::shutdown = nullptr; const uint8_t LBM_FOLDER[] = { #include "mask_folder.lbm" @@ -44,6 +44,19 @@ const uint8_t LBM_USB_PLUGGED[] = { #include "mask_usb_symbol.lbm" }; + +const uint8_t error_bitmap[] = { +#include "mask_error.lbm" +}; + +const uint8_t busy_bitmap[] = { +#include "mask_busy.lbm" +}; + +const uint8_t shutdown_bitmap[] = { +#include "mask_shutdown.lbm" +}; + std::list & getRegisteredThemes() { static std::list themes; @@ -71,12 +84,12 @@ void OpenTxTheme::init() const void OpenTxTheme::load() const { - if (!asterisk) - asterisk = BitmapBuffer::loadBitmap(getFilePath("asterisk.bmp")); - if (!question) - question = BitmapBuffer::loadBitmap(getFilePath("question.bmp")); + if (!error) + error = BitmapBuffer::load8bitMask(error_bitmap); if (!busy) - busy = BitmapBuffer::loadBitmap(getFilePath("busy.bmp")); + busy = BitmapBuffer::load8bitMask(busy_bitmap); + if (!shutdown) + shutdown = BitmapBuffer::load8bitMask(shutdown_bitmap); } ZoneOptionValue * OpenTxTheme::getOptionValue(unsigned int index) const @@ -153,23 +166,18 @@ void OpenTxTheme::drawBackground(BitmapBuffer * dc) const // } //} -void OpenTxTheme::drawCheckBox(BitmapBuffer * dc, bool checked, coord_t x, coord_t y, bool focus) const +void OpenTxTheme::drawCheckBox(BitmapBuffer *dc, bool checked, coord_t x, + coord_t y, bool focus) const { + dc->drawSolidFilledRect(x, y, 16, 16, FIELD_BGCOLOR); if (focus) { - dc->drawSolidFilledRect(x + 0, y + 2, 16, 16, FOCUS_BGCOLOR); - if (checked) { - dc->drawSolidFilledRect(x + 2, y + 4, 12, 12, DEFAULT_BGCOLOR); - dc->drawSolidFilledRect(x + 3, y + 5, 10, 10, CHECKBOX_COLOR); - } - else { - dc->drawSolidFilledRect(x + 2, y + 4, 12, 12, DEFAULT_BGCOLOR); - } + dc->drawSolidRect(x, y, 16, 16, 2, FOCUS_BGCOLOR); } else { - dc->drawSolidRect(x + 0, y + 2, 16, 16, 1, DISABLE_COLOR); - if (checked) { - dc->drawSolidFilledRect(x + 2, y + 4, 12, 12, CHECKBOX_COLOR); - } + dc->drawSolidRect(x, y, 16, 16, 1, FIELD_FRAME_COLOR); + } + if (checked) { + dc->drawSolidFilledRect(x + 3, y + 3, 10, 10, CHECKBOX_COLOR); } } @@ -179,37 +187,43 @@ void OpenTxTheme::drawChoice(BitmapBuffer * dc, ChoiceBase * choice, const char if (choice->isEditMode()) textColor = FOCUS_COLOR; else if (choice->hasFocus()) - textColor = FOCUS_BGCOLOR; - else if (!str || str[0] == '\0') - textColor = DISABLE_COLOR; + textColor = FOCUS_COLOR; + // else if (!str || str[0] == '\0') + // textColor = FOCUS_COLOR; else textColor = DEFAULT_COLOR; - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, str[0] == '\0' ? "---" : str, textColor); - dc->drawBitmapPattern(choice->getRect().w - 20, (choice->getRect().h - 11) / 2, choice->getType() == CHOICE_TYPE_FOLDER ? LBM_FOLDER : LBM_DROPDOWN, textColor); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, + str[0] == '\0' ? "---" : str, textColor); + + dc->drawBitmapPattern( + choice->getRect().w - 20, (choice->getRect().h - 11) / 2, + choice->getType() == CHOICE_TYPE_FOLDER ? LBM_FOLDER : LBM_DROPDOWN, + textColor); } -void OpenTxTheme::drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const +void OpenTxTheme::drawSlider(BitmapBuffer *dc, int vmin, int vmax, int value, + const rect_t &rect, bool edit, bool focus) const { int val = limit(vmin, value, vmax); int w = divRoundClosest((rect.w - 16) * (val - vmin), vmax - vmin); - if (edit) { - dc->drawBitmapPattern(0, 11, LBM_SLIDER_BAR_LEFT, FOCUS_BGCOLOR); - dc->drawSolidFilledRect(4, 11, rect.w - 8, 4, FOCUS_BGCOLOR); - dc->drawBitmapPattern(rect.w - 4, 11, LBM_SLIDER_BAR_RIGHT, FOCUS_BGCOLOR); - } - else { - dc->drawBitmapPattern(0, 11, LBM_SLIDER_BAR_LEFT, LINE_COLOR); - dc->drawSolidFilledRect(4, 11, rect.w - 8, 4, LINE_COLOR); - dc->drawBitmapPattern(rect.w - 4, 11, LBM_SLIDER_BAR_RIGHT, LINE_COLOR); + LcdFlags color = DEFAULT_COLOR; + if (focus) { + color = FOCUS_BGCOLOR; } - dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_OUT, DEFAULT_COLOR); - dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_MID, DEFAULT_BGCOLOR); - // if ((options & INVERS) && (!(options & BLINK) || !BLINK_ON_PHASE)) - if (focus) { - dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_IN, FOCUS_BGCOLOR); + dc->drawBitmapPattern(0, 11, LBM_SLIDER_BAR_LEFT, color); + dc->drawSolidFilledRect(4, 11, rect.w - 8, 4, color); + dc->drawBitmapPattern(rect.w - 4, 11, LBM_SLIDER_BAR_RIGHT, color); + + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_OUT, color); + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_MID, FIELD_BGCOLOR); + + if (edit) { + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_IN, EDIT_MARKER_COLOR); + } else { + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_IN, FIELD_BGCOLOR); } } diff --git a/radio/src/gui/colorlcd/theme.h b/radio/src/gui/colorlcd/theme.h index 90d2db9e8..9645a3084 100644 --- a/radio/src/gui/colorlcd/theme.h +++ b/radio/src/gui/colorlcd/theme.h @@ -85,9 +85,11 @@ class OpenTxTheme: public Theme virtual void drawBackground(BitmapBuffer * dc) const; - virtual void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char *title) const = 0; + virtual void drawPageHeaderBackground(BitmapBuffer *dc, uint8_t icon, + const char *title) const = 0; - virtual void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const = 0; + virtual void drawPageHeader(BitmapBuffer *dc, std::vector &tabs, + uint8_t currentIndex) const = 0; // virtual void drawMessageBox(const char * title, const char * text, const // char * action, uint32_t flags) const override; @@ -111,9 +113,9 @@ class OpenTxTheme: public Theme BitmapBuffer * thumb; public: - static const BitmapBuffer * asterisk; - static const BitmapBuffer * question; + static const BitmapBuffer * error; static const BitmapBuffer * busy; + static const BitmapBuffer * shutdown; }; OpenTxTheme * getTheme(const char * name); @@ -122,4 +124,6 @@ void loadTheme(); std::list & getRegisteredThemes(); +extern const uint8_t LBM_DROPDOWN[]; + #endif // _COLORLCD_THEME_H_ diff --git a/radio/src/gui/colorlcd/themes/480_bitmaps.h b/radio/src/gui/colorlcd/themes/480_bitmaps.h new file mode 100644 index 000000000..2b1df160b --- /dev/null +++ b/radio/src/gui/colorlcd/themes/480_bitmaps.h @@ -0,0 +1,244 @@ +const uint8_t mask_next[] = { +#include "mask_next.lbm" +}; +const uint8_t mask_back[] = { +#include "mask_back.lbm" +}; +const uint8_t mask_currentmenu_bg[] = { +#include "mask_currentmenu_bg.lbm" +}; +const uint8_t mask_currentmenu_dot[] = { +#include "mask_currentmenu_dot.lbm" +}; +const uint8_t mask_currentmenu_shadow[] = { +#include "mask_currentmenu_shadow.lbm" +}; +const uint8_t mask_menu_model[] = { +#include "mask_menu_model.lbm" +}; +const uint8_t mask_menu_radio[] = { +#include "mask_menu_radio.lbm" +}; +const uint8_t mask_menu_stats[] = { +#include "mask_menu_stats.lbm" +}; +const uint8_t mask_menu_theme[] = { +#include "mask_menu_theme.lbm" +}; +const uint8_t mask_model_curves[] = { +#include "mask_model_curves.lbm" +}; +const uint8_t mask_model_flight_modes[] = { +#include "mask_model_flight_modes.lbm" +}; +const uint8_t mask_model_gvars[] = { +#include "mask_model_gvars.lbm" +}; +const uint8_t mask_model_heli[] = { +#include "mask_model_heli.lbm" +}; +const uint8_t mask_model_inputs[] = { +#include "mask_model_inputs.lbm" +}; +const uint8_t mask_model_logical_switches[] = { +#include "mask_model_logical_switches.lbm" +}; +const uint8_t mask_model_lua_scripts[] = { +#include "mask_model_lua_scripts.lbm" +}; +const uint8_t mask_model_mixer[] = { +#include "mask_model_mixer.lbm" +}; +const uint8_t mask_model_outputs[] = { +#include "mask_model_outputs.lbm" +}; +const uint8_t mask_model_setup[] = { +#include "mask_model_setup.lbm" +}; +const uint8_t mask_model_special_functions[] = { +#include "mask_model_special_functions.lbm" +}; +const uint8_t mask_model_telemetry[] = { +#include "mask_model_telemetry.lbm" +}; +const uint8_t mask_monitor[] = { +#include "mask_monitor.lbm" +}; +const uint8_t mask_monitor_channels1[] = { +#include "mask_monitor_channels1.lbm" +}; +const uint8_t mask_monitor_channels2[] = { +#include "mask_monitor_channels2.lbm" +}; +const uint8_t mask_monitor_channels3[] = { +#include "mask_monitor_channels3.lbm" +}; +const uint8_t mask_monitor_channels4[] = { +#include "mask_monitor_channels4.lbm" +}; +const uint8_t mask_monitor_gvars[] = { +#include "mask_monitor_gvars.lbm" +}; +const uint8_t mask_monitor_inver[] = { +#include "mask_monitor_inver.lbm" +}; +const uint8_t mask_monitor_lockch[] = { +#include "mask_monitor_lockch.lbm" +}; +const uint8_t mask_monitor_logsw[] = { +#include "mask_monitor_logsw.lbm" +}; +const uint8_t mask_monitor_mixes1[] = { +#include "mask_monitor_mixes1.lbm" +}; +const uint8_t mask_monitor_mixes2[] = { +#include "mask_monitor_mixes2.lbm" +}; +const uint8_t mask_mplex_add[] = { +#include "mask_mplex_add.lbm" +}; +const uint8_t mask_mplex_multi[] = { +#include "mask_mplex_multi.lbm" +}; +const uint8_t mask_mplex_replace[] = { +#include "mask_mplex_replace.lbm" +}; +const uint8_t mask_topleft[] = { +#include "mask_topleft.lbm" +}; +const uint8_t mask_edgetx[] = { +#include "mask_edgetx.lbm" +}; +const uint8_t mask_opentx_testmode[] = { +#include "mask_opentx_testmode.lbm" +}; +const uint8_t mask_radio_calibration[] = { +#include "mask_radio_calibration.lbm" +}; +const uint8_t mask_radio_global_functions[] = { +#include "mask_radio_global_functions.lbm" +}; +const uint8_t mask_radio_hardware[] = { +#include "mask_radio_hardware.lbm" +}; +const uint8_t mask_radio_sd_browser[] = { +#include "mask_radio_sd_browser.lbm" +}; +const uint8_t mask_radio_setup[] = { +#include "mask_radio_setup.lbm" +}; +const uint8_t mask_radio_tools[] = { +#include "mask_radio_tools.lbm" +}; +const uint8_t mask_radio_trainer[] = { +#include "mask_radio_trainer.lbm" +}; +const uint8_t mask_radio_version[] = { +#include "mask_radio_version.lbm" +}; +const uint8_t mask_sbar_mixer[] = { +#include "mask_sbar_mixer.lbm" +}; +const uint8_t mask_sbar_output[] = { +#include "mask_sbar_output.lbm" +}; +const uint8_t mask_sbar_to[] = { +#include "mask_sbar_to.lbm" +}; +const uint8_t mask_stats_analogs[] = { +#include "mask_stats_analogs.lbm" +}; +const uint8_t mask_stats_debug[] = { +#include "mask_stats_debug.lbm" +}; +const uint8_t mask_stats_lua[] = { +#include "mask_stats_lua.lbm" +}; +const uint8_t mask_stats_throttle_graph[] = { +#include "mask_stats_throttle_graph.lbm" +}; +const uint8_t mask_stats_timers[] = { +#include "mask_stats_timers.lbm" +}; +const uint8_t mask_stats_value[] = { +#include "mask_stats_value.lbm" +}; +const uint8_t mask_switches[] = { +#include "mask_switches.lbm" +}; +const uint8_t mask_textline_curve[] = { +#include "mask_textline_curve.lbm" +}; +const uint8_t mask_textline_delay[] = { +#include "mask_textline_delay.lbm" +}; +const uint8_t mask_textline_delayslow[] = { +#include "mask_textline_delayslow.lbm" +}; +const uint8_t mask_textline_fm[] = { +#include "mask_textline_fm.lbm" +}; +const uint8_t mask_textline_label[] = { +#include "mask_textline_label.lbm" +}; +const uint8_t mask_textline_slow[] = { +#include "mask_textline_slow.lbm" +}; +const uint8_t mask_textline_switch[] = { +#include "mask_textline_switch.lbm" +}; +const uint8_t mask_theme_add_view[] = { +#include "mask_theme_add_view.lbm" +}; +const uint8_t mask_theme_setup[] = { +#include "mask_theme_setup.lbm" +}; +const uint8_t mask_theme_view1[] = { +#include "mask_theme_view1.lbm" +}; +const uint8_t mask_theme_view2[] = { +#include "mask_theme_view2.lbm" +}; +const uint8_t mask_theme_view3[] = { +#include "mask_theme_view3.lbm" +}; +const uint8_t mask_theme_view4[] = { +#include "mask_theme_view4.lbm" +}; +const uint8_t mask_theme_view5[] = { +#include "mask_theme_view5.lbm" +}; +const uint8_t mask_theme_view6[] = { +#include "mask_theme_view6.lbm" +}; +const uint8_t mask_theme_view7[] = { +#include "mask_theme_view7.lbm" +}; +const uint8_t mask_theme_view8[] = { +#include "mask_theme_view8.lbm" +}; +const uint8_t stick_pointer[] = { +#include "alpha_stick_pointer.lbm" +}; +const uint8_t stick_background[] = { +#include "alpha_stick_background.lbm" +}; +const uint8_t trackp_background[] = { +#include "alpha_trackp_background.lbm" +}; +const uint8_t mask_sdfree[] = { +#include "mask_sdfree.lbm" +}; +const uint8_t mask_modelqty[] = { +#include "mask_modelqty.lbm" +}; +const uint8_t mask_modelname[] = { +#include "mask_modelname.lbm" +}; +const uint8_t mask_moveback[] = { +#include "mask_moveback.lbm" +}; +const uint8_t mask_moveico[] = { +#include "mask_moveico.lbm" +}; + diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 67f8f3b97..5d5907536 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -20,6 +20,7 @@ #include "opentx.h" #include "tabsgroup.h" +#include "480_bitmaps.h" const ZoneOption OPTIONS_THEME_DEFAULT[] = { { STR_BACKGROUND_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(WHITE) }, @@ -27,6 +28,16 @@ const ZoneOption OPTIONS_THEME_DEFAULT[] = { { nullptr, ZoneOption::Bool } }; +constexpr uint16_t __TEXT_COLOR = RGB(0x0C, 0x3F, 0x66); +constexpr uint16_t __BACKGROUND_COLOR = RGB(0x12, 0x5E, 0x99); +constexpr uint16_t __FOCUS_COLOR = RGB(0x14, 0xA1, 0xE5); +constexpr uint16_t __DATAFIELD_FRAME_COLOR = RGB(0xB6, 0xE0, 0xF2); +constexpr uint16_t __TAB_BACKGROUND_COLOR = RGB(0xE4, 0xEE, 0xF2); +constexpr uint16_t __PARAM_BACKGROUND_COLOR = WHITE; + +constexpr uint16_t __EDIT_MARKER_COLOR = RGB(0x00, 0x99, 0x09); +constexpr uint16_t __ACTIVE_MARKER_COLOR = RGB(0xFF, 0xDE, 0x00); + class Theme480: public OpenTxTheme { public: @@ -44,46 +55,47 @@ class Theme480: public OpenTxTheme lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(167, 167, 167); lcdColorTable[BARGRAPH_BGCOLOR_INDEX] = RGB(222, 222, 222); lcdColorTable[BATTERY_CHARGE_COLOR_INDEX] = GREEN; - lcdColorTable[CHECKBOX_COLOR_INDEX] = RED; + lcdColorTable[CHECKBOX_COLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[CURVE_COLOR_INDEX] = RED; lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = RED; - lcdColorTable[DEFAULT_BGCOLOR_INDEX] = WHITE; - lcdColorTable[DEFAULT_COLOR_INDEX] = BLACK; + lcdColorTable[DEFAULT_BGCOLOR_INDEX] = __TAB_BACKGROUND_COLOR; + lcdColorTable[DEFAULT_COLOR_INDEX] = __TEXT_COLOR; lcdColorTable[DISABLE_COLOR_INDEX] = LIGHTGREY; - lcdColorTable[FOCUS_BGCOLOR_INDEX] = RED; + lcdColorTable[EDIT_MARKER_COLOR_INDEX] = __EDIT_MARKER_COLOR; + lcdColorTable[FIELD_BGCOLOR_INDEX] = WHITE; + lcdColorTable[FIELD_FRAME_COLOR_INDEX] = __DATAFIELD_FRAME_COLOR; + lcdColorTable[FOCUS_BGCOLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[FOCUS_COLOR_INDEX] = WHITE; - lcdColorTable[HEADER_COLOR_INDEX] = DARKRED; - lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = RED; - lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = RED; - lcdColorTable[HIGHLIGHT_COLOR_INDEX] = YELLOW; + lcdColorTable[HEADER_COLOR_INDEX] = __BACKGROUND_COLOR; + lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = __FOCUS_COLOR; + lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = __BACKGROUND_COLOR; + lcdColorTable[HIGHLIGHT_COLOR_INDEX] = __ACTIVE_MARKER_COLOR; lcdColorTable[LINE_COLOR_INDEX] = GREY; lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = RED; lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = WHITE; - // this is what the status bar uses... - //lcdColorTable[MENU_BGCOLOR_INDEX] = DARKRED; + lcdColorTable[MENU_BGCOLOR_INDEX] = WHITE; + lcdColorTable[MENU_COLOR_INDEX] = __BACKGROUND_COLOR; // Menu font color - // Menu font color - lcdColorTable[MENU_BGCOLOR_INDEX] = DARKRED;//DARKGREY; - lcdColorTable[MENU_COLOR_INDEX] = WHITE; // Selected item in menu - lcdColorTable[MENU_HIGHLIGHT_BGCOLOR_INDEX] = BLACK; + lcdColorTable[MENU_HIGHLIGHT_BGCOLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[MENU_HIGHLIGHT_COLOR_INDEX] = WHITE; + lcdColorTable[MENU_LINE_COLOR_INDEX] = __DATAFIELD_FRAME_COLOR; - lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = RGB(GET_RED(RED)>>1, GET_GREEN(RED)>>1, GET_BLUE(RED)>>1); + lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = __TEXT_COLOR; lcdColorTable[OVERLAY_COLOR_INDEX] = BLACK; - lcdColorTable[SCROLLBAR_COLOR_INDEX] = RED; + lcdColorTable[SCROLLBAR_COLOR_INDEX] = __TEXT_COLOR; lcdColorTable[TEXT_DISABLE_COLOR_INDEX] = GREY; lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = WHITE; - lcdColorTable[TITLE_BGCOLOR_INDEX] = RED; - lcdColorTable[TRIM_BGCOLOR_INDEX] = RED; + lcdColorTable[TITLE_BGCOLOR_INDEX] = __BACKGROUND_COLOR; + + lcdColorTable[TRIM_BGCOLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[TRIM_SHADOW_COLOR_INDEX] = BLACK; } - void loadMenuIcon(uint8_t index, const char * filename, uint32_t color=MENU_COLOR) const + void loadMenuIcon(uint8_t index, const uint8_t * lbm) const { - TRACE("loadMenuIcon %s", getFilePath(filename)); - BitmapBuffer * mask = BitmapBuffer::loadMask(getFilePath(filename)); + BitmapBuffer * mask = BitmapBuffer::load8bitMask(lbm); if (mask) { delete iconMask[index]; iconMask[index] = mask; @@ -91,15 +103,15 @@ class Theme480: public OpenTxTheme delete menuIconNormal[index]; menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, mask->width(), mask->height()); if (menuIconNormal[index]) { - menuIconNormal[index]->clear(MENU_BGCOLOR); - menuIconNormal[index]->drawMask(0, 0, mask, color); + menuIconNormal[index]->clear(HEADER_ICON_BGCOLOR); + menuIconNormal[index]->drawMask(0, 0, mask, FOCUS_COLOR); } delete menuIconSelected[index]; menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, mask->width(), mask->height()); if (menuIconSelected[index]) { menuIconSelected[index]->clear(HEADER_CURRENT_BGCOLOR); - menuIconSelected[index]->drawMask(0, 0, mask, color); + menuIconSelected[index]->drawMask(0, 0, mask, FOCUS_COLOR); } } } @@ -107,76 +119,90 @@ class Theme480: public OpenTxTheme void loadIcons() const { #if defined(LOG_TELEMETRY) || !defined(WATCHDOG) - loadMenuIcon(ICON_OPENTX, "mask_opentx_testmode.png", DEFAULT_COLOR); + loadMenuIcon(ICON_OPENTX, mask_opentx_testmode); #else - loadMenuIcon(ICON_OPENTX, "mask_edgetx.png"); + loadMenuIcon(ICON_OPENTX, mask_edgetx); #endif -#if defined(HARDWARE_TOUCH) - loadMenuIcon(ICON_NEXT, "mask_next.png"); - loadMenuIcon(ICON_BACK, "mask_back.png"); +#if defined(HARDWARE_TOUCH) //TODO: get rid of, and use a real hitbox instead... + loadMenuIcon(ICON_NEXT, mask_next); + loadMenuIcon(ICON_BACK, mask_back); #endif - loadMenuIcon(ICON_RADIO, "mask_menu_radio.png"); - loadMenuIcon(ICON_RADIO_SETUP, "mask_radio_setup.png"); - loadMenuIcon(ICON_RADIO_SD_MANAGER, "mask_radio_sd_browser.png"); - loadMenuIcon(ICON_RADIO_TOOLS, "mask_radio_tools.png"); - loadMenuIcon(ICON_RADIO_SPECTRUM_ANALYSER, "/mask_radio_spectrum_analyser.png"); - loadMenuIcon(ICON_RADIO_GLOBAL_FUNCTIONS, "mask_radio_global_functions.png"); - loadMenuIcon(ICON_RADIO_TRAINER, "mask_radio_trainer.png"); - loadMenuIcon(ICON_RADIO_HARDWARE, "mask_radio_hardware.png"); - loadMenuIcon(ICON_RADIO_CALIBRATION, "mask_radio_calibration.png"); - loadMenuIcon(ICON_RADIO_VERSION, "mask_radio_version.png"); - loadMenuIcon(ICON_MODEL, "mask_menu_model.png"); - loadMenuIcon(ICON_MODEL_SETUP, "mask_model_setup.png"); - loadMenuIcon(ICON_MODEL_HELI, "mask_model_heli.png"); - loadMenuIcon(ICON_MODEL_FLIGHT_MODES, "mask_model_flight_modes.png"); - loadMenuIcon(ICON_MODEL_INPUTS, "mask_model_inputs.png"); - loadMenuIcon(ICON_MODEL_MIXER, "mask_model_mixer.png"); - loadMenuIcon(ICON_MODEL_OUTPUTS, "mask_model_outputs.png"); - loadMenuIcon(ICON_MODEL_CURVES, "mask_model_curves.png"); - loadMenuIcon(ICON_MODEL_GVARS, "mask_model_gvars.png"); - loadMenuIcon(ICON_MODEL_LOGICAL_SWITCHES, "mask_model_logical_switches.png"); - loadMenuIcon(ICON_MODEL_SPECIAL_FUNCTIONS, "mask_model_special_functions.png"); - loadMenuIcon(ICON_MODEL_LUA_SCRIPTS, "mask_model_lua_scripts.png"); - loadMenuIcon(ICON_MODEL_TELEMETRY, "mask_model_telemetry.png"); - loadMenuIcon(ICON_STATS, "mask_menu_stats.png"); - loadMenuIcon(ICON_STATS_THROTTLE_GRAPH, "mask_stats_throttle_graph.png"); - loadMenuIcon(ICON_STATS_TIMERS, "mask_stats_timers.png"); - loadMenuIcon(ICON_STATS_ANALOGS, "mask_stats_analogs.png"); - loadMenuIcon(ICON_STATS_DEBUG, "mask_stats_debug.png"); - loadMenuIcon(ICON_THEME, "mask_menu_theme.png"); - loadMenuIcon(ICON_THEME_SETUP, "mask_theme_setup.png"); - loadMenuIcon(ICON_THEME_VIEW1, "mask_theme_view1.png"); - loadMenuIcon(ICON_THEME_VIEW2, "mask_theme_view2.png"); - loadMenuIcon(ICON_THEME_VIEW3, "mask_theme_view3.png"); - loadMenuIcon(ICON_THEME_VIEW4, "mask_theme_view4.png"); - loadMenuIcon(ICON_THEME_VIEW5, "mask_theme_view5.png"); - loadMenuIcon(ICON_THEME_ADD_VIEW, "mask_theme_add_view.png"); - loadMenuIcon(ICON_MONITOR, "mask_monitor.png"); - loadMenuIcon(ICON_MONITOR_CHANNELS1, "mask_monitor_channels1.png"); - loadMenuIcon(ICON_MONITOR_CHANNELS2, "mask_monitor_channels2.png"); - loadMenuIcon(ICON_MONITOR_CHANNELS3, "mask_monitor_channels3.png"); - loadMenuIcon(ICON_MONITOR_CHANNELS4, "mask_monitor_channels4.png"); - loadMenuIcon(ICON_MONITOR_LOGICAL_SWITCHES, "mask_monitor_logsw.png"); + loadMenuIcon(ICON_RADIO, mask_menu_radio); + loadMenuIcon(ICON_RADIO_SETUP, mask_radio_setup); + loadMenuIcon(ICON_RADIO_SD_MANAGER, mask_radio_sd_browser); + loadMenuIcon(ICON_RADIO_TOOLS, mask_radio_tools); + //loadMenuIcon(ICON_RADIO_SPECTRUM_ANALYSER, mask_radio_spectrum_analyser); + loadMenuIcon(ICON_RADIO_GLOBAL_FUNCTIONS, mask_radio_global_functions); + loadMenuIcon(ICON_RADIO_TRAINER, mask_radio_trainer); + loadMenuIcon(ICON_RADIO_HARDWARE, mask_radio_hardware); + loadMenuIcon(ICON_RADIO_CALIBRATION, mask_radio_calibration); + loadMenuIcon(ICON_RADIO_VERSION, mask_radio_version); + loadMenuIcon(ICON_MODEL, mask_menu_model); + loadMenuIcon(ICON_MODEL_SETUP, mask_model_setup); + loadMenuIcon(ICON_MODEL_HELI, mask_model_heli); + loadMenuIcon(ICON_MODEL_FLIGHT_MODES, mask_model_flight_modes); + loadMenuIcon(ICON_MODEL_INPUTS, mask_model_inputs); + loadMenuIcon(ICON_MODEL_MIXER, mask_model_mixer); + loadMenuIcon(ICON_MODEL_OUTPUTS, mask_model_outputs); + loadMenuIcon(ICON_MODEL_CURVES, mask_model_curves); + loadMenuIcon(ICON_MODEL_GVARS, mask_model_gvars); + loadMenuIcon(ICON_MODEL_LOGICAL_SWITCHES, mask_model_logical_switches); + loadMenuIcon(ICON_MODEL_SPECIAL_FUNCTIONS, mask_model_special_functions); + loadMenuIcon(ICON_MODEL_LUA_SCRIPTS, mask_model_lua_scripts); + loadMenuIcon(ICON_MODEL_TELEMETRY, mask_model_telemetry); + loadMenuIcon(ICON_STATS, mask_menu_stats); + loadMenuIcon(ICON_STATS_THROTTLE_GRAPH, mask_stats_throttle_graph); + loadMenuIcon(ICON_STATS_TIMERS, mask_stats_timers); + loadMenuIcon(ICON_STATS_ANALOGS, mask_stats_analogs); + loadMenuIcon(ICON_STATS_DEBUG, mask_stats_debug); + loadMenuIcon(ICON_THEME, mask_menu_theme); + loadMenuIcon(ICON_THEME_SETUP, mask_theme_setup); + loadMenuIcon(ICON_THEME_VIEW1, mask_theme_view1); + loadMenuIcon(ICON_THEME_VIEW2, mask_theme_view2); + loadMenuIcon(ICON_THEME_VIEW3, mask_theme_view3); + loadMenuIcon(ICON_THEME_VIEW4, mask_theme_view4); + loadMenuIcon(ICON_THEME_VIEW5, mask_theme_view5); + loadMenuIcon(ICON_THEME_ADD_VIEW, mask_theme_add_view); + loadMenuIcon(ICON_MONITOR, mask_monitor); + loadMenuIcon(ICON_MONITOR_CHANNELS1, mask_monitor_channels1); + loadMenuIcon(ICON_MONITOR_CHANNELS2, mask_monitor_channels2); + loadMenuIcon(ICON_MONITOR_CHANNELS3, mask_monitor_channels3); + loadMenuIcon(ICON_MONITOR_CHANNELS4, mask_monitor_channels4); + loadMenuIcon(ICON_MONITOR_LOGICAL_SWITCHES, mask_monitor_logsw); - BitmapBuffer * background = BitmapBuffer::loadMask(getFilePath("mask_currentmenu_bg.png")); - BitmapBuffer * shadow = BitmapBuffer::loadMask(getFilePath("mask_currentmenu_shadow.png")); - BitmapBuffer * dot = BitmapBuffer::loadMask(getFilePath("mask_currentmenu_dot.png")); + BitmapBuffer * background = BitmapBuffer::load8bitMask(mask_currentmenu_bg); + BitmapBuffer * shadow = BitmapBuffer::load8bitMask(mask_currentmenu_shadow); + BitmapBuffer * dot = BitmapBuffer::load8bitMask(mask_currentmenu_dot); if (!currentMenuBackground) { currentMenuBackground = new BitmapBuffer(BMP_RGB565, 36, 53); } if (currentMenuBackground) { - currentMenuBackground->drawSolidFilledRect(0, 0, currentMenuBackground->width(), MENU_HEADER_HEIGHT, MENU_BGCOLOR); - currentMenuBackground->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, currentMenuBackground->width(), MENU_TITLE_TOP - MENU_HEADER_HEIGHT, DEFAULT_BGCOLOR); - currentMenuBackground->drawSolidFilledRect(0, MENU_TITLE_TOP, currentMenuBackground->width(), currentMenuBackground->height() - MENU_TITLE_TOP, TITLE_BGCOLOR); - currentMenuBackground->drawMask(0, 0, background, HEADER_CURRENT_BGCOLOR); + + currentMenuBackground->drawSolidFilledRect( + 0, 0, currentMenuBackground->width(), MENU_HEADER_HEIGHT, + HEADER_COLOR); + + currentMenuBackground->drawSolidFilledRect( + 0, MENU_HEADER_HEIGHT, currentMenuBackground->width(), + MENU_TITLE_TOP - MENU_HEADER_HEIGHT, DEFAULT_BGCOLOR); + + currentMenuBackground->drawSolidFilledRect( + 0, MENU_TITLE_TOP, currentMenuBackground->width(), + currentMenuBackground->height() - MENU_TITLE_TOP, TITLE_BGCOLOR); + + currentMenuBackground->drawMask(0, 0, background, + HEADER_CURRENT_BGCOLOR); + currentMenuBackground->drawMask(0, 0, shadow, TRIM_SHADOW_COLOR); - currentMenuBackground->drawMask(10, 39, dot, MENU_COLOR); + + currentMenuBackground->drawMask(10, 39, dot, FOCUS_COLOR); } delete topleftBitmap; - topleftBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("topleft.png"), TITLE_BGCOLOR, HEADER_COLOR); + topleftBitmap = BitmapBuffer::load8bitMaskOnBackground( + mask_topleft, HEADER_CURRENT_BGCOLOR, HEADER_COLOR); delete background; delete shadow; @@ -187,90 +213,85 @@ class Theme480: public OpenTxTheme { // Calibration screen delete calibStick; - calibStick = BitmapBuffer::loadBitmap(getFilePath("stick_pointer.png")); + calibStick = + BitmapBuffer::loadRamBitmap(stick_pointer, sizeof(stick_pointer)); delete calibStickBackground; - calibStickBackground = BitmapBuffer::loadBitmap(getFilePath("stick_background.png")); + calibStickBackground = BitmapBuffer::loadRamBitmap( + stick_background, sizeof(stick_background)); delete calibTrackpBackground; - calibTrackpBackground = BitmapBuffer::loadBitmap(getFilePath("trackp_background.png")); - - delete calibRadioPict; -#if defined(PCBX10) - if(STICKS_PWM_ENABLED()) { - calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("X10S.bmp")); - } - else { - calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("X10.bmp")); - } -#else - calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("horus.bmp")); -#endif + calibTrackpBackground = BitmapBuffer::loadRamBitmap( + trackp_background, sizeof(trackp_background)); // Model Selection screen - delete modelselIconBitmap; - modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); - if (modelselIconBitmap) { - BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); - modelselIconBitmap->drawBitmap(20, 8, bitmap); - delete bitmap; - } + + // Unused: + // + // delete modelselIconBitmap; + // modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); + // if (modelselIconBitmap) { + // BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); + // modelselIconBitmap->drawBitmap(20, 8, bitmap); + // delete bitmap; + // } delete modelselSdFreeBitmap; - modelselSdFreeBitmap = BitmapBuffer::loadMask(getFilePath("modelsel/mask_sdfree.png")); + modelselSdFreeBitmap = BitmapBuffer::load8bitMask(mask_sdfree); delete modelselModelQtyBitmap; - modelselModelQtyBitmap = BitmapBuffer::loadMask(getFilePath("modelsel/mask_modelqty.png")); + modelselModelQtyBitmap = BitmapBuffer::load8bitMask(mask_modelqty); delete modelselModelNameBitmap; - modelselModelNameBitmap = BitmapBuffer::loadMask(getFilePath("modelsel/mask_modelname.png")); + modelselModelNameBitmap = BitmapBuffer::load8bitMask(mask_modelname); delete modelselModelMoveBackground; - modelselModelMoveBackground = BitmapBuffer::loadMask(getFilePath("modelsel/mask_moveback.png")); + modelselModelMoveBackground = BitmapBuffer::load8bitMask(mask_moveback); delete modelselModelMoveIcon; - modelselModelMoveIcon = BitmapBuffer::loadMask(getFilePath("modelsel/mask_moveico.png")); + modelselModelMoveIcon = BitmapBuffer::load8bitMask(mask_moveico); + //TODO: should be loaded from LUA, not here!!! delete modelselWizardBackground; modelselWizardBackground = BitmapBuffer::loadBitmap(getFilePath("wizard/background.png")); // Channels monitor screen delete chanMonLockedBitmap; - chanMonLockedBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_monitor_lockch.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + chanMonLockedBitmap = BitmapBuffer::load8bitMask(mask_monitor_lockch); delete chanMonInvertedBitmap; - chanMonInvertedBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_monitor_inver.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + chanMonInvertedBitmap = BitmapBuffer::load8bitMask(mask_monitor_inver); // Mixer setup screen delete mixerSetupMixerBitmap; - mixerSetupMixerBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_mixer.png", MENU_COLOR, MENU_BGCOLOR); + mixerSetupMixerBitmap = BitmapBuffer::load8bitMask(mask_sbar_mixer); delete mixerSetupToBitmap; - mixerSetupToBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_to.png", MENU_COLOR, MENU_BGCOLOR); + mixerSetupToBitmap = BitmapBuffer::load8bitMask(mask_sbar_to); delete mixerSetupOutputBitmap; - mixerSetupOutputBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_output.png", MENU_COLOR, MENU_BGCOLOR); + mixerSetupOutputBitmap = BitmapBuffer::load8bitMask(mask_sbar_output); delete mixerSetupAddBitmap; - mixerSetupAddBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_mplex_add.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + mixerSetupAddBitmap = BitmapBuffer::load8bitMask(mask_mplex_add); delete mixerSetupMultiBitmap; - mixerSetupMultiBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_mplex_multi.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + mixerSetupMultiBitmap = BitmapBuffer::load8bitMask(mask_mplex_multi); delete mixerSetupReplaceBitmap; - mixerSetupReplaceBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_mplex_replace.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + mixerSetupReplaceBitmap = BitmapBuffer::load8bitMask(mask_mplex_replace); delete mixerSetupLabelIcon; - mixerSetupLabelIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_label.png")); + mixerSetupLabelIcon = BitmapBuffer::load8bitMask(mask_textline_label); delete mixerSetupCurveIcon; - mixerSetupCurveIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_curve.png")); + mixerSetupCurveIcon = BitmapBuffer::load8bitMask(mask_textline_curve); delete mixerSetupSwitchIcon; - mixerSetupSwitchIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_switch.png")); + mixerSetupSwitchIcon = BitmapBuffer::load8bitMask(mask_textline_switch); delete mixerSetupFlightmodeIcon; - mixerSetupFlightmodeIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_fm.png")); + mixerSetupFlightmodeIcon = BitmapBuffer::load8bitMask(mask_textline_fm); // delete mixerSetupSlowIcon; // mixerSetupSlowIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_slow.png")); @@ -325,8 +346,8 @@ class Theme480: public OpenTxTheme dc->drawBitmap(0 - dc->getOffsetX(), 0 - dc->getOffsetY(), backgroundBitmap); } else { - lcdSetColor(g_eeGeneral.themeData.options[0].value.unsignedValue); - dc->drawSolidFilledRect(0, 0, LCD_W, LCD_H, CUSTOM_COLOR); + dc->drawSolidFilledRect(0 - dc->getOffsetX(), 0 - dc->getOffsetY(), + LCD_W, LCD_H, DEFAULT_BGCOLOR); } } @@ -338,7 +359,8 @@ class Theme480: public OpenTxTheme } } - void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char * title) const override + void drawPageHeaderBackground(BitmapBuffer *dc, uint8_t icon, + const char *title) const override { if (topleftBitmap) { dc->drawBitmap(0, 0, topleftBitmap); @@ -354,10 +376,14 @@ class Theme480: public OpenTxTheme else dc->drawBitmap(5, 7, menuIconSelected[icon]); - dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT, DEFAULT_BGCOLOR); // the white separation line - dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR); // the title line background + dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, + MENU_TITLE_TOP - MENU_HEADER_HEIGHT, + DEFAULT_BGCOLOR); // the white separation line + + dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, + TITLE_BGCOLOR); // the title line background if (title) { - dc->drawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 3, title, MENU_COLOR); + dc->drawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 3, title, FOCUS_COLOR); } drawMenuDatetime(dc); @@ -373,20 +399,24 @@ class Theme480: public OpenTxTheme return state == STATE_DEFAULT ? menuIconNormal[index] : menuIconSelected[index]; } - void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const override + void drawPageHeader(BitmapBuffer *dc, std::vector &tabs, + uint8_t currentIndex) const override { for (unsigned index = 0; index < tabs.size(); index++) { if (index != currentIndex) { - dc->drawBitmap(index*MENU_HEADER_BUTTON_WIDTH + 2, 7, menuIconNormal[tabs[index]->getIcon()]); + dc->drawBitmap(index * MENU_HEADER_BUTTON_WIDTH + 2, 7, + menuIconNormal[tabs[index]->getIcon()]); } } - dc->drawBitmap(currentIndex*MENU_HEADER_BUTTON_WIDTH, 0, currentMenuBackground); - dc->drawBitmap(currentIndex*MENU_HEADER_BUTTON_WIDTH + 2, 7, menuIconSelected[tabs[currentIndex]->getIcon()]); + dc->drawBitmap(currentIndex * MENU_HEADER_BUTTON_WIDTH, 0, + currentMenuBackground); + dc->drawBitmap(currentIndex * MENU_HEADER_BUTTON_WIDTH + 2, 7, + menuIconSelected[tabs[currentIndex]->getIcon()]); } void drawMenuDatetime(BitmapBuffer * dc) const { - dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, FOCUS_COLOR); + //dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, FOCUS_COLOR); struct gtm t; gettime(&t); @@ -402,11 +432,12 @@ class Theme480: public OpenTxTheme dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS)|FOCUS_COLOR|CENTERED); } - void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const override + void drawProgressBar(BitmapBuffer *dc, coord_t x, coord_t y, coord_t w, + coord_t h, int value, int total) const override { dc->drawSolidRect(x, y, w, h, 1, DEFAULT_COLOR); if (value > 0) { - int width = (w * value) / 100; + int width = (w * value) / total; dc->drawSolidFilledRect(x + 2, y + 2, width - 4, h - 4, CHECKBOX_COLOR); } } diff --git a/radio/src/gui/colorlcd/themes/flysky.cpp b/radio/src/gui/colorlcd/themes/flysky.cpp index b10f50fb6..a8c519d41 100644 --- a/radio/src/gui/colorlcd/themes/flysky.cpp +++ b/radio/src/gui/colorlcd/themes/flysky.cpp @@ -215,17 +215,14 @@ class FlyskyTheme: public OpenTxTheme delete calibTrackpBackground; calibTrackpBackground = BitmapBuffer::loadBitmap(getFilePath("trackp_background.png")); - delete calibRadioPict; - calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("NV14.bmp")); - // Model Selection screen - delete modelselIconBitmap; - modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); - if (modelselIconBitmap) { - BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); - modelselIconBitmap->drawBitmap(20, 8, bitmap); - delete bitmap; - } + // delete modelselIconBitmap; + // modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); + // if (modelselIconBitmap) { + // BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); + // modelselIconBitmap->drawBitmap(20, 8, bitmap); + // delete bitmap; + // } delete modelselSdFreeBitmap; modelselSdFreeBitmap = BitmapBuffer::loadMask(getFilePath("modelsel/mask_sdfree.png")); @@ -351,25 +348,30 @@ class FlyskyTheme: public OpenTxTheme } } - void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char * title) const override + void drawPageHeaderBackground(BitmapBuffer *dc, uint8_t icon, + const char *title) const override { -// if (topleftBitmap) { -// dc->drawBitmap(0, 0, topleftBitmap); -// uint16_t width = topleftBitmap->width(); -// dc->drawSolidFilledRect(width, 0, LCD_W-width, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); -// } -// else { - dc->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); -// } -// -// if (icon == ICON_OPENTX) -// dc->drawBitmap(4, 10, menuIconSelected[ICON_OPENTX]); -// else -// dc->drawBitmap(5, 7, menuIconSelected[icon]); -// - dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT, DEFAULT_BGCOLOR); // the white separation line - dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR); // the title line background -// + // if (topleftBitmap) { + // dc->drawBitmap(0, 0, topleftBitmap); + // uint16_t width = topleftBitmap->width(); + // dc->drawSolidFilledRect(width, 0, LCD_W-width, + // MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + // } + // else { + dc->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + // } + // + // if (icon == ICON_OPENTX) + // dc->drawBitmap(4, 10, menuIconSelected[ICON_OPENTX]); + // else + // dc->drawBitmap(5, 7, menuIconSelected[icon]); + // + dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, + MENU_TITLE_TOP - MENU_HEADER_HEIGHT, + DEFAULT_BGCOLOR); // the white separation line + dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, + TITLE_BGCOLOR); // the title line background + // if (title) { dc->drawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 2, title, MENU_COLOR); } @@ -385,19 +387,25 @@ class FlyskyTheme: public OpenTxTheme return state == STATE_DEFAULT ? menuIconNormal[index] : menuIconSelected[index]; } - void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const override + void drawPageHeader(BitmapBuffer *dc, std::vector &tabs, + uint8_t currentIndex) const override { uint8_t padding_left = 4; - dc->drawSolidFilledRect(0, 0, 4, MENU_HEADER_BUTTON_WIDTH, HEADER_BGCOLOR); + dc->drawSolidFilledRect(0, 0, 4, MENU_HEADER_BUTTON_WIDTH, + HEADER_BGCOLOR); for (unsigned i = 0; i < tabs.size(); i++) { - dc->drawBitmap(padding_left + i*MENU_HEADER_BUTTON_WIDTH, 0, theme->getIcon(tabs[i]->getIcon(), currentIndex == i ? STATE_PRESSED : STATE_DEFAULT)); + dc->drawBitmap( + padding_left + i * MENU_HEADER_BUTTON_WIDTH, 0, + theme->getIcon(tabs[i]->getIcon(), + currentIndex == i ? STATE_PRESSED : STATE_DEFAULT)); } -// coord_t x = padding_left + MENU_HEADER_BUTTON_WIDTH * tabs.size(); -// coord_t w = width() - x; -// if (w > 0) { -// dc->drawSolidFilledRect(x, 0, w, MENU_HEADER_BUTTON_WIDTH, HEADER_BGCOLOR); -// } + // coord_t x = padding_left + MENU_HEADER_BUTTON_WIDTH * tabs.size(); + // coord_t w = width() - x; + // if (w > 0) { + // dc->drawSolidFilledRect(x, 0, w, MENU_HEADER_BUTTON_WIDTH, + // HEADER_BGCOLOR); + // } } void drawMenuDatetime(BitmapBuffer * dc) const @@ -420,11 +428,12 @@ class FlyskyTheme: public OpenTxTheme dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS)|FOCUS_COLOR|CENTERED); } - void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const override + void drawProgressBar(BitmapBuffer *dc, coord_t x, coord_t y, coord_t w, + coord_t h, int value, int total) const override { dc->drawSolidRect(x, y, w, h, 1, DEFAULT_COLOR); if (value > 0) { - int width = (w * value) / 100; + int width = (w * value) / total; dc->drawSolidFilledRect(x + 2, y + 2, width - 4, h - 4, CHECKBOX_COLOR); } } diff --git a/radio/src/gui/colorlcd/timeedit.cpp b/radio/src/gui/colorlcd/timeedit.cpp index 72245d17d..804eb4b9c 100644 --- a/radio/src/gui/colorlcd/timeedit.cpp +++ b/radio/src/gui/colorlcd/timeedit.cpp @@ -36,11 +36,13 @@ void TimeEdit::paint(BitmapBuffer * dc) if (editMode) textColor = FOCUS_COLOR; else if (hasFocus()) - textColor = FOCUS_BGCOLOR; + textColor = FOCUS_COLOR; else textColor = DEFAULT_COLOR; - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getTimerString(getValue(), (textFlags & TIMEHOUR) != 0), textColor); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, + getTimerString(getValue(), (textFlags & TIMEHOUR) != 0), + textColor); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/gui/colorlcd/view_channels.cpp b/radio/src/gui/colorlcd/view_channels.cpp index 84d6238bd..8bfd1905a 100644 --- a/radio/src/gui/colorlcd/view_channels.cpp +++ b/radio/src/gui/colorlcd/view_channels.cpp @@ -46,9 +46,9 @@ class ChannelsViewFooter: public Window { // Draw single legend coord_t drawChannelsMonitorLegend(BitmapBuffer * dc, coord_t x, const char * s, int color) { - dc->drawSolidFilledRect(x, 2, LEG_COLORBOX + 2, LEG_COLORBOX + 2, BARGRAPH_BGCOLOR); - dc->drawSolidFilledRect(x + 1, 3, LEG_COLORBOX, LEG_COLORBOX, color); - dc->drawText(x + 20, 2, s, TEXT_STATUSBAR_COLOR); + dc->drawSolidFilledRect(x, 4, LEG_COLORBOX + 2, LEG_COLORBOX + 2, BARGRAPH_BGCOLOR); + dc->drawSolidFilledRect(x + 1, 5, LEG_COLORBOX, LEG_COLORBOX, color); + dc->drawText(x + 20, 4, s, TEXT_STATUSBAR_COLOR); return x + 25 + getTextWidth(s); } @@ -57,8 +57,8 @@ class ChannelsViewFooter: public Window { // Draw legend bar coord_t x = 10; dc->drawSolidFilledRect(0, 0, width(), height(), TITLE_BGCOLOR); - x = drawChannelsMonitorLegend(dc, MENUS_MARGIN_LEFT, STR_MONITOR_OUTPUT_DESC, BARGRAPH1_COLOR); - drawChannelsMonitorLegend(dc, x, STR_MONITOR_MIXER_DESC, BARGRAPH2_COLOR); + x = drawChannelsMonitorLegend(dc, MENUS_MARGIN_LEFT, STR_MONITOR_OUTPUT_DESC, HIGHLIGHT_COLOR); + drawChannelsMonitorLegend(dc, x, STR_MONITOR_MIXER_DESC, FOCUS_BGCOLOR); } }; diff --git a/radio/src/gui/colorlcd/view_logical_switches.cpp b/radio/src/gui/colorlcd/view_logical_switches.cpp index d8c5afc3e..3b4810338 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.cpp +++ b/radio/src/gui/colorlcd/view_logical_switches.cpp @@ -27,27 +27,30 @@ class LogicalSwitchDisplayButton : public TextButton { public: - LogicalSwitchDisplayButton(FormGroup * parent, const rect_t & rect, std::string text, unsigned index): - TextButton(parent, rect, std::move(text)), - index(index) - { - } + LogicalSwitchDisplayButton(FormGroup* parent, const rect_t& rect, + std::string text, unsigned index) : + TextButton(parent, rect, std::move(text), nullptr, OPAQUE), index(index) + { + setBgColorHandler([=]() -> LcdFlags { + if (getSwitch(SWSRC_SW1 + index)) return HIGHLIGHT_COLOR; + return FIELD_BGCOLOR; + }); + } - void checkEvents() override - { - bool newvalue = getSwitch(SWSRC_SW1 + index); - if (value != newvalue) { - if (newvalue) { - setTextFlags(FONT(BOLD) | HIGHLIGHT_COLOR); - } - else { - setTextFlags(FONT(STD) | DEFAULT_COLOR); - } - value = newvalue; - invalidate(); - } - Button::checkEvents(); - } + void checkEvents() override + { + bool newvalue = getSwitch(SWSRC_SW1 + index); + if (value != newvalue) { + if (newvalue) { + setTextFlags(FONT(BOLD) | DEFAULT_COLOR); + } else { + setTextFlags(FONT(STD) | DEFAULT_COLOR); + } + value = newvalue; + invalidate(); + } + Button::checkEvents(); + } protected: unsigned index = 0; @@ -61,21 +64,28 @@ void LogicalSwitchesViewPage::build(FormWindow * window) grid.spacer(PAGE_PADDING); grid.setLabelWidth(8); + // Footer + footer = new LogicalSwitchDisplayFooter( + window, {0, window->height() - LSW_VIEW_FOOTER_HEIGHT, window->width(), + LSW_VIEW_FOOTER_HEIGHT}); + // LSW table - std::string lsString ("LS64"); + std::string lsString("LS64"); lcdColorTable[CUSTOM_COLOR_INDEX] = RGB(160, 160, 160); for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { + strAppendSigned(&lsString[1], i + 1, 2); - auto button = new LogicalSwitchDisplayButton(window, grid.getFieldSlot(8, i % 8), lsString, i); - button->setPressHandler([=]() { - footer->setIndex(i); - footer->invalidate(); + + auto button = new LogicalSwitchDisplayButton( + window, grid.getFieldSlot(8, i % 8), lsString, i); + + button->setFocusHandler([=](bool focus) { + if (focus) { + footer->setIndex(i); + footer->invalidate(); + } return 0; }); - if ((i + 1) % 8 == 0) - grid.nextLine(); + if ((i + 1) % 8 == 0) grid.nextLine(); } - - // Footer - footer = new LogicalSwitchDisplayFooter(window, {0,window->height() - LSW_VIEW_FOOTER_HEIGHT, window->width(), LSW_VIEW_FOOTER_HEIGHT}); } diff --git a/radio/src/gui/colorlcd/view_logical_switches.h b/radio/src/gui/colorlcd/view_logical_switches.h index f7aab6ecf..eb7785ffb 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.h +++ b/radio/src/gui/colorlcd/view_logical_switches.h @@ -30,77 +30,76 @@ constexpr coord_t CSW_4TH_COLUMN = 280; constexpr coord_t CSW_5TH_COLUMN = 340; constexpr coord_t CSW_6TH_COLUMN = 390; -void putsEdgeDelayParam(BitmapBuffer * dc, coord_t x, coord_t y, LogicalSwitchData * ls, LcdFlags flags); +void putsEdgeDelayParam(BitmapBuffer* dc, coord_t x, coord_t y, + LogicalSwitchData* ls, LcdFlags flags); -class LogicalSwitchDisplayFooter: public Window { - public: - explicit LogicalSwitchDisplayFooter(Window * parent, rect_t rect): - Window(parent, rect, OPAQUE), - rect(rect) - { +class LogicalSwitchDisplayFooter : public Window +{ + public: + explicit LogicalSwitchDisplayFooter(Window* parent, rect_t rect) : + Window(parent, rect, OPAQUE), rect(rect) + { + } + + void setIndex(unsigned value) { index = value; } + + void paint(BitmapBuffer* dc) override + { + dc->clear(TITLE_BGCOLOR); + LogicalSwitchData* cs = lswAddress(index); + + LcdFlags textColor = FOCUS_COLOR; + dc->drawTextAtIndex(10, 1, STR_VCSWFUNC, cs->func, textColor); + // CSW params + unsigned int cstate = lswFamily(cs->func); + + if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { + drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); + drawSwitch(dc, CSW_3RD_COLUMN, 1, cs->v2, textColor); + } else if (cstate == LS_FAMILY_EDGE) { + drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); + putsEdgeDelayParam(dc, CSW_3RD_COLUMN, 1, cs, textColor); + } else if (cstate == LS_FAMILY_COMP) { + drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); + drawSource(dc, CSW_3RD_COLUMN, 1, cs->v2, textColor); + } else if (cstate == LS_FAMILY_TIMER) { + dc->drawNumber(CSW_2ND_COLUMN, 1, lswTimerValue(cs->v1), + LEFT | PREC1 | textColor); + dc->drawNumber(CSW_3RD_COLUMN, 1, lswTimerValue(cs->v2), + LEFT | PREC1 | textColor); + } else { + drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); + drawSourceCustomValue( + dc, CSW_3RD_COLUMN, 1, cs->v1, + cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(cs->v2) : cs->v2, + LEFT | textColor); } - void setIndex(unsigned value) - { - index = value; + // CSW AND switch + drawSwitch(dc, CSW_4TH_COLUMN, 1, cs->andsw, textColor); + + // CSW duration + if (cs->duration > 0) + dc->drawNumber(CSW_5TH_COLUMN, 1, cs->duration, + PREC1 | LEFT | textColor); + else + dc->drawTextAtIndex(CSW_5TH_COLUMN, 1, STR_MMMINV, 0, textColor); + + // CSW delay + if (cstate == LS_FAMILY_EDGE) { + dc->drawText(CSW_6TH_COLUMN, 1, STR_NA, textColor); + } else if (cs->delay > 0) { + dc->drawNumber(CSW_6TH_COLUMN, 1, cs->delay, PREC1 | LEFT | textColor); + } else { + dc->drawTextAtIndex(CSW_6TH_COLUMN, 1, STR_MMMINV, 0, textColor); } + } - void paint(BitmapBuffer * dc) override - { - dc->clear(TITLE_BGCOLOR); - LogicalSwitchData * cs = lswAddress(index); - dc->drawTextAtIndex(10, 1, STR_VCSWFUNC, cs->func, MENU_COLOR); - // CSW params - unsigned int cstate = lswFamily(cs->func); - - if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { - drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, MENU_COLOR); - drawSwitch(dc, CSW_3RD_COLUMN, 1, cs->v2, MENU_COLOR); - } - else if (cstate == LS_FAMILY_EDGE) { - drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, MENU_COLOR); - putsEdgeDelayParam(dc, CSW_3RD_COLUMN, 1, cs, MENU_COLOR); - } - else if (cstate == LS_FAMILY_COMP) { - drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, MENU_COLOR); - drawSource(dc, CSW_3RD_COLUMN, 1, cs->v2, MENU_COLOR); - } - else if (cstate == LS_FAMILY_TIMER) { - dc->drawNumber(CSW_2ND_COLUMN, 1, lswTimerValue(cs->v1), LEFT | PREC1 | MENU_COLOR); - dc->drawNumber(CSW_3RD_COLUMN, 1, lswTimerValue(cs->v2), LEFT | PREC1 | MENU_COLOR); - } - else { - drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, MENU_COLOR); - drawSourceCustomValue(dc, CSW_3RD_COLUMN, 1, cs->v1, cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(cs->v2) : cs->v2, LEFT | MENU_COLOR); - } - - // CSW AND switch - drawSwitch(dc, CSW_4TH_COLUMN, 1, cs->andsw, MENU_COLOR); - - // CSW duration - if (cs->duration > 0) - dc->drawNumber(CSW_5TH_COLUMN, 1, cs->duration, PREC1 | LEFT | MENU_COLOR); - else - dc->drawTextAtIndex(CSW_5TH_COLUMN, 1, STR_MMMINV, 0, MENU_COLOR); - - // CSW delay - if (cstate == LS_FAMILY_EDGE) { - dc->drawText(CSW_6TH_COLUMN, 1, STR_NA, MENU_COLOR); - } - else if (cs->delay > 0) { - dc->drawNumber(CSW_6TH_COLUMN, 1, cs->delay, PREC1 | LEFT | MENU_COLOR); - } - else { - dc->drawTextAtIndex(CSW_6TH_COLUMN, 1, STR_MMMINV, 0, MENU_COLOR); - } - } - - protected: - rect_t rect; - unsigned index = 0; + protected: + rect_t rect; + unsigned index = 0; }; - class LogicalSwitchesViewPage : public PageTab { public: diff --git a/radio/src/gui/colorlcd/view_main_decoration.cpp b/radio/src/gui/colorlcd/view_main_decoration.cpp index 16bc8cdfd..19684d928 100644 --- a/radio/src/gui/colorlcd/view_main_decoration.cpp +++ b/radio/src/gui/colorlcd/view_main_decoration.cpp @@ -300,8 +300,8 @@ void ViewMainDecoration::createTrims() VERTICAL_SLIDERS_HEIGHT }; - trims[TRIMS_LV] = new MainViewVerticalTrim(this, r, 1); - trims[TRIMS_RV] = new MainViewVerticalTrim(this, r, 2); + trims[TRIMS_LV] = new MainViewVerticalTrim(this, r, 2); + trims[TRIMS_RV] = new MainViewVerticalTrim(this, r, 1); } void ViewMainDecoration::createFlightMode() diff --git a/radio/src/gui/colorlcd/widget.cpp b/radio/src/gui/colorlcd/widget.cpp index 78de21dbe..350762fd8 100644 --- a/radio/src/gui/colorlcd/widget.cpp +++ b/radio/src/gui/colorlcd/widget.cpp @@ -75,6 +75,10 @@ bool Widget::onTouchEnd(coord_t x, coord_t y) //TODO: forward to widget (lua for instance) return true; } + + if (Window::onTouchEnd(x, y)) { + return true; + } if (hasFocus()) { onPress(); @@ -192,6 +196,12 @@ void registerWidget(const WidgetFactory * factory) getRegisteredWidgets().push_back(factory); } +void unregisterWidget(const WidgetFactory * factory) +{ + TRACE("unregister widget %s", factory->getName()); + getRegisteredWidgets().remove(factory); +} + const WidgetFactory * getWidgetFactory(const char * name) { auto it = getRegisteredWidgets().cbegin(); diff --git a/radio/src/gui/colorlcd/widget.h b/radio/src/gui/colorlcd/widget.h index 1656af236..7906e1a61 100644 --- a/radio/src/gui/colorlcd/widget.h +++ b/radio/src/gui/colorlcd/widget.h @@ -114,6 +114,7 @@ class Widget : public Button }; void registerWidget(const WidgetFactory * factory); +void unregisterWidget(const WidgetFactory * factory); class WidgetFactory { @@ -125,6 +126,8 @@ class WidgetFactory registerWidget(this); } + virtual ~WidgetFactory() {} + inline const char * getName() const { return name; diff --git a/radio/src/gui/colorlcd/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp index 0d5a8d3cd..3eee954d0 100644 --- a/radio/src/gui/colorlcd/widgets/modelbmp.cpp +++ b/radio/src/gui/colorlcd/widgets/modelbmp.cpp @@ -29,48 +29,46 @@ class ModelBitmapWidget: public Widget ModelBitmapWidget(const WidgetFactory * factory, FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData): Widget(factory, parent, rect, persistentData) { + loadBitmap(); } void refresh(BitmapBuffer * dc) override { - if (buffer && ((buffer->width() != width()) || (buffer->height() != height()))) { + if (buffer && + ((buffer->width() != width()) || (buffer->height() != height()) || + (deps_hash != getHash()))) { + loadBitmap(); + deps_hash = getHash(); } // big space to draw if (rect.h >= 96 && rect.w >= 120) { - dc->drawFilledRect(0, 0, rect.w, rect.h, SOLID, MAINVIEW_PANES_COLOR, OPACITY(5)); - + if (buffer) { + dc->drawBitmap(0, 0, buffer.get()); + } + auto iconMask = theme->getIconMask(ICON_MODEL); if (iconMask) { - dc->drawMask(6, 4, iconMask, MAINVIEW_GRAPHICS_COLOR); + dc->drawMask(6, 4, iconMask, HEADER_COLOR); } - dc->drawSizedText(45, 10, g_model.header.name, LEN_MODEL_NAME, FONT(XS)); - dc->drawSolidFilledRect(39, 27, rect.w - 48, 2, MAINVIEW_GRAPHICS_COLOR); - - if (buffer) { - dc->drawBitmap(0, 38, buffer.get()); - } + dc->drawSizedText(45, 10, g_model.header.name, LEN_MODEL_NAME, + FONT(XS) | DEFAULT_COLOR); + dc->drawSolidFilledRect(39, 27, rect.w - 48, 2, HEADER_COLOR); } // smaller space to draw else if (buffer) { - dc->drawBitmap(0, 38, buffer.get()); - } + dc->drawBitmap(0, 0, buffer.get()); + } } void checkEvents() override { Widget::checkEvents(); - - uint32_t new_hash = hash(g_model.header.bitmap, sizeof(g_model.header.bitmap)); - new_hash ^= hash(g_model.header.name, sizeof(g_model.header.name)); - new_hash ^= hash(g_eeGeneral.themeName, sizeof(g_eeGeneral.themeName)); - - if (new_hash != deps_hash) { - deps_hash = new_hash; - loadBitmap(); + if (getHash() != deps_hash) { + invalidate(); } } @@ -78,23 +76,34 @@ class ModelBitmapWidget: public Widget std::unique_ptr buffer; uint32_t deps_hash = 0; + uint32_t getHash() + { + return hash(g_model.header.bitmap, sizeof(g_model.header.bitmap)); + } + void loadBitmap() { std::string filename = std::string(g_model.header.bitmap); std::string fullpath = std::string(BITMAPS_PATH PATH_SEPARATOR) + filename; - std::unique_ptr bitmap(BitmapBuffer::loadBitmap(fullpath.c_str())); - if (!bitmap) { - TRACE("could not load bitmap '%s'", filename.c_str()); - return; - } - if (!buffer || (buffer->width() != width()) || (buffer->height() != height())) { buffer.reset(new BitmapBuffer(BMP_RGB565, width(), height())); } buffer->clear(DEFAULT_BGCOLOR); - buffer->drawScaledBitmap(bitmap.get(), 0, 0, width(), height() - 38); + if (!filename.empty()) { + std::unique_ptr bitmap(BitmapBuffer::loadBitmap(fullpath.c_str())); + if (!bitmap) { + TRACE("could not load bitmap '%s'", filename.c_str()); + return; + } + + if (rect.h >= 96 && rect.w >= 120) { + buffer->drawScaledBitmap(bitmap.get(), 0, 38, width(), height() - 38); + } else { + buffer->drawScaledBitmap(bitmap.get(), 0, 0, width(), height()); + } + } } }; diff --git a/radio/src/gui/colorlcd/widgets/text.cpp b/radio/src/gui/colorlcd/widgets/text.cpp index cbc1f3db3..857fede08 100644 --- a/radio/src/gui/colorlcd/widgets/text.cpp +++ b/radio/src/gui/colorlcd/widgets/text.cpp @@ -53,7 +53,7 @@ class TextWidget: public Widget const ZoneOption TextWidget::options[] = { { STR_TEXT, ZoneOption::String, OPTION_VALUE_STRING(TEXT_WIDGET_DEFAULT_LABEL) }, - { STR_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(RED) }, + { STR_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(DEFAULT_COLOR>>16) }, { STR_SIZE, ZoneOption::TextSize, OPTION_VALUE_UNSIGNED(0) }, { STR_SHADOW, ZoneOption::Bool, OPTION_VALUE_BOOL(false) }, { nullptr, ZoneOption::Bool } diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index b79a19d89..6c8821a2a 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -26,10 +26,11 @@ const coord_t NUMBERS_PADDING = 4; class ValueWidget: public Widget { public: - ValueWidget(const WidgetFactory * factory, FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData): - Widget(factory, parent, rect, persistentData) - { - } + ValueWidget(const WidgetFactory* factory, FormGroup* parent, + const rect_t& rect, Widget::PersistentData* persistentData) : + Widget(factory, parent, rect, persistentData) + { + } void refresh(BitmapBuffer * dc) override { @@ -37,7 +38,7 @@ class ValueWidget: public Widget mixsrc_t field = persistentData->options[0].value.unsignedValue; // get color from options[1] - lcdSetColor(persistentData->options[1].value.unsignedValue); + LcdFlags color = COLOR2FLAGS(persistentData->options[1].value.unsignedValue); coord_t xValue, yValue, xLabel, yLabel; LcdFlags attrValue, attrLabel = 0; @@ -55,7 +56,7 @@ class ValueWidget: public Widget yValue = -2; xLabel = NUMBERS_PADDING; yLabel = +2; - attrValue = RIGHT | NO_UNIT | FONT(XL); + attrValue = RIGHT | NO_UNIT | FONT(L); } else { xValue = NUMBERS_PADDING; @@ -64,7 +65,7 @@ class ValueWidget: public Widget yLabel = 2; if (field >= MIXSRC_FIRST_TELEM) { if (isGPSSensor(1 + (field - MIXSRC_FIRST_TELEM) / 3)) { - attrValue = LEFT | FONT(L) | EXPANDED; + attrValue = LEFT | FONT(L) | PREC1; } else { attrValue = LEFT | FONT(XL); @@ -72,7 +73,7 @@ class ValueWidget: public Widget } #if defined(INTERNAL_GPS) else if (field == MIXSRC_TX_GPS) { - attrValue = LEFT | FONT(L) | EXPANDED; + attrValue = LEFT | FONT(L) | PREC1; } #endif else { @@ -83,27 +84,32 @@ class ValueWidget: public Widget if (field >= MIXSRC_FIRST_TIMER && field <= MIXSRC_LAST_TIMER) { TimerState & timerState = timersStates[field - MIXSRC_FIRST_TIMER]; if (timerState.val < 0) { - lcdSetColor(lcdColorTable[ALARM_COLOR_INDEX]); + color = ALARM_COLOR; } - drawSource(dc, NUMBERS_PADDING, 2, field, CUSTOM_COLOR); - drawSource(dc, NUMBERS_PADDING + 1, 3, field, BLACK); - drawTimer(dc, xValue, yValue, abs(timerState.val), attrValue | FONT(XL) | CUSTOM_COLOR); + drawSource(dc, NUMBERS_PADDING, 2, field, color); + drawSource(dc, NUMBERS_PADDING + 1, 3, field, COLOR2FLAGS(BLACK)); + drawTimer(dc, xValue, yValue, abs(timerState.val), attrValue | FONT(XL) | color); } + if (field == MIXSRC_TX_TIME) { + drawTimer(dc, xValue, yValue, getValue(MIXSRC_TX_TIME), + attrValue | FONT(XL) | color); + } + if (field >= MIXSRC_FIRST_TELEM) { - TelemetryItem & telemetryItem = telemetryItems[(field - MIXSRC_FIRST_TELEM) / 3]; // TODO macro to convert a source to a telemetry index + TelemetryItem & telemetryItem = telemetryItems[(field - MIXSRC_FIRST_TELEM) / 3]; if (!telemetryItem.isAvailable() || telemetryItem.isOld()) { - lcdSetColor(lcdColorTable[TEXT_DISABLE_COLOR_INDEX]); + color = TEXT_DISABLE_COLOR; } } if (persistentData->options[2].value.boolValue) { - drawSource(dc,xLabel + 1, yLabel + 1, field, attrLabel | BLACK); - drawSourceValue(dc, xValue + 1, yValue + 1, field, attrValue | BLACK); + drawSource(dc,xLabel + 1, yLabel + 1, field, attrLabel | COLOR2FLAGS(BLACK)); + drawSourceValue(dc, xValue + 1, yValue + 1, field, attrValue | COLOR2FLAGS(BLACK)); } - drawSource(dc, xLabel, yLabel, field, attrLabel | CUSTOM_COLOR); - drawSourceValue(dc, xValue, yValue, field, attrValue | CUSTOM_COLOR); + drawSource(dc, xLabel, yLabel, field, attrLabel | color); + drawSourceValue(dc, xValue, yValue, field, attrValue | color); } void checkEvents() override diff --git a/radio/src/gui/colorlcd/widgets/widgets_container_impl.h b/radio/src/gui/colorlcd/widgets/widgets_container_impl.h index 0fb615c3a..8d3614ff8 100644 --- a/radio/src/gui/colorlcd/widgets/widgets_container_impl.h +++ b/radio/src/gui/colorlcd/widgets/widgets_container_impl.h @@ -43,10 +43,12 @@ class WidgetsContainerImpl: public WidgetsContainer // remove old one if existing removeWidget(index); - Widget * widget = nullptr; + Widget* widget = nullptr; if (factory) { - strncpy(persistentData->zones[index].widgetName, factory->getName(), sizeof(ZonePersistentData::widgetName)); - widget = factory->create(this, getZone(index), &persistentData->zones[index].widgetData); + strncpy(persistentData->zones[index].widgetName, factory->getName(), + sizeof(ZonePersistentData::widgetName)); + widget = factory->create(this, getZone(index), + &persistentData->zones[index].widgetData); } widgets[index] = widget; @@ -61,12 +63,16 @@ class WidgetsContainerImpl: public WidgetsContainer if (index >= N) return; - if (widgets[index]) + if (widgets[index]) { + removeField(widgets[index]); widgets[index]->deleteLater(); + } widgets[index] = nullptr; - memset(persistentData->zones[index].widgetName, 0, sizeof(ZonePersistentData::widgetName)); - memset(&persistentData->zones[index].widgetData, 0, sizeof(Widget::PersistentData)); + memset(persistentData->zones[index].widgetName, 0, + sizeof(ZonePersistentData::widgetName)); + memset(&persistentData->zones[index].widgetData, 0, + sizeof(Widget::PersistentData)); } Widget * getWidget(unsigned int index) override @@ -86,13 +92,15 @@ class WidgetsContainerImpl: public WidgetsContainer { unsigned int count = getZonesCount(); for (unsigned int i = 0; i < count; i++) { - // remove old widget if (widgets[i]) { + removeField(widgets[i]); widgets[i]->deleteLater(); widgets[i] = nullptr; } + } + for (unsigned int i = 0; i < count; i++) { // and load new one if required if (persistentData->zones[i].widgetName[0]) { char name[WIDGET_NAME_LEN + 1]; diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index ef09d070e..69004bf75 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -168,6 +168,8 @@ enum { ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE, #endif + ITEM_RADIO_HARDWARE_SERIAL_SAMPLE_MODE, + #if defined(BLUETOOTH) ITEM_RADIO_HARDWARE_BLUETOOTH_MODE, ITEM_RADIO_HARDWARE_BLUETOOTH_PAIRING_CODE, @@ -290,6 +292,8 @@ enum { #define MAX_BAUD_ROWS #endif +#define SERIAL_SAMPLE_MODE_ROWS 0, + #if defined(AUX_SERIAL) #define AUX_SERIAL_ROWS 0, #else @@ -321,6 +325,21 @@ void onFactoryResetConfirm(const char * result) } #endif +void restartExternalModule() +{ + if (!IS_EXTERNAL_MODULE_ON()) { + return; + } + pauseMixerCalculations(); + pausePulses(); + EXTERNAL_MODULE_OFF(); + RTOS_WAIT_MS(20); // 20ms so that the pulses interrupt will reinit the frame rate + telemetryProtocol = 255; // force telemetry port + module reinitialization + EXTERNAL_MODULE_ON(); + resumePulses(); + resumeMixerCalculations(); +} + void menuRadioHardware(event_t event) { uint8_t old_editMode = s_editMode; @@ -339,6 +358,7 @@ void menuRadioHardware(event_t event) RTC_ROW TX_CAPACITY_MEASUREMENT_ROWS MAX_BAUD_ROWS + SERIAL_SAMPLE_MODE_ROWS BLUETOOTH_ROWS EXTERNAL_ANTENNA_ROW AUX_SERIAL_ROWS @@ -560,20 +580,20 @@ void menuRadioHardware(event_t event) lcdDrawNumber(HW_SETTINGS_COLUMN2, y, CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate], attr|LEFT); if (attr) { g_eeGeneral.telemetryBaudrate = DIM(CROSSFIRE_BAUDRATES) - 1 - checkIncDecModel(event, DIM(CROSSFIRE_BAUDRATES) - 1 - g_eeGeneral.telemetryBaudrate, 0, DIM(CROSSFIRE_BAUDRATES) - 1); - if (checkIncDec_Ret && IS_EXTERNAL_MODULE_ON()) { - pauseMixerCalculations(); - pausePulses(); - EXTERNAL_MODULE_OFF(); - RTOS_WAIT_MS(20); // 20ms so that the pulses interrupt will reinit the frame rate - telemetryProtocol = 255; // force telemetry port + module reinitialization - EXTERNAL_MODULE_ON(); - resumePulses(); - resumeMixerCalculations(); + if (checkIncDec_Ret) { + restartExternalModule(); } } break; #endif + case ITEM_RADIO_HARDWARE_SERIAL_SAMPLE_MODE: + g_eeGeneral.uartSampleMode = editChoice(HW_SETTINGS_COLUMN2, y, STR_SAMPLE_MODE, STR_SAMPLE_MODES, g_eeGeneral.uartSampleMode, 0, UART_SAMPLE_MODE_MAX, attr, event); + if (attr && checkIncDec_Ret) { + restartExternalModule(); + } + break; + #if defined(BLUETOOTH) case ITEM_RADIO_HARDWARE_BLUETOOTH_MODE: lcdDrawTextAlignedLeft(y, STR_BLUETOOTH); diff --git a/radio/src/logs.cpp b/radio/src/logs.cpp index bd8767d52..b1003b441 100644 --- a/radio/src/logs.cpp +++ b/radio/src/logs.cpp @@ -143,7 +143,7 @@ void writeHeader() TelemetrySensor & sensor = g_model.telemetrySensors[i]; if (sensor.logs) { memset(label, 0, sizeof(label)); - zchar2str(label, sensor.label, TELEM_LABEL_LEN); + strncpy(label, sensor.label, TELEM_LABEL_LEN); uint8_t unit = sensor.unit; if (unit == UNIT_CELLS ) unit = UNIT_VOLTS; if (UNIT_RAW < unit && unit < UNIT_FIRST_VIRTUAL) { diff --git a/radio/src/lua/api_colorlcd.cpp b/radio/src/lua/api_colorlcd.cpp index 356f0a43f..cff137481 100644 --- a/radio/src/lua/api_colorlcd.cpp +++ b/radio/src/lua/api_colorlcd.cpp @@ -27,6 +27,28 @@ #include "api_colorlcd.h" BitmapBuffer* luaLcdBuffer = nullptr; + +static int8_t getTextHorizontalOffset(LcdFlags flags) +{ + // no need to adjust if not right aligned + if (!(flags & RIGHT)) { + return 0; + } + const uint8_t font_index = FONT_INDEX(flags); + if (font_index >= sizeof(text_horizontal_offset)) { + return 0; + } + return text_horizontal_offset[font_index]; +} + +static int8_t getTextVerticalOffset(LcdFlags flags) +{ + const uint8_t font_index = FONT_INDEX(flags); + if (font_index >= sizeof(text_vertical_offset)) { + return 0; + } + return text_vertical_offset[font_index]; +} /*luadoc @function lcd.refresh() @@ -60,7 +82,7 @@ Clear the LCD screen static int luaLcdClear(lua_State * L) { if (luaLcdAllowed && luaLcdBuffer) { - LcdFlags color = luaL_optunsigned(L, 1, DEFAULT_BGCOLOR); + LcdFlags color = luaL_optunsigned(L, 1, DEFAULT_BGCOLOR_INDEX << 16u); luaLcdBuffer->clear(COLOR(COLOR_VAL(color))); } return 0; @@ -202,6 +224,10 @@ static int luaLcdDrawText(lua_State *L) const char * s = luaL_checkstring(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); + // apply text offsets, needed to align 2.4.x to 2.3.x font baselines + x += getTextHorizontalOffset(att); + y += getTextVerticalOffset(att); + bool shadowed = att & SHADOWED; bool invers = (att & INVERS); if (att & BLINK) invers = invers && !(BLINK_ON_PHASE); @@ -217,8 +243,12 @@ static int luaLcdDrawText(lua_State *L) if (invers) { int height = getFontHeight(att & 0xFFFF); int width = getTextWidth(s, 255, att); + int ix = x - INVERT_BOX_MARGIN; + if (att & RIGHT) { + ix = x - width - INVERT_BOX_MARGIN; + } luaLcdBuffer->drawSolidFilledRect( - x - INVERT_BOX_MARGIN, y - INVERT_BOX_MARGIN, + ix, y - INVERT_BOX_MARGIN, width + 2 * INVERT_BOX_MARGIN, height + 2 * INVERT_BOX_MARGIN, FOCUS_BGCOLOR); att = (att & 0xFFFF) | FOCUS_COLOR; @@ -256,6 +286,10 @@ static int luaLcdDrawTimer(lua_State *L) int seconds = luaL_checkinteger(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); + // apply text offsets, needed to align 2.4.x to 2.3.x font baselines + x += getTextHorizontalOffset(att); + y += getTextVerticalOffset(att); + bool shadowed = att & SHADOWED; att = (att & 0xFFFF) | COLOR(COLOR_VAL(att)); @@ -295,8 +329,30 @@ static int luaLcdDrawNumber(lua_State *L) int val = luaL_checkinteger(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); + bool invers = (att & INVERS); att = (att & 0xFFFF) | COLOR(COLOR_VAL(att)); + // apply text offsets, needed to align 2.4.x to 2.3.x font baselines + x += getTextHorizontalOffset(att); + y += getTextVerticalOffset(att); + + char s[49]; + BitmapBuffer::formatNumberAsString(s, 49, val, att & 0xFFFF); + + if (invers) { + int height = getFontHeight(att & 0xFFFF); + int width = getTextWidth(s, 255, att); + int ix = x - INVERT_BOX_MARGIN; + if (att & RIGHT) { + ix = x - width - INVERT_BOX_MARGIN; + } + luaLcdBuffer->drawSolidFilledRect( + ix, y - INVERT_BOX_MARGIN, + width + 2 * INVERT_BOX_MARGIN, height + 2 * INVERT_BOX_MARGIN, + FOCUS_BGCOLOR); + att = (att & 0xFFFF) | FOCUS_COLOR; + } + if ((att & SHADOWED) && !(att & INVERS)) { luaLcdBuffer->drawNumber(x, y, val, att & 0xFFFF); } @@ -664,9 +720,9 @@ static int luaLcdDrawGauge(lua_State *L) int num = luaL_checkinteger(L, 5); int den = luaL_checkinteger(L, 6); unsigned int flags = luaL_optunsigned(L, 7, 0); - + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); - + luaLcdBuffer->drawRect(x, y, w, h, 1, 0xff, flags); uint8_t len = limit((uint8_t)1, uint8_t(w*num/den), uint8_t(w)); luaLcdBuffer->drawSolidFilledRect(x+1, y+1, len, h-2, flags); @@ -727,9 +783,6 @@ Set a color for specific area */ static int luaLcdSetColor(lua_State *L) { - if (!luaLcdAllowed) - return 0; - unsigned int index = luaL_checkunsigned(L, 1) >> 16; unsigned int color = luaL_checkunsigned(L, 2); @@ -750,9 +803,6 @@ Get the color for specific area : see lcd.setColor for area list static int luaLcdGetColor(lua_State *L) { - if (!luaLcdAllowed) - return 0; - unsigned int index = luaL_checkunsigned(L, 1) >> 16; lua_pushunsigned(L, lcdColorTable[index]); @@ -778,9 +828,6 @@ Returns a 5/6/5 rgb color code, that can be used with lcd.setColor */ static int luaRGB(lua_State *L) { - if (!luaLcdAllowed) - return 0; - int r = luaL_checkinteger(L, 1); int g = luaL_checkinteger(L, 2); int b = luaL_checkinteger(L, 3); diff --git a/radio/src/lua/api_colorlcd.h b/radio/src/lua/api_colorlcd.h index ae5f17109..cad42e242 100644 --- a/radio/src/lua/api_colorlcd.h +++ b/radio/src/lua/api_colorlcd.h @@ -33,5 +33,8 @@ constexpr coord_t INVERT_BOX_MARGIN = 2; +constexpr int8_t text_horizontal_offset[7] {-2,-1,-2,-2,-2,-2,-2}; +constexpr int8_t text_vertical_offset[7] {2,2,0,2,3,3,7}; + extern bool luaLcdAllowed; extern BitmapBuffer * luaLcdBuffer; diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index aa7a5a643..724af6d62 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -364,22 +364,20 @@ bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags) // search in telemetry field.desc[0] = '\0'; - for (int i=0; i 0) { - str2zchar(zname, name, 4); - } - else { - zname[0] = hex2zchar((id & 0xf000) >> 12); - zname[1] = hex2zchar((id & 0x0f00) >> 8); - zname[2] = hex2zchar((id & 0x00f0) >> 4); - zname[3] = hex2zchar((id & 0x000f) >> 0); + if (name == NULL || strlen(name) > 0) { + name_buf[0] = hex2char((id & 0xf000) >> 12); + name_buf[1] = hex2char((id & 0x0f00) >> 8); + name_buf[2] = hex2char((id & 0x00f0) >> 4); + name_buf[3] = hex2char((id & 0x000f) >> 0); } if (id | subId | instance) { - int index = setTelemetryValue(PROTOCOL_TELEMETRY_LUA, id, subId, instance, value, unit, prec); + int index = setTelemetryValue(PROTOCOL_TELEMETRY_LUA, id, subId, instance, + value, unit, prec); if (index >= 0) { TelemetrySensor &telemetrySensor = g_model.telemetrySensors[index]; telemetrySensor.id = id; telemetrySensor.subId = subId; telemetrySensor.instance = instance; - telemetrySensor.init(zname, unit, prec); + telemetrySensor.init(name ? name: name_buf, unit, prec); lua_pushboolean(L, true); - } - else { + } else { lua_pushboolean(L, false); } return 1; diff --git a/radio/src/lua/api_model.cpp b/radio/src/lua/api_model.cpp index feff8e6dd..9737e6704 100644 --- a/radio/src/lua/api_model.cpp +++ b/radio/src/lua/api_model.cpp @@ -39,9 +39,9 @@ Get current Model information static int luaModelGetInfo(lua_State *L) { lua_newtable(L); - lua_pushtablestring(L, "name", g_model.header.name); + lua_pushtablenstring(L, "name", g_model.header.name); #if LCD_DEPTH > 1 - lua_pushtablestring(L, "bitmap", g_model.header.bitmap); + lua_pushtablenstring(L, "bitmap", g_model.header.bitmap); #endif return 1; } @@ -66,7 +66,7 @@ static int luaModelSetInfo(lua_State *L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(g_model.header.name, name, sizeof(g_model.header.name)); + strncpy(g_model.header.name, name, sizeof(g_model.header.name)); #if defined(EEPROM) memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name)); #endif @@ -258,7 +258,7 @@ static int luaModelGetTimer(lua_State *L) lua_pushtableinteger(L, "countdownBeep", timer.countdownBeep); lua_pushtableboolean(L, "minuteBeep", timer.minuteBeep); lua_pushtableinteger(L, "persistent", timer.persistent); - lua_pushtablestring(L, "name", timer.name); + lua_pushtablenstring(L, "name", timer.name); } else { lua_pushnil(L); @@ -310,7 +310,7 @@ static int luaModelSetTimer(lua_State *L) } if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(timer.name, name, sizeof(timer.name)); + strncpy(timer.name, name, sizeof(timer.name)); } } storageDirty(EE_MODEL); @@ -424,7 +424,7 @@ static int luaModelGetFlightMode(lua_State * L) if (idx < MAX_FLIGHT_MODES) { FlightModeData * fm = flightModeAddress(idx); lua_newtable(L); - lua_pushtablestring(L, "name", fm->name); + lua_pushtablenstring(L, "name", fm->name); lua_pushtableinteger(L, "switch", fm->swtch); lua_pushtableinteger(L, "fadeIn", fm->fadeIn); lua_pushtableinteger(L, "fadeOut", fm->fadeOut); @@ -477,7 +477,7 @@ static int luaModelSetFlightMode(lua_State * L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(fm->name, name, sizeof(fm->name)); + strncpy(fm->name, name, sizeof(fm->name)); } else if (!strcmp(key, "switch")) { fm->swtch = luaL_checkinteger(L, -1); @@ -546,8 +546,8 @@ static int luaModelGetInput(lua_State *L) if (idx < count) { ExpoData * expo = expoAddress(first+idx); lua_newtable(L); - lua_pushtablestring(L, "name", expo->name); - lua_pushtablestring(L, "inputName", g_model.inputNames[chn]); + lua_pushtablenstring(L, "name", expo->name); + lua_pushtablenstring(L, "inputName", g_model.inputNames[chn]); lua_pushtableinteger(L, "source", expo->srcRaw); lua_pushtableinteger(L, "weight", expo->weight); lua_pushtableinteger(L, "offset", expo->offset); @@ -599,11 +599,11 @@ static int luaModelInsertInput(lua_State *L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(expo->name, name, sizeof(expo->name)); + strncpy(expo->name, name, sizeof(expo->name)); } else if (!strcmp(key, "inputName")) { const char * name = luaL_checkstring(L, -1); - str2zchar(g_model.inputNames[chn], name, LEN_INPUT_NAME); + strncpy(g_model.inputNames[chn], name, LEN_INPUT_NAME); } else if (!strcmp(key, "source")) { expo->srcRaw = luaL_checkinteger(L, -1); @@ -773,7 +773,7 @@ static int luaModelGetMix(lua_State *L) if (idx < count) { MixData * mix = mixAddress(first+idx); lua_newtable(L); - lua_pushtablestring(L, "name", mix->name); + lua_pushtablenstring(L, "name", mix->name); lua_pushtableinteger(L, "source", mix->srcRaw); lua_pushtableinteger(L, "weight", mix->weight); lua_pushtableinteger(L, "offset", mix->offset); @@ -827,7 +827,7 @@ static int luaModelInsertMix(lua_State *L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(mix->name, name, sizeof(mix->name)); + strncpy(mix->name, name, sizeof(mix->name)); } else if (!strcmp(key, "source")) { mix->srcRaw = luaL_checkinteger(L, -1); @@ -1043,7 +1043,7 @@ static int luaModelGetCurve(lua_State *L) if (idx < MAX_CURVES) { CurveHeader & CurveHeader = g_model.curves[idx]; lua_newtable(L); - lua_pushtablestring(L, "name", CurveHeader.name); + lua_pushtablenstring(L, "name", CurveHeader.name); lua_pushtableinteger(L, "type", CurveHeader.type); lua_pushtableboolean(L, "smooth", CurveHeader.smooth); lua_pushtableinteger(L, "points", CurveHeader.points + 5); @@ -1144,7 +1144,7 @@ static int luaModelSetCurve(lua_State *L) const char *key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char *name = luaL_checkstring(L, -1); - str2zchar(newCurveHeader.name, name, sizeof(newCurveHeader.name)); + strncpy(newCurveHeader.name, name, sizeof(newCurveHeader.name)); } else if (!strcmp(key, "type")) { newCurveHeader.type = luaL_checkinteger(L, -1); @@ -1301,7 +1301,7 @@ static int luaModelGetCustomFunction(lua_State *L) lua_pushtableinteger(L, "switch", CFN_SWITCH(cfn)); lua_pushtableinteger(L, "func", CFN_FUNC(cfn)); if (CFN_FUNC(cfn) == FUNC_PLAY_TRACK || CFN_FUNC(cfn) == FUNC_BACKGND_MUSIC || CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT) { - lua_pushtablestring(L, "name", cfn->play.name); + lua_pushtablenstring(L, "name", cfn->play.name); } else { lua_pushtableinteger(L, "value", cfn->all.val); @@ -1398,7 +1398,7 @@ static int luaModelGetOutput(lua_State *L) if (idx < MAX_OUTPUT_CHANNELS) { LimitData * limit = limitAddress(idx); lua_newtable(L); - lua_pushtablestring(L, "name", limit->name); + lua_pushtablenstring(L, "name", limit->name); lua_pushtableinteger(L, "min", limit->min-1000); lua_pushtableinteger(L, "max", limit->max+1000); lua_pushtableinteger(L, "offset", limit->offset); @@ -1440,7 +1440,7 @@ static int luaModelSetOutput(lua_State *L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(limit->name, name, sizeof(limit->name)); + strncpy(limit->name, name, sizeof(limit->name)); } else if (!strcmp(key, "min")) { limit->min = luaL_checkinteger(L, -1)+1000; @@ -1558,7 +1558,7 @@ static int luaModelGetSensor(lua_State *L) TelemetrySensor & sensor = g_model.telemetrySensors[idx]; lua_newtable(L); lua_pushtableinteger(L, "type", sensor.type); - lua_pushtablestring(L, "name", sensor.label); + lua_pushtablenstring(L, "name", sensor.label); lua_pushtableinteger(L, "unit", sensor.unit); lua_pushtableinteger(L, "prec", sensor.prec); if (sensor.type == TELEM_TYPE_CUSTOM) { diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index f7957e269..36f658d1a 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -62,7 +62,20 @@ void luaInitThemesAndWidgets(); #define lua_pushtableboolean(L, k, v) (lua_pushstring(L, (k)), lua_pushboolean(L, (v)), lua_settable(L, -3)) #define lua_pushtableinteger(L, k, v) (lua_pushstring(L, (k)), lua_pushinteger(L, (v)), lua_settable(L, -3)) #define lua_pushtablenumber(L, k, v) (lua_pushstring(L, (k)), lua_pushnumber(L, (v)), lua_settable(L, -3)) + +// size based string (possibly no null-termination) +#define __lua_strncpy(s) \ + char tmp[sizeof(s) + 1]; \ + strncpy(tmp, (s), sizeof(tmp) - 1); \ + tmp[sizeof(s)] = '\0'; + +// size based string (possibly no null-termination) +#define lua_pushnstring(L, s) { __lua_strncpy(s); lua_pushstring(L, tmp); } +#define lua_pushtablenstring(L, k, v) { __lua_strncpy(v); lua_pushstring(L, (k)); lua_pushstring(L, tmp); lua_settable(L, -3); } + +// null-terminated string #define lua_pushtablestring(L, k, v) (lua_pushstring(L, (k)), lua_pushstring(L, (v)), lua_settable(L, -3)) + #define lua_registerlib(L, name, tab) (luaL_newmetatable(L, name), luaL_setfuncs(L, tab, 0), lua_setglobal(L, name)) #define RUN_MIX_SCRIPT (1 << 0) @@ -174,6 +187,9 @@ void registerBitmapClass(lua_State * L); void luaSetInstructionsLimit(lua_State* L, int count); int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * mode); +// Unregister LUA widget factories +void luaUnregisterWidgets(); + #if LCD_W > 350 #define RADIO_TOOL_NAME_MAXLEN 40 #else diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index 1ffe6d182..b1d0bc489 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -225,6 +225,11 @@ class LuaWidgetFactory: public WidgetFactory { } + ~LuaWidgetFactory() + { + unregisterWidget(this); + } + Widget * create(FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData, bool init=true) const override { if (lsWidgets == 0) return 0; @@ -507,3 +512,13 @@ void luaInitThemesAndWidgets() luaDoGc(lsWidgets, true); } } + +void luaUnregisterWidgets() +{ + std::list regWidgets(getRegisteredWidgets()); + for (auto w : regWidgets) { + if (dynamic_cast(w)) { + delete w; + } + } +} diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 45dd923ed..24dd94066 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -98,13 +98,15 @@ void onUSBConnectMenu(const char *result) void openUsbMenu() { - POPUP_MENU_ADD_ITEM(STR_USB_JOYSTICK); - POPUP_MENU_ADD_ITEM(STR_USB_MASS_STORAGE); + if (popupMenuHandler != onUSBConnectMenu) { + POPUP_MENU_ADD_ITEM(STR_USB_JOYSTICK); + POPUP_MENU_ADD_ITEM(STR_USB_MASS_STORAGE); #if defined(DEBUG) - POPUP_MENU_ADD_ITEM(STR_USB_SERIAL); + POPUP_MENU_ADD_ITEM(STR_USB_SERIAL); #endif - POPUP_MENU_TITLE(STR_SELECT_MODE); - POPUP_MENU_START(onUSBConnectMenu); + POPUP_MENU_TITLE(STR_SELECT_MODE); + POPUP_MENU_START(onUSBConnectMenu); + } } void closeUsbMenu() diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 10552b7a2..54d9abf0d 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -634,14 +634,6 @@ void doSplash() } #endif -#if defined(FRSKY_RELEASE) && !defined(COLORLCD) - static uint8_t secondSplash = false; - if (!secondSplash && get_tmr10ms() >= tgtime-200) { - secondSplash = true; - drawSecondSplash(); - } -#endif - #if defined(PCBSKY9X) if (curTime < get_tmr10ms()) { curTime += 10; @@ -1103,6 +1095,10 @@ void getADC() } #endif + DEBUG_TIMER_START(debugTimerAdcRead); + adcRead(); + DEBUG_TIMER_STOP(debugTimerAdcRead); + for (uint8_t x=0; xrun(); + + luaUnregisterWidgets(); luaClose(&lsWidgets); + lsWidgets = 0; #endif #if defined(SDCARD) @@ -1540,6 +1539,11 @@ void opentxResume() TRACE("opentxResume"); sdMount(); +#if defined(COLORLCD) + // reload widgets + luaInitThemesAndWidgets(); +#endif + storageReadAll(); #if defined(COLORLCD) @@ -2074,15 +2078,15 @@ uint32_t pwrCheck() POPUP_CONFIRMATION(STR_MODEL_SHUTDOWN, nullptr); -#if defined(SHUTDOWN_CONFIRMATION) - if (TELEMETRY_STREAMING() && !g_eeGeneral.disableRssiPoweroffAlarm) { - SET_WARNING_INFO(STR_MODEL_STILL_POWERED, sizeof(TR_MODEL_STILL_POWERED), 0); + const char* msg = STR_MODEL_STILL_POWERED; + uint8_t msg_len = sizeof(TR_MODEL_STILL_POWERED); + if (usbPlugged() && getSelectedUsbMode() != USB_UNSELECTED_MODE) { + msg = STR_USB_STILL_CONNECTED; + msg_len = sizeof(TR_USB_STILL_CONNECTED); } -#else - SET_WARNING_INFO(STR_MODEL_STILL_POWERED, sizeof(TR_MODEL_STILL_POWERED), 0); -#endif - + event_t evt = getEvent(false); + SET_WARNING_INFO(msg, msg_len, 0); DISPLAY_WARNING(evt); LED_ERROR_BEGIN(); diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 033a7c339..cc9f4aff8 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -37,6 +37,12 @@ #include "usbd_conf.h" #endif +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif + #if defined(SIMU) #define SWITCH_SIMU(a, b) (a) #else diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 227aa974d..c33a433c4 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -678,7 +678,6 @@ inline void resetMultiProtocolsOptions(uint8_t moduleIdx) else { g_model.moduleData[moduleIdx].multi.autoBindMode = 0; } - g_model.moduleData[moduleIdx].subType = 0; g_model.moduleData[moduleIdx].multi.optionValue = 0; g_model.moduleData[moduleIdx].multi.disableTelemetry = 0; g_model.moduleData[moduleIdx].multi.disableMapping = 0; diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 8ba672aa0..0ec02e49e 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -580,6 +580,7 @@ bool setupPulsesInternalModule(uint8_t protocol) void stopPulsesInternalModule() { if (moduleState[INTERNAL_MODULE].protocol != PROTOCOL_CHANNELS_UNINITIALIZED) { + mixerSchedulerSetPeriod(INTERNAL_MODULE, 0); intmoduleStop(); moduleState[INTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; } @@ -607,6 +608,7 @@ bool setupPulsesInternalModule() void stopPulsesExternalModule() { if (moduleState[EXTERNAL_MODULE].protocol != PROTOCOL_CHANNELS_UNINITIALIZED) { + mixerSchedulerSetPeriod(EXTERNAL_MODULE, 0); extmoduleStop(); moduleState[EXTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; } diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index c58261c44..5683f1afc 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -29,8 +29,8 @@ #define PATH_SEPARATOR "/" #define ROOT_PATH PATH_SEPARATOR -#define MODELS_PATH ROOT_PATH "MODELS24" // no trailing slash = important //TODO Temporary while working on both 2.3 and 2.4 -#define RADIO_PATH ROOT_PATH "RADIO24" // no trailing slash = important +#define MODELS_PATH ROOT_PATH "MODELS" // no trailing slash = important +#define RADIO_PATH ROOT_PATH "RADIO" // no trailing slash = important #define LOGS_PATH ROOT_PATH "LOGS" #define SCREENSHOTS_PATH ROOT_PATH "SCREENSHOTS" #define SOUNDS_PATH ROOT_PATH "SOUNDS/en" @@ -54,8 +54,9 @@ #define LEN_FILE_PATH_MAX (sizeof(SCRIPTS_TELEM_PATH)+1) // longest + "/" #if defined(COLORLCD) +#define RADIO_FILENAME "radio.bin" const char RADIO_MODELSLIST_PATH[] = RADIO_PATH PATH_SEPARATOR "models.txt"; -const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.bin"; +const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR RADIO_FILENAME; #if defined(SDCARD_YAML) const char RADIO_MODELSLIST_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "models.yml"; const char RADIO_SETTINGS_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.yml"; diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index 2ba72bef6..fc663fc9f 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -527,7 +527,7 @@ void OpenTxSim::refreshDisplay() setPixel(x, y, FXRGB(255, 255, 255)); } else { - FXColor color = FXRGB(255*((z&0xF800)>>11)/0x1f, 255*((z&0x07E0)>>5)/0x3F, 255*(z&0x001F)/0x01F); + FXColor color = FXRGB(((z & 0xF800) >> 8) + ((z & 0xE000) >> 13), ((z & 0x07E0) >> 3) + ((z & 0x0600) >> 9), (((z & 0x001F) << 3) & 0x00F8) + ((z & 0x001C) >> 2)); setPixel(x, y, color); } } diff --git a/radio/src/storage/conversions/conversions.cpp b/radio/src/storage/conversions/conversions.cpp index 75fa45aaf..0e474d1c4 100644 --- a/radio/src/storage/conversions/conversions.cpp +++ b/radio/src/storage/conversions/conversions.cpp @@ -21,16 +21,100 @@ #include "opentx.h" #include "conversions.h" +#if defined(COLORLCD) +#include "storage/modelslist.h" +#include "storage/sdcard_common.h" + +static void drawProgressScreen(const char* filename, int progress, int total) +{ + OpenTxTheme* l_theme = static_cast(theme); + + lcd->reset(); + l_theme->drawBackground(lcd); + lcd->drawText(LCD_W/2, LCD_H/2 - 30, STR_CONVERTING, FONT(XL) | CENTERED | ALARM_COLOR); + lcd->drawText(LCD_W/2, LCD_H/2, filename, FONT(STD) | CENTERED | DEFAULT_COLOR); + + l_theme->drawProgressBar(lcd, + LCD_W / 4, + LCD_H / 2 + 40, + LCD_W / 2, + 20, + progress, total); + + WDG_RESET(); + lcdRefresh(); +} + void convertRadioData(int version) { TRACE("convertRadioData(%d)", version); + // the theme has not been loaded before + static_cast(theme)->load(); + + // Init backlight mode before entering alert screens + requiredBacklightBright = BACKLIGHT_FORCED_ON; + g_eeGeneral.blOffBright = 20; + + RAISE_ALERT(STR_STORAGE_WARNING, STR_SDCARD_CONVERSION_REQUIRE, NULL, + AU_NONE); + + // Load models list before converting + modelslist.load(); + + unsigned converted = 0; + auto to_convert = modelslist.getModelsCount() + 1; + + drawProgressScreen(RADIO_FILENAME, converted, to_convert); + TRACE("converting '%s' (%d/%d)", RADIO_FILENAME, converted, to_convert); + #if STORAGE_CONVERSIONS < 220 if (version == 219) { convertRadioData_219_to_220(g_eeGeneral); + storageDirty(EE_GENERAL); + storageCheck(true); } #endif + converted++; + +#if defined(SIMU) + RTOS_WAIT_MS(200); +#endif + + const char* error = nullptr; + for (auto category_ptr : modelslist.getCategories()) { + for (auto model_ptr : *category_ptr) { + + uint8_t model_version; + const char* filename = model_ptr->modelFilename; + + TRACE("converting '%s' (%d/%d)", filename, converted, to_convert); + drawProgressScreen(filename, converted, to_convert); + + error = readModel(filename, (uint8_t *)&g_model, sizeof(g_model), &model_version); + if (!error) { + + convertModelData(model_version); + + char path[256]; + getModelPath(path, filename); + error = writeFile(path, (uint8_t *)&g_model, sizeof(g_model)); + //TODO: what should be done with this error? + } + + converted++; + +#if defined(SIMU) + RTOS_WAIT_MS(200); +#endif + } + } + + // reload models list + modelslist.clear(); + modelslist.load(); } +#endif void convertModelData(int version) { diff --git a/radio/src/storage/conversions/conversions_219_220.cpp b/radio/src/storage/conversions/conversions_219_220.cpp index 8743b2889..9fb21e390 100644 --- a/radio/src/storage/conversions/conversions_219_220.cpp +++ b/radio/src/storage/conversions/conversions_219_220.cpp @@ -94,6 +94,11 @@ static void convertToStr(char* str, size_t len) } } +#if defined(COLORLCD) +extern const LayoutFactory * defaultLayout; +extern OpenTxTheme * defaultTheme; +#endif + void convertModelData_219_to_220(ModelData &model) { ModelData* oldModelAllocated = (ModelData*)malloc(sizeof(ModelData)); @@ -101,11 +106,10 @@ void convertModelData_219_to_220(ModelData &model) memcpy(&oldModel, &model, sizeof(ModelData)); ModelData& newModel = (ModelData&)model; - convertToStr(model.header.name, LEN_MODEL_NAME_219); for (uint8_t i=0; i= TMRMODE_START) { timer_219.mode += 1; } - if (timer_219.mode < TMRMODE_COUNT) { + if (timer_219.mode < TMRMODE_COUNT + && timer_219.mode >=0) { timer.mode = timer_219.mode; } else { timer.mode = TMRMODE_START; - timer.swtch = timer_219.mode - (TMRMODE_COUNT - 1); + if (timer_219.mode > 0) + timer.swtch = timer_219.mode - (TMRMODE_COUNT - 1); + else + timer.swtch = timer_219.mode; } timer.start = timer_219.start; @@ -145,7 +153,7 @@ void convertModelData_219_to_220(ModelData &model) // CurveHeader.name for (uint8_t i=0; igetId()); + defaultLayout->initPersistentData(&newModel.screenData[0].layoutData); + } #endif free(oldModelAllocated); } -#if defined(PCBHORUS) -extern OpenTxTheme * defaultTheme; -#endif - void convertRadioData_219_to_220(RadioData & settings) { TRACE("Radio conversion from v219 to v220"); @@ -217,7 +225,7 @@ void convertRadioData_219_to_220(RadioData & settings) RadioData& oldSettings = *oldSettingsAllocated; memcpy(&oldSettings, &settings, sizeof(RadioData)); -#if defined(PCBHORUS) +#if defined(COLORLCD) // Clear CustomScreenData + TopBarPersistentData // as they cannot be converted (missing option types) strcpy(g_eeGeneral.themeName, defaultTheme->getName()); diff --git a/radio/src/storage/eeprom_common.cpp b/radio/src/storage/eeprom_common.cpp index dffab63fb..839feedb3 100644 --- a/radio/src/storage/eeprom_common.cpp +++ b/radio/src/storage/eeprom_common.cpp @@ -169,7 +169,7 @@ void checkModelIdUnique(uint8_t index, uint8_t module) name = strAppendUnsigned(name+strlen(name), i + 1, 2); } else { - name += zchar2str(name, modelHeaders[i].name, LEN_MODEL_NAME); + name = strAppend(name, modelHeaders[i].name, LEN_MODEL_NAME); } } else { diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 0b5c20b81..1ba806284 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -28,17 +28,14 @@ using std::list; ModelsList modelslist; -ModelCell::ModelCell(const char * name): - valid_rfData(false) +ModelCell::ModelCell(const char* name) : valid_rfData(false) { strncpy(modelFilename, name, sizeof(modelFilename)); } -ModelCell::ModelCell(const char * name, uint8_t len) - : valid_rfData(false) +ModelCell::ModelCell(const char* name, uint8_t len) : valid_rfData(false) { - if (len > sizeof(modelFilename)-1) - len = sizeof(modelFilename)-1; + if (len > sizeof(modelFilename) - 1) len = sizeof(modelFilename) - 1; memcpy(modelFilename, name, len); modelFilename[len] = '\0'; @@ -51,11 +48,13 @@ ModelCell::~ModelCell() void ModelCell::setModelName(char * name) { strncpy(modelName, name, LEN_MODEL_NAME); - if (modelName[0] == 0) { + modelName[LEN_MODEL_NAME] = '\0'; + + if (modelName[0] == '\0') { char * tmp; strncpy(modelName, modelFilename, LEN_MODEL_NAME); tmp = (char *) memchr(modelName, '.', LEN_MODEL_NAME); - if (tmp) + if (tmp != nullptr) *tmp = 0; } } @@ -72,11 +71,10 @@ void ModelCell::setModelName(char* name, uint8_t len) char * tmp; strncpy(modelName, modelFilename, LEN_MODEL_NAME); tmp = (char *) memchr(modelName, '.', LEN_MODEL_NAME); - if (tmp != NULL) + if (tmp != nullptr) *tmp = 0; } - //resetBuffer(); } void ModelCell::setModelId(uint8_t moduleIdx, uint8_t id) @@ -84,93 +82,6 @@ void ModelCell::setModelId(uint8_t moduleIdx, uint8_t id) modelId[moduleIdx] = id; } -// void ModelCell::resetBuffer() -// { -// if (buffer) { -// delete buffer; -// buffer = NULL; -// } -// } - -// const BitmapBuffer * ModelCell::getBuffer() -// { -// if (!buffer) { -// loadBitmap(); -// } -// return buffer; -// } - -// void ModelCell::loadBitmap() -// { -// uint8_t version; - -// PACK(struct { -// ModelHeader header; -// TimerData timers[MAX_TIMERS]; -// }) partialmodel; - -// const char * error = NULL; -// BitmapBuffer * tmp_buffer; - -// if (strncmp(modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0) { -// memcpy(&partialmodel.header, &g_model.header, sizeof(partialmodel)); -// } -// else { -// #if !defined (SDCARD_YAML) -// error = readModel(modelFilename, (uint8_t *)&partialmodel.header, sizeof(partialmodel), &version); -// // LEN_BITMAP_NAME has now 4 bytes more -// if (version <= 218) { -// memmove(partialmodel.timers, &(partialmodel.header.bitmap[10]), sizeof(TimerData)*MAX_TIMERS); -// memclear(&(partialmodel.header.bitmap[10]), 4); -// } -// #else -// memset(&partialmodel.header,0,sizeof(partialmodel)); -// #endif -// } - -// if ((modelName[0] == 0) && ! error) -// setModelName(partialmodel.header.name); // resets buffer!!! - -// tmp_buffer = new BitmapBuffer(BMP_RGB565, MODELCELL_WIDTH, MODELCELL_HEIGHT); -// if (tmp_buffer == NULL) { -// return; -// } -// tmp_buffer->clear(TEXT_BGCOLOR); - -// if (error) { -// tmp_buffer->drawText(5, 2, "(Invalid Model)", TEXT_COLOR); -// tmp_buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, TEXT_COLOR); -// } -// else { -// char timer[LEN_TIMER_STRING]; -// tmp_buffer->drawSizedText(5, 2, modelName, /*LEN_MODEL_NAME*/strlen(modelName), -// SMLSIZE|TEXT_COLOR); - -// getTimerString(timer, 0); -// for (uint8_t i = 0; i < MAX_TIMERS; i++) { -// if (partialmodel.timers[i].mode != 0 && partialmodel.timers[i].persistent) { -// getTimerString(timer, partialmodel.timers[i].value, 1); -// break; -// } -// } -// tmp_buffer->drawText(101, 40, timer, TEXT_COLOR); -// for (int i=0; i<4; i++) { -// tmp_buffer->drawBitmapPattern(104+i*11, 25, LBM_SCORE0, TITLE_BGCOLOR); -// } -// GET_FILENAME(filename, BITMAPS_PATH, partialmodel.header.bitmap, ""); -// const BitmapBuffer * bitmap = BitmapBuffer::load(filename); -// if (bitmap) { -// tmp_buffer->drawScaledBitmap(bitmap, 5, 24, 56, 32); -// delete bitmap; -// } -// else { -// tmp_buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, TEXT_COLOR); -// } -// } -// tmp_buffer->drawSolidHorizontalLine(5, 19, 143, LINE_COLOR); - -// buffer = tmp_buffer; -// } void ModelCell::save(FIL* file) { diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h index 85054c512..f9bde8302 100644 --- a/radio/src/storage/modelslist.h +++ b/radio/src/storage/modelslist.h @@ -29,8 +29,8 @@ #include "sdcard_raw.h" #endif -#define MODELCELL_WIDTH 172 -#define MODELCELL_HEIGHT 59 +// #define MODELCELL_WIDTH 172 +// #define MODELCELL_HEIGHT 59 // modelXXXXXXX.bin F,FF F,3F,FF\r\n #define LEN_MODELS_IDX_LINE (LEN_MODEL_FILENAME + sizeof(" F,FF F,3F,FF\r\n")-1) @@ -65,8 +65,6 @@ class ModelCell void setRfModuleData(uint8_t moduleIdx, ModuleData* modData); bool fetchRfData(); - // void loadBitmap(); - // void resetBuffer(); }; class ModelsCategory: public std::list diff --git a/radio/src/storage/sdcard_common.cpp b/radio/src/storage/sdcard_common.cpp index 59e1e8668..4f3a7b910 100644 --- a/radio/src/storage/sdcard_common.cpp +++ b/radio/src/storage/sdcard_common.cpp @@ -145,6 +145,10 @@ void storageReadAll() { TRACE("storageReadAll"); + // Wipe models list in case + // it's being reloaded after USB connection + modelslist.clear(); + if (loadRadioSettings() != nullptr) { storageEraseAll(true); } @@ -156,6 +160,9 @@ void storageReadAll() } } + // and reload the list + modelslist.load(); + // Current model filename is empty... // Let's fix it! if (strlen(g_eeGeneral.currModelFilename) == 0) { @@ -172,12 +179,5 @@ void storageReadAll() if (loadModel(g_eeGeneral.currModelFilename, false) != nullptr) { TRACE("No current model or SD card error"); } - - // Wipe models list in case - // it's being reloaded after USB connection - modelslist.clear(); - - // and reload the list - modelslist.load(); } diff --git a/radio/src/storage/sdcard_common.h b/radio/src/storage/sdcard_common.h index 5533e004a..9d7683d12 100644 --- a/radio/src/storage/sdcard_common.h +++ b/radio/src/storage/sdcard_common.h @@ -35,6 +35,7 @@ // opens radio.bin or model file const char * openFile(const char * fullpath, FIL * file, uint16_t * size, uint8_t * version); +const char * writeFile(const char * fullpath, const uint8_t * data, uint16_t size); void getModelPath(char * path, const char * filename); diff --git a/radio/src/strhelpers.cpp b/radio/src/strhelpers.cpp index 7777b7335..48bb7a8af 100644 --- a/radio/src/strhelpers.cpp +++ b/radio/src/strhelpers.cpp @@ -312,9 +312,8 @@ char * getSwitchWarningString(char * dest, swsrc_t idx) char * getSwitchName(char * dest, swsrc_t idx) { div_t swinfo = switchInfo(idx); - if (ZEXIST(g_eeGeneral.switchNames[swinfo.quot])) { - dest += zchar2str(dest, g_eeGeneral.switchNames[swinfo.quot], LEN_SWITCH_NAME); - // TODO tous zchar2str + if (g_eeGeneral.switchNames[swinfo.quot][0] != '\0') { + dest = strAppend(dest, g_eeGeneral.switchNames[swinfo.quot], LEN_SWITCH_NAME); } else { *dest++ = 'S'; @@ -404,7 +403,8 @@ char * getSwitchPositionName(char * dest, swsrc_t idx) } #endif else { - zchar2str(s, g_model.telemetrySensors[idx-SWSRC_FIRST_SENSOR].label, TELEM_LABEL_LEN); + strncpy(s, g_model.telemetrySensors[idx-SWSRC_FIRST_SENSOR].label, TELEM_LABEL_LEN); + s[TELEM_LABEL_LEN] = '\0'; } return dest; @@ -419,7 +419,7 @@ char * getSourceString(char * dest, mixsrc_t idx) idx -= MIXSRC_FIRST_INPUT; *dest = CHAR_INPUT; if (strlen(g_model.inputNames[idx])) { - memset(dest + 1, 0, LEN_INPUT_NAME); + memset(dest + 1, 0, LEN_INPUT_NAME+1); strncpy(dest + 1, g_model.inputNames[idx], LEN_INPUT_NAME); } else { @@ -473,8 +473,8 @@ char * getSourceString(char * dest, mixsrc_t idx) } else if (idx <= MIXSRC_LAST_SWITCH) { idx -= MIXSRC_FIRST_SWITCH; - if (ZEXIST(g_eeGeneral.switchNames[idx])) { - zchar2str(dest, g_eeGeneral.switchNames[idx], LEN_SWITCH_NAME); + if (g_eeGeneral.switchNames[idx][0] != '\0') { + strncpy(dest, g_eeGeneral.switchNames[idx], LEN_SWITCH_NAME); dest[LEN_SWITCH_NAME] = '\0'; } else { @@ -497,8 +497,8 @@ char * getSourceString(char * dest, mixsrc_t idx) getStringAtIndex(dest, STR_VSRCRAW, idx-MIXSRC_Rud+1-MAX_LOGICAL_SWITCHES-MAX_TRAINER_CHANNELS-MAX_OUTPUT_CHANNELS-MAX_GVARS); } else if (idx <= MIXSRC_LAST_TIMER) { - if(ZEXIST(g_model.timers[idx-MIXSRC_FIRST_TIMER].name)) { - zchar2str(dest,g_model.timers[idx-MIXSRC_FIRST_TIMER].name, LEN_TIMER_NAME); + if(g_model.timers[idx-MIXSRC_FIRST_TIMER].name[0] != '\0') { + strncpy(dest,g_model.timers[idx-MIXSRC_FIRST_TIMER].name, LEN_TIMER_NAME); dest[LEN_TIMER_NAME] = '\0'; } else { diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index b1c6816c4..a04c16ed8 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -75,7 +75,7 @@ void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length, uint16_t p GPIO_ResetBits(AUX_SERIAL_RX_INVERT_GPIO, AUX_SERIAL_RX_INVERT_GPIO_PIN); #endif -#if defined(SERIAL_DMA_Stream_RX) +#if defined(AUX_SERIAL_DMA_Stream_RX) if (dma) { DMA_InitTypeDef DMA_InitStructure; auxSerialRxFifo.clear(); diff --git a/radio/src/targets/common/arm/stm32/mixer_scheduler_driver.cpp b/radio/src/targets/common/arm/stm32/mixer_scheduler_driver.cpp index 40a1def63..7f7b23a1c 100644 --- a/radio/src/targets/common/arm/stm32/mixer_scheduler_driver.cpp +++ b/radio/src/targets/common/arm/stm32/mixer_scheduler_driver.cpp @@ -74,11 +74,6 @@ extern "C" void MIXER_SCHEDULER_TIMER_IRQHandler(void) // set next period MIXER_SCHEDULER_TIMER->ARR = 2 * getMixerSchedulerPeriod() - 1; - //read ADCs - DEBUG_TIMER_START(debugTimerAdcRead); - adcRead(); - DEBUG_TIMER_STOP(debugTimerAdcRead); - // trigger mixer start mixerSchedulerISRTrigger(); } diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 1ff27544a..bfbb3153e 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -106,6 +106,7 @@ if (PCB STREQUAL X10) add_definitions(-DRADIO_FAMILY_T16) option(INTERNAL_MODULE_MULTI "Support for MULTI internal module" ON) option(BLUETOOTH "Support for bluetooth module" OFF) + set(HARDWARE_TOUCH YES) add_definitions(-DMANUFACTURER_JUMPER) else() set(FLAVOUR x10) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 82d6d8b2a..752ed29d4 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -707,15 +707,27 @@ #endif // I2C Bus -#define I2C_RCC_AHB1Periph RCC_AHB1Periph_GPIOB -#define I2C_RCC_APB1Periph RCC_APB1Periph_I2C1 -#define I2C I2C1 -#define I2C_GPIO GPIOB -#define I2C_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 -#define I2C_SDA_GPIO_PIN GPIO_Pin_9 // PB.09 -#define I2C_GPIO_AF GPIO_AF_I2C1 -#define I2C_SCL_GPIO_PinSource GPIO_PinSource8 -#define I2C_SDA_GPIO_PinSource GPIO_PinSource9 +#if defined(RADIO_T18) + #define I2C_RCC_AHB1Periph RCC_AHB1Periph_GPIOH + #define I2C_RCC_APB1Periph RCC_APB1Periph_I2C3 + #define I2C I2C3 + #define I2C_GPIO GPIOH + #define I2C_SCL_GPIO_PIN GPIO_Pin_7 // PH.07 + #define I2C_SDA_GPIO_PIN GPIO_Pin_8 // PH.08 + #define I2C_GPIO_AF GPIO_AF_I2C3 + #define I2C_SCL_GPIO_PinSource GPIO_PinSource7 + #define I2C_SDA_GPIO_PinSource GPIO_PinSource8 +#else + #define I2C_RCC_AHB1Periph RCC_AHB1Periph_GPIOB + #define I2C_RCC_APB1Periph RCC_APB1Periph_I2C1 + #define I2C I2C1 + #define I2C_GPIO GPIOB + #define I2C_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 + #define I2C_SDA_GPIO_PIN GPIO_Pin_9 // PB.09 + #define I2C_GPIO_AF GPIO_AF_I2C1 + #define I2C_SCL_GPIO_PinSource GPIO_PinSource8 + #define I2C_SDA_GPIO_PinSource GPIO_PinSource9 +#endif #define I2C_SPEED 400000 // Haptic diff --git a/radio/src/targets/horus/lcd_driver.cpp b/radio/src/targets/horus/lcd_driver.cpp index 15764e3da..9fe6efacf 100644 --- a/radio/src/targets/horus/lcd_driver.cpp +++ b/radio/src/targets/horus/lcd_driver.cpp @@ -369,6 +369,10 @@ void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, ui /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -409,6 +413,10 @@ void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -458,6 +466,10 @@ void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_ /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -512,6 +524,10 @@ void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -545,6 +561,10 @@ void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -578,6 +598,10 @@ void lcdCopy(void * dest, void * src) /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } diff --git a/radio/src/targets/horus/libopenui_config.h b/radio/src/targets/horus/libopenui_config.h index 87c7e8f23..2e89621f3 100644 --- a/radio/src/targets/horus/libopenui_config.h +++ b/radio/src/targets/horus/libopenui_config.h @@ -106,9 +106,9 @@ constexpr uint32_t PAGE_TITLE_LEFT = 50; constexpr uint32_t INPUT_EDIT_LABELS_WIDTH = 120; constexpr coord_t INPUT_EDIT_CURVE_WIDTH = 158; -constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = LCD_H - MENU_HEADER_HEIGHT; -constexpr coord_t INPUT_EDIT_CURVE_LEFT = LCD_W - 158; -constexpr coord_t INPUT_EDIT_CURVE_TOP = MENU_HEADER_HEIGHT; +constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = INPUT_EDIT_CURVE_WIDTH; +constexpr coord_t INPUT_EDIT_CURVE_LEFT = PAGE_PADDING; +constexpr coord_t INPUT_EDIT_CURVE_TOP = MENU_HEADER_HEIGHT + PAGE_PADDING; constexpr coord_t MENUS_LINE_HEIGHT = 30; constexpr coord_t MENUS_WIDTH = 200; constexpr coord_t MENUS_OFFSET_TOP = 20; diff --git a/radio/src/targets/horus/tp_gt911.cpp b/radio/src/targets/horus/tp_gt911.cpp index eb8753e6f..624cac3f8 100644 --- a/radio/src/targets/horus/tp_gt911.cpp +++ b/radio/src/targets/horus/tp_gt911.cpp @@ -22,6 +22,196 @@ #include "tp_gt911.h" #include "touch.h" +#if defined (RADIO_T18) +const uint8_t TOUCH_GT911_Cfg[] = { + GT911_CFG_NUMER, // 0x8047 Config version + 0xE0, // 0x8048 X output map : x 480 + 0x01, + 0x10, // 0x804A Y ouptut max : y 272 + 0x01, + GT911_MAX_TP, // 0x804C Touch number + 0x0C, // 0x804D Module switch 1 : bit4= xy change Int mode + 0x00, // 0x804E Module switch 2 + 0x01, // 0x804F Shake_Count + 0x18, // 0x8050 Filter + 0x28, // 0x8051 Larger touch + 0x0F, // 0x8052 Noise reduction + 0x50, // 0x8053 Screen touch level + 0x3C, // 0x8054 Screen touch leave + 0x03, // 0x8055 Low power control + 0x05, // 0x8056 Refresh rate + 0x00, // 0x8057 X threshold + 0x00, // 0x8058 Y threshold + 0x00, // 0x8059 Reserved + 0x00, // 0x805A Reserved + 0x00, // 0x805B Space (top, bottom) + 0x00, // 0x805C Space (left, right) + 0x04, // 0x805D Mini filter + 0x18, // 0x805E Strech R0 + 0x1A, // 0x805F Strech R1 + 0x1E, // 0x8060 Strech R2 + 0x14, // 0x8061 Strech RM + 0x87, // 0x8062 Drv groupA num + 0x29, // 0x8063 Drv groupB num + 0x0A, // 0x8064 Sensor num + 0x4B, // 0x8065 FreqA factor + 0x4D, // 0x8066 FreqB factor + 0xD3, // 0x8067 Panel bit freq + 0x07, + 0x00, // 0x8069 Reserved + 0x00, + 0x00, // 0x806B Panel tx gain + 0x19, // 0x806C Panel rx gain + 0x02, // 0x806D Panel dump shift + 0x10, // 0x806E Drv frame control + 0x32, // 0x806F Charging level up + 0x00, // 0x8070 Module switch 3 + 0x00, // 0x8071 Gesture distance + 0x00, // 0x8072 Gesture long press time + 0x00, // 0x8073 X/Y slope adjust + 0x03, // 0x8074 Gesture control + 0x00, // 0x8075 Gesture switch 1 + 0x00, // 0x8076 Gesture switch 2 + 0x00, // 0x8077 Gesture refresh rate + 0x00, // 0x8078 Gesture touch level + 0x00, // 0x8079 New green wake up level + 0x32, // 0x807A Freq hopping start + 0x73, // 0x807B Freq hopping end + 0x94, // 0x807C Noise detect time + 0xD5, // 0x807D Hopping flag + 0x02, // 0x807E Hopping flag + 0x07, // 0x807F Noise threshold + 0x00, // 0x8080 Nois min threshold old + 0x00, // 0x8081 Reserved + 0x04, // 0x8082 Hopping sensor group + 0xAE, // 0x8083 Hopping Seg1 normalize + 0x36, // 0x8084 Hopping Seg1 factor + 0x00, // 0x8085 Main clock adjust + 0x94, // 0x8086 Hopping Seg2 normalize + 0x40, // 0x8087 Hopping Seg2 factor + 0x00, // 0x8088 Reserved + 0x7E, // 0x8089 Hopping Seg3 normalize + 0x4C, // 0x808A Hopping Seg3 factor + 0x00, // 0x808B Reserved + 0x6D, // 0x808C Hopping Seg4 normalize + 0x59, // 0x808D Hopping Seg4 factor + 0x00, // 0x808E Reserved + 0x5D, // 0x808F Hopping Seg5 normalize + 0x6A, // 0x8090 Hopping Seg5 factor + 0x00, // 0x8091 Reserved + 0x5D, // 0x8092 Hopping Seg6 normalize + 0x00, // 0x8093 Key1 + 0x00, // 0x8094 Key2 + 0x00, // 0x8095 Key3 + 0x00, // 0x8096 Key4 + 0x00, // 0x8097 Key area + 0x00, // 0x8098 Key touch level + 0x00, // 0x8099 Key leave level + 0x00, // 0x809A Key sens + 0x00, // 0x809B Key sens + 0x00, // 0x809C Key restrain + 0x00, // 0x809D Key restrain time + 0x00, // 0x809E Large gesture touch + 0x00, // 0x809F Reserved + 0x00, // 0x80A0 Reserved + 0x00, // 0x80A1 Hotknot noise map + 0x00, // 0x80A2 Link threshold + 0x00, // 0x80A3 Pxy threshold + 0x00, // 0x80A4 GHot dump shift + 0x00, // 0x80A5 GHot rx gain + 0x00, // 0x80A6 Freg gain + 0x00, // 0x80A7 Freg gain 1 + 0x00, // 0x80A8 Freg gain 2 + 0x00, // 0x80A9 Freg gain 3 + 0x00, // 0x80AA Reserved + 0x00, // 0x80AB Reserved + 0x00, // 0x80AC Reserved + 0x00, // 0x80AD Reserved + 0x00, // 0x80AE Reserved + 0x00, // 0x80AF Reserved + 0x00, // 0x80B0 Reserved + 0x00, // 0x80B1 Reserved + 0x00, // 0x80B2 Reserved + 0x00, // 0x80B3 Combine dis + 0x00, // 0x80B4 Split set + 0x00, // 0x80B5 Reserved + 0x00, // 0x80B6 Reserved + 0x0A, // 0x80B7 Sensor CH0 + 0x0C, // 0x80B8 Sensor CH1 + 0x0E, // 0x80B9 Sensor CH2 + 0x10, // 0x80BA Sensor CH3 + 0x12, // 0x80BB Sensor CH4 + 0x14, // 0x80BC Sensor CH5 + 0x16, // 0x80BD Sensor CH6 + 0x18, // 0x80BE Sensor CH7 + 0x1A, // 0x80BF Sensor CH8 + 0x1C, // 0x80C0 Sensor CH9 + 0xFF, // 0x80C1 Sensor CH10 + 0xFF, // 0x80C2 Sensor CH11 + 0xFF, // 0x80C3 Sensor CH12 + 0xFF, // 0x80C4 Sensor CH13 + 0xFF, // 0x80C5 Reserved + 0xFF, // 0x80C6 Reserved + 0xFF, // 0x80C7 Reserved + 0xFF, // 0x80C8 Reserved + 0xFF, // 0x80C9 Reserved + 0xFF, // 0x80CA Reserved + 0xFF, // 0x80CB Reserved + 0xFF, // 0x80CC Reserved + 0xFF, // 0x80CD Reserved + 0xFF, // 0x80CE Reserved + 0xFF, // 0x80CF Reserved + 0xFF, // 0x80D0 Reserved + 0xFF, // 0x80D1 Reserved + 0xFF, // 0x80D2 Reserved + 0xFF, // 0x80D3 Reserved + 0xFF, // 0x80D4 Reserved + 0x00, // 0x80D5 Driver CH0 + 0x02, // 0x80D6 Driver CH1 + 0x04, // 0x80D7 Driver CH2 + 0x06, // 0x80D8 Driver CH3 + 0x08, // 0x80D9 Driver CH4 + 0x0A, // 0x80DA Driver CH5 + 0x0C, // 0x80DB Driver CH6 + 0x1D, // 0x80DC Driver CH7 + 0x1E, // 0x80DD Driver CH8 + 0x1F, // 0x80DE Driver CH9 + 0x20, // 0x80DF Driver CH10 + 0x21, // 0x80E0 Driver CH11 + 0x22, // 0x80E1 Driver CH12 + 0x24, // 0x80E2 Driver CH13 + 0x26, // 0x80E3 Driver CH14 + 0x28, // 0x80E4 Driver CH15 + 0xFF, // 0x80E5 Driver CH16 + 0xFF, // 0x80E6 Driver CH17 + 0xFF, // 0x80E7 Driver CH18 + 0xFF, // 0x80E8 Driver CH19 + 0xFF, // 0x80E9 Driver CH20 + 0xFF, // 0x80EA Driver CH21 + 0xFF, // 0x80EB Driver CH22 + 0xFF, // 0x80EC Driver CH23 + 0xFF, // 0x80ED Driver CH24 + 0xFF, // 0x80EE Driver CH25 + 0xFF, // 0x80EF Reserved + 0xFF, // 0x80F0 Reserved + 0xFF, // 0x80F1 Reserved + 0xFF, // 0x80F2 Reserved + 0xFF, // 0x80F3 Reserved + 0xFF, // 0x80F4 Reserved + 0xFF, // 0x80F5 Reserved + 0xFF, // 0x80F6 Reserved + 0xFF, // 0x80F7 Reserved + 0xFF, // 0x80F8 Reserved + 0xFF, // 0x80F9 Reserved + 0xFF, // 0x80FA Reserved + 0xFF, // 0x80FB Reserved + 0xFF, // 0x80FC Reserved + 0xFF, // 0x80FD Reserved + 0xFF // 0x80FE Reserved +}; + +#else + //GT911 param table const uint8_t TOUCH_GT911_Cfg[] = { @@ -211,6 +401,8 @@ const uint8_t TOUCH_GT911_Cfg[] = 0x00 // 0x80FE Reserved }; +#endif + uint8_t touchGT911Flag = 0; uint8_t touchEventOccured = 0; struct TouchData touchData; @@ -494,6 +686,24 @@ bool touchPanelInit(void) } } +static const char* event2str(uint8_t ev) +{ + switch(ev){ + case TE_NONE: + return "NONE"; + case TE_UP: + return "UP"; + case TE_DOWN: + return "DOWN"; + case TE_SLIDE_END: + return "SLIDE_END"; + case TE_SLIDE: + return "SLIDE"; + default: + return "UNKNOWN"; + } +} + void touchPanelRead() { uint8_t state = 0; @@ -503,7 +713,7 @@ void touchPanelRead() touchEventOccured = false; I2C_GT911_ReadRegister(GT911_READ_XY_REG, &state, 1); - + TRACE("touch state = 0x%x", state); if ((state & 0x80u) == 0x00) { // not ready return; @@ -513,7 +723,7 @@ void touchPanelRead() if (pointsCount > 0 && pointsCount < GT911_MAX_TP) { I2C_GT911_ReadRegister(GT911_READ_XY_REG + 1, touchData.data, pointsCount * sizeof(TouchPoint)); - if (touchData.pointsCount == 0) { + if (touchState.event == TE_NONE || touchState.event == TE_UP || touchState.event == TE_SLIDE_END) { touchState.event = TE_DOWN; touchState.startX = touchState.x = touchData.points[0].x; touchState.startY = touchState.y = touchData.points[0].y; @@ -527,20 +737,21 @@ void touchPanelRead() touchState.y = touchData.points[0].y; } } - touchData.pointsCount = pointsCount; } else { - if (touchData.pointsCount > 0) { - touchData.pointsCount = 0; - if (touchState.event == TE_SLIDE) - touchState.event = TE_SLIDE_END; - else - touchState.event = TE_UP; + if (touchState.event == TE_SLIDE) { + touchState.event = TE_SLIDE_END; + } else if (touchState.event == TE_DOWN) { + touchState.event = TE_UP; + } else if (touchState.event != TE_SLIDE_END){ + touchState.event = TE_NONE; } } uint8_t zero = 0; I2C_GT911_WriteRegister(GT911_READ_XY_REG, &zero, 1); + + TRACE("touch event = %s", event2str(touchState.event)); } extern "C" void TOUCH_INT_EXTI_IRQHandler1(void) diff --git a/radio/src/targets/horus/tp_gt911.h b/radio/src/targets/horus/tp_gt911.h index 624db2c92..edbf3fd57 100644 --- a/radio/src/targets/horus/tp_gt911.h +++ b/radio/src/targets/horus/tp_gt911.h @@ -30,7 +30,7 @@ void touchPanelRead(); bool touchPanelEventOccured(); #define GT911_MAX_TP 5 -#define GT911_CFG_NUMER 0x6A +#define GT911_CFG_NUMER 0x6C //I2C #define GT_CMD_WR 0X28 @@ -69,7 +69,6 @@ PACK(typedef struct { }) TouchPoint; PACK(struct TouchData { - uint8_t pointsCount; union { TouchPoint points[GT911_MAX_TP]; diff --git a/radio/src/targets/taranis/telemetry_driver.cpp b/radio/src/targets/taranis/telemetry_driver.cpp index c16d6feca..08b79b0c7 100644 --- a/radio/src/targets/taranis/telemetry_driver.cpp +++ b/radio/src/targets/taranis/telemetry_driver.cpp @@ -77,6 +77,9 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode) } USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + if (g_eeGeneral.uartSampleMode == UART_SAMPLE_MODE_ONEBIT) { + USART_OneBitMethodCmd(TELEMETRY_USART, ENABLE); + } USART_Init(TELEMETRY_USART, &USART_InitStructure); USART_Cmd(TELEMETRY_USART, ENABLE); diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index c8ddb98f3..842312bf4 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -621,10 +621,10 @@ void TelemetrySensor::init(const char * label, uint8_t unit, uint8_t prec) void TelemetrySensor::init(uint16_t id) { char label[TELEM_LABEL_LEN]; // 4 - label[0] = hex2char((id & 0x0f00) >> 8); - label[1] = hex2char((id & 0x00f0) >> 4); - label[2] = hex2char((id & 0x000f) >> 0); - label[3] = '\0'; + label[0] = hex2char((id & 0xf000) >> 12); + label[1] = hex2char((id & 0x0f00) >> 8); + label[2] = hex2char((id & 0x00f0) >> 4); + label[3] = hex2char((id & 0x000f) >> 0); init(label); } diff --git a/radio/src/tests/conversions.cpp b/radio/src/tests/conversions.cpp index 30e889523..87c8c5cc5 100644 --- a/radio/src/tests/conversions.cpp +++ b/radio/src/tests/conversions.cpp @@ -50,18 +50,18 @@ TEST(Conversions, ConversionX9DPFrom23) EXPECT_EQ(FUNC_LOGS, g_eeGeneral.customFn[0].func); EXPECT_EQ(20, g_eeGeneral.customFn[0].all.val); - EXPECT_STREQ("Tes", g_eeGeneral.switchNames[0]); // ZSTREQ + EXPECT_STRNEQ("Tes", g_eeGeneral.switchNames[0]); // ZSTREQ EXPECT_EQ(SWITCH_3POS, SWITCH_CONFIG(0)); - EXPECT_STREQ("Test", g_model.header.name); // ZSTREQ + EXPECT_STRNEQ("Test", g_model.header.name); // ZSTREQ EXPECT_EQ(TMRMODE_START, g_model.timers[0].mode); // new! EXPECT_EQ(SWSRC_SA0, g_model.timers[0].swtch); // new! EXPECT_EQ(80, g_model.mixData[0].weight); EXPECT_EQ(-100, g_model.limitData[0].max); // 90.0 EXPECT_EQ(80, g_model.expoData[0].weight); EXPECT_EQ(10, g_model.flightModeData[0].gvars[0]); - EXPECT_STREQ("Tes", g_model.gvars[0].name); // ZSTREQ - EXPECT_STREQ("Test", g_model.flightModeData[0].name); // ZSTREQ + EXPECT_STRNEQ("Tes", g_model.gvars[0].name); // ZSTREQ + EXPECT_STRNEQ("Test", g_model.flightModeData[0].name); // ZSTREQ #if defined(INTERNAL_MODULE_PXX2) EXPECT_EQ(MODULE_TYPE_ISRM_PXX2, g_model.moduleData[INTERNAL_MODULE].type); @@ -73,13 +73,31 @@ TEST(Conversions, ConversionX9DPFrom23) EXPECT_EQ(MODULE_TYPE_R9M_PXX1, g_model.moduleData[EXTERNAL_MODULE].type); EXPECT_EQ(MODULE_SUBTYPE_R9M_FCC, g_model.moduleData[EXTERNAL_MODULE].subType); - EXPECT_STREQ("Rud", g_model.inputNames[0]); // ZSTREQ - EXPECT_STREQ("Tes", g_model.telemetrySensors[0].label); // ZSTREQ + EXPECT_STRNEQ("Rud", g_model.inputNames[0]); // ZSTREQ + EXPECT_STRNEQ("Tes", g_model.telemetrySensors[0].label); // ZSTREQ EXPECT_EQ(10, g_model.telemetrySensors[0].id); EXPECT_EQ(10, g_model.telemetrySensors[0].frskyInstance.physID); EXPECT_EQ(MIXSRC_FIRST_TELEM, g_model.logicalSw[0].v1); - EXPECT_STREQ("abc.wav", g_model.customFn[1].play.name); + EXPECT_STRNEQ("abc.wav", g_model.customFn[1].play.name); +} + +TEST(Conversions, ConversionX9DPFrom23_2) +{ + loadEEPROMFile(TESTS_PATH "/eeprom_23_x9d+2.bin"); + + eepromOpen(); + eeLoadGeneralSettingsData(); + convertRadioData_219_to_220(g_eeGeneral); + eeConvertModel(6, 219); + eeLoadModel(6); + + EXPECT_EQ(710, g_eeGeneral.calib[0].spanNeg); + EXPECT_EQ(944, g_eeGeneral.calib[0].mid); + EXPECT_EQ(770, g_eeGeneral.calib[0].spanPos); + + EXPECT_EQ(TMRMODE_START, g_model.timers[0].mode); // new! + EXPECT_EQ(-SWSRC_SA0, g_model.timers[0].swtch); // new! } #endif diff --git a/radio/src/tests/eeprom_23_x9d+2.bin b/radio/src/tests/eeprom_23_x9d+2.bin new file mode 100644 index 000000000..de18f2473 Binary files /dev/null and b/radio/src/tests/eeprom_23_x9d+2.bin differ diff --git a/radio/src/tests/lua.cpp b/radio/src/tests/lua.cpp index 9425726a9..fe49b9e59 100644 --- a/radio/src/tests/lua.cpp +++ b/radio/src/tests/lua.cpp @@ -47,12 +47,12 @@ TEST(Lua, testSetModelInfo) luaExecStr("info.name = 'modelA'"); luaExecStr("model.setInfo(info)"); // luaExecStr("print('model name: '..info.name..' id: '..info.id)"); - EXPECT_ZSTREQ("modelA", g_model.header.name); + EXPECT_STRNEQ("modelA", g_model.header.name); luaExecStr("info.name = 'Model 1'"); luaExecStr("model.setInfo(info)"); // luaExecStr("print('model name: '..info.name..' id: '..info.id)"); - EXPECT_ZSTREQ("Model 1", g_model.header.name); + EXPECT_STRNEQ("Model 1", g_model.header.name); } TEST(Lua, testPanicProtection) @@ -112,7 +112,7 @@ TEST(Lua, testModelInputs) // add one line on Input4 luaExecStr("model.insertInput(3, 0, {name='test1', source=MIXSRC_Thr, weight=56, offset=3, switch=2})"); EXPECT_EQ(3, (int)g_model.expoData[0].chn); - EXPECT_ZSTREQ("test1", g_model.expoData[0].name); + EXPECT_STRNEQ("test1", g_model.expoData[0].name); EXPECT_EQ(MIXSRC_Thr, g_model.expoData[0].srcRaw); EXPECT_EQ(56, g_model.expoData[0].weight); EXPECT_EQ(3, g_model.expoData[0].offset); @@ -121,14 +121,14 @@ TEST(Lua, testModelInputs) // add another one before existing line on Input4 luaExecStr("model.insertInput(3, 0, {name='test2', source=MIXSRC_Rud, weight=-56})"); EXPECT_EQ(3, (int)g_model.expoData[0].chn); - EXPECT_ZSTREQ("test2", g_model.expoData[0].name); + EXPECT_STRNEQ("test2", g_model.expoData[0].name); EXPECT_EQ(MIXSRC_Rud, g_model.expoData[0].srcRaw); EXPECT_EQ(-56, g_model.expoData[0].weight); EXPECT_EQ(0, g_model.expoData[0].offset); EXPECT_EQ(0, g_model.expoData[0].swtch); EXPECT_EQ(3, (int)g_model.expoData[1].chn); - EXPECT_ZSTREQ("test1", g_model.expoData[1].name); + EXPECT_STRNEQ("test1", g_model.expoData[1].name); EXPECT_EQ(MIXSRC_Thr, g_model.expoData[1].srcRaw); EXPECT_EQ(56, g_model.expoData[1].weight); EXPECT_EQ(3, g_model.expoData[1].offset); @@ -137,21 +137,21 @@ TEST(Lua, testModelInputs) // add another line after existing lines on Input4 luaExecStr("model.insertInput(3, model.getInputsCount(3), {name='test3', source=MIXSRC_Ail, weight=100})"); EXPECT_EQ(3, (int)g_model.expoData[0].chn); - EXPECT_ZSTREQ("test2", g_model.expoData[0].name); + EXPECT_STRNEQ("test2", g_model.expoData[0].name); EXPECT_EQ(MIXSRC_Rud, g_model.expoData[0].srcRaw); EXPECT_EQ(-56, g_model.expoData[0].weight); EXPECT_EQ(0, g_model.expoData[0].offset); EXPECT_EQ(0, g_model.expoData[0].swtch); EXPECT_EQ(3, (int)g_model.expoData[1].chn); - EXPECT_ZSTREQ("test1", g_model.expoData[1].name); + EXPECT_STRNEQ("test1", g_model.expoData[1].name); EXPECT_EQ(MIXSRC_Thr, g_model.expoData[1].srcRaw); EXPECT_EQ(56, g_model.expoData[1].weight); EXPECT_EQ(3, g_model.expoData[1].offset); EXPECT_EQ(2, g_model.expoData[1].swtch); EXPECT_EQ(3, (int)g_model.expoData[2].chn); - EXPECT_ZSTREQ("test3", g_model.expoData[2].name); + EXPECT_STRNEQ("test3", g_model.expoData[2].name); EXPECT_EQ(MIXSRC_Ail, g_model.expoData[2].srcRaw); EXPECT_EQ(100, g_model.expoData[2].weight); EXPECT_EQ(0, g_model.expoData[2].offset); diff --git a/radio/src/tests/transparency_CN_480x272.png b/radio/src/tests/transparency_CN_480x272.png index 3f0252491..92083a206 100644 Binary files a/radio/src/tests/transparency_CN_480x272.png and b/radio/src/tests/transparency_CN_480x272.png differ diff --git a/radio/src/tests/transparency_EN_480x272.png b/radio/src/tests/transparency_EN_480x272.png index d6a71c799..a1cc5987b 100644 Binary files a/radio/src/tests/transparency_EN_480x272.png and b/radio/src/tests/transparency_EN_480x272.png differ diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 4b78e03cd..bca0e093b 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 4b78e03cd00608d7870e6603eb1ff974fbd05da6 +Subproject commit bca0e093b001596d27cc8297a3ec142a3b42239b diff --git a/radio/src/timers.cpp b/radio/src/timers.cpp index afda7f0ef..0ee289e78 100644 --- a/radio/src/timers.cpp +++ b/radio/src/timers.cpp @@ -74,7 +74,7 @@ void evalTimers(int16_t throttle, uint8_t tick10ms) for (uint8_t i=0; i" #define TR_CAPAWARNING INDENT "Nízká kapacita" #define TR_TEMPWARNING INDENT "Přehřátí" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Fce." #define TR_V1 "V1" #define TR_V2 "V2" @@ -835,6 +837,10 @@ #define TR_TEMP_CALIB " +=\006Teplota" #define TR_TIME "Čas" #define TR_MAXBAUDRATE "Max baudů" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Hledat" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 384fbd23d..7486afd00 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -782,6 +782,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "SPEICHER" #define TR_EEPROM_CONVERTING "EEPROM Konvertierung" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAS" #define TR_ALARMSWARN "ALARM" #define TR_SWITCHWARN "SCHALTER" @@ -808,7 +810,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Kapaz. niedrig" // wg 9XR-Pro #define TR_TEMPWARNING INDENT "Temp. größer" //wg 9XR-Pro -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funktion" #define TR_V1 "V1" #define TR_V2 "V2" @@ -840,6 +842,10 @@ #define TR_TEMP_CALIB "Temp. abgl." #define TR_TIME "Uhrzeit:" #define TR_MAXBAUDRATE "Max Baud" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index ae77352ea..b990e4484 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -786,6 +786,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "Converting EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "THROTTLE" #define TR_ALARMSWARN "ALARMS" #define TR_SWITCHWARN TR("SWITCH", "CONTROL") @@ -812,7 +814,7 @@ #define TR_COPROC_TEMP "MB Temp." #define TR_CAPAWARNING INDENT "Capacity low" #define TR_TEMPWARNING INDENT "Overheat" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Func" #define TR_V1 "V1" #define TR_V2 "V2" @@ -844,6 +846,11 @@ #define TR_TEMP_CALIB "Temp. Calib" #define TR_TIME "Time" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" + #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" #define TR_BLUETOOTH_INIT "Init" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index d483265d4..cdfdc068f 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -784,6 +784,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "ALMAC." #define TR_EEPROM_CONVERTING "Convirtiendo EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE TR("ACELERAD.", "ACELERADOR") #define TR_ALARMSWARN "ALARMAS" #define TR_SWITCHWARN TR("INTERRUP.", "INTERRUPTOR") @@ -810,7 +812,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Capacidad baja" #define TR_TEMPWARNING INDENT "Sobrecalent" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Función" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 626128ffc..17bd0b0c1 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -798,6 +798,8 @@ #define TR_EEPROMWARN "FINSKA" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "EEPROM Converting" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "THROTTLE" #define TR_ALARMSWARN "ALARMS" #define TR_SWITCHWARN TR("SWITCH","CONTROL") @@ -824,7 +826,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Capacity Low" #define TR_TEMPWARNING INDENT "Overheat" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Func" #define TR_V1 "V1" #define TR_V2 "V2" @@ -856,6 +858,10 @@ #define TR_TEMP_CALIB "Temp. Calib" #define TR_TIME "Time" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 119d95a4d..d2ac7ff04 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -803,6 +803,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STOCKAGE" #define TR_EEPROM_CONVERTING "Conversion EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAZ" #define TR_ALARMSWARN "SON" #define TR_SWITCHWARN TR("INTERS","CONTROLES") @@ -829,7 +831,7 @@ #define TR_COPROC_TEMP "Temp. MB \016>" #define TR_CAPAWARNING INDENT "Capacité Basse" #define TR_TEMPWARNING INDENT "Surchauffe" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Fonction" #define TR_V1 "V1" #define TR_V2 "V2" @@ -861,6 +863,11 @@ #define TR_TEMP_CALIB "Calib. temp" #define TR_TIME "Heure" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" + #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Découvrir" #define TR_BLUETOOTH_INIT "Init" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 435d785d4..db28fde80 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -804,6 +804,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "Conversione EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "MOTORE" #define TR_ALARMSWARN "ALLARMI" #define TR_SWITCHWARN "CONTROLLI" @@ -830,7 +832,7 @@ #define TR_COPROC_TEMP "Temp. MB \016>" #define TR_CAPAWARNING INDENT "Capacità Bassa" #define TR_TEMPWARNING INDENT "Temp. Alta" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funz" #define TR_V1 "V1" #define TR_V2 "V2" @@ -862,6 +864,10 @@ #define TR_TEMP_CALIB "Temp. Calib." #define TR_TIME "Ora" #define TR_MAXBAUDRATE "Max Bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Cerca" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index f8c132f8e..4b4783a38 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -787,6 +787,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "EEPROM Converteren" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAS" #define TR_ALARMSWARN "ALARM" #define TR_SWITCHWARN "SCHAKELAAR" @@ -813,7 +815,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Capaciteit laag" // wg 9XR-Pro #define TR_TEMPWARNING INDENT "Overhitting" //wg 9XR-Pro -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funktie" #define TR_V1 "V1" #define TR_V2 "V2" @@ -845,6 +847,10 @@ #define TR_TEMP_CALIB "Temp. Calib." #define TR_TIME "Tijd:" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index d0259b62a..4b14f6f25 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -801,6 +801,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "Konwersja EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAZ" #define TR_ALARMSWARN "ALARM" #define TR_SWITCHWARN TR("Przełą","Kontrola") @@ -827,7 +829,7 @@ #define TR_COPROC_TEMP "Temp. MB" #define TR_CAPAWARNING INDENT "Mała pojemność" #define TR_TEMPWARNING INDENT "Przegrzanie" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funkc" #define TR_V1 "V1" #define TR_V2 "V2" @@ -859,6 +861,10 @@ #define TR_TEMP_CALIB "Temp. kalibracji" #define TR_TIME "Czas" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index a33ebe0fe..0669c02c4 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -802,6 +802,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "EEPROM Converting" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "ACELERAD" #define TR_ALARMSWARN "ALARMES" #define TR_SWITCHWARN "CHAVES" @@ -828,7 +830,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Aviso Capacidade" #define TR_TEMPWARNING INDENT "Temperat. ALTA" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funcao" #define TR_V1 "V1" #define TR_V2 "V2" @@ -860,6 +862,10 @@ #define TR_TEMP_CALIB "Temp. Calib" #define TR_TIME "Time" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 7addcd639..c39926ca7 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -803,6 +803,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "LAGRING" #define TR_EEPROM_CONVERTING "EEPROM Konvertering" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAS" #define TR_ALARMSWARN "ALARM" #define TR_SWITCHWARN "BRYTARE" @@ -830,7 +832,7 @@ #define TR_COPROC_TEMP "MB temp. \016>" #define TR_CAPAWARNING INDENT "Låg Kapacitet" #define TR_TEMPWARNING TR(INDENT "Hög Temp ", INDENT "Hög Temperatur") -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funk" #define TR_V1 "V1" #define TR_V2 "V2" @@ -862,6 +864,10 @@ #define TR_TEMP_CALIB "Temp. kalib." #define TR_TIME "Tid " #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/tw.h.txt b/radio/src/translations/tw.h.txt index e492070ac..0913cd8f4 100644 --- a/radio/src/translations/tw.h.txt +++ b/radio/src/translations/tw.h.txt @@ -690,6 +690,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "存儲" #define TR_EEPROM_CONVERTING "轉換數據" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "油門" #define TR_ALARMSWARN "警告" #define TR_SWITCHWARN TR("開關", "開關位置") @@ -716,7 +718,7 @@ #define TR_COPROC_TEMP "主板溫度" #define TR_CAPAWARNING INDENT "電流過低" #define TR_TEMPWARNING INDENT "過熱" -#define TR_TTL_WARNING "注意: 端口僅能使用TTL電平或5V" +#define TR_TTL_WARNING "注意:使用逻辑电平3.3V" #define TR_FUNC "功能" #define TR_V1 "V1" #define TR_V2 "V2" @@ -748,6 +750,9 @@ #define TR_TEMP_CALIB "溫度校準" #define TR_TIME "時間" #define TR_MAXBAUDRATE "最大帶寬" +#define TR_SAMPLE_MODE "Sample Mode" +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "藍牙" #define TR_BLUETOOTH_DISC "發現" #define TR_BLUETOOTH_INIT "初始化" diff --git a/tools/build-gh.sh b/tools/build-gh.sh index d97eff033..457742b97 100755 --- a/tools/build-gh.sh +++ b/tools/build-gh.sh @@ -100,6 +100,9 @@ case $FLAVOR in XLITES) BUILD_OPTIONS+="-DPCB=XLITES" ;; + X9D) + BUILD_OPTIONS+="-DPCB=X9D" + ;; X9DP) BUILD_OPTIONS+="-DPCB=X9D+" ;; diff --git a/tools/setup_buildenv_ubuntu20.04.sh b/tools/setup_buildenv_ubuntu20.04.sh index e7b38719b..cce914737 100644 --- a/tools/setup_buildenv_ubuntu20.04.sh +++ b/tools/setup_buildenv_ubuntu20.04.sh @@ -1,6 +1,7 @@ #! /usr/bin/env bash -## Bash script to setup EdgeTX v2.4 development environment on Ubuntu 20.04. +## Bash script to setup EdgeTX v2.4 development environment on Ubuntu 20.04 running on bare-metal or in a virtual machine. +## Does NOT work with WSL/WSL2 under Windows 10! ## Let it run as normal user and when asked, give sudo credentials PAUSEAFTEREACHLINE="false"