Skip to content
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

Add widget functionality into the library #248

Merged
merged 79 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
d9bd8a5
Add BaseWidget class with process and draw methods
forntoh Oct 21, 2024
c84b5ee
Add BaseWidgetValue class for widgets with values.
forntoh Oct 21, 2024
820c012
Add Widget class with value manipulation methods
forntoh Oct 21, 2024
a18c9ce
Add WidgetBool class for toggling ON/OFF states.
forntoh Oct 21, 2024
f9afabd
Add WidgetRange template class for range-based widgets.
forntoh Oct 21, 2024
8377e7c
Add creator information to WidgetBool.h and WidgetRange.h headers
forntoh Oct 21, 2024
45a466e
Add BaseItemZeroWidget class for custom commit handling
forntoh Oct 21, 2024
dc37829
Add BaseItemSingleWidget class for menu items with single widgets
forntoh Oct 21, 2024
5d950b1
Add BaseItemManyWidgets class for handling multiple widgets in a menu.
forntoh Oct 21, 2024
af27188
Adjust whitespace filling logic based on edit mode
forntoh Oct 21, 2024
b9cba42
Add ItemWidget class templates for handling different numbers of widg…
forntoh Oct 21, 2024
fd2e479
clang format
forntoh Oct 21, 2024
d3887a7
Add backticks around handleCommit
forntoh Oct 21, 2024
5613af9
Merge remote-tracking branch 'origin/feature/item-widgets' into featu…
forntoh Oct 21, 2024
e50fef2
Rename blinkerOffset to cursorOffset
forntoh Oct 21, 2024
fa3ba73
Update cycle initialization in WidgetRange constructor
forntoh Oct 21, 2024
15f1080
Update widget header file naming convention
forntoh Oct 21, 2024
53e9336
Typo
forntoh Oct 21, 2024
f01350e
Update docs in BaseWidget
forntoh Oct 21, 2024
3a8ea50
Update widget header file include guard and add a comment.
forntoh Oct 21, 2024
b8f1fc5
Refactor boolean to bool in WidgetBool.h constructor
forntoh Oct 21, 2024
f47e36d
Refactor setActiveWidget method for boundary check
forntoh Oct 21, 2024
4a8575d
Fix underflow issue when decrementing activeWidget
forntoh Oct 21, 2024
81c1a1c
Update activeWidget to be constrained within the range of available w…
forntoh Oct 21, 2024
509022e
Refactor Widget template to remove default type assignment
forntoh Oct 21, 2024
496b9b0
clang
forntoh Oct 21, 2024
0b8da9a
Turn off clang in specific places
forntoh Oct 21, 2024
bc1663d
clang
forntoh Oct 21, 2024
6662bdf
Fix value comparison in WidgetBool and WidgetRange classes
forntoh Oct 21, 2024
f049612
Delete BaseItemSingleWidget class definition
forntoh Oct 21, 2024
7acd1ed
Refactor ItemWidget class to support variable number of widgets
forntoh Oct 21, 2024
d4420a3
Refactor setValue method in BaseWidgetValue
forntoh Oct 21, 2024
43828c5
Remove unnecessary friend class declaration in BaseWidgetValue.
forntoh Oct 21, 2024
47a87ea
Refactor BaseWidgetValue setValue method for value change.
forntoh Oct 21, 2024
b50a0ac
Refactor destructor to handle dynamic memory deletion
forntoh Oct 21, 2024
c5f086a
Refactor setActiveWidget method for BaseItemManyWidgets
forntoh Oct 21, 2024
f7492f9
Refactor widget header for ESP32 and ESP8266 support
forntoh Oct 21, 2024
95c93f8
Refactor snprintf_P to snprintf for better compatibility
forntoh Oct 21, 2024
d942621
Refactor Widget constructor parameters for const references
forntoh Oct 21, 2024
735ac8a
Fix comparison operator in WidgetRange increment method
forntoh Oct 21, 2024
5b86e1f
Update ItemWidget and WidgetRange creation functions
forntoh Oct 21, 2024
aa1e8bf
Refactor value increment and decrement logic doc
forntoh Oct 21, 2024
6415450
Refactor WidgetBool.h for improved parameter handling
forntoh Oct 21, 2024
7b3bb40
Add custom index_sequence for C++11 compatibility on Arduino
forntoh Oct 21, 2024
b812943
clang
forntoh Oct 21, 2024
5492492
Update drawing logic to handle buffer overflow in BaseItemManyWidgets.
forntoh Oct 21, 2024
5624b4f
Refactor WidgetBool.h textOn and textOff initialization
forntoh Oct 21, 2024
e3fc4a9
Refactor draw method to prevent buffer overflow
forntoh Oct 21, 2024
25fb1d2
Refactor widget range class inheritance and add value step handling.
forntoh Oct 21, 2024
c4f017c
Delete Widget class implementation and related methods.
forntoh Oct 21, 2024
359c5a1
Refactor WidgetBool class and draw method
forntoh Oct 21, 2024
bf34139
Refactor boolean value management in WidgetBool
forntoh Oct 21, 2024
041dd75
Update draw method to prevent buffer overflow.
forntoh Oct 21, 2024
1ab3895
Update src/widget/WidgetRange.h
forntoh Oct 21, 2024
664b4b5
Update widget includes and add virtual destructor to BaseItemZeroWidget.
forntoh Oct 21, 2024
3a41dab
Refactor callback invocation in ItemWidget
forntoh Oct 21, 2024
1bb9ae4
Update docs
forntoh Oct 21, 2024
e20fb39
BaseWidget docs
forntoh Oct 21, 2024
4eda332
Refactor cursorOffset initialization, add new WidgetBool function
forntoh Oct 21, 2024
f94daf9
Add logging when moving left or right in menu items
forntoh Oct 21, 2024
cb18d88
Refactor increment and decrement methods for WidgetRange
forntoh Oct 22, 2024
4800802
Refactor WidgetRange increment and decrement methods
forntoh Oct 22, 2024
ff12fc0
Add WidgetCharset class for character selection
forntoh Oct 23, 2024
44b52e1
Refactor widget charset handling for better readability
forntoh Oct 23, 2024
d01991c
Refactor widget classes for command processing
forntoh Oct 23, 2024
1b7c6fa
Add cursor positioning functionality to BaseWidget
forntoh Oct 23, 2024
1acff22
Refactor widget classes to use const references in callbacks
forntoh Oct 23, 2024
6326bd5
Refactor widget range creation functions to use templates
forntoh Oct 23, 2024
57888b0
Update drawItem method to include padding with spaces option
forntoh Oct 23, 2024
a55f3aa
Update getValue method to return a const reference.
forntoh Oct 23, 2024
226f147
Refactor drawItem method signature for flexibility
forntoh Oct 23, 2024
bc2503e
Padd only when last widget
forntoh Oct 23, 2024
6d08755
Add inline specifier to ITEM_WIDGET function
forntoh Oct 23, 2024
49c5f6f
Adjust whitespace filling based on paddWithBlanks flag
forntoh Oct 23, 2024
e15ad85
Remove creator's name from WidgetCharset.h
forntoh Oct 23, 2024
8d3d28c
Refactor WidgetBool creation function
forntoh Oct 23, 2024
3f39ec8
Refactor widget header files for pragma once directive
forntoh Oct 23, 2024
fb67332
Merge branch 'master' into feature/item-widgets
forntoh Oct 24, 2024
877f632
Refactor logging statements to use a unified LOG function
forntoh Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions src/BaseItemManyWidgets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Creator: @ShishkinDmitriy
forntoh marked this conversation as resolved.
Show resolved Hide resolved
#ifndef BASE_ITEM_MANY_WIDGETS_H
#define BASE_ITEM_MANY_WIDGETS_H

#include "LcdMenu.h"
#include "MenuItem.h"
#include "widget/BaseWidget.h"
#include <utils/utils.h>

class BaseItemManyWidgets : public MenuItem {
forntoh marked this conversation as resolved.
Show resolved Hide resolved
protected:
BaseWidget** widgets = nullptr;
const uint8_t size = 0;
uint8_t activeWidget = 0;

forntoh marked this conversation as resolved.
Show resolved Hide resolved
public:
BaseItemManyWidgets(const char* text, BaseWidget** widgets, const uint8_t size, uint8_t activeWidget = 0)
: MenuItem(text), widgets(widgets), size(size), activeWidget(constrain(activeWidget, 0, size)) {}

uint8_t getActiveWidget() const { return activeWidget; }
void setActiveWidget(const uint8_t activeWidget) {
if (activeWidget < size) {
this->activeWidget = activeWidget;
}
}

virtual ~BaseItemManyWidgets() {
for (uint8_t i = 0; i < size; ++i)
delete widgets[i];
delete[] widgets;
}
forntoh marked this conversation as resolved.
Show resolved Hide resolved

protected:
virtual void handleCommit() = 0;
/**
* @brief Reset the active widget to the first widget.
*/
void reset() { activeWidget = 0; }

/**
* @brief Draws the menu item using the provided renderer.
*
* This function is responsible for rendering the menu item and its associated widgets.
* It iterates through each widget, drawing them into a buffer, and then uses the renderer
* to display the combined result. If the renderer is in edit mode, it also handles the
* cursor positioning for editing.
*
* @param renderer A pointer to the MenuRenderer object used for drawing the item.
*
* The function performs the following steps:
* 1. Initializes a buffer to hold the drawn content and variables for indexing and cursor positioning.
* 2. Iterates through each widget, drawing it into the buffer and updating the index.
* 3. If the active widget is being edited, it draws the item using the renderer and calculates the cursor position.
* 4. Draws the final item using the renderer.
* 5. If in edit mode, moves the cursor to the appropriate position for editing.
*/
void draw(MenuRenderer* renderer) override {
char buf[ITEM_DRAW_BUFFER_SIZE];

uint8_t index = 0;
uint8_t cursorCol = 0;

for (uint8_t i = 0; i < size; i++) {
index += widgets[i]->draw(buf, index);
if (i == activeWidget && renderer->isInEditMode()) {
renderer->drawItem(text, buf, i == size - 1);
cursorCol = renderer->getCursorCol() - 1 - widgets[i]->cursorOffset;
}
}
renderer->drawItem(text, buf);

if (renderer->isInEditMode()) {
renderer->moveCursor(cursorCol, renderer->getCursorRow());
}
}

/**
* @brief Processes a command for the active widget in the menu.
*
* This function handles the processing of commands for the active widget in the menu.
* It first attempts to process the command using the active widget. If the widget
* processes the command successfully, it redraws the menu and returns true.
*
* If the renderer is in edit mode, it handles navigation commands (ENTER, RIGHT, LEFT, BACK)
* to navigate through the widgets or exit edit mode.
*
* When the ENTER command is received while the renderer is in edit mode and the active widget
* is not the last widget, it moves to the next widget. If the active widget is the last widget,
* it exits edit mode and calls the handleCommit function to commit the changes.
*
* If the renderer is not in edit mode and the ENTER command is received, it sets the renderer
* to edit mode, redraws the menu, and draws a blinker.
*
* @param menu Pointer to the LcdMenu object.
* @param command The command to be processed.
* @return true if the command was processed successfully, false otherwise.
*/
bool process(LcdMenu* menu, const unsigned char command) override {
MenuRenderer* renderer = menu->getRenderer();
if (widgets[activeWidget]->process(menu, command)) {
draw(renderer);
return true;
}
forntoh marked this conversation as resolved.
Show resolved Hide resolved
if (renderer->isInEditMode()) {
switch (command) {
case ENTER:
if (activeWidget < this->size - 1) {
right(renderer);
} else {
back(renderer);
}
return true;
case RIGHT:
right(renderer);
return true;
case LEFT:
left(renderer);
return true;
case BACK:
back(renderer);
return true;
default:
return false;
}
}
if (command == ENTER) {
renderer->setEditMode(true);
draw(renderer);
renderer->drawBlinker();
LOG(F("ItemWidget::enterEditMode"), this->text);
return true;
}
return false;
}

void left(MenuRenderer* renderer) {
if (activeWidget == 0) {
activeWidget = this->size - 1;
} else {
activeWidget--;
}
draw(renderer);
LOG(F("ItemWidget::left"), activeWidget);
}

void right(MenuRenderer* renderer) {
activeWidget = (activeWidget + 1) % this->size;
draw(renderer);
forntoh marked this conversation as resolved.
Show resolved Hide resolved
LOG(F("ItemWidget::right"), activeWidget);
}

void back(MenuRenderer* renderer) {
renderer->setEditMode(false);
reset();
handleCommit();
renderer->clearBlinker();
draw(renderer);
LOG(F("ItemWidget::exitEditMode"), this->text);
}
};

#endif
45 changes: 45 additions & 0 deletions src/BaseItemZeroWidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Creator: @ShishkinDmitriy
#ifndef BASE_ITEM_ZERO_WIDGET_H
#define BASE_ITEM_ZERO_WIDGET_H

#include "MenuItem.h"

/**
* @class BaseItemZeroWidget
* @brief A base class for menu items that either require custom commit handling or no commit handling.
*
* This class extends the MenuItem class and provides a framework for menu items
* that either need no commit handling or need to handle commit actions in a specific way.
* It requires derived classes to implement the `handleCommit` method for custom commit handling.
*
* @note This class is intended to be used as a base class for other menu items.
* It should not be instantiated directly.
*/
forntoh marked this conversation as resolved.
Show resolved Hide resolved
class BaseItemZeroWidget : public MenuItem {
forntoh marked this conversation as resolved.
Show resolved Hide resolved
public:
virtual ~BaseItemZeroWidget() = default;
/**
* @brief Construct a new BaseItemZeroWidget object.
*
* @param text The text to display for the menu item.
*/
explicit BaseItemZeroWidget(const char* text) : MenuItem(text) {}

protected:
virtual void handleCommit(LcdMenu* menu) = 0;

void draw(MenuRenderer* renderer) override {
renderer->drawItem(text, nullptr);
}

bool process(LcdMenu* menu, const unsigned char command) override {
if (command == ENTER) {
handleCommit(menu);
LOG(F("ItemWidget::enter"), text);
return true;
}
return false;
}
};

#endif
84 changes: 84 additions & 0 deletions src/ItemWidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#pragma once

#include "BaseItemManyWidgets.h"
#include "widget/BaseWidgetValue.h"

// Custom index_sequence implementation for C++11
template <size_t... Is>
struct index_sequence {};

/**
* @brief Custom implementation of index_sequence and make_index_sequence for C++11 compatibility on Arduino.
*
* This template struct generates a compile-time sequence of integers, which is useful for template metaprogramming.
* The implementation is necessary because the standard library's std::index_sequence and std::make_index_sequence
* are not available in C++11, which is commonly used in Arduino projects.
*
* @tparam N The size of the sequence to generate.
* @tparam Is The sequence of integers generated so far.
*/
template <size_t N, size_t... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};

template <size_t... Is>
struct make_index_sequence<0, Is...> {
using type = index_sequence<Is...>;
};

template <typename... Ts>
class ItemWidget : public BaseItemManyWidgets {
public:
using CallbackType = void (*)(Ts...);

protected:
CallbackType callback = nullptr;

void handleCommit() override {
if (callback != nullptr) {
invokeCallback(typename make_index_sequence<sizeof...(Ts)>::type{});
}
}

template <size_t... Is>
void invokeCallback(index_sequence<Is...>) {
callback(static_cast<BaseWidgetValue<Ts>*>(widgets[Is])->getValue()...);
}
forntoh marked this conversation as resolved.
Show resolved Hide resolved

public:
// Constructor for one or more widgets
ItemWidget(const char* text, BaseWidgetValue<Ts>*... widgetPtrs, CallbackType callback = nullptr)
: BaseItemManyWidgets(
text,
// clang-format off
new BaseWidget* [sizeof...(Ts)] { widgetPtrs... },
// clang-format on
sizeof...(Ts)),
callback(callback) {}
forntoh marked this conversation as resolved.
Show resolved Hide resolved

void setValues(Ts... values) {
setValuesImpl(typename make_index_sequence<sizeof...(Ts)>::type{}, values...);
}

private:
template <size_t... Is>
void setValuesImpl(index_sequence<Is...>, Ts... values) {
// Using a dummy array to expand the parameter pack and call setValue on each widget
int dummy[] = {(static_cast<BaseWidgetValue<Ts>*>(widgets[Is])->setValue(values), 0)...};
forntoh marked this conversation as resolved.
Show resolved Hide resolved
static_cast<void>(dummy); // Avoid unused variable warning
}
};

template <typename... Ts>
/**
* @brief Create an ItemWidget object
*
* @param text the text of the item
* @param callback reference to callback function to call when the value of the item is changed
* @param widgetPtrs pointers to the widgets associated with this item
*/
inline MenuItem* ITEM_WIDGET(
const char* text,
typename ItemWidget<Ts...>::CallbackType callback,
BaseWidgetValue<Ts>*... widgetPtrs) {
return new ItemWidget<Ts...>(text, widgetPtrs..., callback);
}
10 changes: 6 additions & 4 deletions src/renderer/CharacterDisplayRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ void CharacterDisplayRenderer::begin() {
static_cast<CharacterDisplayInterface*>(display)->createChar(2, downArrow);
}

void CharacterDisplayRenderer::drawItem(const char* text, const char* value) {
void CharacterDisplayRenderer::drawItem(const char* text, const char* value, bool paddWithBlanks) {
uint8_t cursorCol = 0;
display->setCursor(cursorCol, cursorRow);

Expand Down Expand Up @@ -49,9 +49,11 @@ void CharacterDisplayRenderer::drawItem(const char* text, const char* value) {

uint8_t cursorColEnd = cursorCol;

// Fill remaining space with white spaces
for (; cursorCol < availableColumns; cursorCol++) {
display->draw(' ');
// Fill remaining space with whitespace only when paddWithBlanks is true
if (paddWithBlanks) {
for (; cursorCol < availableColumns; cursorCol++) {
display->draw(' ');
}
forntoh marked this conversation as resolved.
Show resolved Hide resolved
}

// Draw up and down arrows if present
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/CharacterDisplayRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ class CharacterDisplayRenderer : public MenuRenderer {
*
* @param text The text of the menu item to be drawn.
* @param value The value of the menu item to be drawn.
* @param paddWithBlanks A flag indicating whether to pad the text with spaces.
*/
void drawItem(const char* text, const char* value) override;
void drawItem(const char* text, const char* value, bool paddWithBlanks) override;
forntoh marked this conversation as resolved.
Show resolved Hide resolved
void draw(uint8_t byte) override;
void drawBlinker() override;
void clearBlinker() override;
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/MenuRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ class MenuRenderer {
* @brief Draws an item on the display.
* @param text Text of the item to be drawn.
* @param value Value of the item to be drawn.
* @param paddWithBlanks Flag indicating whether to pad the text with spaces.
*/
virtual void drawItem(const char* text, const char* value) = 0;
virtual void drawItem(const char* text, const char* value, bool paddWithBlanks = true) = 0;
forntoh marked this conversation as resolved.
Show resolved Hide resolved

/**
* @brief Function to clear the blinker from the display.
Expand Down
Loading
Loading