cancel
Showing results for 
Search instead for 
Did you mean: 

How to drive reused containers?

M-Schiller
Associate III

I'm currently working on my second ever TouchGFX project and keep stumbling upon the same issues as in my first one.

I have a battery indicator that I would like to show on (almost) every screen. For this reason I created a container that I can easily drop on every screen and implemented a few relevant methods on this container. This battery indicator is obviously driven by hardware (a FreeRTOS message is sent from the HW task to the TGFX task, if that makes any difference). Now the Model receives these messages and notifies the ModelListener via a virtual method like `virtual void notifyBatteryVoltage(uint8_t);` or similar.

Here is where it gets interesting to me:

  • The base ModelListener cannot implement anything UI-related as it doesn't know anything about the View it will be attached to.
  • It probably should not call anything on the base Presenter as this will couple the two aspects (ModelListener, Presenter) too tightly.
  • Even if I implemented the previous point, the base Presenter doesn't have a reference to its related view.
  • As far as I can tell, the base View is not meant to be extended as it is buried within the framework folder and replaced on e.g. version changes in TGFX.

So far, I have always overridden the `notifyBatteryVoltage` method on (almost) all screens' ModelListeners (i.e. Presenters), and implemented another layer of `notifyBatteryVoltage` on the Views that contain the battery indicator. These Views, in turn, will notify the reused container, i.e. call `setBatteryVoltage` on it.

In this way, I wrote the same code over and over again for about ten different Views and ten related Presenters.

I feel like I am missing something fundamental here. Is there any better way to achieve something like this?

1 ACCEPTED SOLUTION

Accepted Solutions
DSwea.1
Associate III

Apparently, there is no way to do this at present within the TouchGFX MVP model. We're stuck with repeating the same code in every single Presenter and View. For a reference, see this thread:

https://community.st.com/s/question/0D53W00001OPx2sSAD/common-popup-for-all-screens

ST employee Yoann Klein's comment indicates that ST is working on a "base class", but there's no indication of when that may be available or what form it will take. Hopefully, if enough users bring this up, it will accelerate the process.

In the meantime, you might try the workaround that I used. I have a "footer" container with a clock that appears on every screen in my project (20 screens and growing). It's just quite impractical to maintain code for updating the footer using the MVP model. So, what I've done is an "end around", allowing the Model to communicate directly with the footer container, in my case with a static utility class as the intermediary (though this is not entirely necessary).

The idea is that whenever a screen is loaded, the footer constructor "registers" itself (via the static class), making it visible to the Model through the static class:

// ScreenFooter() Constructor
//
ScreenFooter::ScreenFooter() {
 
    GuiUtil::setFooterPtr(this);
}
 
ScreenFooter:: ~ScreenFooter() {
 
    GuiUtil::setFooterPtr(0);
}
 

GuiUtil, in turn, stores the footer reference, and uses it to set date and time in the footer. It also provides functions setFooterDate() and setFooterTime() that can be called from Model.cpp to manage the time settings in the footer:

// setFooterPtr()
//
void GuiUtil::setFooterPtr(ScreenFooter* footer) {
 
    screenFooter = footer;
    if (screenFooter) {
        uint8_t hour, minute, second;
        getRtcTime(hour, minute, second);
        screenFooter->setTime(hour, minute, second);
 
        uint8_t month, day, weekday;
        uint16_t year;
        getRtcDate(month, day, year, weekday);
        screenFooter->setDate(month, day, year);
    }
}
 
// setFooterDate()
//
void GuiUtil::setFooterDate(uint8_t month, uint8_t day, uint16_t year) {
 
    if (screenFooter) {
        screenFooter->setDate(month, day, year);
    }
}
 
// setFooterTime()
//
void GuiUtil::setFooterTime(uint8_t hour, uint8_t minute, uint8_t second) {
    if (screenFooter) {
        screenFooter->setTime(hour, minute, second);
    }
}
 

Note that the call to set the footer pointer to 0 in the destructor ScreenFooter:: ~ScreenFooter() ensures that calls to set the date and time when a footer is not present will cause no issues.

This solution is a general means of working around the issue you describe and can be used in other situations where a container is present in every screen, but one cannot afford the overhead of maintaining copies of the same code in every presenter/view.

If you think about it, when one is following the traditional C++ method of declaring functions in .hpp and implementing in .cpp, then you'll need to edit/insert sections in xxPresenter.hpp, xxPresenter.cpp, xxView.hpp and xxView.cpp. If you have 20 screens, that's 80 places you have to edit (and you can't simply copy and paste since each insertion has to be edited to prefix the class name).

Let's hope the ST team comes up with a good solution to this issue. I suspect there are many users who need it since it's quite common to have a common element on all screens in a project.

View solution in original post

11 REPLIES 11
Florian Moser
Senior

I need the exact thing right about now!

Hopefully there's a solution to this.

@Martin KJELDSEN​ 

DSwea.1
Associate III

Apparently, there is no way to do this at present within the TouchGFX MVP model. We're stuck with repeating the same code in every single Presenter and View. For a reference, see this thread:

https://community.st.com/s/question/0D53W00001OPx2sSAD/common-popup-for-all-screens

ST employee Yoann Klein's comment indicates that ST is working on a "base class", but there's no indication of when that may be available or what form it will take. Hopefully, if enough users bring this up, it will accelerate the process.

In the meantime, you might try the workaround that I used. I have a "footer" container with a clock that appears on every screen in my project (20 screens and growing). It's just quite impractical to maintain code for updating the footer using the MVP model. So, what I've done is an "end around", allowing the Model to communicate directly with the footer container, in my case with a static utility class as the intermediary (though this is not entirely necessary).

The idea is that whenever a screen is loaded, the footer constructor "registers" itself (via the static class), making it visible to the Model through the static class:

// ScreenFooter() Constructor
//
ScreenFooter::ScreenFooter() {
 
    GuiUtil::setFooterPtr(this);
}
 
ScreenFooter:: ~ScreenFooter() {
 
    GuiUtil::setFooterPtr(0);
}
 

GuiUtil, in turn, stores the footer reference, and uses it to set date and time in the footer. It also provides functions setFooterDate() and setFooterTime() that can be called from Model.cpp to manage the time settings in the footer:

// setFooterPtr()
//
void GuiUtil::setFooterPtr(ScreenFooter* footer) {
 
    screenFooter = footer;
    if (screenFooter) {
        uint8_t hour, minute, second;
        getRtcTime(hour, minute, second);
        screenFooter->setTime(hour, minute, second);
 
        uint8_t month, day, weekday;
        uint16_t year;
        getRtcDate(month, day, year, weekday);
        screenFooter->setDate(month, day, year);
    }
}
 
// setFooterDate()
//
void GuiUtil::setFooterDate(uint8_t month, uint8_t day, uint16_t year) {
 
    if (screenFooter) {
        screenFooter->setDate(month, day, year);
    }
}
 
// setFooterTime()
//
void GuiUtil::setFooterTime(uint8_t hour, uint8_t minute, uint8_t second) {
    if (screenFooter) {
        screenFooter->setTime(hour, minute, second);
    }
}
 

Note that the call to set the footer pointer to 0 in the destructor ScreenFooter:: ~ScreenFooter() ensures that calls to set the date and time when a footer is not present will cause no issues.

This solution is a general means of working around the issue you describe and can be used in other situations where a container is present in every screen, but one cannot afford the overhead of maintaining copies of the same code in every presenter/view.

If you think about it, when one is following the traditional C++ method of declaring functions in .hpp and implementing in .cpp, then you'll need to edit/insert sections in xxPresenter.hpp, xxPresenter.cpp, xxView.hpp and xxView.cpp. If you have 20 screens, that's 80 places you have to edit (and you can't simply copy and paste since each insertion has to be edited to prefix the class name).

Let's hope the ST team comes up with a good solution to this issue. I suspect there are many users who need it since it's quite common to have a common element on all screens in a project.

Thank you @DSwea.1​ for your response. This seems like a manageable workaround and, while dancing around the framework and its ModelListener infrastructure, it is a good solution for the issues I am facing.

Same here!

Hi guys,

It's correct that the designer team is working on introducing a Base Class concept. If you search this community, i shared an example of this a while ago (maybe 1y+). You can see the architecture here where concrete views and presenters can inherit from ones that have shared functionality for a particular "group". Check it out. It requires modifying the generated code, so i suggested that some script (unless you want to wait) be done to make some basic modifications post code-generation.

Let me know if you can't find it.

/Martin

Yohann KLEIN (ST) posted a comment about work on the base class 2 months ago on the following thread (which refers to the same topic):

https://community.st.com/s/question/0D53W00001OPx2sSAD/common-popup-for-all-screens

But he failed to mention that you'd posted about your initial architecture 1 year ago (before I was using TGFX). If I'd known about it, I might have looked into the possibility of incorporating it. But I'm not crazy about the idea of writing scripts to modify generated code to implement an early version of an architecture that will surely be different when finally released.

And, in any event, I'm contracted for a project with a deadline that at this point does not allow time for exploring this possibility, and my workaround is doing well.

I'm still interested in looking at the post you refer to, to get an idea of what we may expect when it's released. But, as I'm not exactly sure what to search for, I'm curious as to why you suggest that we search for your post rather than providing it directly.

I understand. It's a dumb solution to modify generated code, but since the designer overwrites any changes you make, it can't be helped - For now.

Here's the post - It's the original architecture i pitched to the designer team back then. I'm sure they'll improve upon it and make it easy to use shared functionality/ui from the designer.

Example: Grouping of Views with Common Functionality. (st.com)

There might also be some useful discussions in that thread - I remember a few people opted for the post-gen-script solution.

Hope it helps.

/Martin

I just noticed that i made the post ~2 years ago - yikes. So much for "fast track backlogs" :)

Thank you for sharing this link. For now, I've had good results manually overwriting framework files (e.g. Middlewares/ST/framework/include/mvp/...) and see no real reason to automate overwriting those. It only hurts a little when I upgrade TGFX or someone initially checks out the projects' code - but the build breaks so the issue can be easily found.