├── .gitignore ├── .vscode ├── c_cpp_properties.json └── settings.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── example ├── .vscode │ ├── .cortex-debug.peripherals.state.json │ ├── .cortex-debug.registers.state.json │ ├── c_cpp_properties.json │ ├── launch.json │ └── settings.json ├── CMakeLists.txt ├── menu_example.cpp └── pico_sdk_import.cmake ├── images ├── action.jpg ├── menu.jpg ├── submenu.jpg └── submenu2.jpg ├── include ├── menu_action.hpp ├── menu_base.hpp ├── menu_list.hpp └── pico_menu.hpp ├── pico_menu.cmake ├── pico_sdk_import.cmake └── src ├── menu_action.cpp ├── menu_base.cpp ├── menu_list.cpp └── pico_menu.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | example/build 3 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "${PICO_SDK_PATH}/**", 8 | "${PIM_SDK_PATH}/**" 9 | ], 10 | "defines": [], 11 | "compilerPath": "/usr/bin/gcc", 12 | "cStandard": "gnu17", 13 | "cppStandard": "gnu++14", 14 | "intelliSenseMode": "linux-gcc-x64", 15 | "configurationProvider": "ms-vscode.cmake-tools" 16 | } 17 | ], 18 | "version": 4 19 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "string": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") 4 | 5 | include(pico_sdk_import.cmake) 6 | 7 | project(pico_menu) 8 | 9 | pico_sdk_init() 10 | 11 | add_library(pico_menu INTERFACE) 12 | 13 | add_subdirectory($ENV{PIM_SDK_PATH}/drivers/st7789 st7789) 14 | add_subdirectory($ENV{PIM_SDK_PATH}/libraries/pico_graphics pico_graphics) 15 | add_subdirectory($ENV{PIM_SDK_PATH}/libraries/pico_display pico_display) 16 | 17 | target_sources(pico_menu INTERFACE 18 | ${CMAKE_CURRENT_LIST_DIR}/src/pico_menu.cpp 19 | ${CMAKE_CURRENT_LIST_DIR}/src/menu_base.cpp 20 | ${CMAKE_CURRENT_LIST_DIR}/src/menu_list.cpp 21 | ${CMAKE_CURRENT_LIST_DIR}/src/menu_action.cpp 22 | ) 23 | 24 | target_include_directories(pico_menu INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 25 | 26 | target_link_libraries(pico_menu INTERFACE pico_stdlib hardware_spi st7789 pico_display pico_graphics) 27 | 28 | # Create doxygen make target to make API documentation. 29 | # Run "make doxygen" in the build directory after running cmake to make the documentation. 30 | # Choose which type of documentation you want below: 31 | set(DOXYGEN_GENERATE_HTML YES) 32 | set(DOXYGEN_GENERATE_MAN NO) 33 | 34 | doxygen_add_docs( 35 | doxygen 36 | ${PROJECT_SOURCE_DIR} 37 | COMMENT "Generate API documentation" 38 | ) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hamid Elaosta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2bytes Pico Menu for Pimoroni Pico Display and Raspberry Pi Pico 2 | 3 | ## Requirements 4 | * Download and setup the Raspberry Pi Pico C++ SDK 5 | * Export `PICO_SDK_PATH` with the path to the SDK 6 | * Get the Pimoroni SDK from [Github](https://github.com/hamid-elaosta/pimoroni-pico) or use the Pimoroni original if/when they merge the PR for portrait mode. 7 | * Export `PIM_SDK_PATH` to point at the Pimoroni SDK 8 | 9 | ## Usage 10 | 11 | See [example/](example/) 12 | 13 | ```C++ 14 | // Create a buffer for the screen 15 | uint16_t buffer[PicoDisplay::PORTRAIT_WIDTH*PicoDisplay::PORTRAIT_HEIGHT]; 16 | // Create an instance of PicoDisplay (Portrait only at present) 17 | PicoDisplay display(buffer, PicoDisplay::PORTRAIT_WIDTH, PicoDisplay::PORTRAIT_HEIGHT); 18 | // Create an instance of PicoMenu 19 | twobytes::PicoMenu menu(PicoDisplay::PORTRAIT_WIDTH, PicoDisplay::PORTRAIT_HEIGHT, display); 20 | 21 | 22 | // Sample action functions 23 | void brightnessUp(){ 24 | // increase brightness 25 | } 26 | void brightnessDown(){ 27 | // decrease brightness 28 | } 29 | 30 | void main() { 31 | 32 | // Initialise the display 33 | display.init(); 34 | display.set_backlight((255/100)*50); // Set backlight to 50% 35 | 36 | // Create the main menu 37 | twobytes::MenuList topMenu{}; 38 | topMenu.SetTitle("Pico Menu"); 39 | 40 | // Optionally, set the background, foreground, title background and title foreground colours, otherwise defaults will be used 41 | topMenu.SetTitleBackground(twobytes::MenuBase::create_pen(140,0,200)); // #600080 42 | topMenu.SetTitleForeground(twobytes::MenuBase::create_pen(255,255,255)); // #FFFFFF 43 | topMenu.SetBackground(twobytes::MenuBase::create_pen(200,0,200)); // #800080 44 | topMenu.SetForeground(twobytes::MenuBase::create_pen(255,255,255)); // #FFFFFF 45 | 46 | // Add submenus and/or actions 47 | twobytes::MenuList brightnessMenu{}; 48 | brightnessMenu.SetTitle("Brightness"); 49 | 50 | brightnessMenu.SetBackground(twobytes::MenuList::create_pen(0,0,0)); 51 | brightnessMenu.SetForeground(twobytes::MenuList::create_pen(255,255,255)); 52 | 53 | // Create some new actions 54 | twobytes::MenuAction actionUp{}; 55 | twobytes::MenuAction actionDown{}; 56 | 57 | // Configure actions by setting title, description and function to execute. 58 | actionUp.SetTitle("+ 10%"); 59 | actionUp.SetFunction(brightnessUp); 60 | actionUp.SetDescription("Brightness increased by 10%"); 61 | 62 | actionDown.SetTitle("- 10%"); 63 | actionDown.SetFunction(brightnessDown); 64 | actionDown.SetDescription("Brightness decreased by 10%"); 65 | 66 | // Push the brightness actions to the brightness menu. 67 | brightnessMenu.AddRow(&actionUp); 68 | brightnessMenu.AddRow(&actionDown); 69 | 70 | // Push the brightnessMenu to the top menu 71 | topMenu.AddRow(&brightnessMenu); 72 | 73 | // Add your top menu to the PicoMenu instance 74 | menu.SetMenu(&topMenu); 75 | 76 | // Once we've created all of our menus and actions and added them, we only need to call menu.update() in a loop. 77 | while (true) { 78 | menu.Update(); 79 | } 80 | 81 | } 82 | ``` 83 | 84 | "Screen" shots :D 85 | 86 | ![Main menu in Zelda colours :D](images/menu.jpg) ![Sub menu in different colours](images/submenu.jpg)![Second submenu, different style](images/submenu2.jpg) ![Action description](images/action.jpg) 87 | -------------------------------------------------------------------------------- /example/.vscode/.cortex-debug.peripherals.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /example/.vscode/.cortex-debug.registers.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /example/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "${PICO_SDK_PATH}/**", 8 | "${PIM_SDK_PATH}/**", 9 | "${workspaceFolder}/../**" 10 | ], 11 | "defines": [], 12 | "compilerPath": "/usr/bin/gcc", 13 | "cStandard": "gnu17", 14 | "cppStandard": "gnu++14", 15 | "intelliSenseMode": "linux-gcc-x64", 16 | "configurationProvider": "ms-vscode.cmake-tools" 17 | } 18 | ], 19 | "version": 4 20 | } -------------------------------------------------------------------------------- /example/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "env": { 7 | "PICO_SDK_PATH": "${env:PICO_SDK_PATH}", 8 | }, 9 | "configurations": [ 10 | { 11 | "name": "Pico Debug", 12 | "cwd": "${workspaceRoot}", 13 | "executable": "${command:cmake.launchTargetPath}", 14 | "request": "launch", 15 | "type": "cortex-debug", 16 | "servertype": "openocd", 17 | // On Ubunto 20.04 this is "gdb-multiarch", on Arch Linux it is "arm-none-eabi-gdb" 18 | "gdbPath": "gdb-multiarch", 19 | "device": "RP2040", 20 | "configFiles": [ 21 | "interface/picoprobe.cfg", 22 | "target/rp2040.cfg" 23 | ], 24 | "svdFile": "${PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd", 25 | "runToMain": true, 26 | // Work around for stopping at main on restart 27 | "postRestartCommands": [ 28 | "break main", 29 | "continue" 30 | ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /example/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.configureSettings": { 3 | }, 4 | "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", 5 | } -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | include(../pico_menu.cmake) 4 | 5 | project(Menu C CXX ASM) 6 | 7 | add_executable(Menu menu_example.cpp) 8 | 9 | pico_add_extra_outputs(Menu) 10 | target_link_libraries(Menu pico_stdlib pico_menu) 11 | -------------------------------------------------------------------------------- /example/menu_example.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pico_menu.hpp" 24 | #include "menu_action.hpp" 25 | #include "pico_display.hpp" 26 | 27 | using namespace pimoroni; 28 | 29 | uint16_t buffer[PicoDisplay::PORTRAIT_WIDTH*PicoDisplay::PORTRAIT_HEIGHT]; 30 | PicoDisplay display(buffer, PicoDisplay::PORTRAIT_WIDTH, PicoDisplay::PORTRAIT_HEIGHT); 31 | twobytes::PicoMenu menu(PicoDisplay::PORTRAIT_WIDTH, PicoDisplay::PORTRAIT_HEIGHT, display); 32 | 33 | twobytes::MenuAction action1{}; 34 | 35 | int aNumber = 0; 36 | 37 | int r = 0; 38 | int g = 0; 39 | int b = 0; 40 | 41 | void ledRed() {r=100,g=0,b=0;} 42 | void ledGreen() {r=0,g=100,b=0;} 43 | void ledBlue() {r=0,g=0,b=100;} 44 | void ledOff() {r=0,g=0,b=0;} 45 | 46 | void action1Func() { 47 | aNumber++; 48 | action1.SetDescription("Action1 pressed " + std::to_string(aNumber) + " times"); 49 | } 50 | 51 | int brightness = 50; 52 | 53 | twobytes::MenuAction actionUp{}; 54 | twobytes::MenuAction actionDown{}; 55 | 56 | void brightnessUp() { 57 | if (brightness <= 100) { 58 | actionUp.SetDescription("Brightness increased by 10%"); 59 | brightness += 10; 60 | display.set_backlight((255/100)*brightness); 61 | } else { 62 | actionUp.SetDescription("Maximum Brightness"); 63 | } 64 | } 65 | 66 | void brightnessDown() { 67 | if (brightness >= 30) { 68 | actionDown.SetDescription("Brightness decreased by 10%"); 69 | brightness -= 10; 70 | display.set_backlight((255/100)*brightness); 71 | } else { 72 | actionDown.SetDescription("Minimum Brightness"); 73 | } 74 | } 75 | 76 | 77 | int main() { 78 | 79 | display.init(); 80 | display.set_backlight((255/100)*50); // Set backlight to 50% 81 | 82 | // Create the main menu 83 | twobytes::MenuList topMenu{}; 84 | topMenu.SetTitle("Pico Menu"); 85 | topMenu.SetTitleBackground(twobytes::MenuBase::create_pen(140,0,200)); // #600080 86 | topMenu.SetTitleForeground(twobytes::MenuBase::create_pen(255,255,255)); // #FFFFFF 87 | 88 | topMenu.SetBackground(twobytes::MenuBase::create_pen(200,0,80)); // #600080 89 | topMenu.SetForeground(twobytes::MenuBase::create_pen(255,255,255)); // #FFFFFF 90 | 91 | // Add a submenu 92 | twobytes::MenuList ledMenu{}; 93 | ledMenu.SetTitle("LED Colour"); 94 | 95 | twobytes::MenuAction ledRedAction{}; 96 | ledRedAction.SetTitle("Red"); 97 | ledRedAction.SetDescription("LED set to Red"); 98 | ledRedAction.SetFunction(ledRed); 99 | 100 | twobytes::MenuAction ledGreenAction{}; 101 | ledGreenAction.SetTitle("Green"); 102 | ledGreenAction.SetDescription("LED set to Green"); 103 | ledGreenAction.SetFunction(ledGreen); 104 | 105 | twobytes::MenuAction ledBlueAction{}; 106 | ledBlueAction.SetTitle("Blue"); 107 | ledBlueAction.SetDescription("LED set to Blue"); 108 | ledBlueAction.SetFunction(ledBlue); 109 | 110 | twobytes::MenuAction ledOffAction{}; 111 | ledOffAction.SetTitle("Off"); 112 | ledOffAction.SetDescription("LED set to Off"); 113 | ledOffAction.SetFunction(ledOff); 114 | 115 | twobytes::MenuList brightnessMenu{}; 116 | brightnessMenu.SetTitle("Brightness"); 117 | brightnessMenu.SetBackground(twobytes::MenuList::create_pen(0,0,0)); 118 | brightnessMenu.SetForeground(twobytes::MenuList::create_pen(255,255,255)); 119 | 120 | actionUp.SetTitle("+ 10%"); 121 | actionUp.SetFunction(brightnessUp); 122 | actionUp.SetDescription("Brightness increased by 10%"); 123 | 124 | actionDown.SetTitle("- 10%"); 125 | actionDown.SetFunction(brightnessDown); 126 | actionDown.SetDescription("Brightness decreased by 10%"); 127 | brightnessMenu.AddRow(&actionUp); 128 | brightnessMenu.AddRow(&actionDown); 129 | topMenu.AddRow(&brightnessMenu); 130 | 131 | ledMenu.AddRow(&ledRedAction); 132 | ledMenu.AddRow(&ledGreenAction); 133 | ledMenu.AddRow(&ledBlueAction); 134 | ledMenu.AddRow(&ledOffAction); 135 | topMenu.AddRow(&ledMenu); 136 | 137 | // Set values of a globally defined action and add it to the menu 138 | action1.SetTitle("action1"); 139 | action1.SetFunction(action1Func); 140 | topMenu.AddRow(&action1); 141 | 142 | menu.SetMenu(&topMenu); 143 | 144 | // Once we've created all of our menus and actions and added them, we only need to call menu.update() in a loop. 145 | while (true) { 146 | menu.Update(); 147 | // The pimoroni implementation for the LED requires that it be set on every loop, it's not a toggle, 148 | // so we'll update it here too. 149 | display.set_led(r,g,b); 150 | } 151 | 152 | } -------------------------------------------------------------------------------- /example/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading PICO SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /images/action.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bytes/picomenu/a71609590f452045a555bfc0e7d0fd962f3b8633/images/action.jpg -------------------------------------------------------------------------------- /images/menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bytes/picomenu/a71609590f452045a555bfc0e7d0fd962f3b8633/images/menu.jpg -------------------------------------------------------------------------------- /images/submenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bytes/picomenu/a71609590f452045a555bfc0e7d0fd962f3b8633/images/submenu.jpg -------------------------------------------------------------------------------- /images/submenu2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2bytes/picomenu/a71609590f452045a555bfc0e7d0fd962f3b8633/images/submenu2.jpg -------------------------------------------------------------------------------- /include/menu_action.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | #include "menu_base.hpp" 25 | #include "pico_display.hpp" 26 | 27 | namespace twobytes { 28 | 29 | /** 30 | * @brief Typedef for the action function to call when selected. 31 | * 32 | */ 33 | typedef void(*menu_action)(void); 34 | 35 | /** 36 | * @brief MenuList implements a menu action for Pico Menu. 37 | * It accepts a pointer to a function to execute when selected. 38 | * 39 | */ 40 | class MenuAction : public MenuBase { 41 | public: 42 | /** 43 | * @brief provide the function for the action to call 44 | * when selected. 45 | */ 46 | menu_action action_; 47 | private: 48 | std::string description_; 49 | public: 50 | /** 51 | * @brief Construct a new Menu Action 52 | * 53 | */ 54 | MenuAction(); 55 | /** 56 | * @brief Set the Description text for the action page. 57 | * 58 | * @param description string to be displayed on the action page. 59 | */ 60 | void SetDescription(std::string description); 61 | /** 62 | * @brief Draw function for the action page. 63 | * 64 | * @param display is the Pimoroni PicoDisplay instance 65 | */ 66 | void Draw(pimoroni::PicoDisplay display); 67 | /** 68 | * @brief Set the Function to call when this action is selected 69 | * 70 | * @param action 71 | */ 72 | void SetFunction(menu_action action); 73 | /** 74 | * @brief Execute the provided function on action selection (button press) 75 | * 76 | */ 77 | void Execute(); 78 | }; 79 | } 80 | -------------------------------------------------------------------------------- /include/menu_base.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include "pico_display.hpp" 28 | 29 | namespace twobytes { 30 | 31 | /** 32 | * @brief Base class for menu items. 33 | * Subclass this base to implement drawing, navigation etc. 34 | * See MenuList and MenuAction for example subclasses. 35 | */ 36 | class MenuBase { 37 | 38 | public: 39 | /** 40 | * @brief Create a pen (uint16_t). 41 | * This is a static implementation compatible with the PicoDisplay createPen. 42 | * 43 | * @param r red colour integer 0 to 255. 44 | * @param g green colour integer 0 to 255. 45 | * @param b blue colour integer 0 to 255. 46 | * @return constexpr uint16_t is compatible with pimoroni::Pen 47 | */ 48 | static constexpr uint16_t create_pen(uint8_t r, uint8_t g, uint8_t b) { 49 | uint16_t p = ((r & 0b11111000) << 8) | 50 | ((g & 0b11111100) << 3) | 51 | ((b & 0b11111000) >> 3); 52 | 53 | return __builtin_bswap16(p); 54 | }; 55 | private: 56 | uint16_t bg_pen = create_pen(255,255,255); 57 | uint16_t fg_pen = create_pen(0,0,0); 58 | std::string title_; 59 | public: 60 | /** 61 | * @brief Construct a new Menu Base. 62 | * Subclass this in order to create custom menu or action or other types. 63 | * 64 | */ 65 | MenuBase(); 66 | /** 67 | * @brief Destroy the Menu Base object 68 | * 69 | */ 70 | virtual ~MenuBase() {}; 71 | /** 72 | * @brief draw function to implement in subclasses. 73 | * Will be called by parent menu to draw this type. 74 | * 75 | * @param display is the Pimoroni PicoDisplay to be displayed to and buttons accessed on. 76 | */ 77 | virtual void Draw(const pimoroni::PicoDisplay display) = 0; 78 | /** 79 | * @brief Previous moves the cursor to the previous item in the list, highlighting it 80 | * 81 | */ 82 | virtual void Previous() {}; 83 | /** 84 | * @brief Next moves the cursor to the next item in the list, highlighting it 85 | * 86 | */ 87 | virtual void Next() {}; 88 | /** 89 | * @brief Get the Highlighted Row to interact with it 90 | * 91 | * @return MenuBase* subclass is returned. 92 | */ 93 | virtual MenuBase* GetHighlightedRow() { return NULL; }; 94 | /** 95 | * @brief Execute the function supplied to this action, if there is one. 96 | * 97 | */ 98 | virtual void Execute() {}; 99 | /** 100 | * @brief Set the Title of this type, used for displaying in the title 101 | * of a page, or as the row text in a menu. 102 | * 103 | * @param title String title to display as row text or page title. 104 | */ 105 | void SetTitle(std::string title); 106 | /** 107 | * @brief Get the Title string for drawing. 108 | * 109 | * @return std::string 110 | */ 111 | std::string GetTitle(); 112 | /** 113 | * @brief Set the Background colour for non-title parts of the screen. 114 | * 115 | * @param pen is the pimoroni::Pen colour to set as the background. 116 | */ 117 | void SetBackground(pimoroni::Pen pen); 118 | /** 119 | * @brief Set the Foreground (text) colour for non-title parts of the screen 120 | * 121 | * @param pen is the pimoroni::Pen colour for non-title text. 122 | */ 123 | void SetForeground(pimoroni::Pen pen); 124 | /** 125 | * @brief Get the Background colour 126 | * 127 | * @return pimoroni::Pen background colour for the page. 128 | */ 129 | pimoroni::Pen GetBackground(); 130 | /** 131 | * @brief Get the Foreground (text) colour 132 | * 133 | * @return pimoroni::Pen text colour for the page. 134 | */ 135 | pimoroni::Pen GetForeground(); 136 | }; 137 | } 138 | -------------------------------------------------------------------------------- /include/menu_list.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include "menu_base.hpp" 28 | #include "pico_display.hpp" 29 | 30 | namespace twobytes { 31 | 32 | /** 33 | * @brief MenuList implements a menu system for Pico Menu. It can hold up to 11 rows, 34 | * and supports each row being a subclass which implements MenuBase. 35 | * 36 | */ 37 | class MenuList : public MenuBase { 38 | private: 39 | static const uint8_t kMaxRows = 11; 40 | uint8_t highlighted_row_ = 0; 41 | std::vector < MenuBase* > rows_; 42 | uint16_t title_background_pen_ = create_pen(255,0,0); 43 | uint16_t title_foreground_pen_ = create_pen(255,255,255); 44 | uint8_t rowHeight = 20; 45 | public: 46 | /** 47 | * @brief Construct a new MenuList object 48 | * 49 | */ 50 | MenuList(); 51 | /** 52 | * @brief Add a new row of type MenuBase (MenuMenu, MenuAction) 53 | * 54 | * @param item the menu item of type MenuBase 55 | */ 56 | void AddRow(MenuBase* item); 57 | /** 58 | * @brief Get the Current Row object 59 | * 60 | * @return MenuBase* for the highlighted row 61 | */ 62 | MenuBase* GetHighlightedRow(); 63 | /** 64 | * @brief Move to previous row on the screen 65 | * 66 | */ 67 | void Previous(); 68 | /** 69 | * @brief Move to next row on the screen 70 | * 71 | */ 72 | void Next(); 73 | /** 74 | * @brief The number of rows in the current menu 75 | * 76 | * @return int number of rows 77 | */ 78 | int Size(); 79 | /** 80 | * @brief Currently highlighted row index 81 | * 82 | * @return int index of the highlighted row 83 | */ 84 | int HighlightedRowIndex(); 85 | /** 86 | * @brief Draw function to draw this menu. 87 | * This is called automatically by the parent menu draw call. 88 | * 89 | * @param display Pimoroni PicoDisplay for displaying to and accessing buttons on. 90 | */ 91 | void Draw(pimoroni::PicoDisplay display); 92 | /** 93 | * @brief Set the Title Background colour 94 | * 95 | * @param pen is the pimoroni::Pen to set 96 | * as the background colour for the title. 97 | */ 98 | void SetTitleBackground(pimoroni::Pen pen); 99 | /** 100 | * @brief Set the Title Foreground colour 101 | * 102 | * @param pen is the pimoroni::Pen to set 103 | * as the foreground (text) colour for the title. 104 | */ 105 | void SetTitleForeground(pimoroni::Pen pen); 106 | private: 107 | void DrawTitle(const pimoroni::PicoDisplay display); 108 | void DrawRows(const pimoroni::PicoDisplay display); 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /include/pico_menu.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include "menu_list.hpp" 28 | #include "pico_display.hpp" 29 | 30 | namespace twobytes { 31 | 32 | /** 33 | * @brief Menu provides the pico display menu system 34 | * 35 | */ 36 | class PicoMenu { 37 | private: 38 | uint8_t width_; 39 | uint8_t height_; 40 | pimoroni::PicoDisplay display_; 41 | std::stack< MenuBase* > main_menu_; 42 | uint8_t up_button_ = pimoroni::PicoDisplay::A; 43 | uint8_t down_button_ = pimoroni::PicoDisplay::X; 44 | uint8_t back_button_ = pimoroni::PicoDisplay::B; 45 | uint8_t select_button_ = pimoroni::PicoDisplay::Y; 46 | public: 47 | /** 48 | * @brief Construct a new Menu object 49 | * 50 | * @param display Pimoroni PicoDisplay object for displaying to and accessing buttons on. 51 | */ 52 | PicoMenu(pimoroni::PicoDisplay display); 53 | /** 54 | * @brief Construct a new Menu object 55 | * 56 | * @param width Width if not using the default, e.g. for Portrait 57 | * @param height Height if not using the default, e.g. for Portrait 58 | * @param display Pimoroni PicoDisplay object for displaying and accessing buttons on 59 | */ 60 | PicoMenu(int width, int height, pimoroni::PicoDisplay display); 61 | /** 62 | * @brief Set the top level menu object 63 | * 64 | * @param menu Top level menu item to be displayed 65 | */ 66 | void SetMenu(MenuList* menu); 67 | /** 68 | * @brief Update takes care of reading buttons and updating the dsplay. 69 | * It should be called in a loop. 70 | * 71 | */ 72 | void Update(); 73 | private: 74 | void CheckInputs(); 75 | void Draw(); 76 | void Previous(); 77 | void Next(); 78 | void Select(); 79 | void Back(); 80 | }; 81 | 82 | } 83 | -------------------------------------------------------------------------------- /pico_menu.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | 3 | set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") 4 | 5 | include(pico_sdk_import.cmake) 6 | 7 | project(pico_menu) 8 | 9 | pico_sdk_init() 10 | 11 | add_library(pico_menu INTERFACE) 12 | 13 | add_subdirectory($ENV{PIM_SDK_PATH}/drivers/st7789 st7789) 14 | add_subdirectory($ENV{PIM_SDK_PATH}/libraries/pico_graphics pico_graphics) 15 | add_subdirectory($ENV{PIM_SDK_PATH}/libraries/pico_display pico_display) 16 | 17 | target_sources(pico_menu INTERFACE 18 | ${CMAKE_CURRENT_LIST_DIR}/src/pico_menu.cpp 19 | ${CMAKE_CURRENT_LIST_DIR}/src/menu_base.cpp 20 | ${CMAKE_CURRENT_LIST_DIR}/src/menu_list.cpp 21 | ${CMAKE_CURRENT_LIST_DIR}/src/menu_action.cpp 22 | ) 23 | 24 | target_include_directories(pico_menu INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) 25 | 26 | target_link_libraries(pico_menu INTERFACE pico_stdlib hardware_spi st7789 pico_display pico_graphics) 27 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading PICO SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /src/menu_action.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "menu_action.hpp" 24 | 25 | namespace twobytes { 26 | MenuAction::MenuAction() : action_(NULL) {} 27 | 28 | void MenuAction::Draw(pimoroni::PicoDisplay display) { 29 | display.set_pen(255,255,255); 30 | display.clear(); 31 | display.set_pen(255,0,0); 32 | display.text( 33 | this->description_, 34 | pimoroni::Point(pimoroni::PicoDisplay::PORTRAIT_WIDTH/5, 35 | pimoroni::PicoDisplay::PORTRAIT_HEIGHT/5), 36 | 4*(pimoroni::PicoDisplay::PORTRAIT_WIDTH/5) 37 | ); 38 | display.update(); 39 | } 40 | 41 | void MenuAction::SetFunction(menu_action action) { 42 | this->action_ = action; 43 | } 44 | 45 | void MenuAction::Execute() { 46 | if(this->action_ != NULL) { 47 | this->action_(); 48 | } 49 | } 50 | 51 | void MenuAction::SetDescription(std::string description) { 52 | this->description_ = description; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/menu_base.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "menu_base.hpp" 24 | 25 | #include 26 | 27 | namespace twobytes { 28 | 29 | MenuBase::MenuBase() {} 30 | 31 | void MenuBase::SetTitle(std::string title) { 32 | this->title_ = title; 33 | } 34 | 35 | std::string MenuBase::GetTitle() { 36 | return this->title_; 37 | } 38 | 39 | void MenuBase::SetBackground(pimoroni::Pen pen) { 40 | this->bg_pen = pen; 41 | } 42 | 43 | pimoroni::Pen MenuBase::GetBackground() { 44 | return this->bg_pen; 45 | } 46 | 47 | void MenuBase::SetForeground(pimoroni::Pen pen) { 48 | this->fg_pen = pen; 49 | } 50 | 51 | pimoroni::Pen MenuBase::GetForeground() { 52 | return this->fg_pen; 53 | } 54 | 55 | void draw() {} 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/menu_list.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "menu_list.hpp" 24 | 25 | namespace twobytes { 26 | 27 | MenuList::MenuList() : rows_(0,0) {} 28 | 29 | void MenuList::SetTitleBackground(pimoroni::Pen pen) { 30 | this->title_background_pen_ = pen; 31 | } 32 | 33 | void MenuList::SetTitleForeground(pimoroni::Pen pen) { 34 | this->title_foreground_pen_ = pen; 35 | } 36 | 37 | void MenuList::AddRow(MenuBase* item) { 38 | rows_.push_back(item); 39 | } 40 | 41 | int MenuList::Size() { 42 | return rows_.size(); 43 | } 44 | 45 | MenuBase* MenuList::GetHighlightedRow() { 46 | return rows_.at(highlighted_row_); 47 | } 48 | 49 | void MenuList::Previous() { 50 | if (highlighted_row_ == 0){ 51 | return; 52 | } 53 | 54 | --highlighted_row_; 55 | } 56 | 57 | void MenuList::Next() { 58 | if (highlighted_row_ == rows_.size()-1){ 59 | return; 60 | } 61 | 62 | ++highlighted_row_; 63 | } 64 | 65 | int MenuList::HighlightedRowIndex() { 66 | return highlighted_row_; 67 | } 68 | 69 | 70 | void MenuList::DrawTitle(pimoroni::PicoDisplay display) { 71 | 72 | if (this->GetTitle().length() == 0) { 73 | return; 74 | } 75 | 76 | pimoroni::Rect title_rect(0,0, pimoroni::PicoDisplay::PORTRAIT_WIDTH, 20); 77 | 78 | display.set_pen(title_background_pen_); 79 | display.rectangle(title_rect); 80 | display.set_pen(title_foreground_pen_); 81 | display.text(GetTitle(), pimoroni::Point(20, 3), pimoroni::PicoDisplay::PORTRAIT_WIDTH); 82 | } 83 | 84 | void MenuList::DrawRows(pimoroni::PicoDisplay display){ 85 | 86 | int startY = rowHeight; 87 | int rs = rows_.size(); 88 | 89 | if (this->GetTitle().length() == 0) { 90 | startY = 0; 91 | } 92 | 93 | for(int i = 0; i < rows_.size(); i++) { 94 | MenuBase* mi = rows_.at(i); 95 | if (i == highlighted_row_) { 96 | display.set_pen(255,165,0); 97 | } else { 98 | display.set_pen(this->GetBackground()); 99 | } 100 | pimoroni::Rect bg_rect(0, startY, pimoroni::PicoDisplay::PORTRAIT_WIDTH, 20); 101 | display.rectangle(bg_rect); 102 | display.set_pen(title_background_pen_); 103 | display.line(pimoroni::Point(0, startY+rowHeight-1), pimoroni::Point(pimoroni::PicoDisplay::PORTRAIT_WIDTH, startY+rowHeight-1)); 104 | display.set_pen(this->GetForeground()); 105 | display.text(mi->GetTitle(), pimoroni::Point(2, startY+4), pimoroni::PicoDisplay::PORTRAIT_WIDTH); 106 | startY+=rowHeight; 107 | } 108 | } 109 | 110 | void MenuList::Draw(pimoroni::PicoDisplay display) { 111 | display.set_pen(this->GetBackground()); 112 | display.clear(); 113 | DrawTitle(display); 114 | DrawRows(display); 115 | display.update(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/pico_menu.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2021 Hamid Elaosta 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pico_menu.hpp" 24 | #include "pico_display.hpp" 25 | #include "menu_action.hpp" 26 | 27 | namespace twobytes { 28 | 29 | int aPressedRecently = 0; 30 | int xPressedRecently = 0; 31 | int bPressedRecently = 0; 32 | int yPressedRecently = 0; 33 | int debounce = 10; 34 | 35 | PicoMenu::PicoMenu(pimoroni::PicoDisplay display) : PicoMenu(pimoroni::PicoDisplay::PORTRAIT_WIDTH,pimoroni::PicoDisplay::PORTRAIT_HEIGHT, display) {} 36 | 37 | PicoMenu::PicoMenu(int width, int height, pimoroni::PicoDisplay display) : width_(width), height_(height), display_(display) { 38 | display_.set_pen(0,0,0); 39 | display_.clear(); 40 | } 41 | 42 | void PicoMenu::SetMenu(MenuList* item) { 43 | this->main_menu_.push(item); 44 | } 45 | 46 | void PicoMenu::Update() { 47 | this->CheckInputs(); 48 | this->Draw(); 49 | } 50 | 51 | void PicoMenu::Previous() { 52 | 53 | if(aPressedRecently == 0) { 54 | aPressedRecently = debounce; 55 | main_menu_.top()->Previous(); 56 | } 57 | 58 | if(aPressedRecently > 0) { 59 | aPressedRecently--; 60 | } 61 | 62 | } 63 | 64 | void PicoMenu::Next() { 65 | 66 | if(xPressedRecently == 0) { 67 | xPressedRecently = debounce; 68 | main_menu_.top()->Next(); 69 | } 70 | 71 | if(xPressedRecently > 0) { 72 | xPressedRecently--; 73 | } 74 | } 75 | 76 | void PicoMenu::Select() { 77 | 78 | if(yPressedRecently == 0) { 79 | yPressedRecently = debounce; 80 | 81 | MenuBase* cr = main_menu_.top()->GetHighlightedRow(); 82 | if ( cr != NULL ) { 83 | this->main_menu_.push(cr); 84 | cr->Execute(); 85 | } 86 | 87 | } 88 | 89 | if(yPressedRecently > 0) { 90 | yPressedRecently--; 91 | } 92 | 93 | } 94 | 95 | void PicoMenu::Back() { 96 | 97 | if(bPressedRecently == 0) { 98 | bPressedRecently = debounce; 99 | 100 | if (this->main_menu_.size() > 1) { 101 | this->main_menu_.pop(); 102 | } 103 | } 104 | 105 | if(bPressedRecently > 0) { 106 | bPressedRecently--; 107 | } 108 | } 109 | 110 | void PicoMenu::Draw() { 111 | this->main_menu_.top()->Draw(this->display_); 112 | } 113 | 114 | void PicoMenu::CheckInputs() { 115 | if(display_.is_pressed(up_button_)) { 116 | this->Previous(); 117 | } 118 | 119 | if(display_.is_pressed(down_button_)) { 120 | this->Next(); 121 | } 122 | 123 | if(display_.is_pressed(back_button_)) { 124 | this->Back(); 125 | } 126 | 127 | if(display_.is_pressed(select_button_)) { 128 | this->Select(); 129 | } 130 | } 131 | } 132 | --------------------------------------------------------------------------------