mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-27 21:20:33 +00:00
* TwoButtonExtened mirrors TwoButton but added joystick functionality * basic ui navigation with a joystick settings->joystick.enabled setting added and SETTINGS_VERSION incremented by one in InkHUD/Persistence.h in seeed_wio_tracker_L1_eink/nicheGraphics.h enable joystick and disable "Next Tile" menu item in implement prevTile and prevApplet functions in InkHUD/WindowManager.h,cpp and InkHUD/InkHUD.h,cpp onStickCenterShort, onStickCenterLong, onStickUp, onStickDown, onStickLeft, and onStickRight functions added to: - InkHUD/InkHUD.h,cpp - InkHUD/Events.h,cpp - InkHUD/Applet.h change navigation actions in InkHUD/Events.cpp events based on whether the joystick is enabled or not in seeed_wio_tracker_L1_eink/nicheGraphics.h connect joystick events to the new joystick handler functions * handle joystick input in NotificationApplet and TipsApplet Both the joystick center short press and the user button short press can be used to advance through the Tips applet. dismiss notifications with any joystick input * MenuApplet controls allows menu navigation including a back button * add AlignStickApplet for aligning the joystick with the screen add joystick.aligned and joystick.alignment to InkHUD/Persistence.h for storing alignment status and relative angle create AlignStick applet that prompts the user for a joystick input and rotates the controls to align with the screen AlignStick applet is run after the tips applet if the joystick is enabled and not aligned add menu item for opening the AlignStick applet * update tips applet with joystick controls * format InkHUD additions * fix stroke consistency when resizing joystick graphic * tweak button tips for order consistency * increase joystick debounce * fix comments * remove unnecessary '+' * remap joystick controls to match standard inkHUD behavior Input with a joystick now behaves as follows User Button (joystick center): - short press in applet -> opens menu - long press in applet -> opens menu - short press in menu -> selects - long press in menu -> selects Exit Button: - short press in applet -> switches tile - long press in applet -> nothing for now - short press in menu -> closes menu - long press in menu -> nothing for now --------- Co-authored-by: scobert <scobert57@gmail.com> Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
632 lines
25 KiB
C++
632 lines
25 KiB
C++
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
|
|
|
#include "./WindowManager.h"
|
|
|
|
#include "./Applets/System/AlignStick/AlignStickApplet.h"
|
|
#include "./Applets/System/BatteryIcon/BatteryIconApplet.h"
|
|
#include "./Applets/System/Logo/LogoApplet.h"
|
|
#include "./Applets/System/Menu/MenuApplet.h"
|
|
#include "./Applets/System/Notification/NotificationApplet.h"
|
|
#include "./Applets/System/Pairing/PairingApplet.h"
|
|
#include "./Applets/System/Placeholder/PlaceholderApplet.h"
|
|
#include "./Applets/System/Tips/TipsApplet.h"
|
|
#include "./SystemApplet.h"
|
|
|
|
using namespace NicheGraphics;
|
|
|
|
InkHUD::WindowManager::WindowManager()
|
|
{
|
|
// Convenient references
|
|
inkhud = InkHUD::getInstance();
|
|
settings = &inkhud->persistence->settings;
|
|
}
|
|
|
|
// Register a user applet with InkHUD
|
|
// This is called in setupNicheGraphics()
|
|
// This should be the only time that specific user applets are mentioned in the code
|
|
// If a user applet is not added with this method, its code should not be built
|
|
// Call before begin
|
|
void InkHUD::WindowManager::addApplet(const char *name, Applet *a, bool defaultActive, bool defaultAutoshow, uint8_t onTile)
|
|
{
|
|
inkhud->userApplets.push_back(a);
|
|
|
|
// If requested, mark in settings that this applet should be active by default
|
|
// This means that it will be available for the user to cycle to with short-press of the button
|
|
// This is the default state only: user can activate or deactivate applets through the menu.
|
|
// User's choice of active applets is stored in settings, and will be honored instead of these defaults, if present
|
|
if (defaultActive)
|
|
settings->userApplets.active[inkhud->userApplets.size() - 1] = true;
|
|
|
|
// If requested, mark in settings that this applet should "autoshow" by default
|
|
// This means that the applet will be automatically brought to foreground when it has new data to show
|
|
// This is the default state only: user can select which applets have this behavior through the menu
|
|
// User's selection is stored in settings, and will be honored instead of these defaults, if present
|
|
if (defaultAutoshow)
|
|
settings->userApplets.autoshow[inkhud->userApplets.size() - 1] = true;
|
|
|
|
// If specified, mark this as the default applet for a given tile index
|
|
// Used only to avoid placeholder applet "out of the box", when default settings have more than one tile
|
|
if (onTile != (uint8_t)-1)
|
|
settings->userTiles.displayedUserApplet[onTile] = inkhud->userApplets.size() - 1;
|
|
|
|
// The label that will be show in the applet selection menu, on the device
|
|
a->name = name;
|
|
}
|
|
|
|
// Initial configuration at startup
|
|
void InkHUD::WindowManager::begin()
|
|
{
|
|
assert(inkhud);
|
|
|
|
createSystemApplets();
|
|
placeSystemTiles();
|
|
|
|
createUserApplets();
|
|
createUserTiles();
|
|
placeUserTiles();
|
|
assignUserAppletsToTiles();
|
|
refocusTile();
|
|
}
|
|
|
|
// Focus on a different tile
|
|
// The "focused tile" is the one which cycles applets on user button press,
|
|
// and the one where the menu will be displayed
|
|
void InkHUD::WindowManager::nextTile()
|
|
{
|
|
// Close the menu applet if open
|
|
// We don't *really* want to do this, but it simplifies handling *a lot*
|
|
MenuApplet *menu = (MenuApplet *)inkhud->getSystemApplet("Menu");
|
|
bool menuWasOpen = false;
|
|
if (menu->isForeground()) {
|
|
menu->sendToBackground();
|
|
menuWasOpen = true;
|
|
}
|
|
|
|
// Swap to next tile
|
|
settings->userTiles.focused = (settings->userTiles.focused + 1) % settings->userTiles.count;
|
|
|
|
// Make sure that we don't get stuck on the placeholder tile
|
|
refocusTile();
|
|
|
|
if (menuWasOpen)
|
|
menu->show(userTiles.at(settings->userTiles.focused));
|
|
|
|
// Ask the tile to draw an indicator showing which tile is now focused
|
|
// Requests a render
|
|
// We only draw this indicator if the device uses an aux button to switch tiles.
|
|
// Assume aux button is used to switch tiles if the "next tile" menu item is hidden
|
|
if (!settings->optionalMenuItems.nextTile)
|
|
userTiles.at(settings->userTiles.focused)->requestHighlight();
|
|
}
|
|
|
|
// Focus on a different tile but decrement index
|
|
void InkHUD::WindowManager::prevTile()
|
|
{
|
|
// Close the menu applet if open
|
|
// We don't *really* want to do this, but it simplifies handling *a lot*
|
|
MenuApplet *menu = (MenuApplet *)inkhud->getSystemApplet("Menu");
|
|
bool menuWasOpen = false;
|
|
if (menu->isForeground()) {
|
|
menu->sendToBackground();
|
|
menuWasOpen = true;
|
|
}
|
|
|
|
// Swap to next tile
|
|
if (settings->userTiles.focused == 0)
|
|
settings->userTiles.focused = settings->userTiles.count - 1;
|
|
else
|
|
settings->userTiles.focused--;
|
|
|
|
// Make sure that we don't get stuck on the placeholder tile
|
|
refocusTile();
|
|
|
|
if (menuWasOpen)
|
|
menu->show(userTiles.at(settings->userTiles.focused));
|
|
|
|
// Ask the tile to draw an indicator showing which tile is now focused
|
|
// Requests a render
|
|
// We only draw this indicator if the device uses an aux button to switch tiles.
|
|
// Assume aux button is used to switch tiles if the "next tile" menu item is hidden
|
|
if (!settings->optionalMenuItems.nextTile)
|
|
userTiles.at(settings->userTiles.focused)->requestHighlight();
|
|
}
|
|
|
|
// Show the menu (on the the focused tile)
|
|
// The applet previously displayed there will be restored once the menu closes
|
|
void InkHUD::WindowManager::openMenu()
|
|
{
|
|
MenuApplet *menu = (MenuApplet *)inkhud->getSystemApplet("Menu");
|
|
menu->show(userTiles.at(settings->userTiles.focused));
|
|
}
|
|
|
|
// Bring the AlignStick applet to the foreground
|
|
void InkHUD::WindowManager::openAlignStick()
|
|
{
|
|
if (settings->joystick.enabled) {
|
|
AlignStickApplet *alignStick = (AlignStickApplet *)inkhud->getSystemApplet("AlignStick");
|
|
alignStick->bringToForeground();
|
|
}
|
|
}
|
|
|
|
// On the currently focussed tile: cycle to the next available user applet
|
|
// Applets available for this must be activated, and not already displayed on another tile
|
|
void InkHUD::WindowManager::nextApplet()
|
|
{
|
|
Tile *t = userTiles.at(settings->userTiles.focused);
|
|
|
|
// Abort if zero applets available
|
|
// nullptr means WindowManager::refocusTile determined that there were no available applets
|
|
if (!t->getAssignedApplet())
|
|
return;
|
|
|
|
// Find the index of the applet currently shown on the tile
|
|
uint8_t appletIndex = -1;
|
|
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++) {
|
|
if (inkhud->userApplets.at(i) == t->getAssignedApplet()) {
|
|
appletIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Confirm that we did find the applet
|
|
assert(appletIndex != (uint8_t)-1);
|
|
|
|
// Iterate forward through the WindowManager::applets, looking for the next valid applet
|
|
Applet *nextValidApplet = nullptr;
|
|
for (uint8_t i = 1; i < inkhud->userApplets.size(); i++) {
|
|
uint8_t newAppletIndex = (appletIndex + i) % inkhud->userApplets.size();
|
|
Applet *a = inkhud->userApplets.at(newAppletIndex);
|
|
|
|
// Looking for an applet which is active (enabled by user), but currently in background
|
|
if (a->isActive() && !a->isForeground()) {
|
|
nextValidApplet = a;
|
|
settings->userTiles.displayedUserApplet[settings->userTiles.focused] =
|
|
newAppletIndex; // Remember this setting between boots!
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Confirm that we found another applet
|
|
if (!nextValidApplet)
|
|
return;
|
|
|
|
// Hide old applet, show new applet
|
|
t->getAssignedApplet()->sendToBackground();
|
|
t->assignApplet(nextValidApplet);
|
|
nextValidApplet->bringToForeground();
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST); // bringToForeground already requested, but we're manually forcing FAST
|
|
}
|
|
|
|
// On the currently focussed tile: cycle to the previous available user applet
|
|
// Applets available for this must be activated, and not already displayed on another tile
|
|
void InkHUD::WindowManager::prevApplet()
|
|
{
|
|
Tile *t = userTiles.at(settings->userTiles.focused);
|
|
|
|
// Abort if zero applets available
|
|
// nullptr means WindowManager::refocusTile determined that there were no available applets
|
|
if (!t->getAssignedApplet())
|
|
return;
|
|
|
|
// Find the index of the applet currently shown on the tile
|
|
uint8_t appletIndex = -1;
|
|
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++) {
|
|
if (inkhud->userApplets.at(i) == t->getAssignedApplet()) {
|
|
appletIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Confirm that we did find the applet
|
|
assert(appletIndex != (uint8_t)-1);
|
|
|
|
// Iterate forward through the WindowManager::applets, looking for the previous valid applet
|
|
Applet *prevValidApplet = nullptr;
|
|
for (uint8_t i = 1; i < inkhud->userApplets.size(); i++) {
|
|
uint8_t newAppletIndex = 0;
|
|
if (i > appletIndex)
|
|
newAppletIndex = inkhud->userApplets.size() + appletIndex - i;
|
|
else
|
|
newAppletIndex = (appletIndex - i);
|
|
Applet *a = inkhud->userApplets.at(newAppletIndex);
|
|
|
|
// Looking for an applet which is active (enabled by user), but currently in background
|
|
if (a->isActive() && !a->isForeground()) {
|
|
prevValidApplet = a;
|
|
settings->userTiles.displayedUserApplet[settings->userTiles.focused] =
|
|
newAppletIndex; // Remember this setting between boots!
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Confirm that we found another applet
|
|
if (!prevValidApplet)
|
|
return;
|
|
|
|
// Hide old applet, show new applet
|
|
t->getAssignedApplet()->sendToBackground();
|
|
t->assignApplet(prevValidApplet);
|
|
prevValidApplet->bringToForeground();
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST); // bringToForeground already requested, but we're manually forcing FAST
|
|
}
|
|
|
|
// Rotate the display image by 90 degrees
|
|
void InkHUD::WindowManager::rotate()
|
|
{
|
|
settings->rotation = (settings->rotation + 1) % 4;
|
|
changeLayout();
|
|
}
|
|
|
|
// Change whether the battery icon is displayed (top right corner)
|
|
// Don't toggle the OptionalFeatures value before calling this, our method handles it internally
|
|
void InkHUD::WindowManager::toggleBatteryIcon()
|
|
{
|
|
BatteryIconApplet *batteryIcon = (BatteryIconApplet *)inkhud->getSystemApplet("BatteryIcon");
|
|
|
|
settings->optionalFeatures.batteryIcon = !settings->optionalFeatures.batteryIcon; // Preserve the change between boots
|
|
|
|
// Show or hide the applet
|
|
if (settings->optionalFeatures.batteryIcon)
|
|
batteryIcon->bringToForeground();
|
|
else
|
|
batteryIcon->sendToBackground();
|
|
|
|
// Force-render
|
|
// - redraw all applets
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
|
|
}
|
|
|
|
// Perform necessary reconfiguration when user changes number of tiles (or rotation) at run-time
|
|
// Call after changing settings.tiles.count
|
|
void InkHUD::WindowManager::changeLayout()
|
|
{
|
|
// Recreate tiles
|
|
// - correct number created, from settings.userTiles.count
|
|
// - set dimension and position of tiles, according to layout
|
|
createUserTiles();
|
|
placeUserTiles();
|
|
placeSystemTiles();
|
|
|
|
// Handle fewer tiles
|
|
// - background any applets which have lost their tile
|
|
findOrphanApplets();
|
|
|
|
// Handle more tiles
|
|
// - create extra applets
|
|
// - assign them to the new extra tiles
|
|
createUserApplets();
|
|
assignUserAppletsToTiles();
|
|
|
|
// Focus a valid tile
|
|
// - info: focused tile is the one which cycles applets when user button pressed
|
|
// - may now be out of bounds if tile count has decreased
|
|
refocusTile();
|
|
|
|
// Restore menu
|
|
// - its tile was just destroyed and recreated (createUserTiles)
|
|
// - its assignment was cleared (assignUserAppletsToTiles)
|
|
MenuApplet *menu = (MenuApplet *)inkhud->getSystemApplet("Menu");
|
|
if (menu->isForeground()) {
|
|
Tile *ft = userTiles.at(settings->userTiles.focused);
|
|
menu->show(ft);
|
|
}
|
|
|
|
// Force-render
|
|
// - redraw all applets
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
|
|
}
|
|
|
|
// Perform necessary reconfiguration when user activates or deactivates applets at run-time
|
|
// Call after changing settings.userApplets.active
|
|
void InkHUD::WindowManager::changeActivatedApplets()
|
|
{
|
|
MenuApplet *menu = (MenuApplet *)inkhud->getSystemApplet("Menu");
|
|
|
|
assert(menu->isForeground());
|
|
|
|
// Activate or deactivate applets
|
|
// - to match value of settings.userApplets.active
|
|
createUserApplets();
|
|
|
|
// Assign the placeholder applet
|
|
// - if applet was foreground on a tile when deactivated, swap it with a placeholder
|
|
// - placeholder applet may be assigned to multiple tiles, if needed
|
|
assignUserAppletsToTiles();
|
|
|
|
// Ensure focused tile has a valid applet
|
|
// - if focused tile's old applet was deactivated, give it a real applet, instead of placeholder
|
|
// - reason: nextApplet() won't cycle applets if placeholder is shown
|
|
refocusTile();
|
|
|
|
// Restore menu
|
|
// - its assignment was cleared (assignUserAppletsToTiles)
|
|
if (menu->isForeground()) {
|
|
Tile *ft = userTiles.at(settings->userTiles.focused);
|
|
menu->show(ft);
|
|
}
|
|
|
|
// Force-render
|
|
// - redraw all applets
|
|
inkhud->forceUpdate(EInk::UpdateTypes::FAST);
|
|
}
|
|
|
|
// Some applets may be permitted to bring themselves to foreground, to show new data
|
|
// User selects which applets have this permission via on-screen menu
|
|
// Priority is determined by the order which applets were added to WindowManager in setupNicheGraphics
|
|
// We will only autoshow one applet
|
|
void InkHUD::WindowManager::autoshow()
|
|
{
|
|
// Don't perform autoshow if a system applet has exclusive use of the display right now
|
|
// Note: lockRequests prevents autoshow attempting to hide menuApplet
|
|
for (SystemApplet *sa : inkhud->systemApplets) {
|
|
if (sa->lockRendering || sa->lockRequests)
|
|
return;
|
|
}
|
|
|
|
NotificationApplet *notificationApplet = (NotificationApplet *)inkhud->getSystemApplet("Notification");
|
|
|
|
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++) {
|
|
Applet *a = inkhud->userApplets.at(i);
|
|
if (a->wantsToAutoshow() // Applet wants to become foreground
|
|
&& !a->isForeground() // Not yet foreground
|
|
&& settings->userApplets.autoshow[i]) // User permits this applet to autoshow
|
|
{
|
|
Tile *t = userTiles.at(settings->userTiles.focused); // Get focused tile
|
|
t->getAssignedApplet()->sendToBackground(); // Background whichever applet is already on the tile
|
|
t->assignApplet(a); // Assign our new applet to tile
|
|
a->bringToForeground(); // Foreground our new applet
|
|
|
|
// Check if autoshown applet shows the same information as notification intended to
|
|
// In this case, we can dismiss the notification before it is shown
|
|
// Note: we are re-running the approval process. This normally occurs when the notification is initially triggered.
|
|
if (notificationApplet->isForeground() && !notificationApplet->isApproved())
|
|
notificationApplet->dismiss();
|
|
|
|
break; // One autoshow only! Avoid conflicts
|
|
}
|
|
}
|
|
}
|
|
|
|
// A collection of any user tiles which do not have a valid user applet
|
|
// This can occur in various situations, such as when a user enables fewer applets than their layout has tiles
|
|
// The tiles (and which regions the occupy) are private information of the window manager
|
|
// The renderer needs to know which regions (if any) are empty,
|
|
// in order to fill them with a "placeholder" pattern.
|
|
// -- There may be a tidier way to accomplish this --
|
|
std::vector<InkHUD::Tile *> InkHUD::WindowManager::getEmptyTiles()
|
|
{
|
|
std::vector<Tile *> empty;
|
|
|
|
for (Tile *t : userTiles) {
|
|
Applet *a = t->getAssignedApplet();
|
|
if (!a || !a->isActive())
|
|
empty.push_back(t);
|
|
}
|
|
|
|
return empty;
|
|
}
|
|
|
|
// Complete the configuration of one newly instantiated system applet
|
|
// - link it with its tile
|
|
// Unlike user applets, most system applets have their own unique tile;
|
|
// the only reference to this tile is held by the system applet itself.
|
|
// - give it a name
|
|
// A system applet's name is its unique identifier.
|
|
// The name is our only reference to specific system applets, via InkHUD->getSystemApplet
|
|
// - add it to the list of system applets
|
|
|
|
void InkHUD::WindowManager::addSystemApplet(const char *name, SystemApplet *applet, Tile *tile)
|
|
{
|
|
// Some system applets might not have their own tile (e.g. menu, placeholder)
|
|
if (tile)
|
|
tile->assignApplet(applet);
|
|
|
|
applet->name = name;
|
|
inkhud->systemApplets.push_back(applet);
|
|
}
|
|
|
|
// Create the "system applets"
|
|
// These handle things like bootscreen, pop-up notifications etc
|
|
// They are processed separately from the user applets, because they might need to do "weird things"
|
|
void InkHUD::WindowManager::createSystemApplets()
|
|
{
|
|
addSystemApplet("Logo", new LogoApplet, new Tile);
|
|
addSystemApplet("Pairing", new PairingApplet, new Tile);
|
|
addSystemApplet("Tips", new TipsApplet, new Tile);
|
|
if (settings->joystick.enabled)
|
|
addSystemApplet("AlignStick", new AlignStickApplet, new Tile);
|
|
|
|
addSystemApplet("Menu", new MenuApplet, nullptr);
|
|
|
|
// Battery and notifications *behind* the menu
|
|
addSystemApplet("Notification", new NotificationApplet, new Tile);
|
|
addSystemApplet("BatteryIcon", new BatteryIconApplet, new Tile);
|
|
|
|
// Special handling only, via Rendering::renderPlaceholders
|
|
addSystemApplet("Placeholder", new PlaceholderApplet, nullptr);
|
|
|
|
// System applets are always active
|
|
for (SystemApplet *sa : inkhud->systemApplets)
|
|
sa->activate();
|
|
}
|
|
|
|
// Set the position and size of most system applets
|
|
// Most system applets have their own tile. We manually set the region this tile occupies
|
|
void InkHUD::WindowManager::placeSystemTiles()
|
|
{
|
|
inkhud->getSystemApplet("Logo")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
|
|
inkhud->getSystemApplet("Pairing")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
|
|
inkhud->getSystemApplet("Tips")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
|
|
if (settings->joystick.enabled)
|
|
inkhud->getSystemApplet("AlignStick")->getTile()->setRegion(0, 0, inkhud->width(), inkhud->height());
|
|
|
|
inkhud->getSystemApplet("Notification")->getTile()->setRegion(0, 0, inkhud->width(), 20);
|
|
|
|
const uint16_t batteryIconHeight = Applet::getHeaderHeight() - 2 - 2;
|
|
const uint16_t batteryIconWidth = batteryIconHeight * 1.8;
|
|
inkhud->getSystemApplet("BatteryIcon")
|
|
->getTile()
|
|
->setRegion(inkhud->width() - batteryIconWidth, // x
|
|
2, // y
|
|
batteryIconWidth, // width
|
|
batteryIconHeight); // height
|
|
|
|
// Note: the tiles of placeholder and menu applets are manipulated specially
|
|
// - menuApplet borrows user tiles
|
|
// - placeholder applet is temporarily assigned to each user tile of WindowManager::getEmptyTiles
|
|
}
|
|
|
|
// Activate or deactivate user applets, to match settings
|
|
// Called at boot, or after run-time config changes via menu
|
|
// Note: this method does not instantiate the applets;
|
|
// this is done in setupNicheGraphics, when passing to InkHUD::addApplet
|
|
void InkHUD::WindowManager::createUserApplets()
|
|
{
|
|
// Deactivate and remove any no-longer-needed applets
|
|
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++) {
|
|
Applet *a = inkhud->userApplets.at(i);
|
|
|
|
// If the applet is active, but settings say it shouldn't be:
|
|
// - run applet's custom deactivation code
|
|
// - mark applet as inactive (internally)
|
|
if (a->isActive() && !settings->userApplets.active[i])
|
|
a->deactivate();
|
|
}
|
|
|
|
// Activate and add any new applets
|
|
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++) {
|
|
|
|
// If not activated, but it now should be:
|
|
// - run applet's custom activation code
|
|
// - mark applet as active (internally)
|
|
if (!inkhud->userApplets.at(i)->isActive() && settings->userApplets.active[i])
|
|
inkhud->userApplets.at(i)->activate();
|
|
}
|
|
}
|
|
|
|
// Creates the tiles which will host user applets
|
|
// The amount of these is controlled by the user, via "layout" option in the InkHUD menu
|
|
void InkHUD::WindowManager::createUserTiles()
|
|
{
|
|
// Delete any tiles which currently exist
|
|
for (Tile *t : userTiles)
|
|
delete t;
|
|
userTiles.clear();
|
|
|
|
// Create new tiles
|
|
for (uint8_t i = 0; i < settings->userTiles.count; i++) {
|
|
Tile *t = new Tile;
|
|
userTiles.push_back(t);
|
|
}
|
|
}
|
|
|
|
// Calculate the display region occupied by each tile
|
|
// This determines how pixels are translated from "relative" applet-space to "absolute" windowmanager-space
|
|
// The size and position depend on the amount of tiles the user prefers, set by the "layout" option
|
|
void InkHUD::WindowManager::placeUserTiles()
|
|
{
|
|
for (uint8_t i = 0; i < userTiles.size(); i++)
|
|
userTiles.at(i)->setRegion(settings->userTiles.count, i);
|
|
}
|
|
|
|
// Link "foreground" user applets with tiles
|
|
// Which applet should be *initially* shown on a tile?
|
|
// This initial state changes once WindowManager::nextApplet is called.
|
|
// Performed at startup, or during certain run-time reconfigurations (e.g number of tiles)
|
|
// This state of "which applets are foreground" is preserved between reboots, but the value needs validating at startup.
|
|
void InkHUD::WindowManager::assignUserAppletsToTiles()
|
|
{
|
|
// Each user tile
|
|
for (uint8_t i = 0; i < userTiles.size(); i++) {
|
|
Tile *t = userTiles.at(i);
|
|
|
|
// Check whether tile can display the previously shown applet again
|
|
uint8_t oldIndex = settings->userTiles.displayedUserApplet[i]; // Previous index in WindowManager::userApplets
|
|
bool canRestore = true;
|
|
if (oldIndex > inkhud->userApplets.size() - 1) // Check if old index is now out of bounds
|
|
canRestore = false;
|
|
else if (!settings->userApplets.active[oldIndex]) // Check that old applet is still activated
|
|
canRestore = false;
|
|
else { // Check that the old applet isn't now shown already on a different tile
|
|
for (uint8_t i2 = 0; i2 < i; i2++) {
|
|
if (settings->userTiles.displayedUserApplet[i2] == oldIndex) {
|
|
canRestore = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore previously shown applet if possible,
|
|
// otherwise assign nullptr, which will render specially using placeholderApplet
|
|
if (canRestore) {
|
|
Applet *a = inkhud->userApplets.at(oldIndex);
|
|
t->assignApplet(a);
|
|
a->bringToForeground();
|
|
} else {
|
|
t->assignApplet(nullptr);
|
|
settings->userTiles.displayedUserApplet[i] = -1; // Update settings: current tile has no valid applet
|
|
}
|
|
}
|
|
}
|
|
|
|
// During layout changes, our focused tile setting can become invalid
|
|
// This method identifies that situation and corrects for it
|
|
void InkHUD::WindowManager::refocusTile()
|
|
{
|
|
// Validate "focused tile" setting
|
|
// - info: focused tile responds to button presses: applet cycling, menu, etc
|
|
// - if number of tiles changed, might now be out of index
|
|
if (settings->userTiles.focused >= userTiles.size())
|
|
settings->userTiles.focused = 0;
|
|
|
|
// Give "focused tile" a valid applet
|
|
// - scan for another valid applet, which we can addSubstitution
|
|
// - reason: nextApplet() won't cycle if no applet is assigned
|
|
Tile *focusedTile = userTiles.at(settings->userTiles.focused);
|
|
if (!focusedTile->getAssignedApplet()) {
|
|
// Search for available applets
|
|
for (uint8_t i = 0; i < inkhud->userApplets.size(); i++) {
|
|
Applet *a = inkhud->userApplets.at(i);
|
|
if (a->isActive() && !a->isForeground()) {
|
|
// Found a suitable applet
|
|
// Assign it to the focused tile
|
|
focusedTile->assignApplet(a);
|
|
a->bringToForeground();
|
|
settings->userTiles.displayedUserApplet[settings->userTiles.focused] = i; // Record change: persist after reboot
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Seach for any applets which believe they are foreground, but no longer have a valid tile
|
|
// Tidies up after layout changes at runtime
|
|
void InkHUD::WindowManager::findOrphanApplets()
|
|
{
|
|
for (uint8_t ia = 0; ia < inkhud->userApplets.size(); ia++) {
|
|
Applet *a = inkhud->userApplets.at(ia);
|
|
|
|
// Applet doesn't believe it is displayed: not orphaned
|
|
if (!a->isForeground())
|
|
continue;
|
|
|
|
// Check each tile, to see if anyone claims this applet
|
|
bool foundOwner = false;
|
|
for (uint8_t it = 0; it < userTiles.size(); it++) {
|
|
Tile *t = userTiles.at(it);
|
|
// A tile claims this applet: not orphaned
|
|
if (t->getAssignedApplet() == a) {
|
|
foundOwner = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Orphan found
|
|
// Tell the applet that no tile is currently displaying it
|
|
// This allows the focussed tile to cycle to this applet again by pressing user button
|
|
if (!foundOwner)
|
|
a->sendToBackground();
|
|
}
|
|
}
|
|
|
|
#endif |