Show specific frame when updating screen (#4264)

* Updated setFrames in Screen.cpp

Added code to attempt to revert back to the same frame that user was on prior to setFrame reload.

* Space added Screen.cpp

* Update Screen.cpp

Make screen to revert to Frame 0 if the originally displayed frame is no longer there.

* Update Screen.cpp

Inserted boolean holdPosition into setFrames to indicate the requirement to stay on the same frame ( if =true) or else it will switch to new frame .
Only Screen::handleStatusUpdate calls with setFrame(true). ( For Node Updates)
All other types of updates call as before setFrame(), so it will change focus as needed.

* Hold position, even if number of frames increases

* Hold position, if handling an outgoing text message

* Update Screen.cpp

* Reverted chnages related to devicestate.has_rx_text_message

* Reset to master

* CannedMessages only handles routing packets when waiting for ACK
Previously, this was calling Screen::setFrames at unexpected times

* Gather position info about screen frames while regenerating

* Make admin module observable
Notify only when relevant. Currently: only to handle remove_nodenum.

* Optionally specify which frame to focus when setFrames runs

* UIFrameEvent uses enum instead of multiple booleans

* Allow modules to request their own frame to be focussed
This is done internally by calling MeshModule::requestFocus()
Easier this way, insteady of passing the info in the UIFrameEvent:
* Modules don't always know whether they should be focussed until after the UIFrameEvent has been raised, in dramFrame
* Don't have to pass reference to module instance as parameter though several methods

* E-Ink screensaver uses FOCUS_PRESERVE
Previously, it had its own basic implementation of this.

* Spelling: regional variant

* trunk

* Fix HAS_SCREEN guarding

* More HAS_SCREEN guarding

---------

Co-authored-by: BIST <77391720+slash-bit@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: slash-bit <v-b2@live.com>
This commit is contained in:
todd-herbert
2024-07-12 11:51:26 +12:00
committed by GitHub
parent df194ca0f0
commit eabec5ae34
10 changed files with 239 additions and 51 deletions

View File

@@ -41,6 +41,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mesh/Channels.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "meshUtils.h"
#include "modules/AdminModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "sleep.h"
@@ -1725,6 +1726,7 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
adminMessageObserver.observe(adminModule);
if (textMessageModule)
textMessageObserver.observe(textMessageModule);
if (inputBroker)
@@ -1953,9 +1955,6 @@ void Screen::setWelcomeFrames()
/// Determine which screensaver frame to use, then set the FrameCallback
void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
{
// Remember current frame, restore position at power-on
uint8_t frameNumber = ui->getUiState()->currentFrame;
// Retain specified frame / overlay callback beyond scope of this method
static FrameCallback screensaverFrame;
static OverlayCallback screensaverOverlay;
@@ -1993,9 +1992,8 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
#endif
// Prepare now for next frame, shown when display wakes
ui->setOverlays(NULL, 0); // Clear overlay
setFrames(); // Return to normal display updates
ui->switchToFrame(frameNumber); // Attempt to return to same frame after power-on
ui->setOverlays(NULL, 0); // Clear overlay
setFrames(FOCUS_PRESERVE); // Return to normal display updates, showing same frame as before screensaver, ideally
// Pick a refresh method, for when display wakes
#ifdef EINK_HASQUIRK_GHOSTING
@@ -2006,9 +2004,13 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
}
#endif
// restore our regular frame list
void Screen::setFrames()
// Regenerate the normal set of frames, focusing a specific frame if requested
// Called when a frame should be added / removed, or custom frames should be cleared
void Screen::setFrames(FrameFocus focus)
{
uint8_t originalPosition = ui->getUiState()->currentFrame;
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
LOG_DEBUG("showing standard frames\n");
showingNormalScreen = true;
@@ -2042,20 +2044,33 @@ void Screen::setFrames()
// is the same offset into the moduleFrames vector
// so that we can invoke the module's callback
for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
normalFrames[numframes++] = drawModuleFrame;
// Draw the module frame, using the hack described above
normalFrames[numframes] = drawModuleFrame;
// Check if the module being drawn has requested focus
// We will honor this request later, if setFrames was triggered by a UIFrameEvent
MeshModule *m = *i;
if (m->isRequestingFocus())
fsi.positions.focusedModule = numframes;
numframes++;
}
LOG_DEBUG("Added modules. numframes: %d\n", numframes);
// If we have a critical fault, show it first
if (error_code)
fsi.positions.fault = numframes;
if (error_code) {
normalFrames[numframes++] = drawCriticalFaultFrame;
focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
}
#ifdef T_WATCH_S3
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
#endif
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
fsi.positions.textMessage = numframes;
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
normalFrames[numframes++] = drawTextMessageFrame;
}
@@ -2070,11 +2085,14 @@ void Screen::setFrames()
//
// Since frames are basic function pointers, we have to use a helper to
// call a method on debugInfo object.
fsi.positions.log = numframes;
normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline;
// call a method on debugInfoScreen object (for more details)
fsi.positions.settings = numframes;
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
fsi.positions.wifi = numframes;
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
if (isWifiAvailable()) {
// call a method on debugInfoScreen object (for more details)
@@ -2082,6 +2100,7 @@ void Screen::setFrames()
}
#endif
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
LOG_DEBUG("Finished building frames. numframes: %d\n", numframes);
ui->setFrames(normalFrames, numframes);
@@ -2095,6 +2114,55 @@ void Screen::setFrames()
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
// just changed)
// Focus on a specific frame, in the frame set we just created
switch (focus) {
case FOCUS_DEFAULT:
ui->switchToFrame(0); // First frame
break;
case FOCUS_FAULT:
ui->switchToFrame(fsi.positions.fault);
break;
case FOCUS_TEXTMESSAGE:
ui->switchToFrame(fsi.positions.textMessage);
break;
case FOCUS_MODULE:
// Whichever frame was marked by MeshModule::requestFocus(), if any
// If no module requested focus, will show the first frame instead
ui->switchToFrame(fsi.positions.focusedModule);
break;
case FOCUS_PRESERVE:
// If we can identify which type of frame "originalPosition" was, can move directly to it in the new frameset
FramesetInfo &oldFsi = this->framesetInfo;
if (originalPosition == oldFsi.positions.log)
ui->switchToFrame(fsi.positions.log);
else if (originalPosition == oldFsi.positions.settings)
ui->switchToFrame(fsi.positions.settings);
else if (originalPosition == oldFsi.positions.wifi)
ui->switchToFrame(fsi.positions.wifi);
// If frame count has decreased
else if (fsi.frameCount < oldFsi.frameCount) {
uint8_t numDropped = oldFsi.frameCount - fsi.frameCount;
// Move n frames backwards
if (numDropped <= originalPosition)
ui->switchToFrame(originalPosition - numDropped);
// Unless that would put us "out of bounds" (< 0)
else
ui->switchToFrame(0);
}
// If we're not sure exactly which frame we were on, at least return to the same frame number
// (node frames; module frames)
else
ui->switchToFrame(originalPosition);
break;
}
// Store the info about this frameset, for future setFrames calls
this->framesetInfo = fsi;
setFastFramerate(); // Draw ASAP
}
@@ -2549,7 +2617,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
switch (arg->getStatusType()) {
case STATUS_TYPE_NODE:
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
setFrames(); // Regen the list of screens
setFrames(FOCUS_PRESERVE); // Regen the list of screen frames (returning to same frame, if possible)
}
nodeDB->updateGUI = false;
break;
@@ -2561,23 +2629,33 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
{
if (showingNormalScreen) {
setFrames(); // Regen the list of screens (will show new text message)
// Outgoing message
if (packet->from == 0)
setFrames(FOCUS_PRESERVE); // Return to same frame (quietly hiding the rx text message frame)
// Incoming message
else
setFrames(FOCUS_TEXTMESSAGE); // Focus on the new message
}
return 0;
}
// Triggered by MeshModules
int Screen::handleUIFrameEvent(const UIFrameEvent *event)
{
if (showingNormalScreen) {
if (event->frameChanged) {
setFrames(); // Regen the list of screens (will show new text message)
} else if (event->needRedraw) {
// Regenerate the frameset, potentially honoring a module's internal requestFocus() call
if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET)
setFrames(FOCUS_MODULE);
// Regenerate the frameset, while attempting to maintain focus on the current frame
else if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND)
setFrames(FOCUS_PRESERVE);
// Don't regenerate the frameset, just re-draw whatever is on screen ASAP
else if (event->action == UIFrameEvent::Action::REDRAW_ONLY)
setFastFramerate();
// TODO: We might also want switch to corresponding frame,
// but we don't know the exact frame number.
// ui->switchToFrame(0);
}
}
return 0;
@@ -2612,6 +2690,24 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0;
}
int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
{
// Note: only selected admin messages notify this observer
// If you wish to handle a new type of message, you should modify AdminModule.cpp first
switch (arg->which_payload_variant) {
// Node removed manually (i.e. via app)
case meshtastic_AdminMessage_remove_by_nodenum_tag:
setFrames(FOCUS_PRESERVE);
break;
// Default no-op, in case the admin message observable gets used by other classes in future
default:
break;
}
return 0;
}
} // namespace graphics
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}