mirror of
https://github.com/meshtastic/firmware.git
synced 2026-02-06 17:11:52 +00:00
* Added keyboard option to menu. Shows a keyboard layout but does not type. * Keyboard types into text box and wraps. * send FreeText messages from the send submenu - renamed `KEYBOARD` action to `FREE_TEXT` and moved its menu location to the send submenu - opening the FreeText applet from the menu keeps the menu open and disabled the timeout - the FreeText applet writes to inkhud->freetext - the sending a canned message checks inkhud->freetext and if it isn't empty, sends and clears the inkhud->freetext * Text scrolls along with input * handle free text message completion as an event implements `handleFreeText` and `OnFreeText()` for system applets to interface with the FreeText Applet The FreeText Applet generates an `OnFreeText` event when completing a message which is handled by the first system applet with the `handleFreeText` flag set to true. The Menu Applet now handles this event. * call `onFreeText` whenever the FreeText Applet exits allows the menu to consistently restart its auto-close timeout * Add text cursor * Change UI to remove the header and make text box longer Keyboard displays captial letters for legibility Keyboard types captial letters with long press * center FreeText keys and draw symbolic buttons Move input field and keyboard drawing to their own functions: - `drawInputField()` - `drawKeyboard()` Store the keys in a 1-dimensional array Implement a matching array, `keyWidths`, to set key widths relative to the font size * Add character limit and counter * Fix softlock when hitting character limit * Move text box as its own menu page * rework FreeTextApplet into KeyboardApplet - The Keyboard Applet renders an on-screen keyboard at the lower portion of the screen. - Calling `inkhud->openKeyboard()` sends all the user applets to the background and resizes the first system applet with `handleFreeText` set to True to fit above the on-screen keyboard - `inkhud->closeKeyboard()` reverses this layout change * Fix input box rendering and add character limit to menu free text * remove FREE_TEXT menu page and use the FREE_TEXT menu action solely * force update when changing the free text message * reorganize KeyboardApplet - add comments after each row of `key[]` and `keyWidths[]` to preserve formatting - The selected key is now set using the key index directly - rowWidths are pre-calculated in the KeyboardApplet constructor - removed `drawKeyboard()` and implemented `drawKeyLabel()` * implement `Renderer::clearTile()` to clear the region below a tile * add parameter to forceUpdate() for re-rendering the full screen setting the `all` parameter to true in `inkhud->forceUpdate()` now causes the full screen buffer to clear an re-render. This is helpful for when sending applets to the background and the UI needs a clean canvas. System Applets can now set the `alwaysRender` flag true which causes it to re-render on every screen update. This is set to true in the Battery Icon Applet. * clean up tile clearing loops * implement dirty rendering to let applets draw over their previous render - `Applet::requestUpdate()` now has an optional flag to keep the old canvas - If honored, the renderer calls `render(true)` which runs `onDirtyRender()` instead of `onRender()` for said applet - The renderer will not call a dirty render if the full screen is getting re-rendered * simplify arithmetic in clearTile for better understanding * combine Applet::onRender() and Applet::onDirtyRender() into Applet::onRender(bool full) - add new `full` parameter to onRender() in every applet. This parameter can be ignored by most applets. - `Applet::requestUpdate()` has an optional flag that requests a full render by default * implement tile and partial rendering in KeyboardApplet * add comment for drawKeyLabel() * improve clarity of byte operations in clearTile() * remove typo and commented code * fix inaccurate comments * add null check to openKeyboard() and closeKeyboard() --------- Co-authored-by: zeropt <ferr0fluidmann@gmail.com> Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
132 lines
3.7 KiB
C++
132 lines
3.7 KiB
C++
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
|
|
|
#include "./DMApplet.h"
|
|
|
|
using namespace NicheGraphics;
|
|
|
|
void InkHUD::DMApplet::onActivate()
|
|
{
|
|
textMessageObserver.observe(textMessageModule);
|
|
}
|
|
|
|
void InkHUD::DMApplet::onDeactivate()
|
|
{
|
|
textMessageObserver.unobserve(textMessageModule);
|
|
}
|
|
|
|
// We're not consuming the data passed to this method;
|
|
// we're just just using it to trigger a render
|
|
int InkHUD::DMApplet::onReceiveTextMessage(const meshtastic_MeshPacket *p)
|
|
{
|
|
// Abort if applet fully deactivated
|
|
// Already handled by onActivate and onDeactivate, but good practice for all applets
|
|
if (!isActive())
|
|
return 0;
|
|
|
|
// If DM (not broadcast)
|
|
if (!isBroadcast(p->to)) {
|
|
// Want to update display, if applet is foreground
|
|
requestUpdate();
|
|
|
|
// If this was an incoming message, suggest that our applet becomes foreground, if permitted
|
|
if (getFrom(p) != nodeDB->getNodeNum())
|
|
requestAutoshow();
|
|
}
|
|
|
|
// Return zero: no issues here, carry on notifying other observers!
|
|
return 0;
|
|
}
|
|
|
|
void InkHUD::DMApplet::onRender(bool full)
|
|
{
|
|
// Abort if no text message
|
|
if (!latestMessage->dm.sender) {
|
|
printAt(X(0.5), Y(0.5), "No DMs", CENTER, MIDDLE);
|
|
return;
|
|
}
|
|
|
|
// ===========================
|
|
// Header (sender, timestamp)
|
|
// ===========================
|
|
|
|
// Y position for divider
|
|
// - between header text and messages
|
|
|
|
std::string header;
|
|
|
|
// RX Time
|
|
// - if valid
|
|
std::string timeString = getTimeString(latestMessage->dm.timestamp);
|
|
if (timeString.length() > 0) {
|
|
header += timeString;
|
|
header += ": ";
|
|
}
|
|
|
|
// Sender's id
|
|
// - shortname and long name, if available, or
|
|
// - node id
|
|
meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(latestMessage->dm.sender);
|
|
if (sender && sender->has_user) {
|
|
header += parseShortName(sender); // May be last-four of node if unprintable (emoji, etc)
|
|
header += " (";
|
|
header += parse(sender->user.long_name);
|
|
header += ")";
|
|
} else
|
|
header += hexifyNodeNum(latestMessage->dm.sender);
|
|
|
|
// Draw a "standard" applet header
|
|
drawHeader(header);
|
|
|
|
// Fade the right edge of the header, if text spills over edge
|
|
uint8_t wF = getFont().lineHeight() / 2; // Width of fade effect
|
|
uint8_t hF = getHeaderHeight(); // Height of fade effect
|
|
if (getCursorX() > width())
|
|
hatchRegion(width() - wF - 1, 1, wF, hF, 2, WHITE);
|
|
|
|
// Dimensions of the header
|
|
constexpr int16_t padDivH = 2;
|
|
const int16_t headerDivY = Applet::getHeaderHeight() - 1;
|
|
|
|
// ===================
|
|
// Print message text
|
|
// ===================
|
|
|
|
// Parse any non-ascii chars in the message
|
|
std::string text = parse(latestMessage->dm.text);
|
|
|
|
// Extra gap below the header
|
|
int16_t textTop = headerDivY + padDivH;
|
|
|
|
// Attempt to print with fontLarge
|
|
uint32_t textHeight;
|
|
setFont(fontLarge);
|
|
textHeight = getWrappedTextHeight(0, width(), text);
|
|
if (textHeight <= (uint32_t)height()) {
|
|
printWrapped(0, textTop, width(), text);
|
|
return;
|
|
}
|
|
|
|
// Fallback (too large): attempt to print with fontMedium
|
|
setFont(fontMedium);
|
|
textHeight = getWrappedTextHeight(0, width(), text);
|
|
if (textHeight <= (uint32_t)height()) {
|
|
printWrapped(0, textTop, width(), text);
|
|
return;
|
|
}
|
|
|
|
// Fallback (too large): print with fontSmall
|
|
setFont(fontSmall);
|
|
printWrapped(0, textTop, width(), text);
|
|
}
|
|
|
|
// Don't show notifications for direct messages when our applet is displayed
|
|
bool InkHUD::DMApplet::approveNotification(Notification &n)
|
|
{
|
|
if (n.type == Notification::Type::NOTIFICATION_MESSAGE_DIRECT)
|
|
return false;
|
|
|
|
else
|
|
return true;
|
|
}
|
|
|
|
#endif |