-
Notifications
You must be signed in to change notification settings - Fork 214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Request: Support for sub-pages / layouts #71
Comments
Great suggestion.... some people have previously asked for the same concept: marking some elements as global, so that they are active/visible even after transitioning between pages. Giving some more thought to it, I think there are several ways that this could be done, but my preference would be to implement an approach such that:
Good news -- I have a prototype working of this now! If all goes well with the testing, I might be able to roll this feature out soon for you to try. As for the loboris TFT driver, that is great to hear. Assuming the changes are relatively self-contained (ie. basically a new thanks! |
Would it be possible to display any two pages at the same time? This would even enable swtiching in between different status pages. |
@espHorst -- interesting idea. I have revised my prototype to support selecting [and dynamically changing] the "global page". It is very easy for the user to do this. Basically you create pages as before, but one (or more) page can contain the "global elements". Let's call this the global page.
Is "global page" a good name for this feature/mode? I am open to changing it if you can suggest a more intuitive name. |
Just from a more or less philosophic point of view: what distinguishs a global from a normal page? Would it make sense ( or is it to complicated ) to allow for an arbitrary number ( in an array of pointers ) of pages zo be displayed at the time. Setpagecur just sets array[0] and there could be a function setpage(index, pPage) for setting any of the displayed pages. |
Conceptually there would be little difference in the treatment of a “global” page versus the “current” page. I was mainly attempting to simplify the usage model somewhat, and changes to the API. That said, I see value in extending this concept. Multi-layer ConceptThinking further about the implementation, I see that the current solution could also go a long way to address the request to support pop-up dialogs in #9 (which could offer confirmations or even a reusable pop-up numerical entry page, for example). I suspect the most common use-cases might lend themselves to using 3 “layers” (where a layer is really a reference to a page): back, current and top, though of course it could be generalized further if it made sense. Do you think three might be enough? In this way, a “back” layer could provide the status and “chrome” that could be common between pages. In my Thanks to your suggestion of enabling dynamic changes to these “layers”, we could very easily select a new “top” layer to provide any arbitrary input dialog / modal to the user while the “current” visible layer stays as-is. For example, select The nice thing about this layered approach is that we can show through the other layers, so a data entry or alert box would still reveal the elements dynamically updating in the back and current layers. Furthermore, it would be a simple matter to add a flag per-layer that enables or disables the touch. Therefore, when we present a modal dialog in the top layer, the back and current layers have their touch disabled. Deactivating this top layer would restore the touch on prior layers. I was thinking of calling these additional two pages "layers", but really they are just two extra "pages" that can be shown at the same time as the current "page". Introducing the concept of a "layer" may help clarify the mechanics / restrictions / usage model. Would it make more sense to call these layers? APIThere are a couple things we should keep in mind:
If we restrict it to 3 layers, then the APIs could be tailored to the common use-cases, such as:
If we make the set of layers arbitrary then we could offer:
To present a modal popup dialog, we might:
Perhaps one could even offer a simple wrapper for users that automatically saves/restores the state, so the user just calls:
With all of the above, we could substitute "Page" for "Layer" if it makes more sense. ImplementationBehind the scenes, we could certainly implement this with an array of pages (either pointers or page IDs). One important decision point is whether to include the storage of these layers in the existing GUI structure (wherein I think we would want to limit it to 3 layers), or if we provide a new set of APIs for the user to register their own array of layers/pages. When introducing features like this I have to be mindful of the memory footprint (particularly on SRAM) for Arduino devices. If is incorporated internally, then 3 layers would hopefully consume only ~ 8 bytes for 3 Page IDs and the current/saved per-layer touch enables. Thoughts? |
Here some - maybe silly as I don't have a very deep understanding of some parts of GUIslice - thoughts: A) modal dialog
If we don't need either then just exchanging the current selected page(s) with the modal dialog might work. As long as the display is not cleared you will see the old display content in the background of the modal dialog. B) back and cur page
On each page element you can call "setCurPage" to select which of the individual pages from mX_asPage should be displayed. This concept might even be easier for the user. At the beginning - and maybe for 80% of the users all the time - you don't need to bother with the bottom, cur and top page construct. If you need it, then it is just an element. |
Thanks for the feedback! In general I would prefer to keep the implementation simple yet scalable, so I agree with your thoughts on the modal dialog requirements. I don't think modal stacking is likely to be common, particularly with smaller TFT displays, and the benefit of background updates is marginal. Modal DialogYour suggestion of leaving the main page unrefreshed and drawing the overlay (eg. modal dialog) seems reasonable but we'd have to disable the redraw of background upon needs-full-redraw ( Page within an ElementEmbedding a page within an element is definitely worth exploring further! I'm not sure if you are aware, but I did implement the notion of a "compound element" a long time back (see the So, to avoid any confusion in terminology, let's assume we can create a new Example ApplicationFor the purposes of our discussion, perhaps we can take following example (based loosely on what you provided) in order to ensure I understand your proposal: 2 real pages, a common status bar and a popup dialog.
With the above, I thought we might have:
Page StorageToday, users provide explicit storage for the I was initially thinking that the Layer element might just contain a reference/index to a single page in the global page array. This way the Layer wouldn't need to incorporate page storage within its structure (I'm less keen on having page structures stored in more than one place). As a result, the call to Redraw order would be managed by the user in the order in which elements were added to the root page. From your example:
... it seems like we would be storing multiple pages within each "Layer" element. Would you still have multiple pages also stored in the global page array (registered with In your opinion, do you think it might be too restrictive if we made a Layer element just point to one page (that can be dynamically selected) stored in the global page array (registered by Other notes
From the above, are you are suggesting that we update the clipping region to the element's bounds in order to ensure no overlap? Or that a relative coordinate system is provided to the layer sub-elements? If it sounds like the proposal is closer to what you are envisioning, I'll start to dig through the code to see if there are any gotchas, but it sounds reasonable so far. Thanks again for the great feedback -- I really appreciate the input! |
I'm really impressed about the structured way you approach the new feature. I see that you definitely picked up all the ideas and came up with even more and even better ones. Example ApplicationPerfect! Page / Layer / LayoutLayer is definitely a better word than page. On other gui systems I have seen often the name
Page/Layer/Layout: One name for multiple thingsAt the moment I'm struggling sometimes with the naming page / your proposed name layer / my proposed name layout. They seem to be one and the same name for multiple things.
I propose the following naming:
I propose the following over all structure:
up to here the same as today, now there are new elements coming in.
I think, this is no change in functionality to what you described, I was just thinking about another naming make the architecture more obvious. Page StorageThe tsPage[] array(today) and in the future maybe also the tsLayoutSet[] keeps the pointers to layouts (i. e. pages). Layouts(i.e. pages) themselves are stored as today. Coordinate system
Yes, I think this is a good solution. Like on the Buttons, they already report/work with relative coordinates, don't they? SummaryThere are no incompatible changes to GUIslice, just one (in the future maybe more) element What do you think? |
Here an example how the layoutSets can be used without needing any data memory:
I have successfully compiled this on my STM32F1.
I think that even the layouts themself can be completely defined in flash just
and
from https://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Compound-Literals.html |
I have tried the struct initialization, here just a small example:
and it works. I'm just not sure if it works in plain C (I have here the Arduino C++), literature says it does. Maybe this would be an interesting way to create the layouts. Therefore this could also be a basis for #79 . Would you be interested about discussions towards this direction? |
Thanks a lot for the great suggestions, @espHorst. From a terminology perspective, I think another term I've seen used to describe your Layout might be a pane. Let's stick with layout for now. Note that the your ElementSets are represented today by a ExampleRereading your comments, I believe I understand better what you are proposing with respect to the layoutSets. Taking the scenario I provided earlier, would this concrete example be representative of what you were suggesting?
To keep things simple I would probably start with all Layouts using absolute coordinates, rather than inheriting positioning from the parent container (Stacks / Tabs), but naturally I'll be looking for opportunities to keep this relative if possible. From a rendering perspective, we would begin at the GUI root page/layout. We issue a In this particular "stack" example: When the user wishes to switch between the "Main" and "Config" screens, they wouldn't be using LayoutSet storageYour suggestion regarding the tsLayoutSet variable length array storage without extra memory allocation is definitely worth looking at. Presumably we would still require mutability in the pointer values to enable Popup DialogsIt seems like there could be a couple potential ways to support popup dialogs. The easiest would be to use Alternately. assuming the LayoutSet array of ptrs to Layouts is mutable, it seems like they could also be supported by reserving one array entry (at end) as NULL. When the user wishes to display a popup dialog,
For the moment I'll proceed with the more straightforward Meta-languageI am always open to suggestions for improving ease of use… using struct initialization could be another avenue worth exploring further (perhaps you could open up a separate issue for this?). Note that I had explored something similar when creating the Just a heads-up: I intend to release my initial implementation of the Keyboard / GPIO control (#66) and then can begin a more thorough review of the approach you have outlined here. |
thanks for the well structured example. Mainly based on this example here some findings:
|
I had a look on the ElemCreate*_P() macros. I think this is a very clever way to save ram. Are there any drawbacks in using those macros? Why don't always use macros like this and save ram? On non Arduino ATMEGA chips we could just drop the PROGMEM or just define it as empty. |
@nanokatz deserves the credit for coming up with the original FLASH-based macro concept. One of the interesting challenges in trying to move elements to FLASH was the desire to still support mutable states (such as glowing status, text content, etc.). This involved keeping some mutable flags in the RAM-based While the SRAM footprint is greatly reduced, the main hesitation in using these macros everywhere is that the user code needs to provide the majority of an element's characteristics in the initial Nonetheless, the introduction of the ElemCreate*_P() macros enabled a significant boost to the SRAM efficiency & GUI capabilities for low-memory Arduino devices. Furthermore, the GUIslice Builder (#79) can also generate this arduino_min (FLASH-based) code, so it greatly improves upon the ease of utilizing these macros. |
- This method utilizes the concept of a "Global Page" - Example ex24_ard_tabs has been added - Add API: SetPageGlobal()
Just a quick heads-up that I have now posted the code that I developed for the original "global page" layer strategy referenced in my Oct 27 comment. If interested, please see branch WIP71-GlobalPage and the accompanying example Note that I still intend to explore the more comprehensive layout strategy that espHorst referenced above, but in the meantime I thought I'd post this since there were requests to see the original proof-of-concept. Here is a screenshot from one page of the demo: Obviously the appearance of the tab dialog can be greatly improved -- this was just a proof-of-concept. |
- Implement basic page stack functionality from #71 and #81 - Add example ex24 to show simple tabbed dialog box, base layer and popup - Add example ex25 to show popup - Add `SetPageBase()`, `SetPageOverlay()` to complement `SetPageCur()` - Add `PopupShow()` and `PopupHide()` - Add `SetStackPage()` and `SetStackState()` to permit advanced controls over the page stack - Further redraw optimizations will come later
The basic "Page Stack" implementation has now been integrated into the core library. The code supports an arbitrary number of "pages" in a stack that can be shown concurrently on the "screen" (parameterized to 3 at the moment). The suggested usage model is: "base page", "current page" and "overlay page". This way the API is fully compatible with existing programs, as they will continue to run on the "current page". For example, you can now show:
Example ex24 showing a popup alert on top of a tabbed dialog: After carefully reviewing the great proposals discussed in this issue and testing out the alternate implementations, I decided to proceed with this approach as it appears to deliver the biggest functionality gains for users with almost zero impact to the existing API. While I would prefer to revise the naming applied to "pages", this would have implied a large API naming change. I believe the current implementation will still provide much of the capability that @espHorst has described above (though we don't use relative coordinates). Along with this update, the initial ability to create popup dialog boxes is now available. To show a popup dialog, one creates a "page" for it as before, then simply:
This release also supports modal and modeless dialogs (as controlled by a parameter in Lastly, there is also optional support for enabling each page layer to receive touch events and for enabling background redraw. The latter is useful to prevent updates to underlying pages from "bleeding through" to a popup, for example. If the popup is not placed over dynamically updating controls, then background updates can be enabled allowing other dynamically updated controls to continue to be drawn. Again, a huge thank-you to @espHorst @Grtschnk and @Pconti31 for all of the feedback and suggestions on this feature! |
Thank you for your great software! Compiling and running it on ESP32 with ESP-IDF. Enjoying it a lot. :)
Issue below:
Problem:
I am presenting data on different pages (e.g. Log, Settings, etc..). I also want to display some status information all the time (battery status, wifi connectivity, etc.), regardless of which page is selected. At the current moment, I would need to create a separate 'Status page' and switch to it periodically (or on request), or I would need to create an element multiple times for each page, and update all of them,
Idea:
With both options, the library user needs to be mindful of leaving space for the "Status" related elements (either leaving space inside the page for these elements, or leaving space as the area will be overlapped by a status page). But I think this is not an issue, as it does not differ much from the current situation. After all, we are doing embedded programming and not some HTML ;)
I could imagine possible necessary code changes would relate to issues #9 and #24
I am currently looking at the code, see if I can add this functionality to my fork. Is this functionality that could be added rather easiliy (by myself) or do you think this would require more rework / is not possible?
(I also added drivers/support for loboris TFT driver for ESP32, let me know if you'd like a pull request - currently a bit concious of my coding and git abilities)
The text was updated successfully, but these errors were encountered: