Convert UI to GUI Slice and cleanup some small stuff along the way

This commit is contained in:
KemoNine 2020-09-08 21:53:59 -04:00
parent b765f65d9c
commit 8c8b4fd831
69 changed files with 37841 additions and 48 deletions

2
.gitignore vendored
View file

@ -1 +1,3 @@
build/ build/
serial_debugger*.bak
gui_backup/

15
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,15 @@
{
"files.associations": {
"xtemplate.h": "c",
"guislice_drv.h": "c",
"xradial.h": "c",
"*.tpp": "cpp",
"array": "cpp",
"deque": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"string_view": "cpp",
"initializer_list": "cpp"
}
}

View file

@ -1,3 +1,18 @@
//<App !Start!>
// FILE: [serial_debugger.ino]
// Created by GUIslice Builder version: [0.15.b004]
//
// GUIslice Builder Generated File
//
// For the latest guides, updates and support view:
// https://github.com/ImpulseAdventure/GUIslice
//
//<App !End!>
// ------------------------------------------------
// Headers to include
// ------------------------------------------------
//<Includes !Start!>
// Varous system includes // Varous system includes
#include <Arduino.h> #include <Arduino.h>
@ -8,22 +23,29 @@
#include <BBQ10Keyboard.h> #include <BBQ10Keyboard.h>
#include <CircularBuffer.h> #include <CircularBuffer.h>
// Debugging via serial monitor (don't turn this on unless you're hacking on the firmware code) // Various local includes
#define DEBUG true #include "serial_debugger_GSLC.h"
//<Includes !End!>
// ------------------------------------------------
// Program Globals
// ------------------------------------------------
// Battery level measurement // Battery level measurement
#define VBATPIN A6 #define VBATPIN A6
float measuredVBat; float measuredVBat;
float batteryPercent; int batteryPercent;
// TFT Setup // TFT Setup
#define TFT_CS 9 #define TFT_CS 9
#define TFT_DC 10 #define TFT_DC 10
Adafruit_ILI9341 tft(TFT_CS, TFT_DC); Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
#include <Fonts/FreeMono9pt7b.h> #include <Fonts/FreeMono9pt7b.h>
#define CHARS_HORIZONTAL 28 #define CHARS_HORIZONTAL 29
#define CHARS_ROWS 13 #define CHARS_ROWS 11
CircularBuffer<char,364> textBuffer; #define CHARS_TOTAL CHARS_HORIZONTAL * CHARS_ROWS
CircularBuffer<char,CHARS_TOTAL> textBuffer;
// Keyboard // Keyboard
BBQ10Keyboard keyboard; BBQ10Keyboard keyboard;
@ -44,20 +66,48 @@ BBQ10Keyboard keyboard;
Adafruit_NeoPixel pixels_board(PIXELS_NUM_BOARD, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel pixels_board(PIXELS_NUM_BOARD, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixels_wing(PIXELS_NUM_WING, PIXELS_WING_PIN, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel pixels_wing(PIXELS_NUM_WING, PIXELS_WING_PIN, NEO_GRB + NEO_KHZ800);
// GUI state globals
bool textChanged = false;
// Various loop handlers // Various loop handlers
void handlerKeyboard(); void handlerKeyboard();
void handlerBatteryLevel(); void handlerBatteryLevel();
void processRingBuffer();
// UI screens // Save some element references for direct access
void screenClear(); //<Save_References !Start!>
gslc_tsElemRef* m_pElemBatteryLevel= NULL;
gslc_tsElemRef* m_pElemStatusText = NULL;
gslc_tsElemRef* m_pElemText = NULL;
//<Save_References !End!>
// Color conversion (RGB888 -> RGB565 used by Adafruit GFX) // Define debug message function
uint16_t RGB565(uint8_t r, uint8_t g, uint8_t b) { static int16_t DebugOut(char ch) { if (ch == (char)'\n') Serial.println(""); else Serial.write(ch); return 0; }
return ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3);
} // ------------------------------------------------
// Callback Methods
// ------------------------------------------------
//<Button Callback !Start!>
//<Button Callback !End!>
//<Checkbox Callback !Start!>
//<Checkbox Callback !End!>
//<Keypad Callback !Start!>
//<Keypad Callback !End!>
//<Spinner Callback !Start!>
//<Spinner Callback !End!>
//<Listbox Callback !Start!>
//<Listbox Callback !End!>
//<Draw Callback !Start!>
//<Draw Callback !End!>
//<Slider Enums !Start!>
//<Slider Enums !End!>
//<Tick Callback !Start!>
//<Tick Callback !End!>
// Various things that need to be setup via the Arduino standard methods
void setup() { void setup() {
Serial.begin(115200);
// Setup red LED to indicate device is on (in case we disable NeoPixel battery level later) // Setup red LED to indicate device is on (in case we disable NeoPixel battery level later)
pinMode(3, OUTPUT); pinMode(3, OUTPUT);
digitalWrite(3, HIGH); digitalWrite(3, HIGH);
@ -80,50 +130,68 @@ void setup() {
pixels_board.setPixelColor(0, pixels_board.Color(0, 0, 255)); pixels_board.setPixelColor(0, pixels_board.Color(0, 0, 255));
pixels_board.show(); pixels_board.show();
// Setup TFT
tft.begin();
tft.setRotation(1);
screenClear();
tft.println("");
tft.setFont(&FreeMono9pt7b);
// Setup BBQ10Keyboard // Setup BBQ10Keyboard
Wire.begin(); Wire.begin();
keyboard.begin(); keyboard.begin();
keyboard.setBacklight(0.5f); keyboard.setBacklight(0.5f);
// GUI Slice (TFT is setup through GUI Slice)
gslc_InitDebug(&DebugOut);
InitGUIslice_gen();
} }
// Main program via the Arduino standard methods // -----------------------------------
// Main event loop
// -----------------------------------
void loop() { void loop() {
// Reset text changed state (processing methods will update if needed)
textChanged = false;
// Handle keyboard events // Handle keyboard events
handlerKeyboard(); handlerKeyboard();
// Update battery level as appropriate // Update battery level as appropriate
handlerBatteryLevel(); handlerBatteryLevel();
// Update UI
if (textChanged) {
processRingBuffer();
}
gslc_Update(&m_gui);
} }
// Move text from buffer -> text box on screen
void processRingBuffer() {
// Add keycodes to text box
char* textForDisplay = (char*) calloc(textBuffer.size(), sizeof(char));
for (int i=0; i<textBuffer.size(); i++) {
textForDisplay[i] = textBuffer[i];
}
gslc_ElemXTextboxAdd(&m_gui, m_pElemText, textForDisplay);
}
// Keyboard handler
void handlerKeyboard() { void handlerKeyboard() {
// Check if keys were pressed // Check if keys were pressed
if (keyboard.keyCount()) { if (keyboard.keyCount()) {
// Get keyboard event // Get keyboard event
const BBQ10Keyboard::KeyEvent key = keyboard.keyEvent(); const BBQ10Keyboard::KeyEvent key = keyboard.keyEvent();
char output[28]; // Add key code to display ring buffer *if* it's pressed ; no need for other events presently
snprintf(output, 28, "key: '%c' (dec %d, hex %02x)", key.key, key.key, key.key); if (key.state == BBQ10Keyboard::StatePress) {
for (int i=0; i<sizeof(output); i++) { char output[28];
char toPush = output[i]; snprintf(output, 28, "key: '%c' (dec %d, hex %02x)", key.key, key.key, key.key);
if (toPush != '\0') { textChanged = true;
textBuffer.push(toPush); for (int i=0; i<sizeof(output); i++) {
char toPush = output[i];
if (toPush != '\0') {
textBuffer.push(toPush);
}
else if (toPush == '\0') {
textBuffer.push('\n');
break;
}
} }
else if (toPush == '\0') {
textBuffer.push('\n');
break;
}
}
screenClear();
for (int i=0; i<textBuffer.size(); i++) {
tft.print(textBuffer[i]);
} }
// Pick color for signaling a key was pressed (short or long press) // Pick color for signaling a key was pressed (short or long press)
@ -150,24 +218,32 @@ void handlerBatteryLevel() {
measuredVBat *= 2; // we divided by 2, so multiply back measuredVBat *= 2; // we divided by 2, so multiply back
measuredVBat *= 3.3; // Multiply by 3.3V, our reference voltage measuredVBat *= 3.3; // Multiply by 3.3V, our reference voltage
measuredVBat /= 1024; // convert to voltage measuredVBat /= 1024; // convert to voltage
batteryPercent = measuredVBat / 3.3; batteryPercent = (measuredVBat / 3.3) * 100;
if (batteryPercent >= 0.75) {
gslc_tsColor colorForHeaderElements;
if (batteryPercent >= 75) {
pixels_board.setPixelColor(0, pixels_board.Color(0, 255, 0)); pixels_board.setPixelColor(0, pixels_board.Color(0, 255, 0));
colorForHeaderElements = GSLC_COL_GREEN;
} }
else if (batteryPercent >= 0.50) { else if (batteryPercent >= 50) {
colorForHeaderElements = GSLC_COL_YELLOW;
pixels_board.setPixelColor(0, pixels_board.Color(255, 255, 0)); pixels_board.setPixelColor(0, pixels_board.Color(255, 255, 0));
} }
else if (batteryPercent >= 0.25) { else if (batteryPercent >= 25) {
colorForHeaderElements = GSLC_COL_ORANGE;
pixels_board.setPixelColor(0, pixels_board.Color(255, 128, 0)); pixels_board.setPixelColor(0, pixels_board.Color(255, 128, 0));
} }
else { else {
colorForHeaderElements = GSLC_COL_RED;
pixels_board.setPixelColor(0, pixels_board.Color(255, 0, 0)); pixels_board.setPixelColor(0, pixels_board.Color(255, 0, 0));
} }
gslc_ElemXProgressSetVal(&m_gui, m_pElemBatteryLevel, batteryPercent);
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(&m_gui,m_pElemBatteryLevel,GSLC_TYPEX_PROGRESS,__LINE__);
pGauge->colGauge = colorForHeaderElements;
gslc_ElemSetTxtCol(&m_gui, m_pElemBatteryLevel, colorForHeaderElements);
gslc_ElemSetTxtCol(&m_gui, m_pElemStatusText, colorForHeaderElements);
// Update GUI and NeoPixel with battery status information
pixels_board.show(); pixels_board.show();
} }
// Clear the screen
void screenClear() {
tft.setCursor(0, 0);
tft.fillScreen(RGB565(0, 0, 0));
}

203
serial_debugger.ino.beta Normal file
View file

@ -0,0 +1,203 @@
// Varous system includes
#include <Arduino.h>
// Various library includes
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <BBQ10Keyboard.h>
#include <CircularBuffer.h>
// Various local includes
#include "serial_debugger_GSLC.h"
// Battery level measurement
#define VBATPIN A6
float measuredVBat;
int batteryPercent;
// TFT Setup
#define TFT_CS 9
#define TFT_DC 10
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
#include <Fonts/FreeMono9pt7b.h>
#define CHARS_HORIZONTAL 28
#define CHARS_ROWS 13
CircularBuffer<char,364> textBuffer;
// Keyboard
BBQ10Keyboard keyboard;
#define KBD_BTN_1 0x06
#define KBD_BTN_2 0x11
#define KBD_BTN_3 0x07
#define KBD_BTN_4 0x12
#define KBD_SW_UP 0x01
#define KBD_SW_DN 0x02
#define KBD_SW_LF 0x03
#define KBD_SW_RT 0x04
#define KBD_SW_OK 0x05
// NeoPixels
#define PIXELS_NUM_BOARD 1
#define PIXELS_NUM_WING 1
#define PIXELS_WING_PIN 11
Adafruit_NeoPixel pixels_board(PIXELS_NUM_BOARD, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixels_wing(PIXELS_NUM_WING, PIXELS_WING_PIN, NEO_GRB + NEO_KHZ800);
// Various loop handlers
void handlerKeyboard();
void handlerBatteryLevel();
// GUI Slice
gslc_tsElemRef* m_pElemBatteryLevel= NULL;
gslc_tsElemRef* m_pElemStatusText = NULL;
gslc_tsElemRef* m_pElemText = NULL;
gslc_tsElemRef* m_pTextSlider = NULL;
static int16_t DebugOut(char ch) { if (ch == (char)'\n') Serial.println(""); else Serial.write(ch); return 0; }
bool CbSlidePos(void* pvGui,void* pvElemRef,int16_t nPos);
void setup() {
Serial.begin(115200);
// Setup red LED to indicate device is on (in case we disable NeoPixel battery level later)
pinMode(3, OUTPUT);
digitalWrite(3, HIGH);
// Setup NeoPixels
pixels_board.begin();
pixels_wing.begin();
pixels_board.clear();
pixels_wing.clear();
pixels_board.setBrightness(10);
pixels_wing.setBrightness(5);
// Start with blank wing indicator
pixels_wing.show();
// Green : pixels.Color(0, 255, 0)
// Yellow : pixels.Color(255, 255, 0)
// Orange : pixels.Color(255, 128, 0)
// Red : pixels.Color(255, 0, 0)
pixels_board.setPixelColor(0, pixels_board.Color(0, 0, 255));
pixels_board.show();
// Setup BBQ10Keyboard
Wire.begin();
keyboard.begin();
keyboard.setBacklight(0.5f);
// GUI Slice (TFT is setup through GUI Slice)
gslc_InitDebug(&DebugOut);
InitGUIslice_gen();
}
// Main program via the Arduino standard methods
void loop() {
// Handle keyboard events
handlerKeyboard();
// Update battery level as appropriate
handlerBatteryLevel();
// Update UI
gslc_Update(&m_gui);
}
// Keyboard handler
void handlerKeyboard() {
// Check if keys were pressed
if (keyboard.keyCount()) {
// Get keyboard event
const BBQ10Keyboard::KeyEvent key = keyboard.keyEvent();
char output[28];
snprintf(output, 28, "key: '%c' (dec %d, hex %02x)", key.key, key.key, key.key);
for (int i=0; i<sizeof(output); i++) {
char toPush = output[i];
if (toPush != '\0') {
textBuffer.push(toPush);
}
else if (toPush == '\0') {
textBuffer.push('\n');
break;
}
}
// Add keycodes to text box
for (int i=0; i<textBuffer.size(); i++) {
//tft.print(textBuffer[i]);
}
// Pick color for signaling a key was pressed (short or long press)
uint32_t keyboard_pixel_color = pixels_wing.Color(0, 0, 255);
if (key.state == BBQ10Keyboard::StateLongPress) {
keyboard_pixel_color = pixels_wing.Color(0, 255, 255);
}
// Display indicator accordingly
if (key.state == BBQ10Keyboard::StatePress || key.state == BBQ10Keyboard::StateLongPress) {
pixels_wing.setPixelColor(0, keyboard_pixel_color);
pixels_wing.show();
}
else {
pixels_wing.clear();
pixels_wing.show();
}
}
}
// Measure battery level and change NeoPixel accordingly
void handlerBatteryLevel() {
measuredVBat = analogRead(VBATPIN);
measuredVBat *= 2; // we divided by 2, so multiply back
measuredVBat *= 3.3; // Multiply by 3.3V, our reference voltage
measuredVBat /= 1024; // convert to voltage
batteryPercent = (measuredVBat / 3.3) * 100;
gslc_tsColor colorForHeaderElements;
if (batteryPercent >= 75) {
pixels_board.setPixelColor(0, pixels_board.Color(0, 255, 0));
colorForHeaderElements = GSLC_COL_GREEN;
}
else if (batteryPercent >= 50) {
colorForHeaderElements = GSLC_COL_YELLOW;
pixels_board.setPixelColor(0, pixels_board.Color(255, 255, 0));
}
else if (batteryPercent >= 25) {
colorForHeaderElements = GSLC_COL_ORANGE;
pixels_board.setPixelColor(0, pixels_board.Color(255, 128, 0));
}
else {
colorForHeaderElements = GSLC_COL_RED;
pixels_board.setPixelColor(0, pixels_board.Color(255, 0, 0));
}
gslc_ElemXProgressSetVal(&m_gui, m_pElemBatteryLevel, batteryPercent);
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(&m_gui,m_pElemBatteryLevel,GSLC_TYPEX_PROGRESS,__LINE__);
pGauge->colGauge = colorForHeaderElements;
gslc_ElemSetTxtCol(&m_gui, m_pElemBatteryLevel, colorForHeaderElements);
gslc_ElemSetTxtCol(&m_gui, m_pElemStatusText, colorForHeaderElements);
// Update GUI and NeoPixel with battery status information
gslc_Update(&m_gui);
pixels_board.show();
}
// Slider callback function
bool CbSlidePos(void* pvGui,void* pvElemRef,int16_t nPos) {
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
int16_t nVal;
// From the element's ID we can determine which slider was updated.
switch (pElem->nId) {
case E_TXTSCROLL:
// Fetch the slider position
nVal = gslc_ElemXSliderGetPos(pGui,m_pTextSlider);
break;
default:
break;
}
return true;
}

218
serial_debugger.ino.orig Normal file
View file

@ -0,0 +1,218 @@
//<File !Start!>
// Varous system includes
#include <Arduino.h>
//<File !End!>
// Various library includes
#include <Adafruit_NeoPixel.h>
#include <Adafruit_GFX.h>
//<Includes !Start!>
//<Includes !End!>
#include <Adafruit_ILI9341.h>
#include <BBQ10Keyboard.h>
#include <CircularBuffer.h>
// Various local includes
#include "serial_debugger_GSLC.h"
// Battery level measurement
#define VBATPIN A6
float measuredVBat;
int batteryPercent;
// TFT Setup
#define TFT_CS 9
#define TFT_DC 10
Adafruit_ILI9341 tft(TFT_CS, TFT_DC);
#include <Fonts/FreeMono9pt7b.h>
#define CHARS_HORIZONTAL 28
#define CHARS_ROWS 13
CircularBuffer<char,364> textBuffer;
// Keyboard
BBQ10Keyboard keyboard;
#define KBD_BTN_1 0x06
#define KBD_BTN_2 0x11
#define KBD_BTN_3 0x07
#define KBD_BTN_4 0x12
#define KBD_SW_UP 0x01
#define KBD_SW_DN 0x02
#define KBD_SW_LF 0x03
#define KBD_SW_RT 0x04
#define KBD_SW_OK 0x05
// NeoPixels
#define PIXELS_NUM_BOARD 1
#define PIXELS_NUM_WING 1
#define PIXELS_WING_PIN 11
Adafruit_NeoPixel pixels_board(PIXELS_NUM_BOARD, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixels_wing(PIXELS_NUM_WING, PIXELS_WING_PIN, NEO_GRB + NEO_KHZ800);
// Various loop handlers
void handlerKeyboard();
void handlerBatteryLevel();
// GUI Slice
gslc_tsElemRef* m_pElemBatteryLevel= NULL;
gslc_tsElemRef* m_pElemStatusText = NULL;
gslc_tsElemRef* m_pElemText = NULL;
gslc_tsElemRef* m_pTextSlider = NULL;
static int16_t DebugOut(char ch) { if (ch == (char)'\n') Serial.println(""); else Serial.write(ch); return 0; }
bool CbSlidePos(void* pvGui,void* pvElemRef,int16_t nPos);
void setup() {
Serial.begin(115200);
// Setup red LED to indicate device is on (in case we disable NeoPixel battery level later)
pinMode(3, OUTPUT);
digitalWrite(3, HIGH);
// Setup NeoPixels
pixels_board.begin();
pixels_wing.begin();
pixels_board.clear();
pixels_wing.clear();
pixels_board.setBrightness(10);
pixels_wing.setBrightness(5);
// Start with blank wing indicator
pixels_wing.show();
// Green : pixels.Color(0, 255, 0)
// Yellow : pixels.Color(255, 255, 0)
// Orange : pixels.Color(255, 128, 0)
// Red : pixels.Color(255, 0, 0)
pixels_board.setPixelColor(0, pixels_board.Color(0, 0, 255));
pixels_board.show();
// Setup BBQ10Keyboard
Wire.begin();
keyboard.begin();
keyboard.setBacklight(0.5f);
// GUI Slice (TFT is setup through GUI Slice)
gslc_InitDebug(&DebugOut);
InitGUIslice_gen();
}
// Main program via the Arduino standard methods
void loop() {
// Handle keyboard events
handlerKeyboard();
// Update battery level as appropriate
handlerBatteryLevel();
// Update UI
gslc_Update(&m_gui);
}
// Keyboard handler
void handlerKeyboard() {
// Check if keys were pressed
if (keyboard.keyCount()) {
// Get keyboard event
const BBQ10Keyboard::KeyEvent key = keyboard.keyEvent();
char output[28];
snprintf(output, 28, "key: '%c' (dec %d, hex %02x)", key.key, key.key, key.key);
for (int i=0; i<sizeof(output); i++) {
char toPush = output[i];
if (toPush != '\0') {
textBuffer.push(toPush);
}
else if (toPush == '\0') {
textBuffer.push('\n');
break;
}
}
// Add keycodes to text box
for (int i=0; i<textBuffer.size(); i++) {
//tft.print(textBuffer[i]);
}
// Pick color for signaling a key was pressed (short or long press)
uint32_t keyboard_pixel_color = pixels_wing.Color(0, 0, 255);
if (key.state == BBQ10Keyboard::StateLongPress) {
keyboard_pixel_color = pixels_wing.Color(0, 255, 255);
}
// Display indicator accordingly
if (key.state == BBQ10Keyboard::StatePress || key.state == BBQ10Keyboard::StateLongPress) {
pixels_wing.setPixelColor(0, keyboard_pixel_color);
pixels_wing.show();
}
else {
pixels_wing.clear();
pixels_wing.show();
}
}
}
// Measure battery level and change NeoPixel accordingly
void handlerBatteryLevel() {
measuredVBat = analogRead(VBATPIN);
measuredVBat *= 2; // we divided by 2, so multiply back
measuredVBat *= 3.3; // Multiply by 3.3V, our reference voltage
measuredVBat /= 1024; // convert to voltage
batteryPercent = (measuredVBat / 3.3) * 100;
gslc_tsColor colorForHeaderElements;
if (batteryPercent >= 75) {
pixels_board.setPixelColor(0, pixels_board.Color(0, 255, 0));
colorForHeaderElements = GSLC_COL_GREEN;
}
else if (batteryPercent >= 50) {
colorForHeaderElements = GSLC_COL_YELLOW;
pixels_board.setPixelColor(0, pixels_board.Color(255, 255, 0));
}
else if (batteryPercent >= 25) {
colorForHeaderElements = GSLC_COL_ORANGE;
pixels_board.setPixelColor(0, pixels_board.Color(255, 128, 0));
}
else {
colorForHeaderElements = GSLC_COL_RED;
pixels_board.setPixelColor(0, pixels_board.Color(255, 0, 0));
}
gslc_ElemXProgressSetVal(&m_gui, m_pElemBatteryLevel, batteryPercent);
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(&m_gui,m_pElemBatteryLevel,GSLC_TYPEX_PROGRESS,__LINE__);
pGauge->colGauge = colorForHeaderElements;
gslc_ElemSetTxtCol(&m_gui, m_pElemBatteryLevel, colorForHeaderElements);
gslc_ElemSetTxtCol(&m_gui, m_pElemStatusText, colorForHeaderElements);
// Update GUI and NeoPixel with battery status information
gslc_Update(&m_gui);
pixels_board.show();
}
// Slider callback function
bool CbSlidePos(void* pvGui,void* pvElemRef,int16_t nPos) {
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
int16_t nVal;
// From the element's ID we can determine which slider was updated.
switch (pElem->nId) {
case E_TXTSCROLL:
// Fetch the slider position
nVal = gslc_ElemXSliderGetPos(pGui,m_pTextSlider);
break;
default:
break;
}
return true;
}
//<Checkbox Callback !Start!>
//<Checkbox Callback !End!>
//<Keypad Callback !Start!>
//<Keypad Callback !End!>
//<Spinner Callback !Start!>
//<Spinner Callback !End!>
//<Listbox Callback !Start!>
//<Listbox Callback !End!>

BIN
serial_debugger.prj Normal file

Binary file not shown.

172
serial_debugger_GSLC.h Normal file
View file

@ -0,0 +1,172 @@
//<File !Start!>
// FILE: [serial_debugger_GSLC.h]
// Created by GUIslice Builder version: [0.15.b004]
//
// GUIslice Builder Generated GUI Framework File
//
// For the latest guides, updates and support view:
// https://github.com/ImpulseAdventure/GUIslice
//
//<File !End!>
#ifndef _GUISLICE_GEN_H
#define _GUISLICE_GEN_H
// ------------------------------------------------
// Headers to include
// ------------------------------------------------
#include "src/guislice/GUIslice.h"
#include "src/guislice/GUIslice_drv.h"
// Include any extended elements
//<Includes !Start!>
// Include extended elements
#include "src/guislice/XProgress.h"
#include "src/guislice/XTextbox.h"
//<Includes !End!>
// ------------------------------------------------
// Headers and Defines for fonts
// Note that font files are located within the Adafruit-GFX library folder:
// ------------------------------------------------
//<Fonts !Start!>
#if defined(DRV_DISP_TFT_ESPI)
#error Project tab->Target Platform should be tft_espi
#endif
#include <Adafruit_GFX.h>
#include "src/guislice/NotoMono8pt7b.h"
//<Fonts !End!>
// ------------------------------------------------
// Defines for resources
// ------------------------------------------------
//<Resources !Start!>
//<Resources !End!>
// ------------------------------------------------
// Enumerations for pages, elements, fonts, images
// ------------------------------------------------
//<Enum !Start!>
enum {E_PG_MAIN};
enum {E_ELEM_BATT_LEVEL,E_ELEM_STATUS,E_ELEM_TEXT};
// Must use separate enum for fonts with MAX_FONT at end to use gslc_FontSet.
enum {E_AO_NOTOMONO8PT7B,MAX_FONT};
//<Enum !End!>
// ------------------------------------------------
// Instantiate the GUI
// ------------------------------------------------
// ------------------------------------------------
// Define the maximum number of elements and pages
// ------------------------------------------------
//<ElementDefines !Start!>
#define MAX_PAGE 1
#define MAX_ELEM_PG_MAIN 3 // # Elems total on page
#define MAX_ELEM_PG_MAIN_RAM MAX_ELEM_PG_MAIN // # Elems in RAM
//<ElementDefines !End!>
// ------------------------------------------------
// Create element storage
// ------------------------------------------------
gslc_tsGui m_gui;
gslc_tsDriver m_drv;
gslc_tsFont m_asFont[MAX_FONT];
gslc_tsPage m_asPage[MAX_PAGE];
//<GUI_Extra_Elements !Start!>
gslc_tsElem m_asPage1Elem[MAX_ELEM_PG_MAIN_RAM];
gslc_tsElemRef m_asPage1ElemRef[MAX_ELEM_PG_MAIN];
gslc_tsXProgress m_sXBarGauge1;
gslc_tsXTextbox m_sTextbox1;
char m_acTextboxBuf1[952]; // NRows=17 NCols=56
#define MAX_STR 100
//<GUI_Extra_Elements !End!>
// ------------------------------------------------
// Program Globals
// ------------------------------------------------
// Element References for direct access
//<Extern_References !Start!>
extern gslc_tsElemRef* m_pElemBatteryLevel;
extern gslc_tsElemRef* m_pElemStatusText;
extern gslc_tsElemRef* m_pElemText;
extern gslc_tsElemRef* m_pTextSlider;
//<Extern_References !End!>
// Define debug message function
static int16_t DebugOut(char ch);
// ------------------------------------------------
// Callback Methods
// ------------------------------------------------
bool CbBtnCommon(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY);
bool CbCheckbox(void* pvGui, void* pvElemRef, int16_t nSelId, bool bState);
bool CbDrawScanner(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
bool CbKeypad(void* pvGui, void *pvElemRef, int16_t nState, void* pvData);
bool CbListbox(void* pvGui, void* pvElemRef, int16_t nSelId);
bool CbSlidePos(void* pvGui,void* pvElemRef,int16_t nPos);
bool CbSpinner(void* pvGui, void *pvElemRef, int16_t nState, void* pvData);
bool CbTickScanner(void* pvGui,void* pvScope);
// ------------------------------------------------
// Create page elements
// ------------------------------------------------
void InitGUIslice_gen()
{
gslc_tsElemRef* pElemRef = NULL;
if (!gslc_Init(&m_gui,&m_drv,m_asPage,MAX_PAGE,m_asFont,MAX_FONT)) { return; }
// ------------------------------------------------
// Load Fonts
// ------------------------------------------------
//<Load_Fonts !Start!>
if (!gslc_FontSet(&m_gui,E_AO_NOTOMONO8PT7B,GSLC_FONTREF_PTR,&NotoMono8pt7b,1)) { return; }
//<Load_Fonts !End!>
//<InitGUI !Start!>
gslc_PageAdd(&m_gui,E_PG_MAIN,m_asPage1Elem,MAX_ELEM_PG_MAIN_RAM,m_asPage1ElemRef,MAX_ELEM_PG_MAIN);
// NOTE: The current page defaults to the first page added. Here we explicitly
// ensure that the main page is the correct page no matter the add order.
gslc_SetPageCur(&m_gui,E_PG_MAIN);
// Set Background to a flat color
gslc_SetBkgndColor(&m_gui,GSLC_COL_BLACK);
// -----------------------------------
// PAGE: E_PG_MAIN
// Create progress bar E_ELEM_BATT_LEVEL
pElemRef = gslc_ElemXProgressCreate(&m_gui,E_ELEM_BATT_LEVEL,E_PG_MAIN,&m_sXBarGauge1,
(gslc_tsRect){263,5,50,15},0,100,0,GSLC_COL_GREEN,false);
m_pElemBatteryLevel = pElemRef;
// Create E_ELEM_STATUS text label
pElemRef = gslc_ElemCreateTxt(&m_gui,E_ELEM_STATUS,E_PG_MAIN,(gslc_tsRect){2,2,250,21},
(char*)"UART0 / 1151200",0,E_AO_NOTOMONO8PT7B);
gslc_ElemSetTxtCol(&m_gui,pElemRef,GSLC_COL_GREEN);
m_pElemStatusText = pElemRef;
// Create textbox
pElemRef = gslc_ElemXTextboxCreate(&m_gui,E_ELEM_TEXT,E_PG_MAIN,&m_sTextbox1,
(gslc_tsRect){3,30,313,202},E_AO_NOTOMONO8PT7B,
(char*)&m_acTextboxBuf1,17,56);
gslc_ElemXTextboxWrapSet(&m_gui,pElemRef,true);
gslc_ElemSetTxtCol(&m_gui,pElemRef,GSLC_COL_GRAY_LT3);
gslc_ElemSetCol(&m_gui,pElemRef,GSLC_COL_GRAY,GSLC_COL_BLACK,GSLC_COL_BLACK);
m_pElemText = pElemRef;
//<InitGUI !End!>
//<Startup !Start!>
//<Startup !End!>
}
#endif // end _GUISLICE_GEN_H

5140
src/guislice/GUIslice.c Normal file

File diff suppressed because it is too large Load diff

3801
src/guislice/GUIslice.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,218 @@
#ifndef _GUISLICE_CONFIG_H_
#define _GUISLICE_CONFIG_H_
// =======================================================================
// GUIslice library (user configuration) selection by device
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
// \file GUIslice_config.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// =========================================================================================
// SELECT ONE OF THE FOLLOWING EXAMPLE CONFIGURATIONS OR ADD YOUR OWN
// - Uncomment one of the following lines
// - These example configurations are located in the /configs folder
// - To add your own, make a copy of an example config, rename it
// and add it to the list here.
// - If no line is uncommented, then the default combined configuration
// file will be used, ie. GUIslice_config_ard.h / GUIslice_config_linux.h
// which is selected at the bottom of this file
// - Refer to https://github.com/ImpulseAdventure/GUIslice/wiki/Display-Config-Table
// to help identify a suitable config for your MCU shield / display
// - Multiple configurations can be supported using the method described here:
// https://github.com/ImpulseAdventure/GUIslice/wiki/Arduino-Multiple-Configs
// =========================================================================================
// =========================================================================================
// IMPORTANT: Ensure you backup any custom config files before updating GUIslice!
// The Arduino IDE deletes all files within the library when updating
// =========================================================================================
// ---------------------------------------------------------------------------------------
// Add your own configs here:
// ---------------------------------------------------------------------------------------
//#include "../configs/my-config.h"
// ---------------------------------------------------------------------------------------
// Example configs included in library /configs:
// ---------------------------------------------------------------------------------------
// Arduino, ARM SAMD, Cortex M0/M4, nRF52:
// ------------------------------------------------------
//#include "../configs/ard-shld-adafruit_18_joy.h"
//#include "../configs/ard-shld-adafruit_28_cap.h"
//#include "../configs/ard-shld-adafruit_28_res.h"
//#include "../configs/ard-shld-eastrising_50_ra8875_res.h"
//#include "../configs/ard-shld-eastrising_50_ra8875_sumo_res.h"
//#include "../configs/ard-shld-eastrising_50_ssd1963_res.h"
//#include "../configs/ard-shld-eastrising_70_ra8876_gv.h"
//#include "../configs/ard-shld-elegoo_28_res.h"
//#include "../configs/ard-shld-generic1_35_touch.h"
//#include "../configs/ard-shld-gevino_tft.h"
//#include "../configs/ard-shld-ili9341_16b_touch.h"
//#include "../configs/ard-shld-mcufriend.h"
//#include "../configs/ard-shld-mcufriend_4wire.h"
//#include "../configs/ard-shld-mcufriend_xpt2046.h"
//#include "../configs/ard-shld-osmart_22_68130_touch.h"
//#include "../configs/ard-shld-waveshare_28_touch.h"
//#include "../configs/ard-shld-waveshare_40_notouch.h"
//#include "../configs/ard-shld-waveshare_40_xpt2046.h"
//#include "../configs/ard-adagfx-hx8347-xpt2046.h"
//#include "../configs/ard-adagfx-hx8357-ft6206.h"
//#include "../configs/ard-adagfx-hx8357-notouch.h"
//#include "../configs/ard-adagfx-hx8357-simple.h"
//#include "../configs/ard-adagfx-hx8357-stmpe610.h"
//#include "../configs/ard-adagfx-ili9341-ft6206.h"
//#include "../configs/ard-adagfx-ili9341-input.h"
//#include "../configs/ard-adagfx-ili9341-notouch.h"
//#include "../configs/ard-adagfx-ili9341-simple.h"
//#include "../configs/ard-adagfx-ili9341-stmpe610.h"
//#include "../configs/ard-adagfx-ili9341-xpt2046.h"
//#include "../configs/ard-adagfx-pcd8544-notouch.h"
//#include "../configs/ard-adagfx-ra8875-notouch.h"
//#include "../configs/ard-adagfx-ra8876-notouch.h"
//#include "../configs/ard-adagfx-ra8876-ft5206.h"
//#include "../configs/ard-adagfx-ssd1306-notouch.h"
//#include "../configs/ard-adagfx-st7735-notouch.h"
//#include "../configs/due-adagfx-ili9341-ft6206.h"
//#include "../configs/due-adagfx-ili9341-urtouch.h"
// ESP8266, ESP32, M5stack, TTGO:
// ------------------------------------------------------
//#include "../configs/esp-shld-m5stack.h"
//#include "../configs/esp-shld-ttgo_btc_ticker.h"
//#include "../configs/esp-tftespi-default-ft6206.h"
//#include "../configs/esp-tftespi-default-notouch.h"
//#include "../configs/esp-tftespi-default-simple.h"
//#include "../configs/esp-tftespi-default-stmpe610.h"
//#include "../configs/esp-tftespi-default-xpt2046.h"
//#include "../configs/esp-tftespi-default-xpt2046_int.h"
// Teensy:
// ------------------------------------------------------
//#include "../configs/teensy-adagfx-ili9341-xpt2046.h"
//#include "../configs/teensy-adagfx-ili9341-xpt2046-audio.h"
//#include "../configs/teensy-adagfx-ili9341_t3-xpt2046.h"
//#include "../configs/teensy-adagfx-ili9341_t3-xpt2046-audio.h"
// STM32:
// ------------------------------------------------------
//#include "../configs/stm32-adagfx-mcufriend-notouch.h"
//#include "../configs/stm32-adagfx-mcufriend-simple.h"
// Multi-device shields:
// ------------------------------------------------------
//#include "../configs/mult-shld-adafruit_24_feather_touch.h"
//#include "../configs/mult-shld-adafruit_35_feather_touch.h"
// Raspberry Pi / LINUX:
// ------------------------------------------------------
//#include "../configs/rpi-sdl1-default-tslib.h"
//#include "../configs/rpi-sdl1-default-sdl.h"
//#include "../configs/linux-sdl1-default-mouse.h"
// =========================================================================================
// DETECT DEVICE PLATFORM
// =========================================================================================
// Detect device platform
// #if defined(__linux__)
// #define GSLC_CFG_LINUX
// #elif defined(__AVR__) && !defined(TEENSYDUINO)
// // Note: Teensy 2 also defines __AVR__, so differentiate with TEENSYDUINO
// #define GSLC_CFG_ARD
// #elif defined(ARDUINO_SAMD_ZERO)
// #define GSLC_CFG_ARD
// #elif defined(ESP8266) || defined(ESP32)
// #define GSLC_CFG_ARD
// #elif defined(NRF52)
// #define GSLC_CFG_ARD
// #elif defined(ARDUINO_STM32_FEATHER) || defined(__STM32F1__)
// #define GSLC_CFG_ARD
// #elif defined(ARDUINO_ARCH_STM32) // ST Core from STMicroelectronics
// #define GSLC_CFG_ARD
// #elif defined(ARDUINO_ARCH_SAM) // Arduino Due
// #define GSLC_CFG_ARD
// #elif defined(ARDUINO_ARCH_SAMD) // M0_PRO
// #define GSLC_CFG_ARD
// #elif defined(__AVR__) && defined(TEENSYDUINO) // Teensy 2
// #define GSLC_DEV_TEENSY_2
// #define GSLC_CFG_ARD
// #elif defined(__MKL26Z64__) // Teensy LC
// #define GSLC_CFG_ARD
// #define GSLC_DEV_TEENSY_LC
// #elif defined(__MK20DX256__) // Teensy 3.2
// #define GSLC_CFG_ARD
// #define GSLC_DEV_TEENSY_3_2
// #elif defined(__MK64FX512__) // Teensy 3.5
// #define GSLC_CFG_ARD
// #define GSLC_DEV_TEENSY_3_5
// #elif defined(__MK66FX1M0__) // Teensy 3.6
// #define GSLC_CFG_ARD
// #define GSLC_DEV_TEENSY_3_6
// #elif defined(__MK66FX1M0__) // Teensy 3.6
// #define GSLC_CFG_ARD
// #define GSLC_DEV_TEENSY_3_6
// #elif defined(__IMXRT1062__)
// #define GSLC_CFG_ARD
// #define GSLC_DEV_TEENSY_4_0
// #else
// #warning Unknown
// #error "Unknown device platform"
// #endif
// Adafruit nRF52840 Feather
#include "ard-adagfx-ili9341-stmpe610.h"
// #include "ard-adagfx-ili9341-notouch.h"
#define GSLC_CFG_ARD
// =========================================================================================
// DEFAULT COMBINED CONFIGURATION FILE
// - If no user configuration has been selected, a default config will be selected here
// - Note that the include guard _GUISLICE_CONFIG_ARD_H_ and _GUISLICE_CONFIG_LINUX_H_
// will prevent these from loading if any of the user configs have been loaded
// =========================================================================================
#if defined(GSLC_CFG_LINUX)
#include "GUIslice_config_linux.h"
#elif defined(GSLC_CFG_ARD)
#include "GUIslice_config_ard.h"
#endif
// -----------------------------------------------------------------------------------------
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_CONFIG_H_

View file

@ -0,0 +1,526 @@
#ifndef _GUISLICE_CONFIG_ARD_H_
#define _GUISLICE_CONFIG_ARD_H_
#warning No config selected in GUIslice_config.h - resorting to defaults.
// =============================================================================
// GUIslice library (user configuration) for:
// - Arduino
// - Cortex-M0
// - ESP8266 / ESP32
// - nRF52
// - STM32
//
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =============================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =============================================================================
// \file GUIslice_config_ard.h
// =============================================================================
// User Configuration
// - This file can be modified by the user to match the
// intended target configuration
// - Please refer to "docs/GUIslice_config_guide.xlsx" for detailed examples
// specific to board and display combinations
// =============================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// =============================================================================
// DISPLAY CONFIGURATION - SELECT
// =============================================================================
// Specify the graphics driver library
// - Uncomment one of the following graphics drivers DRV_DISP_*
// applicable to the device type in use
// --------------------------------------------------------------
// Arduino / ATmega / AVR / Cortex-M0 / nRF52:
#define DRV_DISP_ADAGFX // Adafruit-GFX library
// --------------------------------------------------------------
// ESP8266 / ESP32 / NodeMCU:
// #define DRV_DISP_ADAGFX // Adafruit-GFX library
// #define DRV_DISP_TFT_ESPI // Bodmer/TFT_eSPI library
// #define DRV_DISP_M5STACK // m5stack/M5Stack library
// --------------------------------------------------------------
// STM32:
// #define DRV_DISP_ADAGFX // Adafruit-GFX library
// #define DRV_DISP_ADAGFX_AS // Adafruit-GFX-AS library, high speed using DMA
// --------------------------------------------------------------
// =============================================================================
// TOUCH CONFIGURATION - SELECT
// =============================================================================
// Specify the touchscreen driver
// - Uncomment one of the following touchscreen drivers DRV_TOUCH_*
// applicable to the controller chip in use
#define DRV_TOUCH_NONE // No touchscreen support & no input (GPIO / keyboard)
// #define DRV_TOUCH_ADA_STMPE610 // Adafruit STMPE610 touch driver
// #define DRV_TOUCH_ADA_FT6206 // Adafruit FT6206 touch driver
// #define DRV_TOUCH_ADA_SIMPLE // Adafruit Touchscreen
// #define DRV_TOUCH_TFT_ESPI // TFT_eSPI integrated XPT2046 touch driver
// #define DRV_TOUCH_M5STACK // M5stack integrated button driver
// #define DRV_TOUCH_XPT2046_PS // PaulStoffregen/XPT2046_Touchscreen
// #define DRV_TOUCH_XPT2046_STM // Arduino_STM32/Serasidis_XPT2046_touch (XPT2046_touch.h)
// #define DRV_TOUCH_INPUT // No touchscreen support, but input only (GPIO / keyboard)
// #define DRV_TOUCH_HANDLER // touch handler class
// =============================================================================
// DISPLAY CONFIGURATION - DETAILS
// - Graphics display driver-specific additional configuration
// =============================================================================
// -----------------------------------------------------------------------------
#if defined(DRV_DISP_ADAGFX)
// The Adafruit-GFX library supports a number of displays
// - Select a display sub-type by uncommenting one of the
// following DRV_DISP_ADAGFX_* lines
#define DRV_DISP_ADAGFX_ILI9341 // Adafruit ILI9341
//#define DRV_DISP_ADAGFX_ILI9341_8BIT // Adafruit ILI9341 (8-bit interface)
//#define DRV_DISP_ADAGFX_ST7735 // Adafruit ST7735
//#define DRV_DISP_ADAGFX_SSD1306 // Adafruit SSD1306
//#define DRV_DISP_ADAGFX_HX8347 // prenticedavid/HX8347D_kbv
//#define DRV_DISP_ADAGFX_HX8357 // Adafruit HX8357
//#define DRV_DISP_ADAGFX_PCD8544 // Adafruit PCD8544
// For Adafruit-GFX drivers, define pin connections
// - Define general pins (modify these example pin assignments to match your board)
// - Please refer to "docs/GUIslice_config_guide.xlsx" for detailed examples
#define ADAGFX_PIN_CS 10 // Display chip select
#define ADAGFX_PIN_DC 9 // Display SPI data/command
#define ADAGFX_PIN_RST 0 // Display Reset (some displays could use pin 11)
#define ADAGFX_PIN_SDCS 4 // SD card chip select
#define ADAGFX_PIN_WR A1 // Display write pin (for parallel displays)
#define ADAGFX_PIN_RD A0 // Display read pin (for parallel displays)
// Use hardware SPI interface?
// - Set to 1 to enable hardware SPI interface, 0 to use software SPI
// - Software SPI may support the use of custom pin selection (via ADAGFX_PIN_MOSI,
// ADAGFX_PIN_MISO, ADAGFX_PIN_CLK). These pin definitions can be left blank in
// hardware SPI mode.
#define ADAGFX_SPI_HW 1
// Define custom SPI pin connections used in software SPI mode (ADAGFX_SPI_HW=0)
// - These definitions can be left blank in hardware mode (ADAGFX_SPI_HW=1)
#define ADAGFX_PIN_MOSI
#define ADAGFX_PIN_MISO
#define ADAGFX_PIN_CLK
// Set Default rotation
// you can specify values 0,1,2,3, rotation is clockwise
#define GSLC_ROTATE 1
#elif defined(DRV_DISP_ADAGFX_AS)
//NOTE: this is a optimized driver for STM32 only
// The Adafruit-GFX-AS library supports a number of displays
// - Select a display sub-type by uncommenting one of the
// following DRV_DISP_ADAGFX_* lines
#define DRV_DISP_ADAGFX_ILI9341_STM // Adafruit ILI9341 (STM32 version)
// For Adafruit-GFX drivers, define pin connections
// - Define general pins (modify these example pin assignments to match your board)
// - Please refer to "docs/GUIslice_config_guide.xlsx" for detailed examples
//Note: Fixed pin setting for HW SPI1
// PA5 SCLK
// PA6 MISO
// PA7 MOSI
// USE Arduino STM32 PIN Notations
// - Define to use Arduino STM32 PIN Notations
// #define STM32_NOTATION
#if defined(STM32_NOTATION)
// NOTE: Using Arduino STM32 pin notation
#define ADAGFX_PIN_CS PA4 // Display chip select
#define ADAGFX_PIN_DC PB1 // Display SPI data/command
#define ADAGFX_PIN_RST PB0 // Display Reset (set to -1 in order to use Adafruit-GFX drivers w/o reset)
#define ADAGFX_PIN_SDCS // SD card chip select
#define ADAGFX_PIN_WR // Display write pin (for parallel displays)
#define ADAGFX_PIN_RD // Display read pin (for parallel displays)
#else
// NOTE: Using Arduino pin notation
#define ADAGFX_PIN_CS 10 // Display chip select
#define ADAGFX_PIN_DC 9 // Display SPI data/command
#define ADAGFX_PIN_RST 0 // Display Reset (some displays could use pin 11)
#define ADAGFX_PIN_SDCS 4 // SD card chip select
#define ADAGFX_PIN_WR A1 // Display write pin (for parallel displays)
#define ADAGFX_PIN_RD A0 // Display read pin (for parallel displays)
#endif
// Use hardware SPI interface?
// - Set to 1 to enable hardware SPI interface, 0 to use software SPI
// - Software SPI may support the use of custom pin selection (via ADAGFX_PIN_MOSI,
// ADAGFX_PIN_MISO, ADAGFX_PIN_CLK). These pin definitions can be left blank in
// hardware SPI mode.
#define ADAGFX_SPI_HW 1
// Define custom SPI pin connections used in software SPI mode (ADAGFX_SPI_HW=0)
// - These definitions can be left blank in hardware mode (ADAGFX_SPI_HW=1)
#define ADAGFX_PIN_MOSI
#define ADAGFX_PIN_MISO
#define ADAGFX_PIN_CLK
// Set Default rotation
// you can specify values 0,1,2,3, rotation is clockwise
#define GSLC_ROTATE 1
// -----------------------------------------------------------------------------
#elif defined(DRV_DISP_TFT_ESPI)
// NOTE: When using the TFT_eSPI library, there are additional
// library-specific configuration files that may need
// customization (including pin configuration), such as
// "User_Setup_Select.h" (typically located in the
// Arduino /libraries/TFT_eSPI folder). Please refer to
// Bodmer's TFT_eSPI library for more details:
// https://github.com/Bodmer/TFT_eSPI
// NOTE: To avoid potential SPI conflicts, it is recommended
// that SUPPORT_TRANSACTIONS is defined in TFT_eSPI's "User Setup"
// Set Default rotation
// you can specify values 0,1,2,3, rotation is clockwise
#define GSLC_ROTATE 1
// -----------------------------------------------------------------------------
#elif defined(DRV_DISP_M5STACK)
#define ADAGFX_PIN_SDCS 4 // SD card chip select
#define TFT_LIGHT_PIN 32 // display backlight
// Set Default rotation
// you can specify values 0,1,2,3, rotation is clockwise
// NOTE: M5stack has a fixed display. A setting of 1 should
// match the built-in display and not need changing.
#define GSLC_ROTATE 1
#else
#error "Unknown driver for display DRV_DISP_..."
#endif // DRV_DISP_*
// =============================================================================
// TOUCH CONFIGURATION - DETAILS
// - Touch Driver-specific additional configuration
// =============================================================================
// -----------------------------------------------------------------------------
#if defined(DRV_TOUCH_NONE)
//no touch defined
#elif defined(DRV_TOUCH_ADA_STMPE610)
// Select wiring method by setting one of the following to 1
#define ADATOUCH_I2C_HW 0
#define ADATOUCH_SPI_HW 1
#define ADATOUCH_SPI_SW 0 // [TODO]
// For ADATOUCH_I2C_HW=1
#define ADATOUCH_I2C_ADDR 0x41 // I2C address of touch device
// For ADATOUCH_SPI_HW=1
#define ADATOUCH_PIN_CS 8 // From Adafruit 2.8" TFT touch shield
// Calibration values for touch display
// - These values may need to be updated to match your display
// - Typically used in resistive displays
// - These values can be determined from the Adafruit touchtest example sketch
// (check for min and max values reported from program as you touch display
// corners)
// - Note that X & Y directions reference the display's natural orientation
#define ADATOUCH_X_MIN 230
#define ADATOUCH_Y_MIN 260
#define ADATOUCH_X_MAX 3800
#define ADATOUCH_Y_MAX 3700
// -----------------------------------------------------------------------------
#elif defined(DRV_TOUCH_ADA_FT6206)
// Define sensitivity coefficient (capacitive touch)
#define ADATOUCH_SENSITIVITY 40
// -----------------------------------------------------------------------------
#elif defined(DRV_TOUCH_ADA_SIMPLE)
// Define 4-wire resistive touchscreen pinout
#define ADATOUCH_PIN_YP A2 // "Y+": Must be an analog pin, use "An" notation
#define ADATOUCH_PIN_XM A3 // "X-": Must be an analog pin, use "An" notation
#define ADATOUCH_PIN_YM 44 // "Y-": Can be a digital pin
#define ADATOUCH_PIN_XP 45 // "X+": Can be a digital pin
#define ADATOUCH_RX 300 // "rxplate"
// Calibration values for touch display
// - These values may need to be updated to match your display
// - Typically used in resistive displays
#define ADATOUCH_X_MIN 100
#define ADATOUCH_Y_MIN 150
#define ADATOUCH_X_MAX 900
#define ADATOUCH_Y_MAX 900
// Define pressure threshold for detecting a touch
#define ADATOUCH_PRESS_MIN 10
#define ADATOUCH_PRESS_MAX 1000
// -----------------------------------------------------------------------------
#elif defined(DRV_TOUCH_TFT_ESPI)
// The TFT_eSPI display library also includes support for XPT2046 touch controller
// Note that TFT_eSPI's "User_Setup" should define TOUCH_CS
#define DRV_TOUCH_IN_DISP // Use the display driver (TFT_eSPI) for touch events
// Define the XPT2046 touch driver calibration values
// - The following are some example defaults, but they should be updated
// to match your specific touch device.
#define TFT_ESPI_TOUCH_CALIB { 321,3498,280,3593,3 }
// -----------------------------------------------------------------------------
#elif defined(DRV_TOUCH_M5STACK)
// M5stack integrated button handler
#define DRV_TOUCH_IN_DISP // Use the display driver (M5stack) for touch events
// NOTE: Long-press detection is only available in the latest
// M5stack library releases. Uncomment the following
// if the Btn wasReleasefor() API is available.
//
// Define duration (in ms) for a long-press button event
//#define M5STACK_TOUCH_PRESS_LONG 300
// -----------------------------------------------------------------------------
#elif defined(DRV_TOUCH_XPT2046_PS)
// PaulStoffregen/XPT2046_Touchscreen
// Chip select pin for touch
#define XPT2046_CS 3
// Calibration values for touch display
// - These values may need to be updated to match your display
// - empirically found for XPT2046
#define ADATOUCH_X_MIN 246
#define ADATOUCH_Y_MIN 3925
#define ADATOUCH_X_MAX 3837
#define ADATOUCH_Y_MAX 370
// Define pressure threshold for detecting a touch
#define ADATOUCH_PRESS_MIN 10
#define ADATOUCH_PRESS_MAX 1000
// -----------------------------------------------------------------------------
#elif defined(DRV_TOUCH_XPT2046_STM)
// Arduino_STM32/Serasidis_XPT2046_touch (XPT2046_touch.h)
// NOTE: This touch library is included in the Arduino_STM32 library
// While it still works on many non-STM32 targets, it is recommended
// that users use DRV_TOUCH_XPT2046_PS instead.
// SPI2 is used. Due to some known issues of the TFT SPI driver working on SPI1
// it was not possible to share the touch with SPI1.
// On the Arduino STM32 these are the following pins:
// PB13 SCLK
// PB14 MISO
// PB15 MOSI
#define XPT2046_DEFINE_DPICLASS SPIClass XPT2046_spi(2); //Create an SPI instance on SPI2 port
// Chip select pin for touch SPI2
#define XPT2046_CS PB12
// Calibration values for touch display
// - These values may need to be updated to match your display
// - empirically found for XPT2046
#define ADATOUCH_X_MIN 398
#define ADATOUCH_Y_MIN 280
#define ADATOUCH_X_MAX 3877
#define ADATOUCH_Y_MAX 3805
// Define pressure threshold for detecting a touch
#define ADATOUCH_PRESS_MIN 10
#define ADATOUCH_PRESS_MAX 1000
// -----------------------------------------------------------------------------
#elif defined(DRV_TOUCH_INPUT)
// Include basic support for GPIO/keyboard only
// -----------------------------------------------------------------------------
#elif defined(DRV_TOUCH_HANDLER)
// touch handler class
// -----------------------------------------------------------------------------
#else
#error "Unknown driver for touch DRV_TOUCH_..."
#endif // DRV_TOUCH_*
// -----------------------------------------------------------------------------
// TODO: maybe those macros should be moved to one include file which is included by all drivers
#define TOUCH_ROTATION_DATA 0x6350
#define TOUCH_ROTATION_SWAPXY(rotation) ((( TOUCH_ROTATION_DATA >> ((rotation&0x03)*4) ) >> 2 ) & 0x01 )
#define TOUCH_ROTATION_FLIPX(rotation) ((( TOUCH_ROTATION_DATA >> ((rotation&0x03)*4) ) >> 1 ) & 0x01 )
#define TOUCH_ROTATION_FLIPY(rotation) ((( TOUCH_ROTATION_DATA >> ((rotation&0x03)*4) ) >> 0 ) & 0x01 )
// - Set any of the following to 1 to perform touch display
// remapping functions, 0 to disable. Use DBG_TOUCH to determine which
// remapping modes should be enabled for your display
// - Please refer to the wiki for details:
// https://github.com/ImpulseAdventure/GUIslice/wiki/Configure-Touch-Support
#define ADATOUCH_SWAP_XY 0
#define ADATOUCH_FLIP_X 0
#define ADATOUCH_FLIP_Y 0
// Define the maximum number of touch events that are handled
// per gslc_Update() call. Normally this can be set to 1 but certain
// displays may require a greater value (eg. 30) in order to increase
// responsiveness of the touch functionality.
#define GSLC_TOUCH_MAX_EVT 1
// =============================================================================
// COMMON CONFIGURATION
// =============================================================================
// Error reporting
// - Set DEBUG_ERR to 1 to enable error reporting via the Serial connection
// - Enabling DEBUG_ERR increases FLASH memory consumption which may be
// limited on the baseline Arduino (ATmega328P) devices. Therefore it
// is recommended to disable DEBUG_ERR (set to 0) on baseline Arduino
// once initial device operation confirmed to work in examples ex01 and ex02.
//
#if defined(__AVR__)
#define DEBUG_ERR 1 // Debugging enabled by default
#else
// For all other devices, DEBUG_ERR is enabled by default.
// Since this mode increases FLASH memory considerably, it may be
// necessary to disable this feature.
#define DEBUG_ERR 1 // Debugging enabled by default
#endif
// Debug initialization message
// - By default, GUIslice outputs a message in DEBUG_ERR mode
// to indicate the initialization status, even during success.
// - To disable the messages during successful initialization,
// uncomment the following line.
//#define INIT_MSG_DISABLE
// Enable of optional features
// - For memory constrained devices such as Arduino, it is best to
// set the following features to 0 (to disable) unless they are
// required.
#define GSLC_FEATURE_COMPOUND 0 // Compound elements (eg. XSelNum)
#define GSLC_FEATURE_XGAUGE_RADIAL 0 // XGauge control with radial support
#define GSLC_FEATURE_XGAUGE_RAMP 0 // XGauge control with ramp support
#define GSLC_FEATURE_XTEXTBOX_EMBED 0 // XTextbox control with embedded color
#define GSLC_FEATURE_INPUT 0 // Keyboard / GPIO input control
// Enable support for SD card
// - Set to 1 to enable, 0 to disable
// - Note that the inclusion of the SD library consumes considerable
// RAM and flash memory which could be problematic for Arduino models
// with limited resources.
#define GSLC_SD_EN 0
// Define buffer size for loading images from SD
// - A larger buffer will be faster but at the cost of RAM
#define GSLC_SD_BUFFPIXEL 50
// Enable support for graphics clipping (DrvSetClipRect)
// - Note that this will impact performance of drawing graphics primitives
#define GSLC_CLIP_EN 1
// Enable for bitmap transparency and definition of color to use
#define GSLC_BMP_TRANS_EN 1 // 1 = enabled, 0 = disabled
#define GSLC_BMP_TRANS_RGB 0xFF,0x00,0xFF // RGB color (default:pink)
// In "Local String" mode, memory within internal element array is
// used for strings. This mode incurs a memory cost for all elements,
// irrespective of whether strings are used or their length. This
// mode may make string definition convenient but is not memory-efficient.
// Therefore, it is not recommended for limited memory devices such as
// Arduino.
// - When using element local string storage (GSLC_LOCAL_STR=1),
// GSLC_LOCAL_STR_LEN defines the fixed length buffer used for every element
#define GSLC_LOCAL_STR 0 // 1=Use local strings (in element array), 0=External
#define GSLC_LOCAL_STR_LEN 30 // Max string length of text elements
#define GSLC_USE_FLOAT 0 // 1=Use floating pt library, 0=Fixed-point lookup tables
// Debug diagnostic modes
// - Uncomment any of the following to enable specific debug modes
//#define DBG_LOG // Enable debugging log output
//#define DBG_TOUCH // Enable debugging of touch-presses
//#define DBG_FRAME_RATE // Enable diagnostic frame rate reporting
//#define DBG_DRAW_IMM // Enable immediate rendering of drawing primitives
//#define DBG_DRIVER // Enable graphics driver debug reporting
// =============================================================================
// INTERNAL CONFIGURATION
// - Users should not need to modify the following
// =============================================================================
// Display device string is only used in LINUX drivers
#define GSLC_DEV_TOUCH "" // No device path used
// Define compatibility for non-AVR to call PROGMEM functions
#if defined(__AVR__)
#define GSLC_USE_PROGMEM 1
#else
#define GSLC_USE_PROGMEM 0
#endif
// =============================================================================
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_CONFIG_ARD_H_

View file

@ -0,0 +1,191 @@
#ifndef _GUISLICE_CONFIG_LINUX_H_
#define _GUISLICE_CONFIG_LINUX_H_
#warning No config selected in GUIslice_config.h - resorting to defaults.
// =======================================================================
// GUIslice library (user configuration) for LINUX / Raspberry Pi / Beaglebone
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
// \file GUIslice_config_linux.h
// \brief GUIslice library (user configuration) for LINUX / Raspberry Pi / Beaglebone
// =======================================================================
// User Configuration
// - This file can be modified by the user to match the
// intended target configuration
// - Please refer to "docs/GUIslice_config_guide.xlsx" for detailed examples
// specific to board and display combinations
// =======================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// -----------------------------------------------------------------------------------------
// Specify the graphics driver library
// - Uncomment one of the following graphics drivers
#define DRV_DISP_SDL1 // LINUX: SDL 1.2 library
//#define DRV_DISP_SDL2 // LINUX: SDL 2.0 library
// Specify the touchscreen driver
// - Uncomment one of the following touchscreen drivers
//#define DRV_TOUCH_NONE // No touchscreen support
//#define DRV_TOUCH_SDL // LINUX: Use SDL touch driver
#define DRV_TOUCH_TSLIB // LINUX: Use tslib touch driver
// -----------------------------------------------------------------------------------------
// Enable of optional features
// - For memory constrained devices such as Arduino, it is best to
// set the following features to 0 (to disable) unless they are
// required.
#define GSLC_FEATURE_COMPOUND 1 // Compound elements (eg. XSelNum)
#define GSLC_FEATURE_XGAUGE_RADIAL 1 // XGauge control with radial support
#define GSLC_FEATURE_XGAUGE_RAMP 1 // XGauge control with ramp support
#define GSLC_FEATURE_XTEXTBOX_EMBED 0 // XTextbox control with embedded color
#define GSLC_FEATURE_INPUT 1 // Keyboard / GPIO input control
// Error reporting
// - Set DEBUG_ERR to 1 to enable error reporting via the console
#define DEBUG_ERR 1 // Enable by default
// Debug initialization message
// - By default, GUIslice outputs a message in DEBUG_ERR mode
// to indicate the initialization status, even during success.
// - To disable the messages during successful initialization,
// uncomment the following line.
//#define INIT_MSG_DISABLE
// -----------------------------------------------------------------------------------------
// Graphics display driver-specific additional configuration
#if defined(DRV_DISP_SDL1)
// Define default device paths for framebuffer & touchscreen
#define GSLC_DEV_FB "/dev/fb1"
#define GSLC_DEV_TOUCH "/dev/input/touchscreen"
#define GSLC_DEV_VID_DRV "fbcon"
// Enable SDL startup workaround? (1 to enable, 0 to disable)
#define DRV_SDL_FIX_START 0
// Show SDL mouse (1 to show, 0 to hide)
#define DRV_SDL_MOUSE_SHOW 0
#define GSLC_LOCAL_STR 1
#define GSLC_USE_FLOAT 1
#elif defined(DRV_DISP_SDL2)
// Define default device paths for framebuffer & touchscreen
// - The following assumes display driver (eg. fbtft) reads from fb1
// - Raspberry Pi can support hardware acceleration onto fb0
// - To use SDL2.0 with hardware acceleration with such displays,
// use fb0 as the target and then run fbcp to mirror fb0 to fb1
#define GSLC_DEV_FB "/dev/fb0"
#define GSLC_DEV_TOUCH ""
#define GSLC_DEV_VID_DRV "x11"
// Show SDL mouse (1 to show, 0 to hide)
#define DRV_SDL_MOUSE_SHOW 0
// Enable hardware acceleration
#define DRV_SDL_RENDER_ACCEL 1
#define GSLC_LOCAL_STR 1
#define GSLC_USE_FLOAT 1
#endif // DRV_DISP_*
// -----------------------------------------------------------------------------------------
// Touch Driver-specific additional configuration
#if defined(DRV_TOUCH_SDL)
#define DRV_TOUCH_IN_DISP // Use the display driver (SDL) for touch events
#endif // DRV_TOUCH_*
// NOTE: The GSLC_ROTATE feature is not yet supported in SDL mode
// however, the following settings are provided for future use.
// - Set any of the following to 1 to perform touch display
// remapping functions, 0 to disable. Use DBG_TOUCH to determine which
// remapping modes should be enabled for your display
// - Please refer to the wiki for details:
// https://github.com/ImpulseAdventure/GUIslice/wiki/Configure-Touch-Support
#define ADATOUCH_SWAP_XY 0
#define ADATOUCH_FLIP_X 0
#define ADATOUCH_FLIP_Y 0
// Define the maximum number of touch events that are handled
// per gslc_Update() call. Normally this can be set to 1 but certain
// displays may require a greater value (eg. 30) in order to increase
// responsiveness of the touch functionality.
#define GSLC_TOUCH_MAX_EVT 1
// -----------------------------------------------------------------------------------------
// When using element local string storage (GSLC_LOCAL_STR=1),
// this defines the fixed length buffer used for every element
#define GSLC_LOCAL_STR_LEN 30 // Max string length of text elements
// Debug modes
// - Uncomment the following to enable specific debug modes
//#define DBG_LOG // Enable debugging log output
//#define DBG_TOUCH // Enable debugging of touch-presses
//#define DBG_FRAME_RATE // Enable diagnostic frame rate reporting
//#define DBG_DRAW_IMM // Enable immediate rendering of drawing primitives
//#define DBG_DRIVER // Enable graphics driver debug reporting
// Enable for bitmap transparency and definition of color to use
#define GSLC_BMP_TRANS_EN 1 // 1 = enabled, 0 = disabled
#define GSLC_BMP_TRANS_RGB 0xFF,0x00,0xFF // RGB color (default:pink)
// -----------------------------------------------------------------------------------------
// Define compatibility for non-AVR to call PROGMEM functions
#define GSLC_USE_PROGMEM 0
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_CONFIG_LINUX_H_

View file

@ -0,0 +1,66 @@
#ifndef _GUISLICE_DRV_H_
#define _GUISLICE_DRV_H_
// =======================================================================
// GUIslice library (generic driver layer include)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
// =======================================================================
// Generic Driver Layer
// =======================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if defined(DRV_DISP_SDL1)
#include "GUIslice_drv_sdl.h"
#elif defined(DRV_DISP_SDL2)
#include "GUIslice_drv_sdl.h"
#elif defined(DRV_DISP_ADAGFX) || defined(DRV_DISP_ADAGFX_AS)
#include "GUIslice_drv_adagfx.h"
#elif defined(DRV_DISP_TFT_ESPI)
#include "GUIslice_drv_tft_espi.h"
#elif defined(DRV_DISP_M5STACK)
#include "GUIslice_drv_m5stack.h"
#elif defined(DRV_DISP_UTFT)
#include "GUIslice_drv_utft.h"
#else
#error "Driver needs to be specified in GUIslice_config_*.h (DRV_DISP_*)"
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_DRV_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,757 @@
#ifndef _GUISLICE_DRV_ADAGFX_H_
#define _GUISLICE_DRV_ADAGFX_H_
// =======================================================================
// GUIslice library (driver layer for Adafruit-GFX)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file GUIslice_drv_adagfx.h
/// \brief GUIslice library (driver layer for Adafruit-GFX)
// =======================================================================
// Driver Layer for Adafruit-GFX
// =======================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "GUIslice.h"
#include <stdio.h>
// Determine characteristics for configured touch driver
// - DRV_TOUCH_TYPE_EXTERNAL: TDrv* external touch APIs are enabled
// - DRV_TOUCH_TYPE_RES: Resistive overlay
// - DRV_TOUCH_TYPE_CAP: Capacitive overlay
// - DRV_TOUCH_TYPE_ANALOG: Analog input
#if defined(DRV_TOUCH_ADA_STMPE610)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_ADA_FT6206)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_CAP // Capacitive
#elif defined(DRV_TOUCH_ADA_FT5206)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_CAP // Capacitive
#elif defined(DRV_TOUCH_ADA_SIMPLE)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#define DRV_TOUCH_TYPE_ANALOG // Analog
#elif defined(DRV_TOUCH_ADA_RA8875)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_ADA_RA8875_SUMO)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_XPT2046_STM)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_XPT2046_PS)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_URTOUCH)
#define DRV_TOUCH_TYPE_EXTERNAL
// Don't set DRV_TOUCH_TYPE_RES since URTouch provides its own calibration
//#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_INPUT)
#define DRV_TOUCH_TYPE_EXTERNAL
#elif defined(DRV_TOUCH_HANDLER)
#define DRV_TOUCH_TYPE_EXTERNAL
#elif defined(DRV_TOUCH_NONE)
#endif // DRV_TOUCH_*
// =======================================================================
// API support definitions
// - These defines indicate whether the driver includes optimized
// support for various APIs. If a define is set to 0, then the
// GUIslice core emulation will be used instead.
// - At the very minimum, the point draw routine must be available:
// gslc_DrvDrawPoint()
// =======================================================================
#define DRV_HAS_DRAW_POINT 1 ///< Support gslc_DrvDrawPoint()
#define DRV_HAS_DRAW_POINTS 0 ///< Support gslc_DrvDrawPoints()
#define DRV_HAS_DRAW_LINE 1 ///< Support gslc_DrvDrawLine()
#define DRV_HAS_DRAW_RECT_FRAME 1 ///< Support gslc_DrvDrawFrameRect()
#define DRV_HAS_DRAW_RECT_FILL 1 ///< Support gslc_DrvDrawFillRect()
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 1 ///< Support gslc_DrvDrawFrameRoundRect()
#define DRV_HAS_DRAW_RECT_ROUND_FILL 1 ///< Support gslc_DrvDrawFillRoundRect()
#define DRV_HAS_DRAW_CIRCLE_FRAME 1 ///< Support gslc_DrvDrawFrameCircle()
#define DRV_HAS_DRAW_CIRCLE_FILL 1 ///< Support gslc_DrvDrawFillCircle()
#define DRV_HAS_DRAW_TRI_FRAME 1 ///< Support gslc_DrvDrawFrameTriangle()
#define DRV_HAS_DRAW_TRI_FILL 1 ///< Support gslc_DrvDrawFillTriangle()
#define DRV_HAS_DRAW_TEXT 1 ///< Support gslc_DrvDrawTxt()
#define DRV_HAS_DRAW_BMP_MEM 0 ///< Support gslc_DrvDrawBmp24FromMem()
#define DRV_OVERRIDE_TXT_ALIGN 0 ///< Driver provides text alignment
// -----------------------------------------------------------------------
// Driver-specific overrides
// - Some drivers have exceptions to the above support configuration
// -----------------------------------------------------------------------
#if defined(DRV_DISP_WAVESHARE_ILI9486)
#undef DRV_HAS_DRAW_RECT_ROUND_FRAME
#undef DRV_HAS_DRAW_RECT_ROUND_FILL
#undef DRV_HAS_DRAW_TRI_FRAME
#undef DRV_HAS_DRAW_TRI_FILL
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 0
#define DRV_HAS_DRAW_RECT_ROUND_FILL 0
#define DRV_HAS_DRAW_TRI_FRAME 0
#define DRV_HAS_DRAW_TRI_FILL 0
#elif defined(DRV_DISP_LCDGFX)
#undef DRV_HAS_DRAW_RECT_ROUND_FRAME
#undef DRV_HAS_DRAW_RECT_ROUND_FILL
#undef DRV_HAS_DRAW_CIRCLE_FRAME
#undef DRV_HAS_DRAW_CIRCLE_FILL
#undef DRV_HAS_DRAW_TRI_FRAME
#undef DRV_HAS_DRAW_TRI_FILL
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 0
#define DRV_HAS_DRAW_RECT_ROUND_FILL 0
#define DRV_HAS_DRAW_CIRCLE_FRAME 0
#define DRV_HAS_DRAW_CIRCLE_FILL 0
#define DRV_HAS_DRAW_TRI_FRAME 0
#define DRV_HAS_DRAW_TRI_FILL 0
#elif defined(DRV_DISP_ADAGFX_RA8876)
#undef DRV_HAS_DRAW_RECT_ROUND_FRAME
#undef DRV_HAS_DRAW_RECT_ROUND_FILL
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 0
#define DRV_HAS_DRAW_RECT_ROUND_FILL 0
#elif defined(DRV_DISP_ADAGFX_RA8876_GV)
#undef DRV_HAS_DRAW_RECT_ROUND_FRAME
#undef DRV_HAS_DRAW_RECT_ROUND_FILL
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 0
#define DRV_HAS_DRAW_RECT_ROUND_FILL 0
#elif defined(DRV_DISP_ADAGFX_ILI9341)
// BLIT support in library
#undef DRV_HAS_DRAW_BMP_MEM
#define DRV_HAS_DRAW_BMP_MEM 1
#endif
// =======================================================================
// Driver-specific members
// =======================================================================
typedef struct {
gslc_tsColor nColBkgnd; ///< Background color (if not image-based)
gslc_tsRect rClipRect; ///< Clipping rectangle
} gslc_tsDriver;
// =======================================================================
// Public APIs to GUIslice core library
// - These functions define the renderer / driver-dependent
// implementations for the core drawing operations within
// GUIslice.
// =======================================================================
// -----------------------------------------------------------------------
// Configuration Functions
// -----------------------------------------------------------------------
///
/// Initialize the SDL library
/// - Performs clean startup workaround (if enabled)
/// - Configures video mode
/// - Initializes font support
///
/// PRE:
/// - The environment variables should be configured before
/// calling gslc_DrvInit(). This can be done with gslc_DrvInitEnv()
/// or manually in user function.
///
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
bool gslc_DrvInit(gslc_tsGui* pGui);
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTs(gslc_tsGui* pGui,const char* acDev);
///
/// Free up any members associated with the driver
/// - Eg. renderers, windows, background surfaces, etc.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvDestruct(gslc_tsGui* pGui);
///
/// Get the display driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameDisp(gslc_tsGui* pGui);
///
/// Get the touch driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameTouch(gslc_tsGui* pGui);
///
/// Get the native display driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the display driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverDisp(gslc_tsGui* pGui);
///
/// Get the native touch driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the touch driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverTouch(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Image/surface handling Functions
// -----------------------------------------------------------------------
///
/// Load a bitmap (*.bmp) and create a new image resource.
/// Transparency is enabled by GSLC_BMP_TRANS_EN
/// through use of color (GSLC_BMP_TRANS_RGB).
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return Image pointer (surface/texture) or NULL if error
///
void* gslc_DrvLoadImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a bitmap image
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a solid color
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nCol: RGB Color to use
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndColor(gslc_tsGui* pGui,gslc_tsColor nCol);
///
/// Set an element's normal-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageNorm(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Set an element's glow-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageGlow(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Release an image surface
///
/// \param[in] pvImg: Void ptr to image
///
/// \return none
///
void gslc_DrvImageDestruct(void* pvImg);
///
/// Set the clipping rectangle for future drawing updates
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pRect: Rectangular region to constrain edits
///
/// \return true if success, false if error
///
bool gslc_DrvSetClipRect(gslc_tsGui* pGui,gslc_tsRect* pRect);
// -----------------------------------------------------------------------
// Font handling Functions
// -----------------------------------------------------------------------
///
/// Load a font from a resource and return pointer to it
///
/// \param[in] eFontRefType: Font reference type (GSLC_FONTREF_PTR for Arduino)
/// \param[in] pvFontRef: Font reference pointer (Pointer to the GFXFont array)
/// \param[in] nFontSz: Typeface size to use
///
/// \return Void ptr to driver-specific font if load was successful, NULL otherwise
///
const void* gslc_DrvFontAdd(gslc_teFontRefType eFontRefType,const void* pvFontRef,uint16_t nFontSz);
///
/// Release all fonts defined in the GUI
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvFontsDestruct(gslc_tsGui* pGui);
///
/// Get the extent (width and height) of a text string
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pFont: Ptr to Font structure
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[out] pnTxtX: Ptr to offset X of text
/// \param[out] pnTxtY: Ptr to offset Y of text
/// \param[out] pnTxtSzW: Ptr to width of text
/// \param[out] pnTxtSzH: Ptr to height of text
///
/// \return true if success, false if failure
///
bool gslc_DrvGetTxtSize(gslc_tsGui* pGui,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,
int16_t* pnTxtX,int16_t* pnTxtY,uint16_t* pnTxtSzW,uint16_t* pnTxtSzH);
///
/// Draw a text string at the given coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nTxtX: X coordinate of top-left text string
/// \param[in] nTxtY: Y coordinate of top-left text string
/// \param[in] pFont: Ptr to Font
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[in] colTxt: Color to draw text
/// \param[in] colBg: unused in ADAGFX, defaults to black
///
/// \return true if success, false if failure
///
bool gslc_DrvDrawTxt(gslc_tsGui* pGui,int16_t nTxtX,int16_t nTxtY,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,gslc_tsColor colTxt,gslc_tsColor colBg);
// -----------------------------------------------------------------------
// Screen Management Functions
// -----------------------------------------------------------------------
///
/// Force a page flip to occur. This generally copies active
/// screen surface to the display.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvPageFlipNow(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Graphics Primitives Functions
// -----------------------------------------------------------------------
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX: X coordinate of point
/// \param[in] nY: Y coordinate of point
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoint(gslc_tsGui* pGui,int16_t nX,int16_t nY,gslc_tsColor nCol);
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] asPt: Array of points to draw
/// \param[in] nNumPt: Number of points in array
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoints(gslc_tsGui* pGui,gslc_tsPt* asPt,uint16_t nNumPt,gslc_tsColor nCol);
///
/// Draw a framed rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a filled rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a framed rounded rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nRadius: Radius for rounded corners
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRoundRect(gslc_tsGui* pGui,gslc_tsRect rRect,int16_t nRadius,gslc_tsColor nCol);
///
/// Draw a filled rounded rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nRadius: Radius for rounded corners
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRoundRect(gslc_tsGui* pGui,gslc_tsRect rRect,int16_t nRadius,gslc_tsColor nCol);
///
/// Draw a line
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: Line start (X coordinate)
/// \param[in] nY0: Line start (Y coordinate)
/// \param[in] nX1: Line finish (X coordinate)
/// \param[in] nY1: Line finish (Y coordinate)
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawLine(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,int16_t nX1,int16_t nY1,gslc_tsColor nCol);
///
/// Draw a framed circle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nMidX: Center of circle (X coordinate)
/// \param[in] nMidY: Center of circle (Y coordinate)
/// \param[in] nRadius: Radius of circle
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameCircle(gslc_tsGui* pGui,int16_t nMidX,int16_t nMidY,uint16_t nRadius,gslc_tsColor nCol);
///
/// Draw a filled circle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nMidX: Center of circle (X coordinate)
/// \param[in] nMidY: Center of circle (Y coordinate)
/// \param[in] nRadius: Radius of circle
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillCircle(gslc_tsGui* pGui,int16_t nMidX,int16_t nMidY,uint16_t nRadius,gslc_tsColor nCol);
///
/// Draw a framed triangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X Coordinate #1
/// \param[in] nY0: Y Coordinate #1
/// \param[in] nX1: X Coordinate #2
/// \param[in] nY1: Y Coordinate #2
/// \param[in] nX2: X Coordinate #3
/// \param[in] nY2: Y Coordinate #3
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameTriangle(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,
int16_t nX1,int16_t nY1,int16_t nX2,int16_t nY2,gslc_tsColor nCol);
///
/// Draw a filled triangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X Coordinate #1
/// \param[in] nY0: Y Coordinate #1
/// \param[in] nX1: X Coordinate #2
/// \param[in] nY1: Y Coordinate #2
/// \param[in] nX2: X Coordinate #3
/// \param[in] nY2: Y Coordinate #3
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillTriangle(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,
int16_t nX1,int16_t nY1,int16_t nX2,int16_t nY2,gslc_tsColor nCol);
///
/// Copy all of source image to destination screen at specified coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvDrawImage(gslc_tsGui* pGui,int16_t nDstX,int16_t nDstY,gslc_tsImgRef sImgRef);
///
/// Draw a monochrome bitmap from a memory array
/// - Draw from the bitmap buffer using the foreground color
/// defined in the header (unset bits are transparent)
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] pBitmap: Pointer to bitmap buffer
/// \param[in] bProgMem: Bitmap is stored in Flash if true, RAM otherwise
///
/// \return none
///
void gslc_DrvDrawMonoFromMem(gslc_tsGui* pGui,int16_t nDstX, int16_t nDstY, const unsigned char *pBitmap,bool bProgMem);
///
/// Draw a color 24-bit depth bitmap from a memory array
/// - Note that users must convert images from their native
/// format (eg. BMP, PNG, etc.) into a C array. Please
/// refer to the following guide for details:
/// https://github.com/ImpulseAdventure/GUIslice/wiki/Display-Images-from-FLASH
/// - The converted file (c array) can then be included in the sketch.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: X coord for copy
/// \param[in] nDstY: Y coord for copy
/// \param[in] pBitmap: Pointer to bitmap buffer
/// \param[in] bProgMem: Bitmap is stored in Flash if true, RAM otherwise
///
/// \return none
///
void gslc_DrvDrawBmp24FromMem(gslc_tsGui* pGui,int16_t nDstX, int16_t nDstY,const unsigned char* pBitmap,bool bProgMem);
///
/// Copy the background image to destination screen
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
void gslc_DrvDrawBkgnd(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Touch Functions (if using display driver library)
// -----------------------------------------------------------------------
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTouch(gslc_tsGui* pGui,const char* acDev);
///
/// Get the last touch event from the internal touch handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event
/// \param[out] pnY: Ptr to Y coordinate of last touch event
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, 1 for touch)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return true if an event was detected or false otherwise
///
bool gslc_DrvGetTouch(gslc_tsGui* pGui,int16_t* pnX,int16_t* pnY,uint16_t* pnPress,gslc_teInputRawEvent* peInputEvent,int16_t* pnInputVal);
// -----------------------------------------------------------------------
// Touch Functions (if using external touch driver library)
// -----------------------------------------------------------------------
// Check for deprecated config option
// - This check will be removed in future releases
#if defined(DRV_TOUCH_XPT2046)
#error "NOTE: DRV_TOUCH_XPT2046 has been renamed to DRV_TOUCH_XPT2046_STM. Please update your config."
#endif
#if defined(DRV_TOUCH_TYPE_EXTERNAL)
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_TDrvInitTouch(gslc_tsGui* pGui,const char* acDev);
///
/// Get the last touch event from the SDL_Event handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event
/// \param[out] pnY: Ptr to Y coordinate of last touch event
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, 1 for touch)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return true if an event was detected or false otherwise
///
bool gslc_TDrvGetTouch(gslc_tsGui* pGui, int16_t* pnX, int16_t* pnY, uint16_t* pnPress, gslc_teInputRawEvent* peInputEvent, int16_t* pnInputVal);
#endif // DRV_TOUCH_*
// -----------------------------------------------------------------------
// Dynamic Screen rotation and Touch axes swap/flip functions
// -----------------------------------------------------------------------
///
/// Change rotation, automatically adapt touchscreen axes swap/flip
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nRotation: Screen Rotation value (0, 1, 2 or 3)
///
/// \return true if successful
///
bool gslc_DrvRotate(gslc_tsGui* pGui, uint8_t nRotation);
// =======================================================================
// Private Functions
// - These functions are not included in the scope of APIs used by
// the core GUIslice library. Instead, these functions are used
// to support the operations within this driver layer.
// =======================================================================
uint16_t gslc_DrvAdaptColorToRaw(gslc_tsColor nCol);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_DRV_ADAGFX_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,656 @@
#ifndef _GUISLICE_DRV_M5STACK_H_
#define _GUISLICE_DRV_M5STACK_H_
// =======================================================================
// GUIslice library (driver layer for m5stack/M5Stack)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file GUIslice_drv_m5stack.h
/// \brief GUIslice library (driver layer for M5stack)
// =======================================================================
// Driver Layer for m5stack/M5Stack
// - https://github.com/m5stack/M5Stack
// =======================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "GUIslice.h"
#include <stdio.h>
// ------------------------------------------------------------------------
// Error Strings
// ------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// =======================================================================
// API support definitions
// - These defines indicate whether the driver includes optimized
// support for various APIs. If a define is set to 0, then the
// GUIslice core emulation will be used instead.
// - At the very minimum, the point draw routine must be available:
// gslc_DrvDrawPoint()
// =======================================================================
#define DRV_HAS_DRAW_POINT 1 ///< Support gslc_DrvDrawPoint()
#define DRV_HAS_DRAW_POINTS 0 ///< Support gslc_DrvDrawPoints()
#define DRV_HAS_DRAW_LINE 1 ///< Support gslc_DrvDrawLine()
#define DRV_HAS_DRAW_RECT_FRAME 1 ///< Support gslc_DrvDrawFrameRect()
#define DRV_HAS_DRAW_RECT_FILL 1 ///< Support gslc_DrvDrawFillRect()
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 1 ///< Support gslc_DrvDrawFrameRoundRect()
#define DRV_HAS_DRAW_RECT_ROUND_FILL 1 ///< Support gslc_DrvDrawFillRoundRect()
#define DRV_HAS_DRAW_CIRCLE_FRAME 1 ///< Support gslc_DrvDrawFrameCircle()
#define DRV_HAS_DRAW_CIRCLE_FILL 1 ///< Support gslc_DrvDrawFillCircle()
#define DRV_HAS_DRAW_TRI_FRAME 1 ///< Support gslc_DrvDrawFrameTriangle()
#define DRV_HAS_DRAW_TRI_FILL 1 ///< Support gslc_DrvDrawFillTriangle()
#define DRV_HAS_DRAW_TEXT 1 ///< Support gslc_DrvDrawTxt()
#define DRV_HAS_DRAW_BMP_MEM 0 ///< Support gslc_DrvDrawBmp24FromMem()
#define DRV_OVERRIDE_TXT_ALIGN 1 ///< Driver provides text alignment
// =======================================================================
// Driver-specific members
// =======================================================================
typedef struct {
gslc_tsColor nColBkgnd; ///< Background color (if not image-based)
gslc_tsRect rClipRect; ///< Clipping rectangle
} gslc_tsDriver;
// =======================================================================
// Public APIs to GUIslice core library
// - These functions define the renderer / driver-dependent
// implementations for the core drawing operations within
// GUIslice.
// =======================================================================
// -----------------------------------------------------------------------
// Configuration Functions
// -----------------------------------------------------------------------
///
/// Initialize the SDL library
/// - Performs clean startup workaround (if enabled)
/// - Configures video mode
/// - Initializes font support
///
/// PRE:
/// - The environment variables should be configured before
/// calling gslc_DrvInit(). This can be done with gslc_DrvInitEnv()
/// or manually in user function.
///
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
bool gslc_DrvInit(gslc_tsGui* pGui);
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTs(gslc_tsGui* pGui,const char* acDev);
///
/// Free up any members associated with the driver
/// - Eg. renderers, windows, background surfaces, etc.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvDestruct(gslc_tsGui* pGui);
///
/// Get the display driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameDisp(gslc_tsGui* pGui);
///
/// Get the touch driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameTouch(gslc_tsGui* pGui);
///
/// Get the native display driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the display driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverDisp(gslc_tsGui* pGui);
///
/// Get the native touch driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the touch driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverTouch(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Image/surface handling Functions
// -----------------------------------------------------------------------
///
/// Load a bitmap (*.bmp) and create a new image resource.
/// Transparency is enabled by GSLC_BMP_TRANS_EN
/// through use of color (GSLC_BMP_TRANS_RGB).
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return Image pointer (surface/texture) or NULL if error
///
void* gslc_DrvLoadImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a bitmap image
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a solid color
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nCol: RGB Color to use
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndColor(gslc_tsGui* pGui,gslc_tsColor nCol);
///
/// Set an element's normal-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageNorm(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Set an element's glow-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageGlow(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Release an image surface
///
/// \param[in] pvImg: Void ptr to image
///
/// \return none
///
void gslc_DrvImageDestruct(void* pvImg);
///
/// Set the clipping rectangle for future drawing updates
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pRect: Rectangular region to constrain edits
///
/// \return true if success, false if error
///
bool gslc_DrvSetClipRect(gslc_tsGui* pGui,gslc_tsRect* pRect);
// -----------------------------------------------------------------------
// Font handling Functions
// -----------------------------------------------------------------------
///
/// Load a font from a resource and return pointer to it
///
/// \param[in] eFontRefType: Font reference type (GSLC_FONTREF_PTR for Arduino)
/// \param[in] pvFontRef: Font reference pointer (Pointer to the GFXFont array)
/// \param[in] nFontSz: Typeface size to use
///
/// \return Void ptr to driver-specific font if load was successful, NULL otherwise
///
const void* gslc_DrvFontAdd(gslc_teFontRefType eFontRefType,const void* pvFontRef,uint16_t nFontSz);
///
/// Release all fonts defined in the GUI
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvFontsDestruct(gslc_tsGui* pGui);
///
/// Get the extent (width and height) of a text string
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pFont: Ptr to Font structure
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[out] pnTxtX: Ptr to offset X of text
/// \param[out] pnTxtY: Ptr to offset Y of text
/// \param[out] pnTxtSzW: Ptr to width of text
/// \param[out] pnTxtSzH: Ptr to height of text
///
/// \return true if success, false if failure
///
bool gslc_DrvGetTxtSize(gslc_tsGui* pGui,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,
int16_t* pnTxtX,int16_t* pnTxtY,uint16_t* pnTxtSzW,uint16_t* pnTxtSzH);
///
/// Draw a text string at the given coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nTxtX: X coordinate of top-left text string
/// \param[in] nTxtY: Y coordinate of top-left text string
/// \param[in] pFont: Ptr to Font
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[in] colTxt: Color to draw text
/// \param[in] colBg: unused in m5stack, defaults to black
///
/// \return true if success, false if failure
///
bool gslc_DrvDrawTxt(gslc_tsGui* pGui,int16_t nTxtX,int16_t nTxtY,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,gslc_tsColor colTxt,gslc_tsColor colBg);
///
/// Draw a text string in a bounding box using the specified alignment
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X coordinate of top-left of bounding box
/// \param[in] nY0: Y coordinate of top-left of bounding box
/// \param[in] nX1: X coordinate of bot-right of bounding box
/// \param[in] nY1: Y coordinate of bot-right of bounding box
/// \param[in] eTxtAlign: Alignment mode]
/// \param[in] pFont: Ptr to Font
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[in] colTxt: Color to draw text
/// \param[in] colBg: unused in m5stack, defaults to black
///
/// \return true if success, false if failure
///
bool gslc_DrvDrawTxtAlign(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,int16_t nX1,int16_t nY1,int8_t eTxtAlign,
gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,gslc_tsColor colTxt,gslc_tsColor colBg);
// -----------------------------------------------------------------------
// Screen Management Functions
// -----------------------------------------------------------------------
///
/// Force a page flip to occur. This generally copies active
/// screen surface to the display.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvPageFlipNow(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Graphics Primitives Functions
// -----------------------------------------------------------------------
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX: X coordinate of point
/// \param[in] nY: Y coordinate of point
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoint(gslc_tsGui* pGui,int16_t nX,int16_t nY,gslc_tsColor nCol);
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] asPt: Array of points to draw
/// \param[in] nNumPt: Number of points in array
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoints(gslc_tsGui* pGui,gslc_tsPt* asPt,uint16_t nNumPt,gslc_tsColor nCol);
///
/// Draw a framed rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a filled rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a framed rounded rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nRadius: Radius for rounded corners
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRoundRect(gslc_tsGui* pGui,gslc_tsRect rRect,int16_t nRadius,gslc_tsColor nCol);
///
/// Draw a filled rounded rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nRadius: Radius for rounded corners
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRoundRect(gslc_tsGui* pGui,gslc_tsRect rRect,int16_t nRadius,gslc_tsColor nCol);
///
/// Draw a line
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: Line start (X coordinate)
/// \param[in] nY0: Line start (Y coordinate)
/// \param[in] nX1: Line finish (X coordinate)
/// \param[in] nY1: Line finish (Y coordinate)
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawLine(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,int16_t nX1,int16_t nY1,gslc_tsColor nCol);
///
/// Draw a framed circle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nMidX: Center of circle (X coordinate)
/// \param[in] nMidY: Center of circle (Y coordinate)
/// \param[in] nRadius: Radius of circle
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameCircle(gslc_tsGui* pGui,int16_t nMidX,int16_t nMidY,uint16_t nRadius,gslc_tsColor nCol);
///
/// Draw a filled circle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nMidX: Center of circle (X coordinate)
/// \param[in] nMidY: Center of circle (Y coordinate)
/// \param[in] nRadius: Radius of circle
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillCircle(gslc_tsGui* pGui,int16_t nMidX,int16_t nMidY,uint16_t nRadius,gslc_tsColor nCol);
///
/// Draw a framed triangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X Coordinate #1
/// \param[in] nY0: Y Coordinate #1
/// \param[in] nX1: X Coordinate #2
/// \param[in] nY1: Y Coordinate #2
/// \param[in] nX2: X Coordinate #3
/// \param[in] nY2: Y Coordinate #3
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameTriangle(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,
int16_t nX1,int16_t nY1,int16_t nX2,int16_t nY2,gslc_tsColor nCol);
///
/// Draw a filled triangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X Coordinate #1
/// \param[in] nY0: Y Coordinate #1
/// \param[in] nX1: X Coordinate #2
/// \param[in] nY1: Y Coordinate #2
/// \param[in] nX2: X Coordinate #3
/// \param[in] nY2: Y Coordinate #3
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillTriangle(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,
int16_t nX1,int16_t nY1,int16_t nX2,int16_t nY2,gslc_tsColor nCol);
///
/// Copy all of source image to destination screen at specified coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvDrawImage(gslc_tsGui* pGui,int16_t nDstX,int16_t nDstY,gslc_tsImgRef sImgRef);
///
/// Draw a monochrome bitmap from a memory array
/// - Draw from the bitmap buffer using the foreground color
/// defined in the header (unset bits are transparent)
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] pBitmap: Pointer to bitmap buffer
/// \param[in] bProgMem: Bitmap is stored in Flash if true, RAM otherwise
///
/// \return none
///
void gslc_DrvDrawMonoFromMem(gslc_tsGui* pGui,int16_t nDstX, int16_t nDstY, const unsigned char *pBitmap,bool bProgMem);
///
/// Draw a color 24-bit depth bitmap from a memory array
/// - Note that users must convert images from their native
/// format (eg. BMP, PNG, etc.) into a C array. Please
/// refer to the following guide for details:
/// https://github.com/ImpulseAdventure/GUIslice/wiki/Display-Images-from-FLASH
/// - The converted file (c array) can then be included in the sketch.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: X coord for copy
/// \param[in] nDstY: Y coord for copy
/// \param[in] pBitmap: Pointer to bitmap buffer
/// \param[in] bProgMem: Bitmap is stored in Flash if true, RAM otherwise
///
/// \return none
///
void gslc_DrvDrawBmp24FromMem(gslc_tsGui* pGui,int16_t nDstX, int16_t nDstY,const unsigned char* pBitmap,bool bProgMem);
///
/// Copy the background image to destination screen
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
void gslc_DrvDrawBkgnd(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Touch Functions (if using display driver library)
// -----------------------------------------------------------------------
#if defined(DRV_TOUCH_IN_DISP)
// Use M5stack's integrated button handler
///
/// Perform any touch-specific initialization
/// - This function doesn't do anything in M5stack
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTouch(gslc_tsGui* pGui,const char* acDev);
///
/// Get the last touch event from the internal button handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event (UNUSED)
/// \param[out] pnY: Ptr to Y coordinate of last touch event (UNUSED)
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, 1 for touch) (UNUSED)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return true if an event was detected or false otherwise
///
bool gslc_DrvGetTouch(gslc_tsGui* pGui,int16_t* pnX,int16_t* pnY,uint16_t* pnPress,gslc_teInputRawEvent* peInputEvent,int16_t* pnInputVal);
#endif // DRV_TOUCH_IN_DISP
// -----------------------------------------------------------------------
// Dynamic Screen rotation and Touch axes swap/flip functions
// -----------------------------------------------------------------------
///
/// Change rotation, automatically adapt touchscreen axes swap/flip
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nRotation: Screen Rotation value (0, 1, 2 or 3)
///
/// \return true if successful
///
bool gslc_DrvRotate(gslc_tsGui* pGui, uint8_t nRotation);
// =======================================================================
// Private Functions
// - These functions are not included in the scope of APIs used by
// the core GUIslice library. Instead, these functions are used
// to support the operations within this driver layer.
// =======================================================================
uint16_t gslc_DrvAdaptColorToRaw(gslc_tsColor nCol);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_DRV_M5STACK_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,722 @@
#ifndef _GUISLICE_DRV_SDL_H_
#define _GUISLICE_DRV_SDL_H_
// =======================================================================
// GUIslice library (driver layer for SDL 1.2 & 2.0)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file GUIslice_drv_sdl.h
/// \brief GUIslice library (driver layer for LINUX / SDL)
// =======================================================================
// Driver Layer for SDL
// =======================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "GUIslice.h"
#include <stdio.h>
#if defined(DRV_DISP_SDL1)
#include <SDL/SDL.h>
#include <SDL/SDL_getenv.h>
#include <SDL/SDL_ttf.h>
#endif
#if defined(DRV_DISP_SDL2)
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#endif
// Includes for optional tslib touch handling
#if defined(DRV_TOUCH_TSLIB)
#include "tslib.h"
#endif
// =======================================================================
// API support definitions
// - These defines indicate whether the driver includes optimized
// support for various APIs. If a define is set to 0, then the
// GUIslice core emulation will be used instead.
// - At the very minimum, the point draw routine must be available:
// gslc_DrvDrawPoint()
// =======================================================================
#define DRV_HAS_DRAW_POINT 1 ///< Support gslc_DrvDrawPoint()
#if defined(DRV_DISP_SDL1)
#define DRV_HAS_DRAW_POINTS 1 ///< Support gslc_DrvDrawPoints()
#define DRV_HAS_DRAW_LINE 0 ///< Support gslc_DrvDrawLine()
#define DRV_HAS_DRAW_RECT_FRAME 0 ///< Support gslc_DrvDrawFrameRect()
#define DRV_HAS_DRAW_RECT_FILL 1 ///< Support gslc_DrvDrawFillRect()
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 0 ///< Support gslc_DrvDrawFrameRoundRect()
#define DRV_HAS_DRAW_RECT_ROUND_FILL 0 ///< Support gslc_DrvDrawFillRoundRect()
#define DRV_HAS_DRAW_CIRCLE_FRAME 0 ///< Support gslc_DrvDrawFrameCircle()
#define DRV_HAS_DRAW_CIRCLE_FILL 0 ///< Support gslc_DrvDrawFillCircle()
#define DRV_HAS_DRAW_TRI_FRAME 0 ///< Support gslc_DrvDrawFrameTriangle()
#define DRV_HAS_DRAW_TRI_FILL 0 ///< Support gslc_DrvDrawFillTriangle()
#define DRV_HAS_DRAW_TEXT 1 ///< Support gslc_DrvDrawTxt()
#define DRV_HAS_DRAW_BMP_MEM 0 ///< Support gslc_DrvDrawBmp24FromMem()
#endif
#if defined(DRV_DISP_SDL2)
#define DRV_HAS_DRAW_POINTS 1 ///< Support gslc_DrvDrawPoints()
#define DRV_HAS_DRAW_LINE 1 ///< Support gslc_DrvDrawLine()
#define DRV_HAS_DRAW_RECT_FRAME 1 ///< Support gslc_DrvDrawFrameRect()
#define DRV_HAS_DRAW_RECT_FILL 1 ///< Support gslc_DrvDrawFillRect()
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 0 ///< Support gslc_DrvDrawFrameRoundRect()
#define DRV_HAS_DRAW_RECT_ROUND_FILL 0 ///< Support gslc_DrvDrawFillRoundRect()
#define DRV_HAS_DRAW_CIRCLE_FRAME 0 ///< Support gslc_DrvDrawFrameCircle()
#define DRV_HAS_DRAW_CIRCLE_FILL 0 ///< Support gslc_DrvDrawFillCircle()
#define DRV_HAS_DRAW_TRI_FRAME 0 ///< Support gslc_DrvDrawFrameTriangle()
#define DRV_HAS_DRAW_TRI_FILL 0 ///< Support gslc_DrvDrawFillTriangle()
#define DRV_HAS_DRAW_TEXT 1 ///< Support gslc_DrvDrawTxt()
#define DRV_HAS_DRAW_BMP_MEM 0 ///< Support gslc_DrvDrawBmp24FromMem()
#endif
#define DRV_OVERRIDE_TXT_ALIGN 0 ///< Driver provides text alignment
// =======================================================================
// Driver-specific members
// =======================================================================
typedef struct {
#if defined(DRV_DISP_SDL1)
SDL_Surface* pSurfScreen; ///< Surface ptr for screen
#endif
#if defined(DRV_DISP_SDL2)
SDL_Window* pWind; ///< SDL2 Window
SDL_Renderer* pRender; ///< SDL2 Rendering engine
#endif
#if defined(DRV_TOUCH_TSLIB)
struct tsdev* pTsDev; ///< Ptr to touchscreen device
#endif
} gslc_tsDriver;
// =======================================================================
// Public APIs to GUIslice core library
// - These functions define the renderer / driver-dependent
// implementations for the core drawing operations within
// GUIslice.
// =======================================================================
// -----------------------------------------------------------------------
// Configuration Functions
// -----------------------------------------------------------------------
///
/// Initialize the SDL library
/// - Performs clean startup workaround (if enabled)
/// - Configures video mode
/// - Initializes font support
///
/// PRE:
/// - The environment variables should be configured before
/// calling gslc_DrvInit().
///
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
bool gslc_DrvInit(gslc_tsGui* pGui);
///
/// Free up any members associated with the driver
/// - Eg. renderers, windows, background surfaces, etc.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvDestruct(gslc_tsGui* pGui);
///
/// Get the display driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameDisp(gslc_tsGui* pGui);
///
/// Get the touch driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameTouch(gslc_tsGui* pGui);
///
/// Get the native display driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the display driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverDisp(gslc_tsGui* pGui);
///
/// Get the native touch driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the touch driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverTouch(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Image/surface handling Functions
// -----------------------------------------------------------------------
///
/// Load a bitmap (*.bmp) and create a new image resource.
/// Transparency is enabled by GSLC_BMP_TRANS_EN
/// through use of color (GSLC_BMP_TRANS_RGB).
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return Image pointer (surface/texture/path) or NULL if error
///
void* gslc_DrvLoadImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a bitmap image
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a solid color
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nCol: RGB Color to use
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndColor(gslc_tsGui* pGui,gslc_tsColor nCol);
///
/// Set an element's normal-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageNorm(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Set an element's glow-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageGlow(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Release an image surface
///
/// \param[in] pvImg: Void ptr to image
///
/// \return none
///
void gslc_DrvImageDestruct(void* pvImg);
///
/// Set the clipping rectangle for future drawing updates
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pRect: Rectangular region to constrain edits
///
/// \return true if success, false if error
///
bool gslc_DrvSetClipRect(gslc_tsGui* pGui,gslc_tsRect* pRect);
// -----------------------------------------------------------------------
// Font handling Functions
// -----------------------------------------------------------------------
///
/// Load a font from a resource and return pointer to it
///
/// \param[in] eFontRefType: Font reference type (GSLC_FONTREF_FNAME for SDL)
/// \param[in] pvFontRef: Font reference pointer (Pointer to the font filename)
/// \param[in] nFontSz: Typeface size to use
///
/// \return Void ptr to driver-specific font if load was successful, NULL otherwise
///
const void* gslc_DrvFontAdd(gslc_teFontRefType eFontRefType,const void* pvFontRef,uint16_t nFontSz);
///
/// Release all fonts defined in the GUI
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvFontsDestruct(gslc_tsGui* pGui);
///
/// Get the extent (width and height) of a text string
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pFont: Ptr to Font structure
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[out] pnTxtX: Ptr to offset X of text
/// \param[out] pnTxtY: Ptr to offset Y of text
/// \param[out] pnTxtSzW: Ptr to width of text
/// \param[out] pnTxtSzH: Ptr to height of text
///
/// \return true if success, false if failure
///
bool gslc_DrvGetTxtSize(gslc_tsGui* pGui,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,
int16_t* pnTxtX,int16_t* pnTxtY,uint16_t* pnTxtSzW,uint16_t* pnTxtSzH);
///
/// Draw a text string at the given coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nTxtX: X coordinate of top-left text string
/// \param[in] nTxtY: Y coordinate of top-left text string
/// \param[in] pFont: Ptr to Font
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[in] colTxt: Color to draw text
/// \param[in] colBg: unused in SDL, defaults to black
///
/// \return true if success, false if failure
///
bool gslc_DrvDrawTxt(gslc_tsGui* pGui,int16_t nTxtX,int16_t nTxtY,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,gslc_tsColor colTxt,gslc_tsColor colBg);
// -----------------------------------------------------------------------
// Screen Management Functions
// -----------------------------------------------------------------------
///
/// Force a page flip to occur. This generally copies active
/// screen surface to the display.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvPageFlipNow(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Graphics Primitives Functions
// -----------------------------------------------------------------------
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX: X coordinate of point
/// \param[in] nY: Y coordinate of point
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoint(gslc_tsGui* pGui,int16_t nX,int16_t nY,gslc_tsColor nCol);
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] asPt: Array of points to draw
/// \param[in] nNumPt: Number of points in array
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoints(gslc_tsGui* pGui,gslc_tsPt* asPt,uint16_t nNumPt,gslc_tsColor nCol);
///
/// Draw a framed rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a filled rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a line
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: Line start (X coordinate)
/// \param[in] nY0: Line start (Y coordinate)
/// \param[in] nX1: Line finish (X coordinate)
/// \param[in] nY1: Line finish (Y coordinate)
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawLine(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,int16_t nX1,int16_t nY1,gslc_tsColor nCol);
// TODO: Add DrvDrawFrameCircle()
// TODO: Add DrvDrawFillCircle()
// TODO: Add DrvDrawFrameTriangle()
// TODO: Add DrvDrawFillTriangle()
///
/// Copy all of source image to destination screen at specified coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvDrawImage(gslc_tsGui* pGui,int16_t nDstX,int16_t nDstY,gslc_tsImgRef sImgRef);
// TODO: Add DrvDrawMonoFromMem()
// TODO: Add DrvDrawBmp24FromMem()
///
/// Copy the background image to destination screen
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
void gslc_DrvDrawBkgnd(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Touch Functions
// -----------------------------------------------------------------------
///
/// Get the last touch event from the SDL_Event handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event
/// \param[out] pnY: Ptr to Y coordinate of last touch event
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, 1 for touch)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return true if an event was detected or false otherwise
///
bool gslc_DrvGetTouch(gslc_tsGui* pGui,int16_t* pnX,int16_t* pnY,uint16_t* pnPress,gslc_teInputRawEvent* peInputEvent,int16_t* pnInputVal);
// -----------------------------------------------------------------------
// Dynamic Screen rotation and Touch axes swap/flip functions
// -----------------------------------------------------------------------
///
/// Change rotation, automatically adapt touchscreen axes swap/flip
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nRotation: Screen Rotation value (0, 1, 2 or 3)
///
/// \return true if successful
///
bool gslc_DrvRotate(gslc_tsGui* pGui, uint8_t nRotation);
// =======================================================================
// Private Functions
// - These functions are not included in the scope of APIs used by
// the core GUIslice library. Instead, these functions are used
// to support the operations within this driver layer.
// =======================================================================
// -----------------------------------------------------------------------
// Private Configuration Functions
// -----------------------------------------------------------------------
///
/// Ensure SDL initializes cleanly to workaround
/// possible issues if previous SDL application
/// failed to close down gracefully.
///
/// \param[in] sTTY: Terminal device (eg. "/dev/tty0")
///
/// \return true if success
///
bool gslc_DrvCleanStart(const char* sTTY);
///
/// Report driver debug info (before initialization)
///
/// \return none
///
void gslc_DrvReportInfoPre();
///
/// Report driver debug info (after initialization)
///
/// \return none
///
void gslc_DrvReportInfoPost();
// -----------------------------------------------------------------------
// Private Conversion Functions
// -----------------------------------------------------------------------
///
/// Translate a gslc_tsRect into an SDL_Rect
///
/// \param[in] rRect: gslc_tsRect
///
/// \return Converted SDL_Rect
///
SDL_Rect gslc_DrvAdaptRect(gslc_tsRect rRect);
///
/// Translate a gslc_tsColor into an SDL_Color
///
/// \param[in] sCol: gslc_tsColor
///
/// \return Converted SDL_Color
///
SDL_Color gslc_DrvAdaptColor(gslc_tsColor sCol);
// -----------------------------------------------------------------------
// Private Drawing Functions
// -----------------------------------------------------------------------
#if defined(DRV_DISP_SDL1)
///
/// Lock an SDL surface so that direct pixel manipulation
/// can be done safely. This function is called before any
/// direct pixel updates.
///
/// POST:
/// - Primary screen surface is locked
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false otherwise
///
bool gslc_DrvScreenLock(gslc_tsGui* pGui);
///
/// Unlock the SDL surface after pixel manipulation is
/// complete. This function is called after all pixel updates
/// are done.
///
/// POST:
/// - Primary screen surface is unlocked
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvScreenUnlock(gslc_tsGui* pGui);
///
/// Convert an RGB color triplet into the surface pixel value.
/// This is called to produce the native pixel value required by
/// the raw pixel manipulation routines.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nCol: RGB value for conversion
///
/// \return A pixel value for the current screen format
///
uint32_t gslc_DrvAdaptColorRaw(gslc_tsGui* pGui,gslc_tsColor nCol);
///
/// Get the pixel at (X,Y) from the active screen
///
/// PRE:
/// - Screen surface must be locked
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX: Pixel X coordinate
/// \param[in] nY: Pixel Y coordinate
///
/// \return Pixel color value from the coordinate or 0 if error
///
uint32_t gslc_DrvDrawGetPixelRaw(gslc_tsGui* pGui,int16_t nX,int16_t nY);
///
/// Set a pixel on the active screen to the given color
///
/// PRE:
/// - Screen surface must be locked
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX: Pixel X coordinate to set
/// \param[in] nY: Pixel Y coordinate to set
/// \param[in] nPixelCol: Raw color pixel value to assign
///
/// \return none
///
void gslc_DrvDrawSetPixelRaw(gslc_tsGui* pGui,int16_t nX,int16_t nY,uint32_t nPixelCol);
///
/// Copy one image region to another.
/// - This is typically used to copy an image to the main screen surface
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX: Destination X coordinate of copy
/// \param[in] nY: Destination Y coordinate of copy
/// \param[in] pvSrc: Void Ptr to source surface (eg. a loaded image)
/// \param[in] pvDest: Void Ptr to destination surface (typically the screen)
///
/// \return none
///
void gslc_DrvPasteSurface(gslc_tsGui* pGui,int16_t nX, int16_t nY, void* pvSrc, void* pvDest);
#endif // DRV_DISP_SDL1
// -----------------------------------------------------------------------
// Private Touchscreen Functions (if using SDL)
// -----------------------------------------------------------------------
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTouch(gslc_tsGui* pGui,const char* acDev);
// -----------------------------------------------------------------------
// Touchscreen Functions (if using tslib)
// -----------------------------------------------------------------------
#if defined(DRV_TOUCH_TSLIB)
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_TDrvInitTouch(gslc_tsGui* pGui,const char* acDev);
///
/// Get the last touch event from the tslib handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event
/// \param[out] pnY: Ptr to Y coordinate of last touch event
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, >0 for touch)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return non-zero if an event was detected or 0 otherwise
///
bool gslc_TDrvGetTouch(gslc_tsGui* pGui, int16_t* pnX, int16_t* pnY, uint16_t* pnPress, gslc_teInputRawEvent* peInputEvent, int16_t* pnInputVal);
#endif // DRV_TOUCH_TSLIB
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_DRV_SDL_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,755 @@
#ifndef _GUISLICE_DRV_TFT_ESPI_H_
#define _GUISLICE_DRV_TFT_ESPI_H_
// =======================================================================
// GUIslice library (driver layer for bodmer/TFT_eSPI)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file GUIslice_drv_tft_espi.h
/// \brief GUIslice library (driver layer for TFT-eSPI)
// =======================================================================
// Driver Layer for bodmer/TFT_eSPI
// - https://github.com/Bodmer/TFT_eSPI
// =======================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "GUIslice.h"
#include <stdio.h>
// Determine characteristics for configured touch driver
// - DRV_TOUCH_TYPE_EXTERNAL: TDrv* external touch APIs are enabled
// - DRV_TOUCH_TYPE_RES: Resistive overlay
// - DRV_TOUCH_TYPE_CAP: Capacitive overlay
// - DRV_TOUCH_TYPE_ANALOG: Analog input
#if defined(DRV_TOUCH_ADA_STMPE610)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_ADA_FT6206)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_CAP // Capacitive
#elif defined(DRV_TOUCH_ADA_SIMPLE)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#define DRV_TOUCH_TYPE_ANALOG // Analog
#elif defined(DRV_TOUCH_XPT2046_STM)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_XPT2046_PS)
#define DRV_TOUCH_TYPE_EXTERNAL
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_TFT_ESPI)
#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_INPUT)
#define DRV_TOUCH_TYPE_EXTERNAL
#elif defined(DRV_TOUCH_HANDLER)
#define DRV_TOUCH_TYPE_EXTERNAL
#elif defined(DRV_TOUCH_NONE)
#endif // DRV_TOUCH_*
// Additional defines
// - Provide default if not in config file
#if !defined(GSLC_SPIFFS_EN)
#define GSLC_SPIFFS_EN 0
#endif // GSLC_SPIFFS_EN
// =======================================================================
// API support definitions
// - These defines indicate whether the driver includes optimized
// support for various APIs. If a define is set to 0, then the
// GUIslice core emulation will be used instead.
// - At the very minimum, the point draw routine must be available:
// gslc_DrvDrawPoint()
// =======================================================================
#define DRV_HAS_DRAW_POINT 1 ///< Support gslc_DrvDrawPoint()
#define DRV_HAS_DRAW_POINTS 0 ///< Support gslc_DrvDrawPoints()
#define DRV_HAS_DRAW_LINE 1 ///< Support gslc_DrvDrawLine()
#define DRV_HAS_DRAW_RECT_FRAME 1 ///< Support gslc_DrvDrawFrameRect()
#define DRV_HAS_DRAW_RECT_FILL 1 ///< Support gslc_DrvDrawFillRect()
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 1 ///< Support gslc_DrvDrawFrameRoundRect()
#define DRV_HAS_DRAW_RECT_ROUND_FILL 1 ///< Support gslc_DrvDrawFillRoundRect()
#define DRV_HAS_DRAW_CIRCLE_FRAME 1 ///< Support gslc_DrvDrawFrameCircle()
#define DRV_HAS_DRAW_CIRCLE_FILL 1 ///< Support gslc_DrvDrawFillCircle()
#define DRV_HAS_DRAW_TRI_FRAME 1 ///< Support gslc_DrvDrawFrameTriangle()
#define DRV_HAS_DRAW_TRI_FILL 1 ///< Support gslc_DrvDrawFillTriangle()
#define DRV_HAS_DRAW_TEXT 1 ///< Support gslc_DrvDrawTxt()
#define DRV_HAS_DRAW_BMP_MEM 0 ///< Support gslc_DrvDrawBmp24FromMem()
#define DRV_OVERRIDE_TXT_ALIGN 1 ///< Driver provides text alignment
// =======================================================================
// Driver-specific members
// =======================================================================
typedef struct {
gslc_tsColor nColBkgnd; ///< Background color (if not image-based)
gslc_tsRect rClipRect; ///< Clipping rectangle
} gslc_tsDriver;
// =======================================================================
// Public APIs to GUIslice core library
// - These functions define the renderer / driver-dependent
// implementations for the core drawing operations within
// GUIslice.
// =======================================================================
// -----------------------------------------------------------------------
// Configuration Functions
// -----------------------------------------------------------------------
///
/// Initialize the SDL library
/// - Performs clean startup workaround (if enabled)
/// - Configures video mode
/// - Initializes font support
///
/// PRE:
/// - The environment variables should be configured before
/// calling gslc_DrvInit(). This can be done with gslc_DrvInitEnv()
/// or manually in user function.
///
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
bool gslc_DrvInit(gslc_tsGui* pGui);
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTs(gslc_tsGui* pGui,const char* acDev);
///
/// Free up any members associated with the driver
/// - Eg. renderers, windows, background surfaces, etc.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvDestruct(gslc_tsGui* pGui);
///
/// Get the display driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameDisp(gslc_tsGui* pGui);
///
/// Get the touch driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameTouch(gslc_tsGui* pGui);
///
/// Get the native display driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the display driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverDisp(gslc_tsGui* pGui);
///
/// Get the native touch driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the touch driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverTouch(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Image/surface handling Functions
// -----------------------------------------------------------------------
///
/// Load a bitmap (*.bmp) and create a new image resource.
/// Transparency is enabled by GSLC_BMP_TRANS_EN
/// through use of color (GSLC_BMP_TRANS_RGB).
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return Image pointer (surface/texture) or NULL if error
///
void* gslc_DrvLoadImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a bitmap image
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a solid color
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nCol: RGB Color to use
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndColor(gslc_tsGui* pGui,gslc_tsColor nCol);
///
/// Set an element's normal-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageNorm(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Set an element's glow-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageGlow(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Release an image surface
///
/// \param[in] pvImg: Void ptr to image
///
/// \return none
///
void gslc_DrvImageDestruct(void* pvImg);
///
/// Set the clipping rectangle for future drawing updates
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pRect: Rectangular region to constrain edits
///
/// \return true if success, false if error
///
bool gslc_DrvSetClipRect(gslc_tsGui* pGui,gslc_tsRect* pRect);
// -----------------------------------------------------------------------
// Font handling Functions
// -----------------------------------------------------------------------
///
/// Load a font from a resource and return pointer to it
///
/// \param[in] eFontRefType: Font reference type:
/// - GSLC_FONTREF_PTR for Standard TFT_eSPI Fonts
/// - GSLC_FONTREF_FNAME for antialiased Font in SPIFFS
/// \param[in] pvFontRef: Font reference pointer / SPIFFS font filename without ext.
/// \param[in] nFontSz: Typeface size to use, ignored for SPIFFS font
///
/// \return Void ptr to driver-specific font if load was successful, NULL otherwise
///
const void* gslc_DrvFontAdd(gslc_teFontRefType eFontRefType,const void* pvFontRef,uint16_t nFontSz);
///
/// Release all fonts defined in the GUI
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvFontsDestruct(gslc_tsGui* pGui);
///
/// Get the extent (width and height) of a text string
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pFont: Ptr to Font structure
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[out] pnTxtX: Ptr to offset X of text
/// \param[out] pnTxtY: Ptr to offset Y of text
/// \param[out] pnTxtSzW: Ptr to width of text
/// \param[out] pnTxtSzH: Ptr to height of text
///
/// \return true if success, false if failure
///
bool gslc_DrvGetTxtSize(gslc_tsGui* pGui,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,
int16_t* pnTxtX,int16_t* pnTxtY,uint16_t* pnTxtSzW,uint16_t* pnTxtSzH);
///
/// Draw a text string at the given coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nTxtX: X coordinate of top-left text string
/// \param[in] nTxtY: Y coordinate of top-left text string
/// \param[in] pFont: Ptr to Font
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[in] colTxt: Color to draw text
/// \param[in] colBg: Color of Background for antialias blending
///
/// \return true if success, false if failure
///
bool gslc_DrvDrawTxt(gslc_tsGui* pGui,int16_t nTxtX,int16_t nTxtY,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,gslc_tsColor colTxt,gslc_tsColor colBg);
///
/// Draw a text string in a bounding box using the specified alignment
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X coordinate of top-left of bounding box
/// \param[in] nY0: Y coordinate of top-left of bounding box
/// \param[in] nX1: X coordinate of bot-right of bounding box
/// \param[in] nY1: Y coordinate of bot-right of bounding box
/// \param[in] eTxtAlign: Alignment mode]
/// \param[in] pFont: Ptr to Font
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[in] colTxt: Color to draw text
/// \param[in] colBg: Color of Background for antialias blending
//
/// \return true if success, false if failure
///
bool gslc_DrvDrawTxtAlign(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,int16_t nX1,int16_t nY1,int8_t eTxtAlign,
gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,gslc_tsColor colTxt,gslc_tsColor colBg);
// -----------------------------------------------------------------------
// Screen Management Functions
// -----------------------------------------------------------------------
///
/// Force a page flip to occur. This generally copies active
/// screen surface to the display.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvPageFlipNow(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Graphics Primitives Functions
// -----------------------------------------------------------------------
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX: X coordinate of point
/// \param[in] nY: Y coordinate of point
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoint(gslc_tsGui* pGui,int16_t nX,int16_t nY,gslc_tsColor nCol);
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] asPt: Array of points to draw
/// \param[in] nNumPt: Number of points in array
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoints(gslc_tsGui* pGui,gslc_tsPt* asPt,uint16_t nNumPt,gslc_tsColor nCol);
///
/// Draw a framed rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a filled rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a framed rounded rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nRadius: Radius for rounded corners
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRoundRect(gslc_tsGui* pGui,gslc_tsRect rRect,int16_t nRadius,gslc_tsColor nCol);
///
/// Draw a filled rounded rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nRadius: Radius for rounded corners
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRoundRect(gslc_tsGui* pGui,gslc_tsRect rRect,int16_t nRadius,gslc_tsColor nCol);
///
/// Draw a line
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: Line start (X coordinate)
/// \param[in] nY0: Line start (Y coordinate)
/// \param[in] nX1: Line finish (X coordinate)
/// \param[in] nY1: Line finish (Y coordinate)
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawLine(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,int16_t nX1,int16_t nY1,gslc_tsColor nCol);
///
/// Draw a framed circle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nMidX: Center of circle (X coordinate)
/// \param[in] nMidY: Center of circle (Y coordinate)
/// \param[in] nRadius: Radius of circle
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameCircle(gslc_tsGui* pGui,int16_t nMidX,int16_t nMidY,uint16_t nRadius,gslc_tsColor nCol);
///
/// Draw a filled circle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nMidX: Center of circle (X coordinate)
/// \param[in] nMidY: Center of circle (Y coordinate)
/// \param[in] nRadius: Radius of circle
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillCircle(gslc_tsGui* pGui,int16_t nMidX,int16_t nMidY,uint16_t nRadius,gslc_tsColor nCol);
///
/// Draw a framed triangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X Coordinate #1
/// \param[in] nY0: Y Coordinate #1
/// \param[in] nX1: X Coordinate #2
/// \param[in] nY1: Y Coordinate #2
/// \param[in] nX2: X Coordinate #3
/// \param[in] nY2: Y Coordinate #3
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameTriangle(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,
int16_t nX1,int16_t nY1,int16_t nX2,int16_t nY2,gslc_tsColor nCol);
///
/// Draw a filled triangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X Coordinate #1
/// \param[in] nY0: Y Coordinate #1
/// \param[in] nX1: X Coordinate #2
/// \param[in] nY1: Y Coordinate #2
/// \param[in] nX2: X Coordinate #3
/// \param[in] nY2: Y Coordinate #3
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillTriangle(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,
int16_t nX1,int16_t nY1,int16_t nX2,int16_t nY2,gslc_tsColor nCol);
///
/// Copy all of source image to destination screen at specified coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvDrawImage(gslc_tsGui* pGui,int16_t nDstX,int16_t nDstY,gslc_tsImgRef sImgRef);
///
/// Draw a monochrome bitmap from a memory array
/// - Draw from the bitmap buffer using the foreground color
/// defined in the header (unset bits are transparent)
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] pBitmap: Pointer to bitmap buffer
/// \param[in] bProgMem: Bitmap is stored in Flash if true, RAM otherwise
///
/// \return none
///
void gslc_DrvDrawMonoFromMem(gslc_tsGui* pGui,int16_t nDstX, int16_t nDstY, const unsigned char *pBitmap,bool bProgMem);
///
/// Draw a color 24-bit depth bitmap from a memory array
/// - Note that users must convert images from their native
/// format (eg. BMP, PNG, etc.) into a C array. Please
/// refer to the following guide for details:
/// https://github.com/ImpulseAdventure/GUIslice/wiki/Display-Images-from-FLASH
/// - The converted file (c array) can then be included in the sketch.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: X coord for copy
/// \param[in] nDstY: Y coord for copy
/// \param[in] pBitmap: Pointer to bitmap buffer
/// \param[in] bProgMem: Bitmap is stored in Flash if true, RAM otherwise
///
/// \return none
///
void gslc_DrvDrawBmp24FromMem(gslc_tsGui* pGui,int16_t nDstX, int16_t nDstY,const unsigned char* pBitmap,bool bProgMem);
#if (GSLC_SPIFFS_EN)
///
/// This routine uses TFT_eFEX library to draw a BMP file stored in SPIFFS file system
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvDrawBmpFromFile(gslc_tsGui* pGui,int16_t nDstX,int16_t nDstY,gslc_tsImgRef sImgRef);
///
/// This routine uses TFT_eFEX library to draw a JPEG file stored in SPIFFS file system
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvDrawJpegFromFile(gslc_tsGui* pGui,int16_t nDstX,int16_t nDstY,gslc_tsImgRef sImgRef);
#endif // end GSLC_SPIFFS_EN
///
/// Copy the background image to destination screen
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
void gslc_DrvDrawBkgnd(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Touch Functions (if using display driver library)
// -----------------------------------------------------------------------
#if defined(DRV_TOUCH_IN_DISP)
// Use TFT_eSPI's integrated XPT2046 touch driver
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTouch(gslc_tsGui* pGui,const char* acDev);
///
/// Get the last touch event from the internal XPT2046 touch handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event
/// \param[out] pnY: Ptr to Y coordinate of last touch event
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, 1 for touch)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return true if an event was detected or false otherwise
///
bool gslc_DrvGetTouch(gslc_tsGui* pGui,int16_t* pnX,int16_t* pnY,uint16_t* pnPress,gslc_teInputRawEvent* peInputEvent,int16_t* pnInputVal);
#endif // DRV_TOUCH_IN_DISP
// -----------------------------------------------------------------------
// Touch Functions (if using external touch driver library)
// -----------------------------------------------------------------------
// Check for deprecated config option
// - This check will be removed in future releases
#if defined(DRV_TOUCH_XPT2046)
#error "NOTE: DRV_TOUCH_XPT2046 has been renamed to DRV_TOUCH_XPT2046_STM. Please update your config."
#endif
#if defined(DRV_TOUCH_TYPE_EXTERNAL)
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_TDrvInitTouch(gslc_tsGui* pGui,const char* acDev);
///
/// Get the last touch event from the SDL_Event handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event
/// \param[out] pnY: Ptr to Y coordinate of last touch event
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, 1 for touch)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return true if an event was detected or false otherwise
///
bool gslc_TDrvGetTouch(gslc_tsGui* pGui, int16_t* pnX, int16_t* pnY, uint16_t* pnPress, gslc_teInputRawEvent* peInputEvent, int16_t* pnInputVal);
#endif // DRV_TOUCH_*
// -----------------------------------------------------------------------
// Dynamic Screen rotation and Touch axes swap/flip functions
// -----------------------------------------------------------------------
///
/// Change rotation, automatically adapt touchscreen axes swap/flip
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nRotation: Screen Rotation value (0, 1, 2 or 3)
///
/// \return true if successful
///
bool gslc_DrvRotate(gslc_tsGui* pGui, uint8_t nRotation);
// =======================================================================
// Private Functions
// - These functions are not included in the scope of APIs used by
// the core GUIslice library. Instead, these functions are used
// to support the operations within this driver layer.
// =======================================================================
uint16_t gslc_DrvAdaptColorToRaw(gslc_tsColor nCol);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_DRV_TFT_ESPI_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,672 @@
#ifndef _GUISLICE_DRV_UTFT_H_
#define _GUISLICE_DRV_UTFT_H_
// =======================================================================
// GUIslice library (driver layer for UTFT)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file GUIslice_drv_utft.h
/// \brief GUIslice library (driver layer for UTFT)
// =======================================================================
// Driver Layer for UTFT
// =======================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "GUIslice.h"
#include <stdio.h>
// Determine characteristics for configured touch driver
// - DRV_TOUCH_TYPE_EXTERNAL: TDrv* external touch APIs are enabled
// - DRV_TOUCH_TYPE_RES: Resistive overlay
// - DRV_TOUCH_TYPE_CAP: Capacitive overlay
// - DRV_TOUCH_TYPE_ANALOG: Analog input
#if defined(DRV_TOUCH_URTOUCH)
#define DRV_TOUCH_TYPE_EXTERNAL
// Don't set DRV_TOUCH_TYPE_RES since URTouch provides its own calibration
//#define DRV_TOUCH_TYPE_RES // Resistive
#elif defined(DRV_TOUCH_INPUT)
#define DRV_TOUCH_TYPE_EXTERNAL
#elif defined(DRV_TOUCH_NONE)
#endif // DRV_TOUCH_*
// =======================================================================
// API support definitions
// - These defines indicate whether the driver includes optimized
// support for various APIs. If a define is set to 0, then the
// GUIslice core emulation will be used instead.
// - At the very minimum, the point draw routine must be available:
// gslc_DrvDrawPoint()
// =======================================================================
#define DRV_HAS_DRAW_POINT 1 ///< Support gslc_DrvDrawPoint()
#define DRV_HAS_DRAW_POINTS 0 ///< Support gslc_DrvDrawPoints()
#define DRV_HAS_DRAW_LINE 1 ///< Support gslc_DrvDrawLine()
#define DRV_HAS_DRAW_RECT_FRAME 1 ///< Support gslc_DrvDrawFrameRect()
#define DRV_HAS_DRAW_RECT_FILL 1 ///< Support gslc_DrvDrawFillRect()
#define DRV_HAS_DRAW_RECT_ROUND_FRAME 1 ///< Support gslc_DrvDrawFrameRoundRect()
#define DRV_HAS_DRAW_RECT_ROUND_FILL 1 ///< Support gslc_DrvDrawFillRoundRect()
#define DRV_HAS_DRAW_CIRCLE_FRAME 1 ///< Support gslc_DrvDrawFrameCircle()
#define DRV_HAS_DRAW_CIRCLE_FILL 1 ///< Support gslc_DrvDrawFillCircle()
#define DRV_HAS_DRAW_TRI_FRAME 0 ///< Support gslc_DrvDrawFrameTriangle()
#define DRV_HAS_DRAW_TRI_FILL 0 ///< Support gslc_DrvDrawFillTriangle()
#define DRV_HAS_DRAW_TEXT 1 ///< Support gslc_DrvDrawTxt()
#define DRV_HAS_DRAW_BMP_MEM 0 ///< Support gslc_DrvDrawBmp24FromMem()
#define DRV_OVERRIDE_TXT_ALIGN 0 ///< Driver provides text alignment
// =======================================================================
// Driver-specific members
// =======================================================================
typedef struct {
gslc_tsColor nColBkgnd; ///< Background color (if not image-based)
gslc_tsRect rClipRect; ///< Clipping rectangle
} gslc_tsDriver;
// =======================================================================
// Public APIs to GUIslice core library
// - These functions define the renderer / driver-dependent
// implementations for the core drawing operations within
// GUIslice.
// =======================================================================
// -----------------------------------------------------------------------
// Configuration Functions
// -----------------------------------------------------------------------
///
/// Initialize the SDL library
/// - Performs clean startup workaround (if enabled)
/// - Configures video mode
/// - Initializes font support
///
/// PRE:
/// - The environment variables should be configured before
/// calling gslc_DrvInit(). This can be done with gslc_DrvInitEnv()
/// or manually in user function.
///
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
bool gslc_DrvInit(gslc_tsGui* pGui);
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTs(gslc_tsGui* pGui,const char* acDev);
///
/// Free up any members associated with the driver
/// - Eg. renderers, windows, background surfaces, etc.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvDestruct(gslc_tsGui* pGui);
///
/// Get the display driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameDisp(gslc_tsGui* pGui);
///
/// Get the touch driver name
///
/// \param[in] pGui: Pointer to GUI
///
/// \return String containing driver name
///
const char* gslc_DrvGetNameTouch(gslc_tsGui* pGui);
///
/// Get the native display driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the display driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverDisp(gslc_tsGui* pGui);
///
/// Get the native touch driver instance
/// - This can be useful to access special commands
/// available in the selected driver.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return Void pointer to the touch driver instance.
/// This pointer should be typecast to the particular
/// driver being used. If no driver was created then
/// this function will return NULL.
///
void* gslc_DrvGetDriverTouch(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Image/surface handling Functions
// -----------------------------------------------------------------------
///
/// Load a bitmap (*.bmp) and create a new image resource.
/// Transparency is enabled by GSLC_BMP_TRANS_EN
/// through use of color (GSLC_BMP_TRANS_RGB).
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return Image pointer (surface/texture) or NULL if error
///
void* gslc_DrvLoadImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a bitmap image
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndImage(gslc_tsGui* pGui,gslc_tsImgRef sImgRef);
///
/// Configure the background to use a solid color
/// - The background is used when redrawing the entire page
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nCol: RGB Color to use
///
/// \return true if success, false if fail
///
bool gslc_DrvSetBkgndColor(gslc_tsGui* pGui,gslc_tsColor nCol);
///
/// Set an element's normal-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageNorm(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Set an element's glow-state image
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElem: Pointer to Element to update
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if error
///
bool gslc_DrvSetElemImageGlow(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsImgRef sImgRef);
///
/// Release an image surface
///
/// \param[in] pvImg: Void ptr to image
///
/// \return none
///
void gslc_DrvImageDestruct(void* pvImg);
///
/// Set the clipping rectangle for future drawing updates
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pRect: Rectangular region to constrain edits
///
/// \return true if success, false if error
///
bool gslc_DrvSetClipRect(gslc_tsGui* pGui,gslc_tsRect* pRect);
// -----------------------------------------------------------------------
// Font handling Functions
// -----------------------------------------------------------------------
///
/// Load a font from a resource and return pointer to it
///
/// \param[in] eFontRefType: Font reference type (GSLC_FONTREF_PTR for Arduino)
/// \param[in] pvFontRef: Font reference pointer (Pointer to the GFXFont array)
/// \param[in] nFontSz: Typeface size to use
///
/// \return Void ptr to driver-specific font if load was successful, NULL otherwise
///
const void* gslc_DrvFontAdd(gslc_teFontRefType eFontRefType,const void* pvFontRef,uint16_t nFontSz);
///
/// Release all fonts defined in the GUI
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvFontsDestruct(gslc_tsGui* pGui);
///
/// Get the extent (width and height) of a text string
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pFont: Ptr to Font structure
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[out] pnTxtX: Ptr to offset X of text
/// \param[out] pnTxtY: Ptr to offset Y of text
/// \param[out] pnTxtSzW: Ptr to width of text
/// \param[out] pnTxtSzH: Ptr to height of text
///
/// \return true if success, false if failure
///
bool gslc_DrvGetTxtSize(gslc_tsGui* pGui,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,
int16_t* pnTxtX,int16_t* pnTxtY,uint16_t* pnTxtSzW,uint16_t* pnTxtSzH);
///
/// Draw a text string at the given coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nTxtX: X coordinate of top-left text string
/// \param[in] nTxtY: Y coordinate of top-left text string
/// \param[in] pFont: Ptr to Font
/// \param[in] pStr: String to display
/// \param[in] eTxtFlags: Flags associated with text string
/// \param[in] colTxt: Color to draw text
/// \param[in] colBg: unused in ADAGFX, defaults to black
///
/// \return true if success, false if failure
///
bool gslc_DrvDrawTxt(gslc_tsGui* pGui,int16_t nTxtX,int16_t nTxtY,gslc_tsFont* pFont,const char* pStr,gslc_teTxtFlags eTxtFlags,gslc_tsColor colTxt,gslc_tsColor colBg);
// -----------------------------------------------------------------------
// Screen Management Functions
// -----------------------------------------------------------------------
///
/// Force a page flip to occur. This generally copies active
/// screen surface to the display.
///
/// \param[in] pGui: Pointer to GUI
///
/// \return none
///
void gslc_DrvPageFlipNow(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Graphics Primitives Functions
// -----------------------------------------------------------------------
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX: X coordinate of point
/// \param[in] nY: Y coordinate of point
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoint(gslc_tsGui* pGui,int16_t nX,int16_t nY,gslc_tsColor nCol);
///
/// Draw a point
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] asPt: Array of points to draw
/// \param[in] nNumPt: Number of points in array
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawPoints(gslc_tsGui* pGui,gslc_tsPt* asPt,uint16_t nNumPt,gslc_tsColor nCol);
///
/// Draw a framed rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a filled rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRect(gslc_tsGui* pGui,gslc_tsRect rRect,gslc_tsColor nCol);
///
/// Draw a framed rounded rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to frame
/// \param[in] nRadius: Radius for rounded corners
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameRoundRect(gslc_tsGui* pGui,gslc_tsRect rRect,int16_t nRadius,gslc_tsColor nCol);
///
/// Draw a filled rounded rectangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] rRect: Rectangular region to fill
/// \param[in] nRadius: Radius for rounded corners
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillRoundRect(gslc_tsGui* pGui,gslc_tsRect rRect,int16_t nRadius,gslc_tsColor nCol);
///
/// Draw a line
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: Line start (X coordinate)
/// \param[in] nY0: Line start (Y coordinate)
/// \param[in] nX1: Line finish (X coordinate)
/// \param[in] nY1: Line finish (Y coordinate)
/// \param[in] nCol: Color RGB value to draw
///
/// \return true if success, false if error
///
bool gslc_DrvDrawLine(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,int16_t nX1,int16_t nY1,gslc_tsColor nCol);
///
/// Draw a framed circle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nMidX: Center of circle (X coordinate)
/// \param[in] nMidY: Center of circle (Y coordinate)
/// \param[in] nRadius: Radius of circle
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameCircle(gslc_tsGui* pGui,int16_t nMidX,int16_t nMidY,uint16_t nRadius,gslc_tsColor nCol);
///
/// Draw a filled circle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nMidX: Center of circle (X coordinate)
/// \param[in] nMidY: Center of circle (Y coordinate)
/// \param[in] nRadius: Radius of circle
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillCircle(gslc_tsGui* pGui,int16_t nMidX,int16_t nMidY,uint16_t nRadius,gslc_tsColor nCol);
///
/// Draw a framed triangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X Coordinate #1
/// \param[in] nY0: Y Coordinate #1
/// \param[in] nX1: X Coordinate #2
/// \param[in] nY1: Y Coordinate #2
/// \param[in] nX2: X Coordinate #3
/// \param[in] nY2: Y Coordinate #3
/// \param[in] nCol: Color RGB value to frame
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFrameTriangle(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,
int16_t nX1,int16_t nY1,int16_t nX2,int16_t nY2,gslc_tsColor nCol);
///
/// Draw a filled triangle
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nX0: X Coordinate #1
/// \param[in] nY0: Y Coordinate #1
/// \param[in] nX1: X Coordinate #2
/// \param[in] nY1: Y Coordinate #2
/// \param[in] nX2: X Coordinate #3
/// \param[in] nY2: Y Coordinate #3
/// \param[in] nCol: Color RGB value to fill
///
/// \return true if success, false if error
///
bool gslc_DrvDrawFillTriangle(gslc_tsGui* pGui,int16_t nX0,int16_t nY0,
int16_t nX1,int16_t nY1,int16_t nX2,int16_t nY2,gslc_tsColor nCol);
///
/// Copy all of source image to destination screen at specified coordinate
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] sImgRef: Image reference
///
/// \return true if success, false if fail
///
bool gslc_DrvDrawImage(gslc_tsGui* pGui,int16_t nDstX,int16_t nDstY,gslc_tsImgRef sImgRef);
///
/// Draw a monochrome bitmap from a memory array
/// - Draw from the bitmap buffer using the foreground color
/// defined in the header (unset bits are transparent)
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: Destination X coord for copy
/// \param[in] nDstY: Destination Y coord for copy
/// \param[in] pBitmap: Pointer to bitmap buffer
/// \param[in] bProgMem: Bitmap is stored in Flash if true, RAM otherwise
///
/// \return none
///
void gslc_DrvDrawMonoFromMem(gslc_tsGui* pGui,int16_t nDstX, int16_t nDstY, const unsigned char *pBitmap,bool bProgMem);
///
/// Draw a color 24-bit depth bitmap from a memory array
/// - Note that users must convert images from their native
/// format (eg. BMP, PNG, etc.) into a C array. Please
/// refer to the following guide for details:
/// https://github.com/ImpulseAdventure/GUIslice/wiki/Display-Images-from-FLASH
/// - The converted file (c array) can then be included in the sketch.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nDstX: X coord for copy
/// \param[in] nDstY: Y coord for copy
/// \param[in] pBitmap: Pointer to bitmap buffer
/// \param[in] bProgMem: Bitmap is stored in Flash if true, RAM otherwise
///
/// \return none
///
void gslc_DrvDrawBmp24FromMem(gslc_tsGui* pGui,int16_t nDstX, int16_t nDstY,const unsigned char* pBitmap,bool bProgMem);
///
/// Copy the background image to destination screen
///
/// \param[in] pGui: Pointer to GUI
///
/// \return true if success, false if fail
///
void gslc_DrvDrawBkgnd(gslc_tsGui* pGui);
// -----------------------------------------------------------------------
// Touch Functions (if using display driver library)
// -----------------------------------------------------------------------
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_DrvInitTouch(gslc_tsGui* pGui,const char* acDev);
///
/// Get the last touch event from the internal touch handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event
/// \param[out] pnY: Ptr to Y coordinate of last touch event
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, 1 for touch)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return true if an event was detected or false otherwise
///
bool gslc_DrvGetTouch(gslc_tsGui* pGui,int16_t* pnX,int16_t* pnY,uint16_t* pnPress,gslc_teInputRawEvent* peInputEvent,int16_t* pnInputVal);
// -----------------------------------------------------------------------
// Touch Functions (if using external touch driver library)
// -----------------------------------------------------------------------
#if defined(DRV_TOUCH_TYPE_EXTERNAL)
///
/// Perform any touchscreen-specific initialization
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] acDev: Device path to touchscreen
/// eg. "/dev/input/touchscreen"
///
/// \return true if successful
///
bool gslc_TDrvInitTouch(gslc_tsGui* pGui,const char* acDev);
///
/// Get the last touch event from the SDL_Event handler
///
/// \param[in] pGui: Pointer to GUI
/// \param[out] pnX: Ptr to X coordinate of last touch event
/// \param[out] pnY: Ptr to Y coordinate of last touch event
/// \param[out] pnPress: Ptr to Pressure level of last touch event (0 for none, 1 for touch)
/// \param[out] peInputEvent Indication of event type
/// \param[out] pnInputVal Additional data for event type
///
/// \return true if an event was detected or false otherwise
///
bool gslc_TDrvGetTouch(gslc_tsGui* pGui, int16_t* pnX, int16_t* pnY, uint16_t* pnPress, gslc_teInputRawEvent* peInputEvent, int16_t* pnInputVal);
#endif // DRV_TOUCH_*
// -----------------------------------------------------------------------
// Dynamic Screen rotation and Touch axes swap/flip functions
// -----------------------------------------------------------------------
///
/// Change rotation, automatically adapt touchscreen axes swap/flip
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nRotation: Screen Rotation value (0, 1, 2 or 3)
///
/// \return true if successful
///
bool gslc_DrvRotate(gslc_tsGui* pGui, uint8_t nRotation);
// =======================================================================
// Private Functions
// - These functions are not included in the scope of APIs used by
// the core GUIslice library. Instead, these functions are used
// to support the operations within this driver layer.
// =======================================================================
uint16_t gslc_DrvAdaptColorToRaw(gslc_tsColor nCol);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_DRV_ADAGFX_H_

View file

@ -0,0 +1,52 @@
#ifndef _GUISLICE_EX_H_
#define _GUISLICE_EX_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file GUIslice_ex.h
// Provide backwards compatibility until we have removed GUIslice_ex
#include "XCheckbox.h"
#include "XGauge.h"
#include "XGraph.h"
#include "XSelNum.h"
#include "XSlider.h"
#include "XTextbox.h"
// Warn users that they should update their includes
// #warning "NOTE: Please replace `GUIslice_ex` per https://github.com/ImpulseAdventure/GUIslice/wiki/Note-_-Include-Extended-Elements"
#endif // _GUISLICE_EX_H_

View file

@ -0,0 +1,119 @@
// Abstract touch handler (TH) class
// This is the abstract base class for creating specific touch handlers
// The touch handler performs the adaption in between the GUIslice framework and any touch driver
// The touch handler used is specified in the main program by calling gslc_InitTouchHandler(&touchHandler);
/// \file GUIslice_th.cpp
#include "GUIslice_th.h"
///////////////////////////////////////////////////
// Point x,y,z Class for usage in the touch handler
THPoint::THPoint(void) {
x = y = z = 0;
}
THPoint::THPoint(uint16_t x0, uint16_t y0, uint16_t z0) {
x = x0;
y = y0;
z = z0;
}
bool THPoint::operator==(THPoint p1) {
return ((p1.x == x) && (p1.y == y) && (p1.z == z));
}
bool THPoint::operator!=(THPoint p1) {
return ((p1.x != x) || (p1.y != y) || (p1.z != z));
}
///////////////////////////////////////////////////
// Abstract TouchHandler - you have to inherit this
void TouchHandler::setSize(uint16_t _disp_xSize, uint16_t _disp_ySize)
{
disp_xSize = _disp_xSize;
disp_ySize = _disp_ySize;
}
void TouchHandler::setCalibration(uint16_t _ts_xMin, uint16_t _ts_xMax, uint16_t _ts_yMin, uint16_t _ts_yMax)
{
ts_xMin = _ts_xMin;
ts_xMax = _ts_xMax;
ts_yMin = _ts_yMin;
ts_yMax = _ts_yMax;
}
void TouchHandler::setSwapFlip(bool _swapXY,bool _flipX,bool _flipY)
{
swapXY = _swapXY;
flipX = _flipX;
flipY = _flipY;
}
THPoint TouchHandler::scale(THPoint pIn)
{
THPoint pOut;
pOut.x = map(pIn.x, ts_xMin,ts_xMax, 0,disp_xSize);
pOut.y = map(pIn.y, ts_yMin,ts_yMax, 0,disp_ySize);
pOut.z = pIn.z;
pOut.x = constrain(pOut.x,0,disp_xSize-1);
pOut.y = constrain(pOut.y,0,disp_ySize-1);
pOut.z = constrain(pOut.z,0,4095);
if (swapXY)
{
uint16_t x = pOut.x;
pOut.x = pOut.y;
pOut.y = x;
}
if (flipX)
pOut.x = ( (!swapXY) ? disp_xSize-1 : disp_ySize-1 ) - pOut.x;
if (flipY)
pOut.y = ( (!swapXY) ? disp_ySize-1 : disp_xSize-1 ) - pOut.y;
//Serial.print("disp_xSize= ");Serial.println(disp_xSize);
return pOut;
}
// overwrite this with your code to add initialisation of the touch driver used
void TouchHandler::begin(void) {
return;
}
// overwrite this with your code to return the scaled touch coordinates
THPoint TouchHandler::getPoint(void) {
return THPoint();
}
/////////////////////////////////
// init and set the touch handler
// Pointer to touch handler used by GUIslice
TouchHandler *pTouchHandler = NULL; // NULL => no handler is available
// Init and set the touch hander
void gslc_InitTouchHandler(TouchHandler *pTH) {
//begin
pTH->begin();
//set the touch handler to be used by GUIslice_drv_...
pTouchHandler = pTH;
}
// Get the touch handler
TouchHandler* gslc_getTouchHandler(void)
{
return pTouchHandler;
}

View file

@ -0,0 +1,71 @@
// Abstract touch handler (TH) class
// This is the abstract base class for creating specific touch handlers
// The touch handler performs the adaption in between the GUIslice framework and any touch driver
// The touch handler used is specified in the main program by calling gslc_InitTouchHandler(&touchHandler);
/// \file GUIslice_th.h
#ifndef _GUISLICE_TH_H_
#define _GUISLICE_TH_H_
#include <Arduino.h>
///////////////////////////////////////////////////
// Point x,y,z Class for usage in the touch handler
class THPoint {
public:
THPoint(void);
THPoint(uint16_t x, uint16_t y, uint16_t z);
bool operator==(THPoint);
bool operator!=(THPoint);
uint16_t x, y, z;
};
///////////////////////////////////////////////////
// Abstract TouchHandler - you have to inherit this
class TouchHandler {
public:
//in order to create a specific touch handler you have to write your own constructor
TouchHandler() {};
void setSize(uint16_t _disp_xSize, uint16_t _disp_ySize);
void setCalibration(uint16_t ts_xMin, uint16_t ts_xMax, uint16_t ts_yMin, uint16_t ts_yMax);
//order of operations: map, swap, constraint, flip
void setSwapFlip(bool _swapXY,bool _flipX,bool _flipY);
THPoint scale(THPoint pIn);
//in order to create a specific touch handler you have to overwrite this methods
virtual void begin(void);
virtual THPoint getPoint(void);
private:
//landscape perspective: x: width, y: heigth
uint16_t disp_xSize = 320;
uint16_t disp_ySize = 240;
uint16_t ts_xMin = 0;
uint16_t ts_xMax = 4095;
uint16_t ts_yMin = 0;
uint16_t ts_yMax = 4095;
bool swapXY = false;
bool flipX = false;
bool flipY = false;
};
/////////////////////////////////
// init and set the touch handler
void gslc_InitTouchHandler(TouchHandler *pTHO);
TouchHandler* gslc_getTouchHandler(void);
#endif

View file

@ -0,0 +1,52 @@
// touch handler (TH) for XPT2046 using the arduino built in driver <XPT2046_touch.h>
// The touch handler performs the adaption in between the GUIslice framework and any touch driver
// The touch handler used is specified in the main program by calling gslc_InitTouchHandler(&touchHandler);
/// \file GUIslice_th_XPT2046.h
#ifndef _GUISLICE_TH_XPT2046_H_
#define _GUISLICE_TH_XPT2046_H_
#include <Arduino.h>
#include <GUIslice_th.h>
#include <XPT2046_touch.h>
class TouchHandler_XPT2046: public TouchHandler {
public:
// parameters:
// spi object to be used
// chip select pin for spi
TouchHandler_XPT2046(SPIClass &spi, uint8_t spi_cs_pin) : spi(spi), touchDriver(XPT2046_touch(spi_cs_pin, spi)) {
//empirical calibration values, can be updated by calling setCalibration in the user program
setCalibration(398,3877,280,3805);
//swapping and flipping to adopt to default GUIslice orientation
setSwapFlip(true,false,true);
}
//begin is called by gslc_InitTouchHandler
void begin(void) {
//init the touch driver
touchDriver.begin();
}
//this method returns the scaled point provided by the touch driver
THPoint getPoint(void) {
//get the coordinates from the touch driver
TS_Point pt = touchDriver.getPoint();
//Serial.print("pt= ");Serial.print(pt.x);Serial.print(",");Serial.print(pt.y);Serial.print(",");Serial.println(pt.z);
//perform scaling (this includes swapping and flipping)
THPoint ps = scale( THPoint(pt.x,pt.y,pt.z) );
//Serial.print("ps= ");Serial.print(ps.x);Serial.print(",");Serial.print(ps.y);Serial.print(",");Serial.println(ps.z);
return ps;
};
SPIClass spi;
XPT2046_touch touchDriver;
};
#endif

View file

@ -0,0 +1,42 @@
#ifndef _GUISLICE_VERSION_H_
#define _GUISLICE_VERSION_H_
// =======================================================================
// GUIslice library (version control)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
// =======================================================================
// Define current release (X.Y.Z) & build number
// =======================================================================
#define GUISLICE_VER "0.15.0"
#endif // _GUISLICE_VERSION_H_

View file

@ -0,0 +1,185 @@
const uint8_t NotoMono8pt7bBitmaps[] PROGMEM = {
0x00, 0xFF, 0xFD, 0x0F, 0xCF, 0x3C, 0xF3, 0x08, 0x86, 0x61, 0x11, 0xFF,
0x11, 0x0C, 0xC2, 0x23, 0xFE, 0x22, 0x08, 0x84, 0x41, 0x10, 0x10, 0x21,
0xFE, 0x89, 0x1A, 0x1C, 0x0E, 0x16, 0x26, 0x5F, 0xE1, 0x02, 0x00, 0x71,
0xA2, 0x48, 0xB2, 0x28, 0x74, 0x01, 0x00, 0x80, 0x2E, 0x14, 0x4D, 0x12,
0x45, 0x8E, 0x38, 0x36, 0x11, 0x08, 0x82, 0x81, 0x89, 0xE5, 0x96, 0x86,
0x63, 0x33, 0xCF, 0x30, 0xFF, 0x11, 0x98, 0x8C, 0x63, 0x10, 0xC6, 0x30,
0x86, 0x18, 0x40, 0x43, 0x0C, 0x21, 0x8C, 0x61, 0x18, 0xC6, 0x23, 0x31,
0x00, 0x10, 0x22, 0x4F, 0xF2, 0x8D, 0x99, 0x00, 0x10, 0x20, 0x47, 0xF1,
0x02, 0x04, 0x00, 0x6F, 0x60, 0xFC, 0xF0, 0x0C, 0x20, 0x86, 0x10, 0xC3,
0x08, 0x61, 0x04, 0x30, 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0x81, 0x81, 0xC3,
0xC3, 0x42, 0x66, 0x3C, 0x37, 0xD1, 0x11, 0x11, 0x11, 0x11, 0x3C, 0xE3,
0x03, 0x01, 0x03, 0x02, 0x04, 0x08, 0x10, 0x20, 0xC0, 0xFF, 0x7E, 0xC3,
0x03, 0x03, 0x06, 0x38, 0x06, 0x03, 0x01, 0x03, 0x86, 0xFC, 0x06, 0x07,
0x02, 0x83, 0x43, 0x21, 0x11, 0x09, 0x84, 0xFF, 0x81, 0x00, 0x80, 0x40,
0xFE, 0xC0, 0xC0, 0x80, 0x80, 0xFC, 0x06, 0x03, 0x01, 0x03, 0x86, 0xFC,
0x1E, 0x30, 0x40, 0xC0, 0xFC, 0xE2, 0xC3, 0x81, 0xC1, 0xC3, 0x66, 0x3C,
0xFF, 0x03, 0x02, 0x02, 0x06, 0x04, 0x0C, 0x0C, 0x18, 0x18, 0x10, 0x30,
0x3C, 0xC3, 0x81, 0xC3, 0x46, 0x3C, 0x66, 0xC3, 0x81, 0xC3, 0xC2, 0x3C,
0x3C, 0x66, 0xC3, 0x83, 0x81, 0xC3, 0x47, 0x3F, 0x03, 0x02, 0x04, 0x78,
0xF0, 0x03, 0xC0, 0x6C, 0x00, 0x03, 0x7B, 0x00, 0x01, 0x06, 0x38, 0xE0,
0xE0, 0x38, 0x06, 0x01, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x60, 0x1C,
0x07, 0x07, 0x1C, 0x60, 0x80, 0x7C, 0xC3, 0x03, 0x01, 0x03, 0x06, 0x18,
0x10, 0x30, 0x00, 0x30, 0x30, 0x1E, 0x08, 0x64, 0x09, 0x79, 0x92, 0x68,
0x9A, 0x26, 0x89, 0xA6, 0xF7, 0x64, 0x00, 0x80, 0x1F, 0x00, 0x0C, 0x03,
0x01, 0xE0, 0x48, 0x12, 0x0C, 0xC2, 0x10, 0xFC, 0x61, 0x90, 0x24, 0x0B,
0x03, 0xFE, 0x87, 0x83, 0x83, 0x83, 0xFC, 0x86, 0x83, 0x83, 0x83, 0x86,
0xFC, 0x1F, 0x73, 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x60, 0x71,
0x1F, 0xF8, 0x8E, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x86, 0x8C,
0xF8, 0xFF, 0x02, 0x04, 0x08, 0x1F, 0xE0, 0x40, 0x81, 0x02, 0x07, 0xF0,
0xFF, 0x02, 0x04, 0x08, 0x1F, 0xE0, 0x40, 0x81, 0x02, 0x04, 0x00, 0x1E,
0x72, 0x40, 0xC0, 0xC0, 0xC7, 0xC1, 0xC1, 0xC1, 0x41, 0x73, 0x3F, 0x81,
0x81, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFC,
0x82, 0x08, 0x20, 0x82, 0x08, 0x20, 0x82, 0x3F, 0x02, 0x04, 0x08, 0x10,
0x20, 0x40, 0x81, 0x02, 0x0E, 0x37, 0xC0, 0x81, 0xC1, 0xA1, 0x91, 0x89,
0x85, 0x83, 0x61, 0x18, 0x84, 0x43, 0x20, 0xD0, 0x30, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF, 0xC3, 0xC3, 0xC3,
0xC3, 0xA5, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99, 0x91, 0xC1, 0xC1, 0xA1,
0xA1, 0x91, 0x91, 0x89, 0x89, 0x85, 0x85, 0x83, 0x83, 0x3C, 0x66, 0x42,
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C, 0xFC, 0x86, 0x83,
0x83, 0x83, 0x82, 0xFC, 0x80, 0x80, 0x80, 0x80, 0x80, 0x3C, 0x66, 0x42,
0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C, 0x04, 0x02, 0x01,
0xFC, 0x43, 0x20, 0xD0, 0x68, 0x34, 0x13, 0xF1, 0x18, 0x86, 0x41, 0x20,
0xD0, 0x30, 0x3F, 0xE3, 0xC0, 0xC0, 0xC0, 0x78, 0x0E, 0x03, 0x01, 0x03,
0x86, 0xFC, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xC3, 0xC3,
0x66, 0x3C, 0xC0, 0xD0, 0x24, 0x09, 0x86, 0x21, 0x08, 0x43, 0x30, 0x48,
0x12, 0x07, 0x80, 0xC0, 0x30, 0x80, 0x70, 0x1C, 0x0F, 0x03, 0x4C, 0x93,
0x24, 0xC9, 0x4A, 0x52, 0x94, 0xA6, 0x18, 0x84, 0xC0, 0xD8, 0x62, 0x10,
0xCC, 0x1E, 0x03, 0x00, 0xC0, 0x78, 0x33, 0x08, 0x44, 0x1B, 0x03, 0xC0,
0x90, 0x66, 0x10, 0x88, 0x12, 0x05, 0x00, 0xC0, 0x20, 0x08, 0x02, 0x00,
0x80, 0x20, 0xFF, 0x03, 0x02, 0x04, 0x0C, 0x08, 0x10, 0x30, 0x20, 0x40,
0xC0, 0xFF, 0xF8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF0, 0xC1, 0x04,
0x18, 0x20, 0xC3, 0x04, 0x18, 0x20, 0x83, 0xF1, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0xF0, 0x18, 0x18, 0x24, 0x24, 0x46, 0x42, 0x83, 0xFF, 0xC0,
0xC8, 0x80, 0x7E, 0x43, 0x03, 0x01, 0x3F, 0xC1, 0x83, 0xC7, 0x7D, 0x80,
0x80, 0x80, 0x80, 0xBC, 0xE6, 0xC3, 0x83, 0x81, 0x83, 0xC3, 0xE6, 0xBC,
0x3F, 0x61, 0xC0, 0xC0, 0x80, 0xC0, 0xC0, 0x61, 0x3F, 0x01, 0x01, 0x01,
0x01, 0x3D, 0x67, 0xC3, 0xC1, 0x81, 0xC1, 0xC3, 0x67, 0x3D, 0x3C, 0x66,
0xC3, 0xC1, 0xFF, 0x80, 0xC0, 0x61, 0x3E, 0x1E, 0x60, 0x81, 0x0F, 0xC4,
0x08, 0x10, 0x20, 0x40, 0x81, 0x02, 0x00, 0x3F, 0xB1, 0x90, 0x48, 0x26,
0x31, 0xF1, 0x80, 0x80, 0x7F, 0x60, 0xE0, 0x38, 0x37, 0xE0, 0x80, 0x80,
0x80, 0x80, 0xBE, 0xE3, 0xC3, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x10,
0x20, 0x00, 0x07, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x8F, 0xE0, 0x0C,
0x30, 0x00, 0x7C, 0x10, 0x41, 0x04, 0x10, 0x41, 0x04, 0x10, 0x43, 0xF8,
0x80, 0x80, 0x80, 0x80, 0x84, 0x88, 0x90, 0xA0, 0xE0, 0x90, 0x88, 0x84,
0x86, 0x70, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x8F,
0xE0, 0xB6, 0xDB, 0xD9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xBE, 0xE3,
0xC3, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x3C, 0x66, 0xC3, 0xC3, 0x81,
0xC3, 0xC3, 0x66, 0x3C, 0xBC, 0xE6, 0xC3, 0x83, 0x81, 0x83, 0xC3, 0xE6,
0xBC, 0x80, 0x80, 0x80, 0x80, 0x3D, 0x67, 0xC3, 0xC1, 0x81, 0xC1, 0xC3,
0x67, 0x3D, 0x01, 0x01, 0x01, 0x01, 0x9F, 0x47, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x7F, 0x18, 0x38, 0x38, 0x30, 0x63, 0xF8, 0x20, 0x43, 0xF9,
0x02, 0x04, 0x08, 0x10, 0x20, 0x60, 0x78, 0x81, 0x81, 0x81, 0x81, 0x81,
0x81, 0xC3, 0xC7, 0x7D, 0xC3, 0x42, 0x42, 0x66, 0x24, 0x24, 0x3C, 0x18,
0x18, 0x8C, 0x73, 0x34, 0xC9, 0x7A, 0x52, 0x94, 0xA5, 0x28, 0x8C, 0x21,
0x00, 0x43, 0x66, 0x24, 0x18, 0x18, 0x3C, 0x24, 0x66, 0xC3, 0xC3, 0x42,
0x42, 0x66, 0x24, 0x24, 0x3C, 0x18, 0x18, 0x18, 0x10, 0x30, 0xE0, 0xFC,
0x30, 0x84, 0x30, 0x84, 0x30, 0xFC, 0x1C, 0xC2, 0x08, 0x20, 0x82, 0x30,
0x60, 0x82, 0x08, 0x20, 0xC1, 0xC0, 0xFF, 0xFF, 0x80, 0xE0, 0xC1, 0x04,
0x10, 0x41, 0x03, 0x18, 0x41, 0x04, 0x10, 0xCE, 0x00, 0x70, 0x99, 0x0E };
const GFXglyph NotoMono8pt7bGlyphs[] PROGMEM = {
{ 0, 1, 1, 10, 0, 0 }, // 0x20 ' '
{ 1, 2, 12, 10, 4, -11 }, // 0x21 '!'
{ 4, 6, 4, 10, 2, -11 }, // 0x22 '"'
{ 7, 10, 12, 10, 0, -11 }, // 0x23 '#'
{ 22, 7, 14, 10, 2, -12 }, // 0x24 '$'
{ 35, 10, 12, 10, 0, -11 }, // 0x25 '%'
{ 50, 9, 12, 10, 1, -11 }, // 0x26 '&'
{ 64, 2, 4, 10, 4, -11 }, // 0x27 '''
{ 65, 5, 15, 10, 3, -11 }, // 0x28 '('
{ 75, 5, 15, 10, 2, -11 }, // 0x29 ')'
{ 85, 7, 7, 10, 1, -12 }, // 0x2A '*'
{ 92, 7, 7, 10, 1, -8 }, // 0x2B '+'
{ 99, 3, 4, 10, 3, -1 }, // 0x2C ','
{ 101, 6, 1, 10, 2, -4 }, // 0x2D '-'
{ 102, 2, 2, 10, 4, -1 }, // 0x2E '.'
{ 103, 6, 12, 10, 2, -11 }, // 0x2F '/'
{ 112, 8, 12, 10, 1, -11 }, // 0x30 '0'
{ 124, 4, 12, 10, 2, -11 }, // 0x31 '1'
{ 130, 8, 12, 10, 1, -11 }, // 0x32 '2'
{ 142, 8, 12, 10, 1, -11 }, // 0x33 '3'
{ 154, 9, 12, 10, 1, -11 }, // 0x34 '4'
{ 168, 8, 12, 10, 1, -11 }, // 0x35 '5'
{ 180, 8, 12, 10, 1, -11 }, // 0x36 '6'
{ 192, 8, 12, 10, 1, -11 }, // 0x37 '7'
{ 204, 8, 12, 10, 1, -11 }, // 0x38 '8'
{ 216, 8, 12, 10, 1, -11 }, // 0x39 '9'
{ 228, 2, 9, 10, 4, -8 }, // 0x3A ':'
{ 231, 3, 11, 10, 3, -8 }, // 0x3B ';'
{ 236, 8, 8, 10, 1, -9 }, // 0x3C '<'
{ 244, 8, 5, 10, 1, -7 }, // 0x3D '='
{ 249, 8, 8, 10, 1, -9 }, // 0x3E '>'
{ 257, 8, 12, 10, 1, -11 }, // 0x3F '?'
{ 269, 10, 13, 10, 0, -11 }, // 0x40 '@'
{ 286, 10, 12, 10, 0, -11 }, // 0x41 'A'
{ 301, 8, 12, 10, 1, -11 }, // 0x42 'B'
{ 313, 8, 12, 10, 1, -11 }, // 0x43 'C'
{ 325, 8, 12, 10, 1, -11 }, // 0x44 'D'
{ 337, 7, 12, 10, 2, -11 }, // 0x45 'E'
{ 348, 7, 12, 10, 2, -11 }, // 0x46 'F'
{ 359, 8, 12, 10, 1, -11 }, // 0x47 'G'
{ 371, 8, 12, 10, 1, -11 }, // 0x48 'H'
{ 383, 6, 12, 10, 2, -11 }, // 0x49 'I'
{ 392, 7, 12, 10, 1, -11 }, // 0x4A 'J'
{ 403, 9, 12, 10, 1, -11 }, // 0x4B 'K'
{ 417, 8, 12, 10, 1, -11 }, // 0x4C 'L'
{ 429, 8, 12, 10, 1, -11 }, // 0x4D 'M'
{ 441, 8, 12, 10, 1, -11 }, // 0x4E 'N'
{ 453, 8, 12, 10, 1, -11 }, // 0x4F 'O'
{ 465, 8, 12, 10, 1, -11 }, // 0x50 'P'
{ 477, 8, 15, 10, 1, -11 }, // 0x51 'Q'
{ 492, 9, 12, 10, 1, -11 }, // 0x52 'R'
{ 506, 8, 12, 10, 1, -11 }, // 0x53 'S'
{ 518, 8, 12, 10, 1, -11 }, // 0x54 'T'
{ 530, 8, 12, 10, 1, -11 }, // 0x55 'U'
{ 542, 10, 12, 10, 0, -11 }, // 0x56 'V'
{ 557, 10, 12, 10, 0, -11 }, // 0x57 'W'
{ 572, 10, 12, 10, 0, -11 }, // 0x58 'X'
{ 587, 10, 12, 10, 0, -11 }, // 0x59 'Y'
{ 602, 8, 12, 10, 1, -11 }, // 0x5A 'Z'
{ 614, 4, 15, 10, 4, -11 }, // 0x5B '['
{ 622, 6, 12, 10, 2, -11 }, // 0x5C '\'
{ 631, 4, 15, 10, 2, -11 }, // 0x5D ']'
{ 639, 8, 7, 10, 1, -11 }, // 0x5E '^'
{ 646, 10, 1, 10, 0, 2 }, // 0x5F '_'
{ 648, 3, 3, 10, 4, -12 }, // 0x60 '`'
{ 650, 8, 9, 10, 1, -8 }, // 0x61 'a'
{ 659, 8, 13, 10, 1, -12 }, // 0x62 'b'
{ 672, 8, 9, 10, 1, -8 }, // 0x63 'c'
{ 681, 8, 13, 10, 1, -12 }, // 0x64 'd'
{ 694, 8, 9, 10, 1, -8 }, // 0x65 'e'
{ 703, 7, 13, 10, 2, -12 }, // 0x66 'f'
{ 715, 9, 13, 10, 0, -8 }, // 0x67 'g'
{ 730, 8, 13, 10, 1, -12 }, // 0x68 'h'
{ 743, 7, 13, 10, 1, -12 }, // 0x69 'i'
{ 755, 6, 17, 10, 1, -12 }, // 0x6A 'j'
{ 768, 8, 13, 10, 2, -12 }, // 0x6B 'k'
{ 781, 7, 13, 10, 1, -12 }, // 0x6C 'l'
{ 793, 8, 9, 10, 1, -8 }, // 0x6D 'm'
{ 802, 8, 9, 10, 1, -8 }, // 0x6E 'n'
{ 811, 8, 9, 10, 1, -8 }, // 0x6F 'o'
{ 820, 8, 13, 10, 1, -8 }, // 0x70 'p'
{ 833, 8, 13, 10, 1, -8 }, // 0x71 'q'
{ 846, 7, 9, 10, 2, -8 }, // 0x72 'r'
{ 854, 6, 9, 10, 2, -8 }, // 0x73 's'
{ 861, 7, 11, 10, 2, -10 }, // 0x74 't'
{ 871, 8, 9, 10, 1, -8 }, // 0x75 'u'
{ 880, 8, 9, 10, 1, -8 }, // 0x76 'v'
{ 889, 10, 9, 10, 0, -8 }, // 0x77 'w'
{ 901, 8, 9, 10, 1, -8 }, // 0x78 'x'
{ 910, 8, 13, 10, 1, -8 }, // 0x79 'y'
{ 923, 6, 9, 10, 2, -8 }, // 0x7A 'z'
{ 930, 6, 15, 10, 2, -11 }, // 0x7B '{'
{ 942, 1, 17, 10, 4, -12 }, // 0x7C '|'
{ 945, 6, 15, 10, 2, -11 }, // 0x7D '}'
{ 957, 8, 3, 10, 1, -6 } }; // 0x7E '~'
const GFXfont NotoMono8pt7b PROGMEM = {
(uint8_t *)NotoMono8pt7bBitmaps,
(GFXglyph *)NotoMono8pt7bGlyphs,
0x20, 0x7E, 18 };
// Approx. 1632 bytes

517
src/guislice/XCheckbox.c Normal file
View file

@ -0,0 +1,517 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XCheckbox.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XCheckbox.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Checkbox
// - Checkbox with custom handler for touch tracking which
// enables glow to be defined whenever touch is tracked over
// the element.
// ============================================================================
// Create a checkbox element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw but does not track touch/click
gslc_tsElemRef* gslc_ElemXCheckboxCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXCheckbox* pXData,gslc_tsRect rElem,bool bRadio,gslc_teXCheckboxStyle nStyle,
gslc_tsColor colCheck,bool bChecked)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXCheckboxCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_CHECKBOX,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures &= ~GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_GLOW_EN;
// Default group assignment. Can override later with ElemSetGroup()
sElem.nGroup = GSLC_GROUP_ID_NONE;
// Define other extended data
pXData->bRadio = bRadio;
pXData->bChecked = bChecked;
pXData->colCheck = colCheck;
pXData->nStyle = nStyle;
pXData->pfuncXToggle = NULL;
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXCheckboxDraw;
// Specify the custom touch tracking callback
// - NOTE: This is optional (and can be set to NULL).
// See the discussion under gslc_ElemXCheckboxTouch()
sElem.pfuncXTouch = &gslc_ElemXCheckboxTouch;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
bool gslc_ElemXCheckboxGetState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
gslc_tsXCheckbox* pCheckbox = (gslc_tsXCheckbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_CHECKBOX, __LINE__);
if (!pCheckbox) return false;
return pCheckbox->bChecked;
}
// Determine which checkbox in the group has been "checked"
gslc_tsElemRef* gslc_ElemXCheckboxFindChecked(gslc_tsGui* pGui,int16_t nGroupId)
{
int16_t nCurInd;
gslc_tsElemRef* pCurElemRef = NULL;
gslc_tsElem* pCurElem = NULL;
int16_t nCurType;
int16_t nCurGroup;
bool bCurChecked;
gslc_tsElemRef* pFoundElemRef = NULL;
// Operate on current page
// TODO: Support other page layers
gslc_tsPage* pPage = pGui->apPageStack[GSLC_STACK_CUR];
if (pPage == NULL) {
return NULL; // No page added yet
}
if (pGui == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXCheckboxFindChecked";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsCollect* pCollect = &pPage->sCollect;
for (nCurInd=0;nCurInd<pCollect->nElemCnt;nCurInd++) {
// Fetch extended data
pCurElemRef = &(pCollect->asElemRef[nCurInd]);
pCurElem = gslc_GetElemFromRef(pGui,pCurElemRef);
nCurType = pCurElem->nType;
// Only want to proceed if it is a checkbox
if (nCurType != GSLC_TYPEX_CHECKBOX) {
continue;
}
nCurGroup = pCurElem->nGroup;
bCurChecked = gslc_ElemXCheckboxGetState(pGui,pCurElemRef);
// If this is in a different group, ignore it
if (nCurGroup != nGroupId) {
continue;
}
// Did we find an element in the group that was checked?
if (bCurChecked) {
pFoundElemRef = pCurElemRef;
break;
}
} // nCurInd
return pFoundElemRef;
}
// Assign the callback function for checkbox/radio state change events
void gslc_ElemXCheckboxSetStateFunc(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, GSLC_CB_XCHECKBOX pfuncCb)
{
gslc_tsXCheckbox* pCheckbox = (gslc_tsXCheckbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_CHECKBOX, __LINE__);
if (!pCheckbox) return;
pCheckbox->pfuncXToggle = pfuncCb;
}
// Helper routine for gslc_ElemXCheckboxSetState()
// - Updates the checkbox/radio control's state but does
// not touch any other controls in the group
void gslc_ElemXCheckboxSetStateHelp(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bChecked,bool bDoCb)
{
gslc_tsXCheckbox* pCheckbox = (gslc_tsXCheckbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_CHECKBOX, __LINE__);
if (!pCheckbox) return;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
// Update our data element
bool const bCheckedOld = pCheckbox->bChecked;
pCheckbox->bChecked = bChecked;
// Element needs redraw
if (bChecked != bCheckedOld) {
// Only need an incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
} else {
// Same state as before
// - No need to do anything further since we don't need to
// trigger any callbacks or redraw
return;
}
// If no callbacks requested, exit now
if (!bDoCb) {
return;
}
// If any state callback is defined, call it now
// - In all cases, return the ElementRef of the element that issued the callback
//
// For checkbox:
// - If not selected: return current ID and state=false
// - If selected: return current ID and state=true
// For radio button:
// - If none selected: return ID_NONE and state=false
// - If one selected: return selected ID and state=true
if (pCheckbox->pfuncXToggle != NULL) {
gslc_tsElemRef* pRetRef = NULL;
int16_t nGroup = GSLC_GROUP_ID_NONE;
int16_t nSelId = GSLC_ID_NONE;
if (!pCheckbox->bRadio) {
// Checkbox
nSelId = pElem->nId;
} else {
// Radio button
// - Determine the group that the radio button belongs to
nGroup = pElem->nGroup;
// Determine if any radio button in the group has been selected
pRetRef = gslc_ElemXCheckboxFindChecked(pGui, nGroup);
if (pRetRef != NULL) {
// One has been selected, return its ID
bChecked = true;
nSelId = pRetRef->pElem->nId;
} else {
// No radio button selected, return ID NONE
bChecked = false;
nSelId = GSLC_ID_NONE;
}
}
// Now send the callback notification
(*pCheckbox->pfuncXToggle)((void*)(pGui), (void*)(pElemRef), nSelId, bChecked);
} // pfuncXToggle
}
// Update the checkbox/radio control's state. If it is a radio button
// then also update the state of all other buttons in the group.
void gslc_ElemXCheckboxSetState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bChecked)
{
gslc_tsXCheckbox* pCheckbox = (gslc_tsXCheckbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_CHECKBOX, __LINE__);
if (!pCheckbox) return;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsPage* pPage = NULL;
bool bRadio = pCheckbox->bRadio;
int16_t nGroup = pElem->nGroup;
int16_t nElemId = pElem->nId;
// Special handling when we select a radio button
if (bRadio && bChecked) {
// If we are selecting a radio button that is already
// selected, then skip further update events.
// NOTE: This check is not very efficient, but it avoids
// the creation of extra events.
gslc_tsElemRef* pTmpRef = gslc_ElemXCheckboxFindChecked(pGui, nGroup);
if (pTmpRef == pElemRef) {
// Same element, so skip
return;
}
// Proceed to deselect any other selected items in the group.
// Note that SetState calls itself to deselect other items so it
// is important to qualify this logic with bChecked=true
int16_t nCurInd;
int16_t nCurId;
gslc_tsElem* pCurElem = NULL;
gslc_tsElemRef* pCurElemRef = NULL;
int16_t nCurType;
int16_t nCurGroup;
// We use the GUI pointer for access to other elements
// Check all pages in case we are affecting radio buttons on other pages
for (int8_t nPageInd=0;nPageInd<pGui->nPageCnt;nPageInd++) {
pPage = &pGui->asPage[nPageInd];
if (!pPage) {
// If this stack page is not enabled, skip to next stack page
continue;
}
gslc_tsCollect* pCollect = &pPage->sCollect;
for (nCurInd=0;nCurInd<pCollect->nElemRefCnt;nCurInd++) {
// Fetch extended data
pCurElemRef = &pCollect->asElemRef[nCurInd];
pCurElem = gslc_GetElemFromRef(pGui,pCurElemRef);
// FIXME: Handle pCurElemRef->eElemFlags
nCurId = pCurElem->nId;
nCurType = pCurElem->nType;
// Only want to proceed if it is a checkbox
if (nCurType != GSLC_TYPEX_CHECKBOX) {
continue;
}
nCurGroup = pCurElem->nGroup;
// If this is in a different group, ignore it
if (nCurGroup != nGroup) {
continue;
}
// Is this our element? If so, ignore the deselect operation
if (nCurId == nElemId) {
continue;
}
// Deselect all other elements
// - But don't trigger any callbacks
gslc_ElemXCheckboxSetStateHelp(pGui,pCurElemRef,false,false);
} // nInd
} // nStackPage
} // bRadio
// Set the state of the current element
// - Trigger callback if enabled
gslc_ElemXCheckboxSetStateHelp(pGui,pElemRef,bChecked,true);
}
// Toggle the checkbox control's state
void gslc_ElemXCheckboxToggleState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXCheckboxToggleState";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
// Update the data element
bool bCheckNew = (gslc_ElemXCheckboxGetState(pGui,pElemRef))? false : true;
gslc_ElemXCheckboxSetState(pGui,pElemRef,bCheckNew);
// Element needs redraw
// - Only incremental is needed
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
// Redraw the checkbox
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXCheckboxDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXCheckbox* pCheckbox = (gslc_tsXCheckbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_CHECKBOX, __LINE__);
if (!pCheckbox) return false;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsRect rInner;
bool bChecked = pCheckbox->bChecked;
gslc_teXCheckboxStyle nStyle = pCheckbox->nStyle;
bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
// Draw the background
gslc_DrawFillRect(pGui,pElem->rElem,pElem->colElemFill);
// Generic coordinate calcs
int16_t nX0,nY0,nX1,nY1,nMidX,nMidY;
nX0 = pElem->rElem.x;
nY0 = pElem->rElem.y;
nX1 = pElem->rElem.x + pElem->rElem.w - 1;
nY1 = pElem->rElem.y + pElem->rElem.h - 1;
nMidX = (nX0+nX1)/2;
nMidY = (nY0+nY1)/2;
if (nStyle == GSLCX_CHECKBOX_STYLE_BOX) {
// Draw the center indicator if checked
rInner = gslc_ExpandRect(pElem->rElem,-5,-5);
if (bChecked) {
// If checked, fill in the inner region
gslc_DrawFillRect(pGui,rInner,pCheckbox->colCheck);
} else {
// Assume the background fill has already been done so
// we don't need to do anything more in the unchecked case
}
// Draw a frame around the checkbox
gslc_DrawFrameRect(pGui,pElem->rElem,(bGlow)?pElem->colElemFrameGlow:pElem->colElemFrame);
} else if (nStyle == GSLCX_CHECKBOX_STYLE_X) {
// Draw an X through center if checked
if (bChecked) {
gslc_DrawLine(pGui,nX0,nY0,nX1,nY1,pCheckbox->colCheck);
gslc_DrawLine(pGui,nX0,nY1,nX1,nY0,pCheckbox->colCheck);
}
// Draw a frame around the checkbox
gslc_DrawFrameRect(pGui,pElem->rElem,(bGlow)?pElem->colElemFrameGlow:pElem->colElemFrame);
} else if (nStyle == GSLCX_CHECKBOX_STYLE_ROUND) {
// Draw inner circle if checked
if (bChecked) {
gslc_DrawFillCircle(pGui,nMidX,nMidY,5,pCheckbox->colCheck);
}
// Draw a frame around the checkbox
gslc_DrawFrameCircle(pGui,nMidX,nMidY,(pElem->rElem.w/2),(bGlow)?pElem->colElemFrameGlow:pElem->colElemFrame);
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
// This callback function is called by gslc_ElemSendEventTouch()
// after any touch event
// - NOTE: Adding this touch callback is optional. Without it, we
// can still have a functional checkbox, but doing the touch
// tracking allows us to change the glow state of the element
// dynamically, as well as updating the checkbox state if the
// user releases over it (ie. a click event).
//
bool gslc_ElemXCheckboxTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXCheckbox* pCheckbox = (gslc_tsXCheckbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_CHECKBOX, __LINE__);
if (!pCheckbox) return false;
//gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
bool bRadio = pCheckbox->bRadio;
bool bCheckedOld = pCheckbox->bChecked;
bool bGlowingOld = gslc_ElemGetGlow(pGui,pElemRef);
switch(eTouch) {
case GSLC_TOUCH_DOWN_IN:
gslc_ElemSetGlow(pGui,pElemRef,true);
break;
case GSLC_TOUCH_MOVE_IN:
gslc_ElemSetGlow(pGui,pElemRef,true);
break;
case GSLC_TOUCH_MOVE_OUT:
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
case GSLC_TOUCH_UP_IN:
gslc_ElemSetGlow(pGui,pElemRef,false);
// Now that we released on element, update the state
bool bCheckNew;
if (bRadio) {
// Radio button action: set
bCheckNew = true;
} else {
// Checkbox button action: toggle
bCheckNew = (pCheckbox->bChecked)?false:true;
}
gslc_ElemXCheckboxSetState(pGui,pElemRef,bCheckNew);
break;
case GSLC_TOUCH_UP_OUT:
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
default:
return false;
break;
}
// If the checkbox changed state, redraw
bool bChanged = false;
if (gslc_ElemGetGlow(pGui,pElemRef) != bGlowingOld) { bChanged = true; }
if (pCheckbox->bChecked != bCheckedOld) { bChanged = true; }
if (bChanged) {
// Incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
return true;
#endif // !DRV_TOUCH_NONE
}
// ============================================================================

290
src/guislice/XCheckbox.h Normal file
View file

@ -0,0 +1,290 @@
#ifndef _GUISLICE_EX_XCHECKBOX_H_
#define _GUISLICE_EX_XCHECKBOX_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Checkbox/Radio button control
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XCheckbox.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Checkbox
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_CHECKBOX GSLC_TYPE_BASE_EXTEND + 1
/// Checkbox drawing style
typedef enum {
GSLCX_CHECKBOX_STYLE_BOX, ///< Inner box
GSLCX_CHECKBOX_STYLE_X, ///< Crossed
GSLCX_CHECKBOX_STYLE_ROUND, ///< Circular
} gslc_teXCheckboxStyle;
/// Callback function for checkbox/radio element state change
/// - nSelId: Selected element's ID or GSLC_ID_NONE
/// - bChecked: Element was selected if true, false otherwise
typedef bool (*GSLC_CB_XCHECKBOX)(void* pvGui,void* pvElemRef,int16_t nSelId, bool bChecked);
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Checkbox element
typedef struct {
bool bRadio; ///< Radio-button operation if true
gslc_teXCheckboxStyle nStyle; ///< Drawing style for element
bool bChecked; ///< Indicates if it is selected (checked)
gslc_tsColor colCheck; ///< Color of checked inner fill
GSLC_CB_XCHECKBOX pfuncXToggle; ///< Callback event to say element has changed
} gslc_tsXCheckbox;
///
/// Create a Checkbox or Radio button Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining checkbox size
/// \param[in] bRadio: Radio-button functionality if true
/// \param[in] nStyle: Drawing style for checkbox / radio button
/// \param[in] colCheck: Color for inner fill when checked
/// \param[in] bChecked: Default state
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXCheckboxCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXCheckbox* pXData,gslc_tsRect rElem,bool bRadio,
gslc_teXCheckboxStyle nStyle,gslc_tsColor colCheck,bool bChecked);
///
/// Get a Checkbox element's current state
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return Current state
///
bool gslc_ElemXCheckboxGetState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Set a Checkbox element's current state
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] bChecked: New state
///
/// \return none
///
void gslc_ElemXCheckboxSetState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bChecked);
///
/// Find the checkbox within a group that has been checked
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nGroupId: Group ID to search
///
/// \return Element Ptr or NULL if none checked
///
gslc_tsElemRef* gslc_ElemXCheckboxFindChecked(gslc_tsGui* pGui,int16_t nGroupId);
///
/// Toggle a Checkbox element's current state
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return none
///
void gslc_ElemXCheckboxToggleState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Assign the state callback function for a checkbox/radio button
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] pfuncCb: Function pointer to callback routine (or NULL for none)
///
/// \return none
///
void gslc_ElemXCheckboxSetStateFunc(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, GSLC_CB_XCHECKBOX pfuncCb);
///
/// Draw a Checkbox element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXCheckboxDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Handle touch events to Checkbox element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXCheckboxTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
/// \def gslc_ElemXCheckboxCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,nGroup,bRadio_,nStyle_,colCheck_,bChecked_)
///
/// Create a Checkbox or Radio button Element in Flash
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Unique element ID to assign
/// \param[in] nPage: Page ID to attach element to
/// \param[in] nX: X coordinate of element
/// \param[in] nY: Y coordinate of element
/// \param[in] nW: Width of element
/// \param[in] nH: Height of element
/// \param[in] colFill: Color for the control background fill
/// \param[in] bFillEn: True if background filled, false otherwise (recommend True)
/// \param[in] nGroup: Group ID that radio buttons belong to (else GSLC_GROUP_NONE)
/// \param[in] bRadio_: Radio-button functionality if true
/// \param[in] nStyle_: Drawing style for checkbox / radio button
/// \param[in] colCheck_: Color for inner fill when checked
/// \param[in] bChecked_: Default state
///
/// \return none
///
#if (GSLC_USE_PROGMEM)
#define gslc_ElemXCheckboxCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,colFill,bFillEn,nGroup,bRadio_,nStyle_,colCheck_,bChecked_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_CLICK_EN | (bFillEn?GSLC_ELEM_FEA_FILL_EN:0); \
static gslc_tsXCheckbox sCheckbox##nElemId; \
sCheckbox##nElemId.bRadio = bRadio_; \
sCheckbox##nElemId.bChecked = bChecked_; \
sCheckbox##nElemId.colCheck = colCheck_; \
sCheckbox##nElemId.nStyle = nStyle_; \
static const gslc_tsElem sElem##nElemId PROGMEM = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_CHECKBOX, \
(gslc_tsRect){nX,nY,nW,nH}, \
nGroup, \
GSLC_COL_GRAY,colFill,GSLC_COL_WHITE,GSLC_COL_BLACK, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sCheckbox##nElemId), \
NULL, \
&gslc_ElemXCheckboxDraw, \
&gslc_ElemXCheckboxTouch, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#else
#define gslc_ElemXCheckboxCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,colFill,bFillEn,nGroup,bRadio_,nStyle_,colCheck_,bChecked_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_CLICK_EN | (bFillEn?GSLC_ELEM_FEA_FILL_EN:0); \
static gslc_tsXCheckbox sCheckbox##nElemId; \
sCheckbox##nElemId.bRadio = bRadio_; \
sCheckbox##nElemId.bChecked = bChecked_; \
sCheckbox##nElemId.colCheck = colCheck_; \
sCheckbox##nElemId.nStyle = nStyle_; \
static const gslc_tsElem sElem##nElemId = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_CHECKBOX, \
(gslc_tsRect){nX,nY,nW,nH}, \
nGroup, \
GSLC_COL_GRAY,colFill,GSLC_COL_WHITE,GSLC_COL_BLACK, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sCheckbox##nElemId), \
NULL, \
&gslc_ElemXCheckboxDraw, \
&gslc_ElemXCheckboxTouch, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_CONST | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XCHECKBOX_H_

699
src/guislice/XGauge.c Normal file
View file

@ -0,0 +1,699 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XGauge.c
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// WARNING: The XGauge element has been replaced by XProgress / XRadial / XRamp
// Please update your code according to the migration notes in:
// https://github.com/ImpulseAdventure/GUIslice/pull/157
// XGauge may be removed in a future release.
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XGauge.h"
#include <stdio.h>
#include <math.h> // For sin/cos in XGauge(RADIAL)
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Gauge
// - Basic progress bar with support for positive/negative state
// and vertical / horizontal orientation.
// ============================================================================
// Create a gauge element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw but does not track touch/click
gslc_tsElemRef* gslc_ElemXGaugeCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXGauge* pXData,gslc_tsRect rElem,
int16_t nMin,int16_t nMax,int16_t nVal,gslc_tsColor colGauge,bool bVert)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGaugeCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_GAUGE,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_CLICK_EN; // Element is not "clickable"
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN;
sElem.nGroup = GSLC_GROUP_ID_NONE;
pXData->nMin = nMin;
pXData->nMax = nMax;
pXData->nVal = nVal;
pXData->nStyle = GSLCX_GAUGE_STYLE_PROG_BAR; // Default to progress bar
pXData->bVert = bVert;
pXData->bFlip = false;
pXData->colGauge = colGauge;
pXData->colTick = GSLC_COL_GRAY;
pXData->nTickCnt = 8;
pXData->nTickLen = 5;
pXData->nIndicLen = 10; // Dummy default to be overridden
pXData->nIndicTip = 3; // Dummy default to be overridden
pXData->bIndicFill = false;
sElem.pXData = (void*)(pXData);
sElem.pfuncXDraw = &gslc_ElemXGaugeDraw;
sElem.pfuncXTouch = NULL; // No need to track touches
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_GRAY;
GSLC_DEBUG_PRINT("NOTE: XGauge has been replaced by XProgress/XRadial/XRamp\n","");
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
void gslc_ElemXGaugeSetStyle(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teXGaugeStyle nStyle)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGaugeSetStyle";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGauge* pGauge = (gslc_tsXGauge*)(pElem->pXData);
// Update the type element
pGauge->nStyle = nStyle;
// Just in case we were called at runtime, mark as needing redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXGaugeSetIndicator(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor colGauge,
uint16_t nIndicLen,uint16_t nIndicTip,bool bIndicFill)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGaugeSetIndicator";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGauge* pGauge = (gslc_tsXGauge*)(pElem->pXData);
// Update the config
pGauge->colGauge = colGauge;
pGauge->nIndicLen = nIndicLen;
pGauge->nIndicTip = nIndicTip;
pGauge->bIndicFill = bIndicFill;
// Just in case we were called at runtime, mark as needing redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXGaugeSetTicks(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor colTick,uint16_t nTickCnt,uint16_t nTickLen)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGaugeSetTicks";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGauge* pGauge = (gslc_tsXGauge*)(pElem->pXData);
// Update the config
pGauge->colTick = colTick;
pGauge->nTickCnt = nTickCnt;
pGauge->nTickLen = nTickLen;
// Just in case we were called at runtime, mark as needing redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// Update the gauge control's current position
void gslc_ElemXGaugeUpdate(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGaugeUpdate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGauge* pGauge = (gslc_tsXGauge*)(pElem->pXData);
// Update the data element
int16_t nValOld = pGauge->nVal;
pGauge->nVal = nVal;
// Element needs redraw
if (nVal != nValOld) {
// We only need an incremental redraw
// NOTE: If the user configures the indicator to be
// long enough that it overlaps some of the gauge indicators
// then a full redraw should be done instead.
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Update the gauge's fill direction
// - Setting bFlip causes the gauge to be filled in the reverse direction
// to the default
// - Default fill direction for horizontal gauges: left-to-right
// - Default fill direction for vertical gauges: bottom-to-top
void gslc_ElemXGaugeSetFlip(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bFlip)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGaugeSetFlip";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
// Fetch the element's extended data structure
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGauge* pGauge = (gslc_tsXGauge*)(pElem->pXData);
if (pGauge == NULL) {
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXGaugeSetFlip(%s) pXData is NULL\n","");
return;
}
pGauge->bFlip = bFlip;
// Mark for redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// Redraw the gauge
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXGaugeDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGaugeDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
// Fetch the element's extended data structure
gslc_tsXGauge* pGauge;
pGauge = (gslc_tsXGauge*)(pElem->pXData);
if (pGauge == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXGaugeDraw(%s) pXData is NULL\n","");
return false;
}
switch (pGauge->nStyle) {
case GSLCX_GAUGE_STYLE_PROG_BAR:
gslc_ElemXGaugeDrawProgressBar(pGui,pElemRef,eRedraw);
break;
case GSLCX_GAUGE_STYLE_RADIAL:
#if (GSLC_FEATURE_XGAUGE_RADIAL)
gslc_ElemXGaugeDrawRadial(pGui,pElemRef,eRedraw);
#endif
break;
case GSLCX_GAUGE_STYLE_RAMP:
#if (GSLC_FEATURE_XGAUGE_RAMP)
gslc_ElemXGaugeDrawRamp(pGui,pElemRef,eRedraw);
#endif
break;
default:
// ERROR
break;
}
// Save as "last state" to support incremental erase/redraw
pGauge->nValLast = pGauge->nVal;
pGauge->bValLastValid = true;
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
return true;
}
bool gslc_ElemXGaugeDrawProgressBar(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw)
{
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGauge* pGauge = (gslc_tsXGauge*)(pElem->pXData);
gslc_tsRect rTmp; // Temporary rect for drawing
gslc_tsRect rGauge; // Filled portion of gauge
gslc_tsRect rEmpty; // Empty portion of gauge
uint16_t nElemW,nElemH;
int16_t nElemX0,nElemY0,nElemX1,nElemY1;
int16_t nGaugeX0,nGaugeY0,nGaugeX1,nGaugeY1;
nElemX0 = pElem->rElem.x;
nElemY0 = pElem->rElem.y;
nElemX1 = pElem->rElem.x + pElem->rElem.w - 1;
nElemY1 = pElem->rElem.y + pElem->rElem.h - 1;
nElemW = pElem->rElem.w;
nElemH = pElem->rElem.h;
bool bVert = pGauge->bVert;
bool bFlip = pGauge->bFlip;
int16_t nMax = pGauge->nMax;
int16_t nMin = pGauge->nMin;
int16_t nRng = pGauge->nMax - pGauge->nMin;
uint32_t nScl = 1;
int16_t nGaugeMid = 0;
int16_t nLen = 0;
int16_t nTmp = 0;
int32_t nTmpL = 0;
if (nRng == 0) {
GSLC_DEBUG2_PRINT("ERROR: ElemXGaugeDraw() Zero gauge range [%d,%d]\n",nMin,nMax);
return false;
}
if (bVert) {
nScl = nElemH*32768/nRng;
} else {
nScl = nElemW*32768/nRng;
}
// Calculate the control midpoint/zeropoint (for display purposes)
nTmpL = -((int32_t)nMin * (int32_t)nScl / 32768);
nGaugeMid = (int16_t)nTmpL;
// Calculate the length of the bar
// - Use long mult/divide to avoid need for floating point
nTmpL = (int32_t)(pGauge->nVal) * (int32_t)(nScl) / 32768;
nLen = (int16_t)(nTmpL);
// Define the gauge's fill rectangle region
// depending on the orientation (bVert) and whether
// the current position is negative or positive.
if (nLen >= 0) {
if (bVert) {
nGaugeY0 = nElemY0 + nGaugeMid;
nGaugeY1 = nElemY0 + nGaugeMid + nLen;
} else {
nGaugeX0 = nElemX0 + nGaugeMid;
nGaugeX1 = nElemX0 + nGaugeMid + nLen;
}
} else {
if (bVert) {
nGaugeY0 = nElemY0 + nGaugeMid + nLen;
nGaugeY1 = nElemY0 + nGaugeMid;
} else {
nGaugeX0 = nElemX0 + nGaugeMid + nLen;
nGaugeX1 = nElemX0 + nGaugeMid;
}
}
if (bVert) {
nGaugeX0 = nElemX0;
nGaugeX1 = nElemX1;
} else {
nGaugeY0 = nElemY0;
nGaugeY1 = nElemY1;
}
// Clip the region
nGaugeX0 = (nGaugeX0 < nElemX0)? nElemX0 : nGaugeX0;
nGaugeY0 = (nGaugeY0 < nElemY0)? nElemY0 : nGaugeY0;
nGaugeX1 = (nGaugeX1 > nElemX1)? nElemX1 : nGaugeX1;
nGaugeY1 = (nGaugeY1 > nElemY1)? nElemY1 : nGaugeY1;
// Support flipping of gauge directionality
// - The bFlip flag reverses the fill direction
// - Vertical gauges are flipped by default
if (bVert && !bFlip) {
nTmp = nElemY0+(nElemY1-nGaugeY1); // nTmp will be swapped into nGaugeY0
nGaugeY1 = nElemY1-(nGaugeY0-nElemY0);
nGaugeY0 = nTmp;
nGaugeMid = nElemH-nGaugeMid-1;
} else if (!bVert && bFlip) {
nTmp = nElemX0+(nElemX1-nGaugeX1); // nTmp will be swapped into nGaugeX0
nGaugeX1 = nElemX1-(nGaugeX0-nElemX0);
nGaugeX0 = nTmp;
nGaugeMid = nElemW-nGaugeMid-1;
}
#ifdef DBG_LOG
//printf("Gauge: nMin=%4d nMax=%4d nRng=%d nVal=%4d fScl=%6.3f nGaugeMid=%4d RectX=%4d RectW=%4d\n",
// nMin,nMax,nRng,pGauge->nGaugeVal,fScl,nGaugeMid,rGauge.x,rGauge.w);
#endif
// Draw a frame around the gauge
// - Only draw this during full redraw
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFrameRect(pGui, pElem->rElem, pElem->colElemFrame);
}
// To avoid flicker, we only erase the portion of the gauge
// that isn't "filled". Determine the gauge empty region and erase it
// There are two empty regions (one in negative and one in positive)
int16_t nEmptyPos;
if (bVert) {
// Empty Region #1 (negative)
nEmptyPos = (nGaugeY0 > nElemY1) ? nElemY1 : nGaugeY0;
rEmpty = (gslc_tsRect){nElemX0,nElemY0,nElemX1-nElemX0+1,nEmptyPos-nElemY0+1};
rTmp = gslc_ExpandRect(rEmpty,-1,-1);
gslc_DrawFillRect(pGui,rTmp,pElem->colElemFill);
// Empty Region #2 (positive)
nEmptyPos = (nGaugeY1 < nElemY0) ? nElemY0 : nGaugeY1;
rEmpty = (gslc_tsRect){nElemX0,nEmptyPos,nElemX1-nElemX0+1,nElemY1-nEmptyPos+1};
rTmp = gslc_ExpandRect(rEmpty,-1,-1);
gslc_DrawFillRect(pGui,rTmp,pElem->colElemFill);
} else {
// Empty Region #1 (negative)
nEmptyPos = (nGaugeX0 > nElemX1) ? nElemX1 : nGaugeX0;
rEmpty = (gslc_tsRect){nElemX0,nElemY0,nEmptyPos-nElemX0+1,nElemY1-nElemY0+1};
rTmp = gslc_ExpandRect(rEmpty,-1,-1);
gslc_DrawFillRect(pGui, rTmp, pElem->colElemFill);
// Empty Region #2 (positive)
nEmptyPos = (nGaugeX1 < nElemX0) ? nElemX0 : nGaugeX1;
rEmpty = (gslc_tsRect){nEmptyPos,nElemY0,nElemX1-nEmptyPos+1,nElemY1-nElemY0+1};
rTmp = gslc_ExpandRect(rEmpty,-1,-1);
gslc_DrawFillRect(pGui, rTmp, pElem->colElemFill);
}
// Draw the gauge fill region
rGauge = (gslc_tsRect){nGaugeX0,nGaugeY0,nGaugeX1-nGaugeX0+1,nGaugeY1-nGaugeY0+1};
rTmp = gslc_ExpandRect(rGauge,-1,-1);
gslc_DrawFillRect(pGui,rTmp,pGauge->colGauge);
// Draw the midpoint line
if (bVert) {
if (nElemY0 + nGaugeMid < nElemY1) {
gslc_DrawLine(pGui, nElemX0, nElemY0 + nGaugeMid, nElemX1, nElemY0 + nGaugeMid, pElem->colElemFrame);
}
} else {
if (nElemX0 + nGaugeMid < nElemX1) {
gslc_DrawLine(pGui, nElemX0 + nGaugeMid, nElemY0, nElemX0 + nGaugeMid, nElemY1, pElem->colElemFrame);
}
}
return true;
}
#if (GSLC_FEATURE_XGAUGE_RADIAL)
void gslc_ElemXGaugeDrawRadialHelp(gslc_tsGui* pGui,int16_t nX,int16_t nY,uint16_t nArrowLen,uint16_t nArrowSz,int16_t n64Ang,bool bFill,gslc_tsColor colFrame)
{
int16_t nTipX,nTipY;
int16_t nBaseX1,nBaseY1,nBaseX2,nBaseY2;
int16_t nTipBaseX,nTipBaseY;
gslc_PolarToXY(nArrowLen,n64Ang,&nTipX,&nTipY);
gslc_PolarToXY(nArrowLen-nArrowSz,n64Ang,&nTipBaseX,&nTipBaseY);
gslc_PolarToXY(nArrowSz,n64Ang-90*64,&nBaseX1,&nBaseY1);
gslc_PolarToXY(nArrowSz,n64Ang+90*64,&nBaseX2,&nBaseY2);
if (!bFill) {
// Framed
gslc_DrawLine(pGui,nX+nBaseX1,nY+nBaseY1,nX+nBaseX1+nTipBaseX,nY+nBaseY1+nTipBaseY,colFrame);
gslc_DrawLine(pGui,nX+nBaseX2,nY+nBaseY2,nX+nBaseX2+nTipBaseX,nY+nBaseY2+nTipBaseY,colFrame);
gslc_DrawLine(pGui,nX+nBaseX1+nTipBaseX,nY+nBaseY1+nTipBaseY,nX+nTipX,nY+nTipY,colFrame);
gslc_DrawLine(pGui,nX+nBaseX2+nTipBaseX,nY+nBaseY2+nTipBaseY,nX+nTipX,nY+nTipY,colFrame);
gslc_DrawLine(pGui,nX+nBaseX1,nY+nBaseY1,nX+nBaseX2,nY+nBaseY2,colFrame);
} else {
// Filled
gslc_tsPt asPt[4];
// Main body of pointer
asPt[0] = (gslc_tsPt){nX+nBaseX1,nY+nBaseY1};
asPt[1] = (gslc_tsPt){nX+nBaseX1+nTipBaseX,nY+nBaseY1+nTipBaseY};
asPt[2] = (gslc_tsPt){nX+nBaseX2+nTipBaseX,nY+nBaseY2+nTipBaseY};
asPt[3] = (gslc_tsPt){nX+nBaseX2,nY+nBaseY2};
gslc_DrawFillQuad(pGui,asPt,colFrame);
// Tip of pointer
asPt[0] = (gslc_tsPt){nX+nBaseX1+nTipBaseX,nY+nBaseY1+nTipBaseY};
asPt[1] = (gslc_tsPt){nX+nTipX,nY+nTipY};
asPt[2] = (gslc_tsPt){nX+nBaseX2+nTipBaseX,nY+nBaseY2+nTipBaseY};
gslc_DrawFillTriangle(pGui,asPt[0].x,asPt[0].y,asPt[1].x,asPt[1].y,asPt[2].x,asPt[2].y,colFrame);
}
}
bool gslc_ElemXGaugeDrawRadial(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw)
{
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGauge* pGauge = (gslc_tsXGauge*)(pElem->pXData);
uint16_t nElemW,nElemH,nElemRad;
int16_t nElemX0,nElemY0,nElemX1,nElemY1;
int16_t nElemMidX,nElemMidY;
nElemX0 = pElem->rElem.x;
nElemY0 = pElem->rElem.y;
nElemX1 = pElem->rElem.x + pElem->rElem.w - 1;
nElemY1 = pElem->rElem.y + pElem->rElem.h - 1;
nElemMidX = (nElemX0+nElemX1)/2;
nElemMidY = (nElemY0+nElemY1)/2;
nElemW = pElem->rElem.w;
nElemH = pElem->rElem.h;
nElemRad = (nElemW>=nElemH)? nElemH/2 : nElemW/2;
int16_t nMax = pGauge->nMax;
int16_t nMin = pGauge->nMin;
int16_t nRng = pGauge->nMax - pGauge->nMin;
int16_t nVal = pGauge->nVal;
int16_t nValLast = pGauge->nValLast;
bool bValLastValid = pGauge->bValLastValid;
uint16_t nTickLen = pGauge->nTickLen;
uint16_t nTickAng = 360 / pGauge->nTickCnt;
uint16_t nArrowLen = pGauge->nIndicLen;
uint16_t nArrowSize = pGauge->nIndicTip;
bool bFill = pGauge->bIndicFill;
int16_t n64Ang,n64AngLast;
int16_t nInd;
if (nRng == 0) {
GSLC_DEBUG2_PRINT("ERROR: ElemXRadialDraw() Zero range [%d,%d]\n",nMin,nMax);
return false;
}
// Support reversing of direction
// TODO: Clean up excess integer typecasting
if (pGauge->bFlip) {
n64Ang = (int32_t)(nMax - nVal )* 360*64 /nRng;
n64AngLast = (int32_t)(nMax - nValLast)* 360*64 /nRng;
} else {
n64Ang = (int32_t)(nVal - nMin)* 360*64 /nRng;
n64AngLast = (int32_t)(nValLast - nMin)* 360*64 /nRng;
}
// Clear old
if (bValLastValid) {
gslc_ElemXGaugeDrawRadialHelp(pGui,nElemMidX,nElemMidY,nArrowLen,nArrowSize,n64AngLast,bFill,pElem->colElemFill);
}
// Draw frame
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFillCircle(pGui,nElemMidX,nElemMidY,nElemRad,pElem->colElemFill); // Erase first
gslc_DrawFrameCircle(pGui,nElemMidX,nElemMidY,nElemRad,pElem->colElemFrame);
for (nInd=0;nInd<360;nInd+=nTickAng) {
gslc_DrawLinePolar(pGui,nElemMidX,nElemMidY,nElemRad-nTickLen,nElemRad,nInd*64,pGauge->colTick);
}
}
// Draw pointer
gslc_ElemXGaugeDrawRadialHelp(pGui,nElemMidX,nElemMidY,nArrowLen,nArrowSize,n64Ang,bFill,pGauge->colGauge);
return true;
}
#endif // GSLC_FEATURE_XGAUGE_RADIAL
#ifdef GSLC_FEATURE_XGAUGE_RAMP
bool gslc_ElemXGaugeDrawRamp(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw)
{
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGauge* pGauge = (gslc_tsXGauge*)(pElem->pXData);
uint16_t nElemW,nElemH;
int16_t nElemX0,nElemY1;
nElemX0 = pElem->rElem.x;
nElemY1 = pElem->rElem.y + pElem->rElem.h - 1;
nElemW = pElem->rElem.w;
nElemH = pElem->rElem.h;
int16_t nMax = pGauge->nMax;
int16_t nMin = pGauge->nMin;
int16_t nRng = pGauge->nMax - pGauge->nMin;
int16_t nVal = pGauge->nVal;
int16_t nValLast = pGauge->nValLast;
bool bValLastValid = pGauge->bValLastValid;
int16_t nInd;
if (nRng == 0) {
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXGaugeDrawRamp() Zero range [%d,%d]\n",nMin,nMax);
return false;
}
uint32_t nSclFX;
uint16_t nHeight;
int32_t nHeightTmp;
uint16_t nHeightBot;
uint16_t nX;
uint16_t nColInd;
// Calculate region to draw or clear
bool bModeErase;
int16_t nValStart;
int16_t nValEnd;
if ((eRedraw == GSLC_REDRAW_INC) && (!bValLastValid)) {
// - If the request was incremental (GSLC_REDRAW_INC) but
// the last value wasn't marked as valid (!bValLastValid)
// then we want to force a full redraw.
// - We don't expect to enter here since bValLastValid
// should always be set after we perform our first
// redraw.
eRedraw = GSLC_REDRAW_FULL;
}
if (eRedraw == GSLC_REDRAW_FULL) {
// If we haven't drawn anything before, draw full range from zero
bModeErase = false;
nValStart = 0;
nValEnd = nVal;
} else {
if (nVal >= nValLast) {
// As we are advancing the control, we just draw the new range
bModeErase = false;
nValStart = nValLast;
nValEnd = nVal;
} else {
// Since we are retracting the control, we erase the new range
bModeErase = true;
nValStart = nVal;
nValEnd = nValLast;
}
}
// Calculate the scaled gauge position
// - TODO: Also support reversing of direction
int16_t nPosXStart,nPosXEnd;
nPosXStart = (nValStart - nMin)*nElemW/nRng;
nPosXEnd = (nValEnd - nMin)*nElemW/nRng;
nSclFX = (uint32_t)nElemH*32767/(nElemW*nElemW);
for (nX=nPosXStart;nX<nPosXEnd;nX++) {
nInd = nElemW-nX;
nHeightTmp = nSclFX * nInd*nInd /32767;
nHeight = nElemH-nHeightTmp;
if (nHeight >= 20) {
nHeightBot = nHeight-20;
} else {
nHeightBot = 0;
}
gslc_tsColor nCol;
uint16_t nSteps = 10;
uint16_t nGap = 3;
if (nSteps == 0) {
nColInd = nX*1000/nElemW;
nCol = gslc_ColorBlend3(GSLC_COL_GREEN,GSLC_COL_YELLOW,GSLC_COL_RED,500,nColInd);
} else {
uint16_t nBlockLen,nSegLen,nSegInd,nSegOffset,nSegStart;
nBlockLen = (nElemW-(nSteps-1)*nGap)/nSteps;
nSegLen = nBlockLen + nGap;
nSegInd = nX/nSegLen;
nSegOffset = nX % nSegLen;
nSegStart = nSegInd * nSegLen;
if (nSegOffset <= nBlockLen) {
// Inside block
nColInd = (uint32_t)nSegStart*1000/nElemW;
nCol = gslc_ColorBlend3(GSLC_COL_GREEN,GSLC_COL_YELLOW,GSLC_COL_RED,500,nColInd);
} else {
// Inside gap
// - No draw
nCol = pElem->colElemFill;
}
}
if (bModeErase) {
nCol = pElem->colElemFill;
}
gslc_DrawLine(pGui,nElemX0+nX,nElemY1-nHeightBot,nElemX0+nX,nElemY1-nHeight,nCol);
}
return true;
}
#endif // GLSC_FEATURE_XGAUGE_RAMP
// ============================================================================

394
src/guislice/XGauge.h Normal file
View file

@ -0,0 +1,394 @@
#ifndef _GUISLICE_EX_XGAUGE_H_
#define _GUISLICE_EX_XGAUGE_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Gauge control (See replacement warning below)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XGauge.h
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// WARNING: The XGauge element has been replaced by XProgress / XRadial / XRamp
// Please update your code according to the migration notes in:
// https://github.com/ImpulseAdventure/GUIslice/pull/157
// XGauge may be removed in a future release.
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Gauge
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_GAUGE GSLC_TYPE_BASE_EXTEND + 0
/// Gauge drawing style
typedef enum {
GSLCX_GAUGE_STYLE_PROG_BAR, ///< Progress bar
GSLCX_GAUGE_STYLE_RADIAL, ///< Radial indicator
GSLCX_GAUGE_STYLE_RAMP, ///< Ramp indicator
} gslc_teXGaugeStyle;
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Gauge element
typedef struct {
// Range config
int16_t nMin; ///< Minimum control value
int16_t nMax; ///< Maximum control value
// Current value
int16_t nVal; ///< Current control value
// Previous value
int16_t nValLast; ///< Last value
bool bValLastValid; ///< Last value valid?
// Appearance config
gslc_teXGaugeStyle nStyle; ///< Gauge sub-type
gslc_tsColor colGauge; ///< Color of gauge fill bar
gslc_tsColor colTick; ///< Color of gauge tick marks
uint16_t nTickCnt; ///< Number of gauge tick marks
uint16_t nTickLen; ///< Length of gauge tick marks
bool bVert; ///< Vertical if true, else Horizontal
bool bFlip; ///< Reverse direction of gauge
uint16_t nIndicLen; ///< Indicator length
uint16_t nIndicTip; ///< Size of tip at end of indicator
bool bIndicFill; ///< Fill the indicator if true
} gslc_tsXGauge;
///
/// Create a Gauge Element
/// - Draws a gauge element that represents a proportion (nVal)
/// between nMin and nMax.
/// - Support gauge sub-types:
/// - GSLC_TYPEX_GAUGE_PROG_BAR: Horizontal or vertical box with filled region
/// - GSLC_TYPEX_GAUGE_RADIAL: Radial / compass indicator
/// - Default appearance is a horizontal progress bar, but can be changed
/// with gslc_ElemXGaugeSetStyle())
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining gauge size
/// \param[in] nMin: Minimum value of gauge for nVal comparison
/// \param[in] nMax: Maximum value of gauge for nVal comparison
/// \param[in] nVal: Starting value of gauge
/// \param[in] colGauge: Color for the gauge indicator
/// \param[in] bVert: Flag to indicate vertical vs horizontal action
/// (true = vertical, false = horizontal)
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXGaugeCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXGauge* pXData,gslc_tsRect rElem,int16_t nMin,int16_t nMax,int16_t nVal,gslc_tsColor colGauge,bool bVert);
///
/// Configure the style of a Gauge element
/// - This function is used to select between one of several gauge types
/// (eg. progress bar, radial dial, etc.)
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nType: Gauge style enumeration
///
/// \return none
///
void gslc_ElemXGaugeSetStyle(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teXGaugeStyle nType);
///
/// Configure the appearance of the Gauge indicator
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] colGauge: Color of the indicator
/// \param[in] nIndicLen: Length of the indicator
/// \param[in] nIndicTip: Size of the indicator tip
/// \param[in] bIndicFill: Fill in the indicator if true
///
/// \return none
///
void gslc_ElemXGaugeSetIndicator(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor colGauge,
uint16_t nIndicLen,uint16_t nIndicTip,bool bIndicFill);
///
/// Configure the appearance of the Gauge ticks
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] colTick: Color of the gauge ticks
/// \param[in] nTickCnt: Number of ticks to draw around / along gauge
/// \param[in] nTickLen: Length of the tick marks to draw
///
/// \return none
///
void gslc_ElemXGaugeSetTicks(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor colTick,uint16_t nTickCnt,uint16_t nTickLen);
///
/// Update a Gauge element's current value
/// - Note that min & max values are assigned in create()
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nVal: New value to show in gauge
///
/// \return none
///
void gslc_ElemXGaugeUpdate(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal);
///
/// Set a Gauge element's fill direction
/// - Setting bFlip reverses the default fill direction
/// - Default fill direction for horizontal gauges: left-to-right
/// - Default fill direction for vertical gauges: bottom-to-top
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] bFlip: If set, reverse direction of fill from default
///
/// \return none
///
void gslc_ElemXGaugeSetFlip(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bFlip);
///
/// Draw a gauge element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXGaugeDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Helper function to draw a gauge with style: progress bar
/// - Called from gslc_ElemXGaugeDraw()
///
/// \param[in] pGui: Ptr to GUI
/// \param[in] pElemRef: Ptr to Element reference
/// \param[in] eRedraw: Redraw status
///
/// \return true if success, false otherwise
///
bool gslc_ElemXGaugeDrawProgressBar(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw);
#if (GSLC_FEATURE_XGAUGE_RADIAL)
///
/// Helper function to draw a gauge with style: radial
/// - Called from gslc_ElemXGaugeDraw()
///
/// \param[in] pGui: Ptr to GUI
/// \param[in] pElemRef: Ptr to Element reference
/// \param[in] eRedraw: Redraw status
///
/// \return true if success, false otherwise
///
bool gslc_ElemXGaugeDrawRadial(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw);
#endif
#if (GSLC_FEATURE_XGAUGE_RAMP)
///
/// Helper function to draw a gauge with style: ramp
/// - Called from gslc_ElemXGaugeDraw()
///
/// \param[in] pGui: Ptr to GUI
/// \param[in] pElemRef: Ptr to Element reference
/// \param[in] eRedraw: Redraw status
///
/// \return true if success, false otherwise
///
bool gslc_ElemXGaugeDrawRamp(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw);
#endif
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
/// \def gslc_ElemXGaugeCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,
/// nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_,bVert_)
///
/// Create a Gauge Element in Flash
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Unique element ID to assign
/// \param[in] nPage: Page ID to attach element to
/// \param[in] nX: X coordinate of element
/// \param[in] nY: Y coordinate of element
/// \param[in] nW: Width of element
/// \param[in] nH: Height of element
/// \param[in] nMin_: Minimum value of gauge for nVal comparison
/// \param[in] nMax_: Maximum value of gauge for nVal comparison
/// \param[in] nVal_: Starting value of gauge
/// \param[in] colFrame_: Color for the gauge frame
/// \param[in] colFill_: Color for the gauge background fill
/// \param[in] colGauge_: Color for the gauge indicator
/// \param[in] bVert_: Flag to indicate vertical vs horizontal action
/// (true = vertical, false = horizontal)
///
/// \return none
///
#if (GSLC_USE_PROGMEM)
#define gslc_ElemXGaugeCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,\
nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_,bVert_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXGauge sGauge##nElemId; \
sGauge##nElemId.nMin = nMin_; \
sGauge##nElemId.nMax = nMax_; \
sGauge##nElemId.nVal = nVal_; \
sGauge##nElemId.nValLast = nVal_; \
sGauge##nElemId.bValLastValid = false; \
sGauge##nElemId.nStyle = GSLCX_GAUGE_STYLE_PROG_BAR; \
sGauge##nElemId.colGauge = colGauge_; \
sGauge##nElemId.colTick = GSLC_COL_GRAY; \
sGauge##nElemId.nTickCnt = 8; \
sGauge##nElemId.nTickLen = 5; \
sGauge##nElemId.bVert = bVert_; \
sGauge##nElemId.bFlip = false; \
sGauge##nElemId.nIndicLen = 10; \
sGauge##nElemId.nIndicTip = 3; \
sGauge##nElemId.bIndicFill = false; \
static const gslc_tsElem sElem##nElemId PROGMEM = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_GAUGE, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sGauge##nElemId), \
NULL, \
&gslc_ElemXGaugeDraw, \
NULL, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#else
#define gslc_ElemXGaugeCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,\
nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_,bVert_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXGauge sGauge##nElemId; \
sGauge##nElemId.nMin = nMin_; \
sGauge##nElemId.nMax = nMax_; \
sGauge##nElemId.nVal = nVal_; \
sGauge##nElemId.nValLast = nVal_; \
sGauge##nElemId.bValLastValid = false; \
sGauge##nElemId.nStyle = GSLCX_GAUGE_STYLE_PROG_BAR; \
sGauge##nElemId.colGauge = colGauge_; \
sGauge##nElemId.colTick = GSLC_COL_GRAY; \
sGauge##nElemId.nTickCnt = 8; \
sGauge##nElemId.nTickLen = 5; \
sGauge##nElemId.bVert = bVert_; \
sGauge##nElemId.bFlip = false; \
sGauge##nElemId.nIndicLen = 10; \
sGauge##nElemId.nIndicTip = 3; \
sGauge##nElemId.bIndicFill = false; \
static const gslc_tsElem sElem##nElemId = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_GAUGE, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sGauge##nElemId), \
NULL, \
&gslc_ElemXGaugeDraw, \
NULL, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_CONST | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XGAUGE_H_

348
src/guislice/XGlowball.c Normal file
View file

@ -0,0 +1,348 @@
// =======================================================================
// GUIslice library extension: XGlowball
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XGlowball.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XGlowball.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: XGlowball
// ============================================================================
// Create a XGlowball element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw and touch
gslc_tsElemRef* gslc_ElemXGlowballCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXGlowball* pXData, int16_t nMidX, int16_t nMidY, gslc_tsXGlowballRing* pRings, uint8_t nNumRings)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
// Calculate the actual element dimensions based on the ring defintion
// - Note that we use the outer radius of the last ring
int16_t nRadMax;
gslc_tsRect rElem;
nRadMax = pRings[nNumRings - 1].nRad2;
rElem.w = 2 * nRadMax;
rElem.h = 2 * nRadMax;
rElem.x = nMidX - nRadMax;
rElem.y = nMidY - nRadMax;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_GLOW,rElem,NULL,0,GSLC_FONT_NONE);
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_GRAY;
sElem.colElemText = GSLC_COL_WHITE;
sElem.colElemTextGlow = GSLC_COL_WHITE;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_GLOW_EN;
sElem.eTxtAlign = GSLC_ALIGN_MID_LEFT;
sElem.nGroup = GSLC_GROUP_ID_NONE;
// Initialize any pxData members with default parameters
pXData->nVal = 0;
pXData->nQuality = 72;
pXData->nAngStart = 0;
pXData->nAngEnd = 360;
pXData->pRings = pRings;
pXData->nNumRings = nNumRings;
pXData->nMidX = nMidX;
pXData->nMidY = nMidY;
pXData->colBg = GSLC_COL_BLACK;
pXData->nValLast = 0;
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXGlowballDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = NULL;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
void drawXGlowballArc(gslc_tsGui* pGui, gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nRad1, int16_t nRad2, gslc_tsColor cArc, uint16_t nAngStart, uint16_t nAngEnd)
{
gslc_tsPt anPts[4];
// TODO: Cleanup
int16_t nStep64 = 64*360 / pGlowball->nQuality;
int16_t nAng64;
int16_t nX, nY;
int16_t nSegStart, nSegEnd;
nSegStart = nAngStart * pGlowball->nQuality / 360;
nSegEnd = nAngEnd * pGlowball->nQuality / 360;
for (uint16_t nSegInd = nSegStart; nSegInd < nSegEnd; nSegInd++) {
nAng64 = nSegInd * nStep64;
nAng64 = nAng64 % (360 * 64);
gslc_PolarToXY(nRad1, nAng64, &nX, &nY);
anPts[0] = (gslc_tsPt) { nMidX + nX, nMidY + nY };
gslc_PolarToXY(nRad2, nAng64, &nX, &nY);
anPts[1] = (gslc_tsPt) { nMidX + nX, nMidY + nY };
gslc_PolarToXY(nRad2, nAng64 + nStep64, &nX, &nY);
anPts[2] = (gslc_tsPt) { nMidX + nX, nMidY + nY };
gslc_PolarToXY(nRad1, nAng64 + nStep64, &nX, &nY);
anPts[3] = (gslc_tsPt) { nMidX + nX, nMidY + nY };
gslc_DrawFillQuad(pGui, anPts, cArc);
}
}
void drawXGlowballRing(gslc_tsGui* pGui, gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nVal, uint16_t nAngStart, uint16_t nAngEnd, bool bErase)
{
int16_t nRad1, nRad2;
gslc_tsColor cCol;
if ((nVal <= 0) || (nVal > pGlowball->nNumRings)) {
return;
}
nRad1 = pGlowball->pRings[nVal - 1].nRad1;
nRad2 = pGlowball->pRings[nVal - 1].nRad2;
cCol = (bErase) ? pGlowball->colBg : pGlowball->pRings[nVal - 1].cCol;
drawXGlowballArc(pGui, pGlowball, nMidX, nMidY, nRad1, nRad2, cCol, nAngStart, nAngEnd);
}
// nAngStart & nAngEnd define the range (in degrees) for the XGlowball arcs
// - Angles are measured with 0 degrees at the top, incrementing clockwise
// - Note that the supported range is 0..510 degrees
// nVal represents the value to set (0.. NUM_RINGS)
// nMidX,nMidY define the center of the XGlowball
void drawXGlowball(gslc_tsGui* pGui,gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nVal, uint16_t nAngStart, uint16_t nAngEnd)
{
int16_t nValLast = pGlowball->nValLast;
int8_t nRingInd;
if (nVal > nValLast) {
for (nRingInd = nValLast + 1; nRingInd <= nVal; nRingInd++) {
drawXGlowballRing(pGui, pGlowball, nMidX, nMidY, nRingInd, nAngStart, nAngEnd, false);
}
} else {
for (nRingInd = nValLast; nRingInd > nVal; nRingInd--) {
drawXGlowballRing(pGui, pGlowball, nMidX, nMidY, nRingInd, nAngStart, nAngEnd, true);
}
}
pGlowball->nValLast = nVal;
}
void gslc_ElemXGlowballSetVal(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nVal)
{
if ((pGui == NULL) || (pElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballSetVal";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGlowball* pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
// Update the value
pGlowball->nVal = nVal;
// Mark for redraw
// - Only need incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
void gslc_ElemXGlowballSetAngles(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nAngStart, int16_t nAngEnd)
{
if ((pGui == NULL) || (pElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballSetAngles";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGlowball* pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
// Update the angular ranges
pGlowball->nAngStart = nAngStart;
pGlowball->nAngEnd = nAngEnd;
// Mark for redraw
// - Force full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXGlowballSetQuality(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nQuality)
{
if ((pGui == NULL) || (pElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballSetQuality";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGlowball* pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
// Update the rendering quality setting
pGlowball->nQuality = nQuality;
// Mark for redraw
// - Force full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXGlowballSetColorBack(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colBg)
{
if ((pGui == NULL) || (pElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballSetColorBack";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXGlowball* pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
// Update the background color
pGlowball->colBg = colBg;
// Mark for redraw
// - Force full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// Redraw the element
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXGlowballDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGlowballDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
// Fetch the element's extended data structure
gslc_tsXGlowball* pGlowball;
pGlowball = (gslc_tsXGlowball*)(pElem->pXData);
if (pGlowball == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXGlowballDraw(%s) pXData is NULL\n","");
return false;
}
// --------------------------------------------------------------------------
// Init for default drawing
// --------------------------------------------------------------------------
int16_t nElemX,nElemY;
uint16_t nElemW,nElemH;
nElemX = pElem->rElem.x;
nElemY = pElem->rElem.y;
nElemW = pElem->rElem.w;
nElemH = pElem->rElem.h;
// Calculate center of XGlowball
int16_t nMidX, nMidY;
nMidX = nElemX + (nElemW / 2);
nMidY = nElemY + (nElemH / 2);
// Fetch the current value
int16_t nVal = pGlowball->nVal;
int16_t nAngStart = pGlowball->nAngStart;
int16_t nAngEnd = pGlowball->nAngEnd;
// Check to see if we need full redraw
if (eRedraw == GSLC_REDRAW_FULL) {
// Erase the region
gslc_DrawFillRect(pGui, pElem->rElem, pGlowball->colBg);
// Now force redraw from zero level to current value
pGlowball->nValLast = 0;
}
// Draw the glowball
drawXGlowball(pGui, pGlowball, nMidX, nMidY, nVal, nAngStart, nAngEnd);
// --------------------------------------------------------------------------
// Mark the element as no longer requiring redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
return true;
}
// ============================================================================

136
src/guislice/XGlowball.h Normal file
View file

@ -0,0 +1,136 @@
#ifndef _GUISLICE_EX_XGLOWBALL_H_
#define _GUISLICE_EX_XGLOWBALL_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: XGlowball
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XGlowball.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Example template
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_GLOW GSLC_TYPE_BASE_EXTEND + 24
// Defines the attributes of each ring in the XGlowball gauge
typedef struct {
uint8_t nRad1; // Inner radius
uint8_t nRad2; // Outer radius
gslc_tsColor cCol; // Fill color
} gslc_tsXGlowballRing;
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Slider element
typedef struct {
// Config
int16_t nMidX; ///< Gauge midpoint X coord
int16_t nMidY; ///< Gauge midpoint Y coord
gslc_tsXGlowballRing* pRings; ///< Ring definition array
uint8_t nNumRings; ///< Number of rings in definition
// Style config
uint16_t nQuality; ///< Rendering quality (number of segments / rotation)
int16_t nAngStart; ///< Starting angle (0..510 degrees)
int16_t nAngEnd; ///< Ending angle (0..510 degrees)
gslc_tsColor colBg; ///< Background color (for redraw)
// State
int16_t nVal; ///< Current value
int16_t nValLast; ///< Previous value
// Callbacks
} gslc_tsXGlowball;
///
/// Create a XGlowball element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] nMidX: Center X coordinate
/// \param[in] nMidY: Center Y coordinate
/// \param[in] pRings: Pointer to tsXGlowballRing structure array defining appearance
/// \param[in] nNumRings: Number of rings in pRings array
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXGlowballCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXGlowball* pXData, int16_t nMidX, int16_t nMidY, gslc_tsXGlowballRing* pRings, uint8_t nNumRings);
///
/// Draw the XGlowball element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXGlowballDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
// XGlowball draw helper routines
void drawXGlowballArc(gslc_tsGui* pGui, gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nRad1, int16_t nRad2, gslc_tsColor cArc, uint16_t nAngStart, uint16_t nAngEnd);
void drawXGlowballRing(gslc_tsGui* pGui, gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nVal, uint16_t nAngStart, uint16_t nAngEnd, bool bErase);
void drawXGlowball(gslc_tsGui* pGui, gslc_tsXGlowball* pGlowball, int16_t nMidX, int16_t nMidY, int16_t nVal, uint16_t nAngStart, uint16_t nAngEnd);
void gslc_ElemXGlowballSetAngles(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nAngStart, int16_t nAngEnd);
void gslc_ElemXGlowballSetVal(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nVal);
void gslc_ElemXGlowballSetQuality(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nQuality);
void gslc_ElemXGlowballSetColorBack(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colBg);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XGLOWBALL_H_

366
src/guislice/XGraph.c Normal file
View file

@ -0,0 +1,366 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XGraph.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XGraph.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
gslc_tsElemRef* gslc_ElemXGraphCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXGraph* pXData,gslc_tsRect rElem,int16_t nFontId,int16_t* pBuf,
uint16_t nBufMax,gslc_tsColor colGraph)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGraphCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_GRAPH,rElem,NULL,0,nFontId);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN;
// Default group assignment. Can override later with ElemSetGroup()
sElem.nGroup = GSLC_GROUP_ID_NONE;
// Define other extended data
pXData->pBuf = pBuf;
pXData->nMargin = 5;
pXData->nBufMax = nBufMax;
pXData->nBufCnt = 0;
pXData->nPlotIndStart = 0;
pXData->colGraph = colGraph;
pXData->eStyle = GSLCX_GRAPH_STYLE_DOT;
// Define the visible region of the window
// - The range in value can be overridden by the user
pXData->nWndHeight = rElem.h - (2*pXData->nMargin);
pXData->nWndWidth = rElem.w - (2*pXData->nMargin);
// Default scale is
// - Each data point (buffer row) gets 1 pixel in X direction
// - Data value is directly mapped to height in Y direction
pXData->nPlotValMin = 0;
pXData->nPlotValMax = pXData->nWndHeight;
pXData->nPlotIndMax = pXData->nWndWidth;
// Clear the buffer
memset(pBuf,0,nBufMax*sizeof(int16_t));
// Determine if scrollbar should be enabled
if (pXData->nPlotIndMax >= pXData->nBufMax) {
// Disable scrollbar as the window is larger
// than the number of rows in the buffer
pXData->bScrollEn = false;
pXData->nScrollPos = 0;
} else {
// Scrollbar is enabled
pXData->bScrollEn = true;
pXData->nScrollPos = pXData->nBufMax - pXData->nPlotIndMax;
}
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXGraphDraw;
sElem.pfuncXTouch = NULL;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
void gslc_ElemXGraphSetStyle(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,
gslc_teXGraphStyle eStyle,uint8_t nMargin)
{
gslc_tsXGraph* pBox;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXGraph*)(pElem->pXData);
pBox->eStyle = eStyle;
pBox->nMargin = nMargin;
// TODO: Recalculate the window extents and defaults
// - Note that ElemXGraphSetStyle() was not intended to be
// called after the control is already in use/displayed
// Set the redraw flag
// - Force full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// TODO: Support scaling in X direction
void gslc_ElemXGraphSetRange(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,
int16_t nYMin,int16_t nYMax)
{
gslc_tsXGraph* pBox;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXGraph*)(pElem->pXData);
pBox->nPlotValMax = nYMax;
pBox->nPlotValMin = nYMin;
// Set the redraw flag
// - As we are changing the scale, force a full redraw
// since incremental redraws may make assumption about
// prior coordinate scaling
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXGraphScrollSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,uint8_t nScrollPos,uint8_t nScrollMax)
{
gslc_tsXGraph* pBox;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXGraph*)(pElem->pXData);
// Ensure scrollbar is enabled
if (!pBox->bScrollEn) {
// Scrollbar is disabled, so ignore
return;
}
// Assign proportional value based on visible window region
uint16_t nScrollPosOld = pBox->nScrollPos;
pBox->nScrollPos = nScrollPos * (pBox->nBufMax - pBox->nPlotIndMax) / nScrollMax;
// Set the redraw flag
// - Only need incremental redraw
// - Only redraw if changed actual scroll row
if (pBox->nScrollPos != nScrollPosOld) {
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Write a data value to the buffer
// - Advance the write ptr, wrap if needed
// - If encroach upon buffer read ptr, then drop the oldest line from the buffer
void gslc_ElemXGraphAdd(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal)
{
gslc_tsXGraph* pBox = NULL;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXGraph*)(pElem->pXData);
// Add the data value
pBox->pBuf[pBox->nBufCnt] = nVal;
// Advance the pointer
// - Wrap the pointers around end of buffer
pBox->nBufCnt = (pBox->nBufCnt+1) % pBox->nBufMax;
// Set the redraw flag
// - Only need incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
bool gslc_ElemXGraphDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXGraphDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
// Fetch the element's extended data structure
gslc_tsXGraph* pBox;
pBox = (gslc_tsXGraph*)(pElem->pXData);
if (pBox == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXGraphDraw(%s) pXData is NULL\n","");
return false;
}
bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
bool bFrameEn = (pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN);
// Draw the frame
if (eRedraw == GSLC_REDRAW_FULL) {
if (bFrameEn) {
gslc_DrawFrameRect(pGui,pElem->rElem,pElem->colElemFrame);
}
}
// Clear the background (inset from frame)
// TODO: Support incremental redraw in which case we don't
// erase the inner region. Instead we would just erase
// old values and redraw new ones
gslc_tsRect rInner = gslc_ExpandRect(pElem->rElem,-1,-1);
gslc_DrawFillRect(pGui,rInner,(bGlow)?pElem->colElemFillGlow:pElem->colElemFill);
int16_t nDataVal;
uint16_t nCurX = 0;
int16_t nPixX,nPixY,nPixYBase,nPixYOffset;
gslc_tsColor colGraph;
uint8_t nScrollMax;
// Initialize color state
colGraph = pBox->colGraph;
// Calculate the current window position based on
// the current buffer write pointer and scroll
// position
nScrollMax = pBox->nBufMax - pBox->nPlotIndMax;
pBox->nPlotIndStart = pBox->nBufMax + pBox->nBufCnt;
pBox->nPlotIndStart -= pBox->nPlotIndMax;
// Only correct for scrollbar position if enabled
if (pBox->bScrollEn) {
pBox->nPlotIndStart -= (nScrollMax - pBox->nScrollPos);
}
pBox->nPlotIndStart = pBox->nPlotIndStart % pBox->nBufMax;
uint8_t nPlotInd = 0;
uint8_t nIndMax = 0;
nIndMax = (pBox->nBufMax < pBox->nPlotIndMax)? pBox->nBufMax : pBox->nPlotIndMax;
for (nPlotInd=0;nPlotInd<nIndMax;nPlotInd++) {
// Calculate row offset after accounting for buffer wrap
// and current window starting offset
uint16_t nBufInd = pBox->nPlotIndStart + nPlotInd;
nBufInd = nBufInd % pBox->nBufMax;
// NOTE: At the start of buffer fill when we have
// only written a few values, we will continue to read
// values out of the buffer so we are dependent upon
// the reset to initialize the buffer to zero.
nDataVal = pBox->pBuf[nBufInd];
// Clip the value to the plot range
if (nDataVal > pBox->nPlotValMax) { nDataVal = pBox->nPlotValMax; }
else if (nDataVal < pBox->nPlotValMin) { nDataVal = pBox->nPlotValMin; }
// TODO: Make nCurX a scaled version of nPlotInd
// - Support different modes for mapping multiple data points
// a single X coordinate and mapping a single data point
// to multiple X coordinates
// - For now, just have a 1:1 correspondence between data
// points and the X coordinate
nCurX = nPlotInd;
// TODO: Scale data value
// TODO: Consider supporting various color mapping modes
//colGraph = gslc_ColorBlend2(GSLC_COL_BLACK,GSLC_COL_WHITE,500,nDataVal*500/200);
// Determine the drawing coordinates
nPixX = pElem->rElem.x + pBox->nMargin + nCurX;
nPixYBase = pElem->rElem.y - pBox->nMargin + pElem->rElem.h-1;
// Calculate Y coordinate
nPixYOffset = nDataVal;
// Clip plot Y coordinate
if (nPixY > pBox->nWndHeight) { nPixY = pBox->nWndHeight; }
if (nPixY < 0) { nPixY = 0; }
// Calculate final Y coordinate
nPixY = pElem->rElem.y - pBox->nMargin + pElem->rElem.h-1 - nPixYOffset;
// Render the datapoints
if (pBox->eStyle == GSLCX_GRAPH_STYLE_DOT) {
gslc_DrawSetPixel(pGui,nPixX,nPixY,colGraph);
} else if (pBox->eStyle == GSLCX_GRAPH_STYLE_LINE) {
} else if (pBox->eStyle == GSLCX_GRAPH_STYLE_FILL) {
gslc_DrawLine(pGui,nPixX,nPixYBase,nPixX,nPixY,colGraph);
}
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
// ============================================================================

186
src/guislice/XGraph.h Normal file
View file

@ -0,0 +1,186 @@
#ifndef _GUISLICE_EX_XGRAPH_H_
#define _GUISLICE_EX_XGRAPH_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Graph control
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XGraph.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Graph
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_GRAPH GSLC_TYPE_BASE_EXTEND + 5
/// Gauge drawing style
typedef enum {
GSLCX_GRAPH_STYLE_DOT, ///< Dot
GSLCX_GRAPH_STYLE_LINE, ///< Line
GSLCX_GRAPH_STYLE_FILL, ///< Filled
} gslc_teXGraphStyle;
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Graph element
typedef struct {
// Config
int16_t* pBuf; ///< Ptr to the data buffer (circular buffer))
uint8_t nMargin; ///< Margin for graph area within element rect
gslc_tsColor colGraph; ///< Color of the graph
gslc_teXGraphStyle eStyle; ///< Style of the graph
uint16_t nBufMax; ///< Maximum number of points in buffer
bool bScrollEn; ///< Enable for scrollbar
uint16_t nScrollPos; ///< Current scrollbar position
uint16_t nWndHeight; ///< Visible window height
uint16_t nWndWidth; ///< Visible window width
int16_t nPlotValMax; ///< Visible window maximum value
int16_t nPlotValMin; ///< Visible window minimum value
uint16_t nPlotIndMax; ///< Number of data points to show in window
// Current status
uint16_t nBufCnt; ///< Number of points in buffer
uint16_t nPlotIndStart; ///< First row of current window
} gslc_tsXGraph;
///
/// Create a Graph Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining checkbox size
/// \param[in] nFontId: Font ID to use for graph area
/// \param[in] pBuf: Ptr to data buffer (already allocated)
/// with size (nBufMax) int16_t
/// \param[in] nBufRows: Maximum number of points in buffer
/// \param[in] colGraph: Color of the graph
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXGraphCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXGraph* pXData,gslc_tsRect rElem,int16_t nFontId,int16_t* pBuf,
uint16_t nBufRows,gslc_tsColor colGraph);
///
/// Set the graph's additional drawing characteristics
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] eStyle: Drawing style for the graph
/// \param[in] nMargin: Margin to provide around graph area inside frame
///
/// \return none
///
void gslc_ElemXGraphSetStyle(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,
gslc_teXGraphStyle eStyle,uint8_t nMargin);
///
/// Set the graph's drawing range
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nYMin: Minimum Y value to draw
/// \param[in] nYMax: Maximum Y value to draw
///
/// \return none
///
void gslc_ElemXGraphSetRange(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,
int16_t nYMin,int16_t nYMax);
///
/// Draw a Graph element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXGraphDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
/// Add a value to the graph at the latest position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nVal: Data value to add
///
/// \return none
///
void gslc_ElemXGraphAdd(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal);
///
/// Set the graph scroll position (nScrollPos) as a fraction of
/// nScrollMax
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nScrollPos: New scroll position
/// \param[in] nScrollMax: Maximum scroll position
///
/// \return none
///
void gslc_ElemXGraphScrollSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,uint8_t nScrollPos,uint8_t nScrollMax);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XGRAPH_H_

714
src/guislice/XKeyPad.c Normal file
View file

@ -0,0 +1,714 @@
// =======================================================================
// GUIslice library extension: XKeyPad control
// - Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XKeyPad.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XKeyPad.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
#if (GSLC_FEATURE_COMPOUND)
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: KeyPad
// - Keypad element with numeric input
// - Optionally supports floating point values
// - Optionally supports negative values
// ============================================================================
// Define the button labels
// - TODO: Create more efficient storage for the labels
// so that it doesn't consume 4 bytes even for labels
// that can be generated (eg. 0..9, a--z, etc.)
// - NOTE: Not using "const char" in order to support
// modification of labels by user.
const char KEYPAD_DISP_NEGATIVE = '-';
const char KEYPAD_DISP_DECIMAL_PT = '.';
// --------------------------------------------------------------------------
// Create the keypad definition
// --------------------------------------------------------------------------
void XKeyPadAddKeyElem(gslc_tsGui* pGui, gslc_tsXKeyPad* pXData, int16_t nKeyId, bool bTxtField, int16_t nRow, int16_t nCol, int8_t nRowSpan, int8_t nColSpan,
gslc_tsColor cColFill, gslc_tsColor cColGlow, bool bVisible)
{
gslc_tsXKeyPadCfg* pConfig;
char* pKeyStr = NULL;
pConfig = &(pXData->sConfig);
gslc_tsElemRef* pElemRefTmp = NULL;
gslc_tsElem* pElemTmp = NULL;
int16_t nButtonSzW = pConfig->nButtonSzW;
int16_t nButtonSzH = pConfig->nButtonSzH;
int16_t nOffsetX = pConfig->nOffsetX;
int16_t nOffsetY = pConfig->nOffsetY;
int8_t nFontId = pConfig->nFontId;
int16_t nButtonW = (nColSpan * nButtonSzW);
int16_t nButtonH = (nRowSpan * nButtonSzH);
// Fetch the keypad key string
if (bTxtField) {
pKeyStr = (char*)pXData->acValStr;
} else {
XKEYPAD_LOOKUP pfuncLookup = pXData->pfuncLookup;
int16_t nKeyInd = (*pfuncLookup)(pGui, nKeyId);
pKeyStr = pXData->pacKeys[nKeyInd];
}
//GSLC_DEBUG2_PRINT("DBG: KeyId=%d R=%d C=%d str=[%s] SubElemMax=%d\n", nKeyId, nRow,nCol,pKeyStr,pXData->nSubElemMax);
if (bTxtField) {
// Text field
pElemRefTmp = gslc_ElemCreateTxt(pGui, nKeyId, GSLC_PAGE_NONE,
(gslc_tsRect) { nOffsetX + (nCol*nButtonSzW), nOffsetY + (nRow*nButtonSzH), nButtonW-1, nButtonH - 1 },
pKeyStr, XKEYPAD_VAL_LEN, nFontId);
gslc_ElemSetFrameEn(pGui, pElemRefTmp, true);
gslc_ElemSetTxtMargin(pGui, pElemRefTmp, 5); // Apply default margin
} else {
// Text button
//GSLC_DEBUG2_PRINT("DrawBtn: %d [%s] @ r=%d,c=%d,width=%d\n", nKeyId, pXData->pacKeys[nKeyId], nRow, nCol, nButtonW);
pElemRefTmp = gslc_ElemCreateBtnTxt(pGui, nKeyId, GSLC_PAGE_NONE,
(gslc_tsRect) { nOffsetX + (nCol*nButtonSzW), nOffsetY + (nRow*nButtonSzH), nButtonW - 1, nButtonH - 1 },
pKeyStr, sizeof(pKeyStr), nFontId, &gslc_ElemXKeyPadClick);
// For the text buttons, optionally use rounded profile if enabled
gslc_ElemSetRoundEn(pGui, pElemRefTmp, pConfig->bRoundEn);
}
// Set color
gslc_ElemSetTxtCol(pGui, pElemRefTmp, GSLC_COL_WHITE);
gslc_ElemSetCol(pGui, pElemRefTmp, GSLC_COL_WHITE, cColFill, cColGlow);
// Set the visibility status
// FIXME: Need to fix dynamic visibility change
// gslc_ElemSetVisible(pGui, pElemRefTmp, bVisible);
if (!bVisible) {
// For now, skip addition of invisible buttons
return;
}
// Add element to compound element collection
pElemTmp = gslc_GetElemFromRef(pGui, pElemRefTmp);
pElemRefTmp = gslc_CollectElemAdd(pGui, &pXData->sCollect, pElemTmp, GSLC_ELEMREF_DEFAULT);
}
gslc_tsElemRef* gslc_ElemXKeyPadCreateBase(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXKeyPad* pXData, int16_t nX0, int16_t nY0, int8_t nFontId, gslc_tsXKeyPadCfg* pConfig,
XKEYPAD_CREATE pfuncCreate, XKEYPAD_LOOKUP pfuncLookup)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXKeyPadCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL, FUNCSTR);
return NULL;
}
// Fetch config options
int8_t nButtonSzW = pConfig->nButtonSzW;
int8_t nButtonSzH = pConfig->nButtonSzH;
gslc_tsRect rElem;
rElem.x = nX0;
rElem.y = nY0;
rElem.w = (nButtonSzW * pConfig->nMaxCols) + (2 * pConfig->nFrameMargin);
rElem.h = (nButtonSzH * pConfig->nMaxRows) + (2 * pConfig->nFrameMargin);
gslc_tsElem sElem;
// Initialize composite element
sElem = gslc_ElemCreate(pGui, nElemId, nPage, GSLC_TYPEX_KEYPAD, rElem, NULL, 0, GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN; // Don't need to glow outer element
sElem.nGroup = GSLC_GROUP_ID_NONE;
gslc_CollectReset(&pXData->sCollect, pXData->psElem, pXData->nSubElemMax, pXData->psElemRef, pXData->nSubElemMax);
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXKeyPadDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = &gslc_ElemXKeyPadTouch;
// shouldn't be used
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_BLUE;
sElem.colElemFrameGlow = GSLC_COL_BLUE_LT4;
// Now create the sub elements
gslc_tsElemRef* pElemRef = NULL;
// Determine offset coordinate of compound element so that we can
// specify relative positioning during the sub-element Create() operations.
int16_t nOffsetX = nX0 + pConfig->nFrameMargin;
int16_t nOffsetY = nY0 + pConfig->nFrameMargin;
// Reset value string
memset(pXData->acValStr, 0, XKEYPAD_VAL_LEN);
pXData->nValStrPos = 0;
pXData->pacKeys = pConfig->pacKeys;
pXData->pfuncCb = NULL;
pXData->pfuncLookup = pfuncLookup;
pXData->nTargetId = GSLC_ID_NONE; // Default to no target defined
pXData->bValPositive = true;
pXData->bValDecimalPt = false;
// Update config with constructor settings
pConfig->nFontId = nFontId;
pConfig->nOffsetX = nOffsetX;
pConfig->nOffsetY = nOffsetY;
// Copy content into local structure
pXData->sConfig = *pConfig;
// Create the keypad definition
// -- Call XKeyPadCreateKeys()
if (pfuncCreate != NULL) {
(*pfuncCreate)(pGui, pXData);
}
// Now proceed to add the compound element to the page
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui, nPage, &sElem, GSLC_ELEMREF_DEFAULT);
// Now propagate the parent relationship to enable a cascade
// of redrawing from low-level elements to the top
gslc_CollectSetParent(pGui, &pXData->sCollect, pElemRef);
return pElemRef;
}
else {
#if defined(DEBUG_LOG)
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXKeyPadCreate(%s) Compound elements inside compound elements not supported\n", "");
#endif
return NULL;
}
}
void gslc_ElemXKeyPadValSet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, const char* pStrBuf)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
// Copy over the new value string
strncpy(pKeyPad->acValStr, pStrBuf, XKEYPAD_VAL_LEN);
pKeyPad->acValStr[XKEYPAD_VAL_LEN - 1] = 0; // Force null terminate
// Handle negation and floating point detection
pKeyPad->bValPositive = true;
if (pKeyPad->acValStr[0] == KEYPAD_DISP_NEGATIVE) {
pKeyPad->bValPositive = false;
}
pKeyPad->bValDecimalPt = false;
if (strchr(pKeyPad->acValStr, KEYPAD_DISP_DECIMAL_PT)) {
pKeyPad->bValDecimalPt = true;
}
// Update the current buffer position
pKeyPad->nValStrPos = strlen(pKeyPad->acValStr);
// Find element associated with text field
gslc_tsElemRef* pTxtElemRef = gslc_CollectFindElemById(pGui, &pKeyPad->sCollect, KEYPAD_ID_TXT);
// Mark as needing redraw
gslc_ElemSetRedraw(pGui,pTxtElemRef,GSLC_REDRAW_INC);
}
void gslc_ElemXKeyPadTargetIdSet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nTargetId)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
pKeyPad->nTargetId = nTargetId;
}
int16_t gslc_ElemXKeyPadDataTargetIdGet(gslc_tsGui* pGui, void* pvData)
{
if (pvData == NULL) {
#if defined(DEBUG_LOG)
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXKeyPadDataTargetIdGet() NULL data\n", "");
#endif
return GSLC_ID_NONE;
}
gslc_tsXKeyPadData* pData = (gslc_tsXKeyPadData*)pvData;
return pData->nTargetId;
}
char* gslc_ElemXKeyPadDataValGet(gslc_tsGui* pGui, void* pvData)
{
if (pvData == NULL) {
#if defined(DEBUG_LOG)
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXKeyPadDataValGet() NULL data\n", "");
#endif
return NULL;
}
gslc_tsXKeyPadData* pData = (gslc_tsXKeyPadData*)pvData;
return pData->pStr;
}
// NOTE: API changed to pass nStrBufLen (total buffer size including terminator)
// instead of nStrBufMax (max index value)
bool gslc_ElemXKeyPadValGet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, char* pStrBuf, uint8_t nStrBufLen)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return false;
// - Check for negation flag
// - If negative, copy minus sign
// - Copy the remainder of the string
// FIXME: check for shortest of XKEYPAD_VAL_LEN & nStrBufLen
char* pBufPtr = pStrBuf;
uint8_t nMaxCopyLen = nStrBufLen - 1;
// FIXME: Do we still need to do this step?
if (!pKeyPad->bValPositive) {
*pBufPtr = KEYPAD_DISP_NEGATIVE;
pBufPtr++;
nMaxCopyLen--;
}
strncpy(pBufPtr, pKeyPad->acValStr, nMaxCopyLen);
pStrBuf[nStrBufLen-1] = '\0'; // Force termination
// TODO: Do we need to reset the contents?
return true;
}
// Redraw the KeyPad element
// - When drawing a KeyPad we do not clear the background.
// We do redraw the sub-element collection.
bool gslc_ElemXKeyPadDraw(void* pvGui, void* pvElemRef, gslc_teRedrawType eRedraw)
{
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return false;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui, pElemRef);
// Draw any parts of the compound element itself
if (eRedraw == GSLC_REDRAW_FULL) {
// Force the fill to ensure the background doesn't bleed thorugh gaps
// between the sub-elements
gslc_DrawFillRect(pGui, pElem->rElem, pElem->colElemFill);
if (pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN) {
gslc_DrawFrameRect(pGui, pElem->rElem, pElem->colElemFrame);
}
}
// Draw the sub-elements
gslc_tsCollect* pCollect = &pKeyPad->sCollect;
if (eRedraw != GSLC_REDRAW_NONE) {
uint8_t sEventSubType = GSLC_EVTSUB_DRAW_NEEDED;
if (eRedraw == GSLC_REDRAW_FULL) {
sEventSubType = GSLC_EVTSUB_DRAW_FORCE;
}
gslc_tsEvent sEvent = gslc_EventCreate(pGui, GSLC_EVT_DRAW, sEventSubType, (void*)(pCollect), NULL);
gslc_CollectEvent(pGui, sEvent);
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui, true);
return true;
}
void gslc_ElemXKeyPadValSetCb(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, GSLC_CB_INPUT pfuncCb)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
pKeyPad->pfuncCb = pfuncCb;
}
// Change the sign of the number string
// - This function also performs in-place shifting of the content
void gslc_ElemXKeyPadValSetSign(gslc_tsGui* pGui, gslc_tsElemRef *pElemRef, bool bPositive)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
char* pStrBuf = pKeyPad->acValStr;
//GSLC_DEBUG2_PRINT("SetSign: old=%d new=%d\n", pKeyPad->bValPositive, bPositive);
if (pKeyPad->bValPositive == bPositive) {
// No change to sign
return;
}
//GSLC_DEBUG2_PRINT("SetSign: str_old=[%s] idx=%d\n", pStrBuf,pKeyPad->nValStrPos);
if ((pKeyPad->bValPositive) && (!bPositive)) {
// Change from positive to negative
// - Shift string right one position, and replace first position with minus sign
// - Increase current buffer position
memmove(pStrBuf+1, pStrBuf, XKEYPAD_VAL_LEN-1);
pStrBuf[0] = KEYPAD_DISP_NEGATIVE; // Overwrite with minus sign
pStrBuf[XKEYPAD_VAL_LEN-1] = 0; // Force terminate
pKeyPad->nValStrPos++; // Advance buffer position
} else if ((!pKeyPad->bValPositive) && (bPositive)) {
// Change from negative to positive
// - Shift string left one position, overwriting the minus sign
memmove(pStrBuf, pStrBuf+1, XKEYPAD_VAL_LEN-1);
pStrBuf[XKEYPAD_VAL_LEN-1] = 0; // Force terminate
if (pKeyPad->nValStrPos > 0) {
// Expect that original position should be non-zero if previously minux
pKeyPad->nValStrPos--; // Reduce buffer position
}
}
//GSLC_DEBUG2_PRINT("SetSign: str_new=[%s] idx=%d\n", pStrBuf,pKeyPad->nValStrPos);
// Update sign state
pKeyPad->bValPositive = bPositive;
// The text string sub-element content has already been updated,
// so now we can simply mark it for redraw
gslc_tsElemRef* pTxtElemRef = gslc_CollectFindElemById(pGui, &pKeyPad->sCollect, KEYPAD_ID_TXT);
gslc_ElemSetRedraw(pGui,pTxtElemRef,GSLC_REDRAW_INC);
}
bool ElemXKeyPadAddChar(gslc_tsGui* pGui, gslc_tsXKeyPad* pKeyPad, char ch)
{
bool bRedraw = false;
// Do we have space for this character?
if (pKeyPad->nValStrPos < XKEYPAD_VAL_LEN - 1) {
pKeyPad->acValStr[pKeyPad->nValStrPos] = ch;
pKeyPad->nValStrPos++;
pKeyPad->acValStr[pKeyPad->nValStrPos] = '\0'; // Zero terminate
bRedraw = true;
}
return bRedraw;
}
// Handle the compound element main functionality
// - This routine is called by gslc_ElemEvent() to handle
// any click events that resulted from the touch tracking process.
// - The code here will generally represent the core
// functionality of the compound element and any communication
// between sub-elements.
// - pvElemRef is a void pointer to the element ref being tracked. From
// the pElemRefParent member we can get the parent/compound element
// data structures.
bool gslc_ElemXKeyPadClick(void* pvGui, void *pvElemRef, gslc_teTouch eTouch, int16_t nX, int16_t nY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRefD(pGui, pElemRef, __LINE__);
// Fetch the parent of the clicked element which is the compound
// element itself. This enables us to access the extra control data.
gslc_tsElemRef* pElemRefParent = pElem->pElemRefParent;
if (pElemRefParent == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXKeyPadClick(%s) parent ElemRef ptr NULL\n", "");
return false;
}
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRefParent, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return false;
//GSLC_DEBUG2_PRINT("KeyPad Click Touch=%d\n", eTouch);
// Handle the various button presses
if (eTouch == GSLC_TOUCH_UP_IN) {
// Get the tracked element ID
gslc_tsElemRef* pElemRefTracked = pKeyPad->sCollect.pElemRefTracked;
gslc_tsElem* pElemTracked = gslc_GetElemFromRef(pGui, pElemRefTracked);
int nSubElemId = pElemTracked->nId;
int16_t nKeyInd = 0;
bool bValRedraw = false;
GSLC_CB_INPUT pfuncXInput = pKeyPad->pfuncCb;
XKEYPAD_LOOKUP pfuncLookup = pKeyPad->pfuncLookup;
gslc_tsXKeyPadData sKeyPadData;
if (pfuncLookup != NULL) {
// FIXME: ERROR
}
// Prepare the return data
sKeyPadData.pStr = pKeyPad->acValStr;
sKeyPadData.nTargetId = pKeyPad->nTargetId;
//GSLC_DEBUG2_PRINT("KeyPad Click ID=%d\n", nSubElemId);
switch (nSubElemId) {
case KEYPAD_ID_ENTER:
//GSLC_DEBUG2_PRINT("KeyPad Key=ENT\n", "");
//GSLC_DEBUG2_PRINT("KeyPad Done Str=[%s]\n", pKeyPad->acValStr);
// Issue callback with Done status
if (pfuncXInput != NULL) {
(*pfuncXInput)(pvGui, (void*)(pElemRefParent), XKEYPAD_CB_STATE_DONE, (void*)(&sKeyPadData));
}
// Clear the contents
memset(pKeyPad->acValStr, 0, XKEYPAD_VAL_LEN);
pKeyPad->nValStrPos = 0;
pKeyPad->nTargetId = GSLC_ID_NONE;
break;
case KEYPAD_ID_ESC:
//GSLC_DEBUG2_PRINT("KeyPad Key=ESC\n", "");
// Clear the contents
memset(pKeyPad->acValStr, 0, XKEYPAD_VAL_LEN);
pKeyPad->nValStrPos = 0;
pKeyPad->nTargetId = GSLC_ID_NONE;
bValRedraw = true;
//GSLC_DEBUG2_PRINT("KeyPad Done Str=[%s]\n", pKeyPad->acValStr);
// Issue callback with Cancel status
if (pfuncXInput != NULL) {
(*pfuncXInput)(pvGui, (void*)(pElemRefParent), XKEYPAD_CB_STATE_CANCEL, (void*)(&sKeyPadData));
}
break;
case KEYPAD_ID_DECIMAL:
//GSLC_DEBUG2_PRINT("KeyPad Key=Decimal\n", "");
if (!pKeyPad->sConfig.bFloatEn) break; // Ignore if floating point not enabled
if (!pKeyPad->bValDecimalPt) {
bValRedraw |= ElemXKeyPadAddChar(pGui, pKeyPad, KEYPAD_DISP_DECIMAL_PT);
}
break;
case KEYPAD_ID_MINUS:
//GSLC_DEBUG2_PRINT("KeyPad Key=Minus\n", "");
if (!pKeyPad->sConfig.bSignEn) break; // Ignore if negative numbers not enabled
// Toggle sign
gslc_ElemXKeyPadValSetSign(pGui, pElemRefParent, pKeyPad->bValPositive ? false : true);
bValRedraw = true;
break;
case KEYPAD_ID_BACKSPACE:
//GSLC_DEBUG2_PRINT("KeyPad Key=BS\n", "");
if (pKeyPad->nValStrPos < 1) break;
pKeyPad->nValStrPos--;
// Handle the special case of decimal point if floating point enabled
if (pKeyPad->sConfig.bFloatEn) {
if (pKeyPad->acValStr[pKeyPad->nValStrPos] == KEYPAD_DISP_DECIMAL_PT) {
//GSLC_DEBUG2_PRINT("KeyPad Key=BS across decimal\n", "");
pKeyPad->bValDecimalPt = false;
}
}
if (pKeyPad->nValStrPos == 0) {
memset(pKeyPad->acValStr, 0, XKEYPAD_VAL_LEN);
pKeyPad->bValPositive = true;
}
pKeyPad->acValStr[pKeyPad->nValStrPos] = '\0';
bValRedraw = true;
break;
case KEYPAD_ID_PERIOD:
case KEYPAD_ID_SPACE:
default:
// Normal character
//GSLC_DEBUG2_PRINT("KeyPad Key=Digit\n", "");
// For basic buttons, we need to fetch the keypad string index
nKeyInd = (*pfuncLookup)(pGui, nSubElemId);
bValRedraw |= ElemXKeyPadAddChar(pGui, pKeyPad, pKeyPad->pacKeys[nKeyInd][0]);
break;
} // switch
// Do we need to redraw the text field?
if (bValRedraw) {
pElemRef = gslc_CollectFindElemById(pGui, &pKeyPad->sCollect, KEYPAD_ID_TXT);
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_INC);
// Issue callback with Update status
if (pfuncXInput != NULL) {
(*pfuncXInput)(pvGui, (void*)(pElemRefParent), XKEYPAD_CB_STATE_UPDATE, (void*)(&sKeyPadData));
}
} // bValRedraw
} // eTouch
return true;
#endif // !DRV_TOUCH_NONE
}
bool gslc_ElemXKeyPadTouch(void* pvGui, void* pvElemRef, gslc_teTouch eTouch, int16_t nRelX, int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXKeyPadTouch";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL, FUNCSTR);
return false;
}
gslc_tsGui* pGui = NULL;
gslc_tsElemRef* pElemRef = NULL;
gslc_tsElem* pElem = NULL;
gslc_tsXKeyPad* pKeyPad = NULL;
// Typecast the parameters to match the GUI
pGui = (gslc_tsGui*)(pvGui);
pElemRef = (gslc_tsElemRef*)(pvElemRef);
pElem = gslc_GetElemFromRef(pGui, pElemRef);
pKeyPad = (gslc_tsXKeyPad*)(pElem->pXData);
// Get Collection
gslc_tsCollect* pCollect = &pKeyPad->sCollect;
// Cascade the touch event to the sub-element collection
return gslc_CollectTouchCompound(pvGui, pvElemRef, eTouch, nRelX, nRelY, pCollect);
#endif // !DRV_TOUCH_NONE
}
void gslc_ElemXKeyPadCfgSetButtonSz(gslc_tsXKeyPadCfg* pConfig, int8_t nButtonSzW, int8_t nButtonSzH)
{
pConfig->nButtonSzW = nButtonSzW;
pConfig->nButtonSzH = nButtonSzH;
}
void gslc_ElemXKeyPadCfgSetFloatEn(gslc_tsXKeyPadCfg* pConfig, bool bEn)
{
pConfig->bFloatEn = bEn;
}
void gslc_ElemXKeyPadCfgSetSignEn(gslc_tsXKeyPadCfg* pConfig, bool bEn)
{
pConfig->bSignEn = bEn;
}
void gslc_ElemXKeyPadCfgSetRoundEn(gslc_tsXKeyPadCfg* pConfig, bool bEn)
{
pConfig->bRoundEn = bEn;
}
// FIXME: Runtime API not fully functional yet - Do not use
void gslc_ElemXKeyPadSetFloatEn(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, bool bEn)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
pKeyPad->sConfig.bFloatEn = bEn;
// Mark as needing full redraw as button visibility may have changed
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// FIXME: Runtime API not fully functional yet - Do not use
void gslc_ElemXKeyPadSetSignEn(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, bool bEn)
{
gslc_tsXKeyPad* pKeyPad = (gslc_tsXKeyPad*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_KEYPAD, __LINE__);
if (!pKeyPad) return;
pKeyPad->sConfig.bSignEn = bEn;
// Mark as needing full redraw as button visibility may have changed
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXKeyPadInputAsk(gslc_tsGui* pGui, gslc_tsElemRef* pKeyPadRef, int16_t nPgPopup, gslc_tsElemRef* pTxtRef)
{
// Fetch the Element ID from the text element
gslc_tsElem* pTxtElem = gslc_GetElemFromRefD(pGui,pTxtRef,__LINE__);
if (!pTxtElem) return;
int16_t nTxtElemId = pTxtElem->nId;
// Associate the keypad with the text field
gslc_ElemXKeyPadTargetIdSet(pGui, pKeyPadRef, nTxtElemId);
// Show a popup box for the keypad
gslc_PopupShow(pGui, nPgPopup, true);
// Preload text field with current value
gslc_ElemXKeyPadValSet(pGui, pKeyPadRef, gslc_ElemGetTxtStr(pGui, pTxtRef));
}
char* gslc_ElemXKeyPadInputGet(gslc_tsGui* pGui, gslc_tsElemRef* pTxtRef, void* pvCbData)
{
char* pStr = NULL;
// Fetch the current value of the keypad popup
pStr = gslc_ElemXKeyPadDataValGet(pGui, pvCbData);
// Update the linked text item
gslc_ElemSetTxtStr(pGui, pTxtRef, gslc_ElemXKeyPadDataValGet(pGui, pvCbData));
// Hide the popup
gslc_PopupHide(pGui);
// Return the text string in case the user wants it
return pStr;
}
#endif // GSLC_FEATURE_COMPOUND
// ============================================================================

409
src/guislice/XKeyPad.h Normal file
View file

@ -0,0 +1,409 @@
#ifndef _GUISLICE_EX_XKEYPAD_H_
#define _GUISLICE_EX_XKEYPAD_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: XKeyPad control
// - Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XKeyPad.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if (GSLC_FEATURE_COMPOUND)
// ============================================================================
// Extended Element: KeyPad (Number Selector)
// - Demonstration of a compound element consisting of
// a counter text field along with an increment and
// decrement button.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_KEYPAD GSLC_TYPE_BASE_EXTEND + 20
#define XKEYPAD_VAL_LEN 12 // Maximum buffer length for input string
// Define the status for GSLC_CB_INPUT callback
#define XKEYPAD_CB_STATE_DONE 1
#define XKEYPAD_CB_STATE_CANCEL 2
#define XKEYPAD_CB_STATE_UPDATE 3
// Define global list of button ID types
// - A given keypad may not use all of these
enum {
// --------------------------------------------
// --- Special Buttons
KEYPAD_ID_BACKSPACE,
KEYPAD_ID_PERIOD,
KEYPAD_ID_SPACE,
KEYPAD_ID_DECIMAL,
KEYPAD_ID_MINUS,
KEYPAD_ID_ESC,
KEYPAD_ID_ENTER,
// --- Basic Buttons
KEYPAD_ID_BASIC_START=100,
// --------------------------------------------
// --- Extra elements
KEYPAD_ID_TXT=200, // Value string text area
};
/// Function for KeyPad creation
typedef int16_t (*XKEYPAD_LOOKUP)(gslc_tsGui* pGui, int16_t nKeyId);
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Configuration for the KeyPad
typedef struct {
bool bFloatEn; ///< Enable floating point (ie. decimal point)
bool bSignEn; ///< Enable negative numbers
bool bRoundEn; ///< Enable rounded corners
int8_t nButtonSzW; ///< Button width (in pixels)
int8_t nButtonSzH; ///< Button height (in pixels)
char** pacKeys; ///< Array of character strings for KeyPad labels
// From constructor
int16_t nFontId; ///< Configured font for KeyPad labels
int16_t nOffsetX; ///< Configured offset (X direction) for buttons from parent container
int16_t nOffsetY; ///< Configured offset (Y direction) for buttons from parent container
int8_t nFrameMargin; ///< Margin around text value field
uint8_t nMaxCols; ///< Maximum number of columns to occupy
uint8_t nMaxRows; ///< Maximum number of rows to occupy
} gslc_tsXKeyPadCfg;
/// Input callback data structure
/// - This struct is returned in GSLC_CB_INPUT when the
/// KeyPad edits are complete, and is used to provide
/// the resulting edited value.
typedef struct {
char* pStr; ///< Final value of edited value field
int16_t nTargetId; ///< Target element ID to receive the value
} gslc_tsXKeyPadData;
/// Extended data for KeyPad element
typedef struct {
// State
uint8_t nValStrPos; ///< Current number of characters stored in edit value string
char acValStr[XKEYPAD_VAL_LEN]; ///< Storage for edit value string
bool bValPositive; ///< Value is positive if true, negative if false
bool bValDecimalPt; ///< Value string includes decimal point
// Config
char** pacKeys; ///< Array of character strings for KeyPad labels
gslc_tsXKeyPadCfg sConfig; ///< Configuration options
GSLC_CB_INPUT pfuncCb; ///< Callback function for KeyPad actions
XKEYPAD_LOOKUP pfuncLookup; ///< Callback function for converting key into key label
int16_t nTargetId; ///< Target element ID associated with keypad (GSLC_CB_INPUT)
// Internal sub-element members
gslc_tsCollect sCollect; ///< Collection management for sub-elements
uint8_t nSubElemMax; ///< Maximum number of sub-elements to create within KeyPad container
gslc_tsElemRef* psElemRef; ///< Ptr to storage for sub-element references
gslc_tsElem* psElem; ///< Ptr to storage for sub-elements
} gslc_tsXKeyPad;
// Callback function for KeyPad creation
typedef void (*XKEYPAD_CREATE)(gslc_tsGui* pGui,gslc_tsXKeyPad* pXData);
///
/// Add a key to the KeyPad control
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] nKeyId: ID to associated with the key
/// \param[in] bTxtField: Is this the text value field?
/// \param[in] nRow: Element placement position (row index, 0 at top)
/// \param[in] nCol: Element placement position (column index, 0 at left)
/// \param[in] nRowSpan: Number of columns to occupy by element (1 for normal size, 2 for double width)
/// \param[in] nColSpan: Number of rows to occupy by element (1 for normal size, 2 for double height)
/// \param[in] cColFill: Fill color for element
/// \param[in] cColGlow: Fill color for element when glowing
/// \param[in] bVisible: Initial key visibility state
///
/// \return none
///
void XKeyPadAddKeyElem(gslc_tsGui* pGui, gslc_tsXKeyPad* pXData, int16_t nKeyId, bool bTxtField, int16_t nRow, int16_t nCol,
int8_t nRowSpan, int8_t nColSpan, gslc_tsColor cColFill, gslc_tsColor cColGlow, bool bVisible);
///
/// Create a KeyPad Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] nX0: X KeyPad Starting Coordinate
/// \param[in] nY0: Y KeyPad Starting Coordinate
/// \param[in] nFontId: Font ID to use for drawing the element
/// \param[in] pConfig: Ptr to Config options
/// \param[in] pfuncCreate: Ptr to callback function for creation
/// \param[in] pfuncLookup: Ptr to callback function for button lookups
///
/// \return Pointer to Element or NULL if failure
///
gslc_tsElemRef* gslc_ElemXKeyPadCreateBase(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXKeyPad* pXData, int16_t nX0, int16_t nY0, int8_t nFontId, gslc_tsXKeyPadCfg* pConfig,
XKEYPAD_CREATE pfuncCreate, XKEYPAD_LOOKUP pfuncLookup);
///
/// Set the current value for the editable text field
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to KeyPad Element reference
/// \param[in] pStrBuf: String to copy into keypad
///
/// \return none
///
void gslc_ElemXKeyPadValSet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, const char* pStrBuf);
///
/// Set target element ID for KeyPad return value
/// - The Target ID is used in the GSLC_CB_INPUT callback so that the user
/// has the context needed to determine which field should be edited
/// with the contents of the KeyPad edit field
/// - It is expected that the user will call this function when showing
/// the KeyPad popup dialog
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to KeyPad Element reference
/// \param[in] nId: Element enum ID for target of KeyPad value
///
/// \return none
///
void gslc_ElemXKeyPadTargetIdSet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nId);
///
/// Set the current output string buffer associated with NumericInput element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to KeyPad Element reference
/// \param[in] pStrBuf: String to copy into element
/// \param[in] nStrBufMax: Maximum length of string buffer (pStrBuf)
///
/// \return none
///
bool gslc_ElemXKeyPadValGet(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, char* pStrBuf, uint8_t nStrBufMax);
///
/// Fetch the edited value string from the KeyPad
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pvData : Void ptr to callback data structure
///
/// \return Pointer to edited character string
///
char* gslc_ElemXKeyPadDataValGet(gslc_tsGui* pGui, void* pvData);
///
/// Fetch the element target ID associated with this KeyPad
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pvData : Void ptr to callback data structure
///
/// \return Target Element ID or GSLC_ID_NONE if unspecified
///
int16_t gslc_ElemXKeyPadDataTargetIdGet(gslc_tsGui* pGui, void* pvData);
///
/// Draw a KeyPad element on the screen
/// - Called during redraw
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXKeyPadDraw(void* pvGui, void* pvElemRef, gslc_teRedrawType eRedraw);
///
/// Handle a click event within the KeyPad
/// - This is called internally by the KeyPad touch handler
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element Ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nX: Touch X coord
/// \param[in] nY: Touch Y coord
///
/// \return none
///
bool gslc_ElemXKeyPadClick(void* pvGui, void *pvElemRef, gslc_teTouch eTouch, int16_t nX, int16_t nY);
///
/// Handle touch (up,down,move) events to KeyPad element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXKeyPadTouch(void* pvGui, void* pvElemRef, gslc_teTouch eTouch, int16_t nRelX, int16_t nRelY);
///
/// Set the callback function associated with the KeyPad
/// - This function will be called during updates and OK / Cancel
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element Reference for KeyPad
/// \param[in] pfuncCb: Callback function pointer
///
/// \return none
///
void gslc_ElemXKeyPadValSetCb(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, GSLC_CB_INPUT pfuncCb);
///
/// Update the KeyPad configuration to enable floating point numbers
/// - Effectively disables/enables the decimal point button & handling
///
/// \param[in] pConfig: Pointer to the XKeyPad config structure
/// \param[in] bEn: Enable flag (true if floating point enabled)
///
/// \return none
///
void gslc_ElemXKeyPadCfgSetFloatEn(gslc_tsXKeyPadCfg* pConfig,bool bEn);
///
/// Update the KeyPad configuration to enable negative numbers
/// - Effectively disables/enables the sign button & handling
///
/// \param[in] pConfig: Pointer to the XKeyPad config structure
/// \param[in] bEn: Enable flag (true if negative numbers enabled)
///
/// \return none
///
void gslc_ElemXKeyPadCfgSetSignEn(gslc_tsXKeyPadCfg* pConfig,bool bEn);
///
/// Update the KeyPad configuration to enable rounded button corners
///
/// \param[in] pConfig: Pointer to the XKeyPad config structure
/// \param[in] bEn: Enable rounded corners
///
/// \return none
///
void gslc_ElemXKeyPadCfgSetRoundEn(gslc_tsXKeyPadCfg* pConfig,bool bEn);
///
/// Update the KeyPad configuration to define the KeyPad button sizing
///
/// \param[in] pConfig: Pointer to the XKeyPad config structure
/// \param[in] nButtonSzW: Width of buttons in pixels
/// \param[in] nButtonSzH: Width of buttons in pixels
///
/// \return none
///
void gslc_ElemXKeyPadCfgSetButtonSz(gslc_tsXKeyPadCfg* pConfig, int8_t nButtonSzW, int8_t nButtonSzH);
///
/// Update the KeyPad active configuration to enable negative numbers
/// - Effectively disables/enables the sign button & handling
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to KeyPad element reference
/// \param[in] bEn: Enable flag (true if negative numbers enabled)
///
/// \return none
///
void gslc_ElemXKeyPadSetFloatEn(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, bool bEn);
///
/// Update the KeyPad active configuration to enable negative numbers
/// - Effectively disables/enables the sign button & handling
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to KeyPad element reference
/// \param[in] bEn: Enable flag (true if negative numbers enabled)
///
/// \return none
///
void gslc_ElemXKeyPadSetSignEn(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, bool bEn);
///
/// Trigger a KeyPad popup and associate it with a text element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pKeyPadRef: Pointer to KeyPad element reference
/// \param[in] nPgPopup: Page enum that contains the popup to show
/// \param[in] pTxtRef: Pointer to associated text field element reference
///
/// \return none
///
void gslc_ElemXKeyPadInputAsk(gslc_tsGui* pGui, gslc_tsElemRef* pKeyPadRef, int16_t nPgPopup, gslc_tsElemRef* pTxtRef);
///
/// Complete a KeyPad popup by retrieving the input data and storing it in the text element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pTxtRef: Pointer to associated text field element reference
/// \param[in] pvCbData: Void pointer to callback function's pvData
///
/// \return The text string that was fetched from the KeyPad
///
char* gslc_ElemXKeyPadInputGet(gslc_tsGui* pGui, gslc_tsElemRef* pTxtRef, void* pvCbData);
#endif // GSLC_FEATURE_COMPOUND
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XKEYPAD_H_

View file

@ -0,0 +1,204 @@
// =======================================================================
// GUIslice library extension: XKeyPad control (Alpha entry)
// - Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XKeyPad.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XKeyPad.h"
#include "XKeyPad_Alpha.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
#if (GSLC_FEATURE_COMPOUND)
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: KeyPad
// - Keypad element with alpha input
// ============================================================================
// Define the button labels
// - TODO: Create more efficient storage for the labels
// so that it doesn't consume 4 bytes even for labels
// that can be generated (eg. 0..9, a--z, etc.)
// - TODO: Support use of PROGMEM. Note that these labels
// are not currently using "const char" since we may
// want to support user-modification of the labels.
static char* KEYPAD_LABEL_STRINGS[] = {
// Special buttons
"<", ".", " ", "ESC", "ENT",
// Basic buttons
"A", "B", "C", "D", "E", "F", "G", "H" ,"I", "J",
"K", "L", "M", "N", "O", "P", "Q", "R" ,"S", "T",
"U", "V", "W", "X", "Y", "Z",
};
// Define enums for KEYPAD_LABEL_STRINGS
enum {
// - Special buttons
KEYPAD_LBL_BACKSPACE,
KEYPAD_LBL_PERIOD,
KEYPAD_LBL_SPACE,
KEYPAD_LBL_ESC,
KEYPAD_LBL_ENTER,
// - Basic buttons
KEYPAD_LBL_BASIC_START
};
// Generate the keypad layout
void XKeyPadCreateKeys_Alpha(gslc_tsGui* pGui, gslc_tsXKeyPad* pXData)
{
int16_t nKeyInd;
int16_t nRow, nCol;
// - Create the "special" buttons
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_BACKSPACE, false, 1, 6, 1, 2, GSLC_COL_GRAY_LT1, GSLC_COL_GRAY_LT3, true);
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_PERIOD, false, 3, 7, 1, 1, GSLC_COL_GRAY_LT1, GSLC_COL_GRAY_LT3, true);
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_SPACE, false, 3, 6, 1, 1, GSLC_COL_GRAY_LT1, GSLC_COL_GRAY_LT3, true);
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_ESC, false, 2, 6, 1, 2, GSLC_COL_RED, GSLC_COL_RED_LT4, true);
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_ENTER, false, 0, 6, 1, 2, GSLC_COL_GREEN, GSLC_COL_GREEN_LT4, true);
// - Create the "simple" buttons
for (int16_t nKeyId = KEYPAD_ID_BASIC_START; nKeyId < (KEYPAD_ID_BASIC_START + XKEYPADALPHA_BTN_BASIC); nKeyId++) {
nKeyInd = nKeyId - KEYPAD_ID_BASIC_START;
nRow = (nKeyInd / 6) + 1;
nCol = nKeyInd % 6;
XKeyPadAddKeyElem(pGui, pXData, nKeyId, false, nRow, nCol, 1, 1, GSLC_COL_BLUE_LT1, GSLC_COL_BLUE_LT4, true);
}
// - Create the text field
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_TXT, true, 0, 0, 1, 6, GSLC_COL_BLACK, GSLC_COL_BLACK, true);
}
// Convert between keypad ID and the index into the keypad label array
int16_t XKeyPadLookup_Alpha(gslc_tsGui* pGui, int16_t nKeyId)
{
int16_t nKeyInd;
// Basic button
if (nKeyId >= KEYPAD_ID_BASIC_START) {
nKeyInd = (nKeyId - KEYPAD_ID_BASIC_START) + KEYPAD_LBL_BASIC_START;
} else {
// Special button
switch (nKeyId) {
case KEYPAD_ID_PERIOD:
nKeyInd = KEYPAD_LBL_PERIOD;
break;
case KEYPAD_ID_SPACE:
nKeyInd = KEYPAD_LBL_SPACE;
break;
case KEYPAD_ID_BACKSPACE:
nKeyInd = KEYPAD_LBL_BACKSPACE;
break;
case KEYPAD_ID_ESC:
nKeyInd = KEYPAD_LBL_ESC;
break;
case KEYPAD_ID_ENTER:
nKeyInd = KEYPAD_LBL_ENTER;
break;
default:
// FIXME: ERROR
nKeyInd = -1; // Not expected
break;
}
}
return nKeyInd;
}
// Create the XKeyPad_Alpha compound element
// - Note that this also revises some of the members of the base XKeyPad struct
gslc_tsElemRef* gslc_ElemXKeyPadCreate_Alpha(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXKeyPad_Alpha* pXData, int16_t nX0, int16_t nY0, int8_t nFontId, gslc_tsXKeyPadCfg* pConfig)
{
gslc_tsXKeyPad* pXDataBase = (gslc_tsXKeyPad*)pXData;
pXDataBase->nSubElemMax = XKEYPADALPHA_ELEM_MAX;
pXDataBase->psElemRef = pXData->asElemRef;
pXDataBase->psElem = pXData->asElem;
// Provide default config if none supplied
gslc_tsXKeyPadCfg sConfigTmp;
if (pConfig == NULL) {
sConfigTmp = gslc_ElemXKeyPadCfgInit_Alpha();
pConfig = &sConfigTmp;
}
return gslc_ElemXKeyPadCreateBase(pGui, nElemId, nPage, pXDataBase, nX0, nY0, nFontId, pConfig,
&XKeyPadCreateKeys_Alpha,&XKeyPadLookup_Alpha);
}
// Reset the XKeyPad config struct
// - This must be called before any XKeyPad config update APIs are called
gslc_tsXKeyPadCfg gslc_ElemXKeyPadCfgInit_Alpha()
{
gslc_tsXKeyPadCfg sConfig;
sConfig.nButtonSzW = 25;
sConfig.nButtonSzH = 25;
sConfig.bFloatEn = false; // Unused
sConfig.bSignEn = false; // Unused
sConfig.bRoundEn = false;
sConfig.nFontId = GSLC_FONT_NONE; // Will be overwritten
sConfig.pacKeys = KEYPAD_LABEL_STRINGS;
sConfig.nMaxCols = 8;
sConfig.nMaxRows = 6;
sConfig.nFrameMargin = 2;
return sConfig;
}
#endif // GSLC_FEATURE_COMPOUND
// ============================================================================

View file

@ -0,0 +1,100 @@
#ifndef _GUISLICE_EX_XKEYPAD_ALPHA_H_
#define _GUISLICE_EX_XKEYPAD_ALPHA_H_
#include "GUIslice.h"
#include "XKeyPad.h"
// =======================================================================
// GUIslice library extension: XKeyPad control (alpha entry)
// - Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XKeyPad.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if (GSLC_FEATURE_COMPOUND)
// Define number of buttons & elements
// - Refer to the definitions in the XKeyPad_*.c file
#define XKEYPADALPHA_BTN_BASIC 26
#define XKEYPADALPHA_ELEM_MAX (6 + XKEYPADALPHA_BTN_BASIC)
// ============================================================================
// Extended Element: KeyPad Character entry
// - NOTE: The XKeyPad_Alpha extends the XKeyPad base element
// ============================================================================
typedef struct {
// Base XKeyPad struct
// - The base type must appear at the top of the derived struct
gslc_tsXKeyPad sKeyPad; ///< Base XKeyPad element
gslc_tsElemRef asElemRef[XKEYPADALPHA_ELEM_MAX]; ///< Storage for sub-element references
gslc_tsElem asElem[XKEYPADALPHA_ELEM_MAX]; ///< Storage for sub-elements
} gslc_tsXKeyPad_Alpha;
///
/// Create a KeyPad Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] nX0: X KeyPad Starting Coordinate
/// \param[in] nY0: Y KeyPad Starting Coordinate
/// \param[in] nFontId: Font ID to use for drawing the element
/// \param[in] pConfig: Ptr to config options
///
/// \return Pointer to Element or NULL if failure
///
gslc_tsElemRef* gslc_ElemXKeyPadCreate_Alpha(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXKeyPad_Alpha* pXData, int16_t nX0, int16_t nY0, int8_t nFontId, gslc_tsXKeyPadCfg* pConfig);
///
/// Initialize the KeyPad config structure
/// - This routine should be called to initialize the configuration
/// data structure before calling any of the KeyPad config APIs
///
/// \return Initialized KeyPad config structure
///
gslc_tsXKeyPadCfg gslc_ElemXKeyPadCfgInit_Alpha();
// ============================================================================
#endif // GSLC_FEATURE_COMPOUND
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XKEYPAD_ALPHA_H_

206
src/guislice/XKeyPad_Num.c Normal file
View file

@ -0,0 +1,206 @@
// =======================================================================
// GUIslice library extension: XKeyPad control (Numeric entry)
// - Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XKeyPad.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XKeyPad.h"
#include "XKeyPad_Num.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
#if (GSLC_FEATURE_COMPOUND)
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: KeyPad
// - Keypad element with numeric input
// - Optionally supports floating point values
// - Optionally supports negative values
// ============================================================================
// Define the button labels
// - TODO: Create more efficient storage for the labels
// so that it doesn't consume 4 bytes even for labels
// that can be generated (eg. 0..9, a--z, etc.)
// - TODO: Support use of PROGMEM. Note that these labels
// are not currently using "const char" since we may
// want to support user-modification of the labels.
static char* KEYPAD_LABEL_STRINGS[] = {
// Special buttons
"<", ".", "-", "ESC", "ENT",
// Basic buttons
"0", "1", "2", "3", "4", "5", "6", "7" ,"8", "9",
};
// Define enums for KEYPAD_LABEL_STRINGS
enum {
// - Special buttons
KEYPAD_LBL_BACKSPACE,
KEYPAD_LBL_DECIMAL,
KEYPAD_LBL_MINUS,
KEYPAD_LBL_ESC,
KEYPAD_LBL_ENTER,
// - Basic buttons
KEYPAD_LBL_BASIC_START
};
// Generate the keypad layout
void XKeyPadCreateKeys_Num(gslc_tsGui* pGui, gslc_tsXKeyPad* pXData)
{
int16_t nKeyInd;
int16_t nRow, nCol;
gslc_tsXKeyPadCfg* pConfig = &pXData->sConfig;
// - Create the "special" buttons
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_BACKSPACE, false, 1, 6, 1, 2, GSLC_COL_GRAY_LT1, GSLC_COL_GRAY_LT3, true);
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_MINUS, false, 1, 5, 1, 1, GSLC_COL_GRAY_LT1, GSLC_COL_GRAY_LT3, pConfig->bSignEn);
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_DECIMAL, false, 2, 5, 1, 1, GSLC_COL_GRAY_LT1, GSLC_COL_GRAY_LT3, pConfig->bFloatEn);
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_ESC, false, 2, 6, 1, 2, GSLC_COL_RED, GSLC_COL_RED_LT4, true);
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_ENTER, false, 0, 6, 1, 2, GSLC_COL_GREEN, GSLC_COL_GREEN_LT4, true);
// - Create the "simple" buttons
for (int16_t nKeyId = KEYPAD_ID_BASIC_START; nKeyId < (KEYPAD_ID_BASIC_START + XKEYPADNUM_BTN_BASIC); nKeyId++) {
nKeyInd = nKeyId - KEYPAD_ID_BASIC_START;
nRow = (nKeyInd / 5) + 1;
nCol = nKeyInd % 5;
XKeyPadAddKeyElem(pGui, pXData, nKeyId, false, nRow, nCol, 1, 1, GSLC_COL_BLUE_LT1, GSLC_COL_BLUE_LT4, true);
}
// - Create the text field
XKeyPadAddKeyElem(pGui, pXData, KEYPAD_ID_TXT, true, 0, 0, 1, 6, GSLC_COL_BLACK, GSLC_COL_BLACK, true);
}
// Convert between keypad ID and the index into the keypad label array
int16_t XKeyPadLookup_Num(gslc_tsGui* pGui, int16_t nKeyId)
{
int16_t nKeyInd;
// Basic button
if (nKeyId >= KEYPAD_ID_BASIC_START) {
nKeyInd = (nKeyId - KEYPAD_ID_BASIC_START) + KEYPAD_LBL_BASIC_START;
} else {
// Special button
switch (nKeyId) {
case KEYPAD_ID_DECIMAL:
nKeyInd = KEYPAD_LBL_DECIMAL;
break;
case KEYPAD_ID_MINUS:
nKeyInd = KEYPAD_LBL_MINUS;
break;
case KEYPAD_ID_BACKSPACE:
nKeyInd = KEYPAD_LBL_BACKSPACE;
break;
case KEYPAD_ID_ESC:
nKeyInd = KEYPAD_LBL_ESC;
break;
case KEYPAD_ID_ENTER:
nKeyInd = KEYPAD_LBL_ENTER;
break;
default:
// FIXME: ERROR
nKeyInd = -1; // Not expected
break;
}
}
return nKeyInd;
}
// Create the XKeyPad_Alpha compound element
// - Note that this also revises some of the members of the base XKeyPad struct
gslc_tsElemRef* gslc_ElemXKeyPadCreate_Num(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXKeyPad_Num* pXData, int16_t nX0, int16_t nY0, int8_t nFontId, gslc_tsXKeyPadCfg* pConfig)
{
gslc_tsXKeyPad* pXDataBase = (gslc_tsXKeyPad*)pXData;
pXDataBase->nSubElemMax = XKEYPADNUM_ELEM_MAX;
pXDataBase->psElemRef = pXData->asElemRef;
pXDataBase->psElem = pXData->asElem;
// Provide default config if none supplied
gslc_tsXKeyPadCfg sConfigTmp;
if (pConfig == NULL) {
sConfigTmp = gslc_ElemXKeyPadCfgInit_Num();
pConfig = &sConfigTmp;
}
return gslc_ElemXKeyPadCreateBase(pGui, nElemId, nPage, pXDataBase, nX0, nY0, nFontId, pConfig,
&XKeyPadCreateKeys_Num,&XKeyPadLookup_Num);
}
// Reset the XKeyPad config struct
// - This must be called before any XKeyPad config update APIs are called
gslc_tsXKeyPadCfg gslc_ElemXKeyPadCfgInit_Num()
{
gslc_tsXKeyPadCfg sConfig;
sConfig.nButtonSzW = 25;
sConfig.nButtonSzH = 25;
sConfig.bFloatEn = true;
sConfig.bSignEn = true;
sConfig.bRoundEn = false;
sConfig.nFontId = GSLC_FONT_NONE; // Will be overwritten
sConfig.pacKeys = KEYPAD_LABEL_STRINGS;
sConfig.nMaxCols = 8;
sConfig.nMaxRows = 3;
sConfig.nFrameMargin = 2;
return sConfig;
}
#endif // GSLC_FEATURE_COMPOUND
// ============================================================================

100
src/guislice/XKeyPad_Num.h Normal file
View file

@ -0,0 +1,100 @@
#ifndef _GUISLICE_EX_XKEYPAD_NUM_H_
#define _GUISLICE_EX_XKEYPAD_NUM_H_
#include "GUIslice.h"
#include "XKeyPad.h"
// =======================================================================
// GUIslice library extension: XKeyPad control (numeric entry)
// - Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XKeyPad.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if (GSLC_FEATURE_COMPOUND)
// Define number of buttons & elements
// - Refer to the definitions in the XKeyPad_*.c file
#define XKEYPADNUM_BTN_BASIC 10
#define XKEYPADNUM_ELEM_MAX (6 + XKEYPADNUM_BTN_BASIC)
// ============================================================================
// Extended Element: KeyPad Character entry
// - NOTE: The XKeyPad_Alpha extends the XKeyPad base element
// ============================================================================
typedef struct {
// Base XKeyPad struct
// - The base type must appear at the top of the derived struct
gslc_tsXKeyPad sKeyPad; ///< Base XKeyPad element
gslc_tsElemRef asElemRef[XKEYPADNUM_ELEM_MAX]; ///< Storage for sub-element references
gslc_tsElem asElem[XKEYPADNUM_ELEM_MAX]; ///< Storage for sub-elements
} gslc_tsXKeyPad_Num;
///
/// Create a KeyPad Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] nX0: X KeyPad Starting Coordinate
/// \param[in] nY0: Y KeyPad Starting Coordinate
/// \param[in] nFontId: Font ID to use for drawing the element
/// \param[in] pConfig: Ptr to config options
///
/// \return Pointer to Element or NULL if failure
///
gslc_tsElemRef* gslc_ElemXKeyPadCreate_Num(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXKeyPad_Num* pXData, int16_t nX0, int16_t nY0, int8_t nFontId, gslc_tsXKeyPadCfg* pConfig);
///
/// Initialize the KeyPad config structure
/// - This routine should be called to initialize the configuration
/// data structure before calling any of the KeyPad config APIs
///
/// \return Initialized KeyPad config structure
///
gslc_tsXKeyPadCfg gslc_ElemXKeyPadCfgInit_Num();
// ============================================================================
#endif // GSLC_FEATURE_COMPOUND
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XKEYPAD_NUM_H_

982
src/guislice/XListbox.c Normal file
View file

@ -0,0 +1,982 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XListbox.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XListbox.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
// ----------------------------------------------------------------------------
// TODO: Combine with GUIslice MAX_STR
// Defines the maximum length of a listbox item
#define XLISTBOX_MAX_STR 20
// ============================================================================
// Extended Element: Listbox
// - A Listbox control
// ============================================================================
// Basic debug functionality
/*
void debug_ElemXListboxDump(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef)
{
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui, pElemRef);
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)(pElem->pXData);
// Prevent runaway looping due to damaged buffer
// Don't ask why I know to do this - PaulC
int16_t nSafety=0;
char acTxt[50 + 1];
GSLC_DEBUG2_PRINT("Xlistbox:Dump (nBufPos=%d nItemCnt=%d)\n", pListbox->nBufItemsPos,pListbox->nItemCnt);
for (unsigned nInd = 0; nInd < pListbox->nBufItemsPos; nInd++) {
nSafety++;
if (nSafety > pListbox->nBufItemsMax) break;
char ch = pListbox->pBufItems[nInd];
if (ch == 0) {
snprintf(acTxt, 40,"0x%04x: [%2u] = %02X = [NULL]", (char*)&pListbox->pBufItems[nInd],nInd, ch);
}
else {
snprintf(acTxt, 40,"0x%04x: [%2u] = %02X = [%c]", (char*)&pListbox->pBufItems[nInd],nInd, ch, ch);
}
GSLC_DEBUG2_PRINT("XListbox:Dump: %s\n", acTxt);
}
}
*/
char* gslc_ElemXListboxGetItemAddr(gslc_tsXListbox* pListbox, int16_t nItemCurSel)
{
char* pBuf = NULL;
uint16_t nBufPos = 0;
uint16_t nItemInd = 0;
bool bFound = false;
while (1) {
if (nItemInd == nItemCurSel) {
bFound = true;
break;
}
if (nBufPos >= pListbox->nBufItemsMax) {
break;
}
if (pListbox->pBufItems[nBufPos] == 0) {
nItemInd++;
}
nBufPos++;
}
if (bFound) {
pBuf = (char*)&(pListbox->pBufItems[nBufPos]);
return pBuf;
} else {
return NULL;
}
}
// Recalculate listbox item sizing if enabled
bool gslc_ElemXListboxRecalcSize(gslc_tsXListbox* pListbox,gslc_tsRect rElem)
{
int16_t nElem;
int16_t nElemInner;
int16_t nItemOuter;
int16_t nItemWOld;
bool bNeedRedraw = false;
// If number of rows was auto-sized based on content, then calculate now
if (pListbox->nRows == XLISTBOX_SIZE_AUTO) {
if (pListbox->nItemCnt == 0) {
pListbox->nRows = 1; // Force at least one row
} else {
pListbox->nRows = ((pListbox->nItemCnt + 1) / pListbox->nCols);
}
}
// NOTE: In the nElemInner calculation, we add nItemGap to account
// for the fact that the last column does not include a "gap" after it.
if (pListbox->bItemAutoSizeW) {
nItemWOld = pListbox->nItemW;
nElem = rElem.w;
nElemInner = nElem - (2 * pListbox->nMarginW) + pListbox->nItemGap;
nItemOuter = nElemInner / pListbox->nCols;
pListbox->nItemW = nItemOuter - pListbox->nItemGap;
if (pListbox->nItemW != nItemWOld) {
bNeedRedraw = true;
}
}
if (pListbox->bItemAutoSizeH) {
nItemWOld = pListbox->nItemH;
nElem = rElem.h;
nElemInner = nElem - (2 * pListbox->nMarginH) + pListbox->nItemGap;
nItemOuter = nElemInner / pListbox->nRows;
pListbox->nItemH = nItemOuter - pListbox->nItemGap;
if (pListbox->nItemH != nItemWOld) {
bNeedRedraw = true;
}
}
// Clear Need Recalc flag
pListbox->bNeedRecalc = false;
return bNeedRedraw;
}
void gslc_ElemXListboxSetSize(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int8_t nRows, int8_t nCols)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return;
pListbox->nRows = nRows;
pListbox->nCols = nCols;
pListbox->bNeedRecalc = true;
// Mark as needing full redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
}
void gslc_ElemXListboxSetMargin(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int8_t nMarginW, int8_t nMarginH)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return;
pListbox->nMarginW = nMarginW;
pListbox->nMarginH = nMarginH;
pListbox->bNeedRecalc = true;
// Mark as needing full redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
}
void gslc_ElemXListboxItemsSetSize(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nItemW, int16_t nItemH)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return;
pListbox->nItemW = nItemW;
pListbox->nItemH = nItemH;
// Determine if auto-sizing requested
pListbox->bItemAutoSizeW = (nItemW == XLISTBOX_SIZE_AUTO) ? true : false;
pListbox->bItemAutoSizeH = (nItemH == XLISTBOX_SIZE_AUTO) ? true : false;
pListbox->bNeedRecalc = true;
// Mark as needing full redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
}
void gslc_ElemXListboxItemsSetGap(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int8_t nGap, gslc_tsColor colGap)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return;
pListbox->nItemGap = nGap;
pListbox->colGap = colGap;
pListbox->bNeedRecalc = true;
// Mark as needing full redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
}
void gslc_ElemXListboxReset(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return;
pListbox->nBufItemsPos = 0;
pListbox->nItemCnt = 0;
pListbox->nItemCurSel = XLISTBOX_SEL_NONE;
pListbox->bNeedRecalc = true;
// Mark as needing full redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
}
bool gslc_ElemXListboxAddItem(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, const char* pStrItem)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return false;
int8_t nStrItemLen;
char* pBuf = NULL;
uint16_t nBufItemsPos = pListbox->nBufItemsPos;
uint16_t nBufItemsMax = pListbox->nBufItemsMax;
nStrItemLen = strlen(pStrItem);
if (nStrItemLen == 0) {
// Nothing to add
return false;
}
// Ensure we won't overrun the buffer, including the terminator
if (nBufItemsPos + nStrItemLen + 1 > nBufItemsMax) {
// Proceed with truncation
nStrItemLen = nBufItemsMax - pListbox->nBufItemsPos - 1;
GSLC_DEBUG2_PRINT("ERROR: ElemXListboxAddItem() buffer too small\n", "");
}
// If the truncation left nothing to add, skip out now
if (nStrItemLen <= 0) {
return false;
}
// Treat this section of the buffer as [char]
pBuf = (char*)pListbox->pBufItems + nBufItemsPos;
strncpy(pBuf, pStrItem, nStrItemLen);
pListbox->nBufItemsPos += nStrItemLen;
// Ensure terminator added
pBuf += nStrItemLen;
*pBuf = 0;
pListbox->nBufItemsPos++;
pListbox->nItemCnt++;
//GSLC_DEBUG2_PRINT("Xlistbox:Add\n", "");
//debug_ElemXListboxDump(pGui, pElemRef);
// Inidicate sizing may need update
pListbox->bNeedRecalc = true;
// Mark as needing full redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
return true;
}
bool gslc_ElemXListboxInsertItemAt(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nInsertPos,
const char* pStrItem)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return false;
int8_t nStrItemLen;
char* pBuf = NULL;
uint16_t nBufItemsPos = pListbox->nBufItemsPos;
uint16_t nBufItemsMax = pListbox->nBufItemsMax;
if (nInsertPos > pListbox->nItemCnt) {
GSLC_DEBUG2_PRINT("ERROR: ElemXListboxInsertItemAt() Current Count: %d Invalid Position %d\n",
pListbox->nItemCnt,nInsertPos);
return false;
}
nStrItemLen = strlen(pStrItem);
if (nStrItemLen == 0) {
// Nothing to add
return false;
}
// Ensure we won't overrun the buffer, including the terminator
if (nBufItemsPos + nStrItemLen + 1 > nBufItemsMax) {
GSLC_DEBUG2_PRINT("ERROR: ElemXListboxInsertItemAt() buffer too small\n", "");
return false;
}
// If the truncation left nothing to add, skip out now
if (nStrItemLen <= 0) {
return false;
}
// GSLC_DEBUG2_PRINT("Xlistbox:InsertAt: %d\n", nInsertPos);
// debug_ElemXListboxDump(pGui, pElemRef);
pBuf = gslc_ElemXListboxGetItemAddr(pListbox, nInsertPos);
// If position is incorrect, bail out...
if (pBuf == NULL) {
return false;
}
// Make a hole in the buffer to slot in the new item
char* pSrc = (char*)pListbox->pBufItems+nBufItemsPos-1;
char* pDest = pSrc+nStrItemLen+1;
int16_t nMoveLen = (int16_t)(pSrc - pBuf)+1;
uint8_t ch;
for (uint16_t nInd = 0; nInd < nMoveLen; nInd++) {
ch = *pSrc;
*pDest = ch;
pDest--;
pSrc--;
}
// Now slot in the new item
memcpy(pBuf, pStrItem, nStrItemLen+1);
// update our buffer information
pListbox->nBufItemsPos += nStrItemLen+1;
pListbox->nItemCnt++;
// GSLC_DEBUG2_PRINT("Xlistbox:After InsertAt %d\n", nInsertPos);
// debug_ElemXListboxDump(pGui, pElemRef);
// Indicate sizing may need update
pListbox->bNeedRecalc = true;
// Mark as needing full redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
return true;
}
bool gslc_ElemXListboxDeleteItemAt(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nDeletePos)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return false;
int8_t nStrItemLen;
char* pBuf = NULL;
uint16_t nBufItemsPos = pListbox->nBufItemsPos;
// GSLC_DEBUG2_PRINT("Xlistbox:DeleteAt: %d\n", nDeletePos);
// debug_ElemXListboxDump(pGui, pElemRef);
pBuf = gslc_ElemXListboxGetItemAddr(pListbox, nDeletePos);
// If position is incorrect, bail out...
if (pBuf == NULL) {
return false;
}
nStrItemLen = strlen(pBuf);
// Pull items after this delete position up to delete this item
char* pSrc = (char*)pBuf+nStrItemLen+1;
char* pDest = (char*)pBuf;
char* pEndOfBuf = (char*)(pListbox->pBufItems+nBufItemsPos);
int16_t nMoveLen = (int16_t)(pEndOfBuf-pSrc)+1;
uint8_t ch;
if (nMoveLen > 1) {
for (uint16_t nInd = 0; nInd < nMoveLen; nInd++) {
ch = *pSrc;
*pDest = ch;
pDest++;
pSrc++;
}
}
// update our buffer information
pListbox->nBufItemsPos -= nStrItemLen+1;
pListbox->nItemCnt--;
// unselect item
gslc_ElemXListboxSetSel(pGui, pElemRef, XLISTBOX_SEL_NONE);
// GSLC_DEBUG2_PRINT("Xlistbox:After DeleteAt %d\n", nDeletePos);
// debug_ElemXListboxDump(pGui, pElemRef);
// Indicate sizing may need update
pListbox->bNeedRecalc = true;
// Mark as needing full redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
return true;
}
bool gslc_ElemXListboxGetItem(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nItemCurSel, char* pStrItem,
uint8_t nStrItemLen)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return false;
// Ensure user provided valid string
if ((pStrItem == NULL) || (nStrItemLen == 0)) {
// ERROR
return false;
}
uint16_t nBufPos = 0;
uint16_t nItemInd = 0;
uint8_t* pBuf = NULL;
bool bFound = false;
while (1) {
if (nItemInd == nItemCurSel) {
bFound = true;
break;
}
if (nBufPos >= pListbox->nBufItemsMax) {
break;
}
if (pListbox->pBufItems[nBufPos] == 0) {
nItemInd++;
}
nBufPos++;
}
if (bFound) {
pBuf = &(pListbox->pBufItems[nBufPos]);
strncpy(pStrItem, (char*)pBuf, nStrItemLen);
// Ensure null terminated in case the buffer
// was smaller than the item source
pStrItem[nStrItemLen - 1] = 0;
return true;
} else {
// If no item was found, return an empty string (NULL)
pStrItem[0] = 0;
return false;
}
}
int16_t gslc_ElemXListboxGetItemCnt(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return 0;
return pListbox->nItemCnt;
}
// Create a Listbox element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw and touch
gslc_tsElemRef* gslc_ElemXListboxCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXListbox* pXData,gslc_tsRect rElem,int16_t nFontId,uint8_t* pBufItems,uint16_t nBufItemsMax,int16_t nItemDefault)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXListboxCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_LISTBOX,rElem,NULL,0,nFontId);
sElem.nFeatures &= ~GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_GLOW_EN;
sElem.nGroup = GSLC_GROUP_ID_NONE;
pXData->pBufItems = pBufItems;
pXData->nBufItemsMax = nBufItemsMax;
pXData->nBufItemsPos = 0;
pXData->nItemCnt = 0;
pXData->nItemCurSel = nItemDefault;
pXData->nItemCurSelLast = XLISTBOX_SEL_NONE;
pXData->nItemSavedSel = XLISTBOX_SEL_NONE;
pXData->nItemTop = 0;
pXData->pfuncXSel = NULL;
pXData->nCols = 1;
pXData->nRows = XLISTBOX_SIZE_AUTO; // Auto-calculated from content
pXData->bNeedRecalc = true; // Force auto-sizing
pXData->nMarginW = 5;
pXData->nMarginH = 5;
pXData->bItemAutoSizeW = true; // Force auto-sizing
pXData->bItemAutoSizeH = false;
pXData->nItemW = XLISTBOX_SIZE_AUTO; // Auto-sized
pXData->nItemH = 30;
pXData->nItemGap = 2;
pXData->colGap = GSLC_COL_BLACK;
pXData->nItemCurSelLast = XLISTBOX_SEL_NONE;
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXListboxDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = &gslc_ElemXListboxTouch;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
// Set default text alignment:
// - Vertical center, left justify
sElem.eTxtAlign = GSLC_ALIGN_MID_LEFT;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
// Redraw the Listbox
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXListboxDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return false;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
//bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
int8_t nItemCurSel = pListbox->nItemCurSel;
gslc_tsRect rElemRect;
if (pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN) {
rElemRect = gslc_ExpandRect(pElem->rElem, -1, -1);
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFrameRect(pGui, pElem->rElem, pElem->colElemFrame);
}
} else {
rElemRect = pElem->rElem;
}
// If full redraw and gap is enabled:
// - Clear background with gap color, as list items
// will be overdrawn in colBg color
if (eRedraw == GSLC_REDRAW_FULL) {
if (pListbox->nItemGap > 0) {
gslc_DrawFillRect(pGui, rElemRect, pListbox->colGap);
}
}
// Determine if we need to recalculate the item sizing
if (pListbox->bNeedRecalc) {
gslc_ElemXListboxRecalcSize(pListbox, rElemRect);
}
int16_t nX0, nY0;
nX0 = rElemRect.x;
nY0 = rElemRect.y;
int8_t nRows = pListbox->nRows;
int8_t nCols = pListbox->nCols;
gslc_tsRect rItemRect;
int16_t nItemBaseX, nItemBaseY;
int16_t nItemX, nItemY;
int16_t nItemW, nItemH;
bool bItemSel;
gslc_tsColor colFill, colTxt;
// Error check
if (nCols == 0) {
return false;
}
// Determine top-left coordinate of list matrix
nItemBaseX = nX0 + pListbox->nMarginW;
nItemBaseY = nY0 + pListbox->nMarginH;
char acStr[XLISTBOX_MAX_STR+1] = "";
// Loop through the items in the list
int8_t nItemTop = pListbox->nItemTop;
int8_t nItemCnt = pListbox->nItemCnt;
int8_t nItemInd;
// Note that nItemTop is always pointing to an
// item index at the start of a row
// Determine the list indices to display in the visible window due to scrolling
int8_t nDispIndMax = (nRows * nCols);
for (int8_t nDispInd = 0; nDispInd < nDispIndMax; nDispInd++) {
// Calculate the item index based on the display index
nItemInd = nItemTop + nDispInd;
// Did we go past the end of our list?
if (nItemInd >= nItemCnt) {
break;
}
// Fetch the list item
bool bOk = gslc_ElemXListboxGetItem(pGui, pElemRef, nItemInd, acStr, XLISTBOX_MAX_STR);
if (!bOk) {
// TODO: Erorr handling
break;
}
int8_t nItemIndX, nItemIndY;
int16_t nItemOuterW, nItemOuterH;
// Convert linear count into row & column
nItemIndY = nDispInd / nCols; // Round down
nItemIndX = nDispInd % nCols;
// Calculate total spacing between items (including gap)
nItemOuterW = pListbox->nItemW + pListbox->nItemGap;
nItemOuterH = pListbox->nItemH + pListbox->nItemGap;
// Determine top-left corner of each item
nItemW = pListbox->nItemW;
nItemH = pListbox->nItemH;
nItemY = nItemBaseY + (nItemIndY * nItemOuterH);
nItemX = nItemBaseX + (nItemIndX * nItemOuterW);
// Create rect for item
rItemRect = (gslc_tsRect) { nItemX, nItemY, nItemW, nItemH };
// Is the item selected?
bItemSel = (nItemInd == nItemCurSel) ? true : false;
// Determine the color based on state
colFill = (bItemSel) ? pElem->colElemFillGlow : pElem->colElemFill;
colTxt = (bItemSel) ? pElem->colElemTextGlow : pElem->colElemText;
bool bDoRedraw = false;
if (eRedraw == GSLC_REDRAW_FULL) {
bDoRedraw = true;
} else if (eRedraw == GSLC_REDRAW_INC) {
// Redraw both the old selected item (ie. to unselect it)
// and the current selected item (ie. to select it)
if (nItemInd == pListbox->nItemCurSelLast) {
bDoRedraw = true;
} else if (nItemInd == nItemCurSel) {
bDoRedraw = true;
}
}
// Draw the list item
if (bDoRedraw) {
gslc_DrawFillRect(pGui, rItemRect, colFill);
// Set the text flags to indicate that the user has separately
// allocated memory for the text strings.
gslc_teTxtFlags eTxtFlags = GSLC_TXT_MEM_RAM | GSLC_TXT_ALLOC_EXT;
// Draw the aligned text string (by default it is GSLC_ALIGN_MID_LEFT)
gslc_DrawTxtBase(pGui, acStr, rItemRect, pElem->pTxtFont, eTxtFlags,
pElem->eTxtAlign, colTxt, colFill, pElem->nTxtMarginX, pElem->nTxtMarginY);
}
}
// Save the last selected item during redraw
pListbox->nItemCurSelLast = nItemCurSel;
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
bool gslc_ElemXListboxTouch(void* pvGui, void* pvElemRef, gslc_teTouch eTouch, int16_t nRelX, int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
gslc_tsGui* pGui = NULL;
gslc_tsElemRef* pElemRef = NULL;
// Typecast the parameters to match the GUI
pGui = (gslc_tsGui*)(pvGui);
pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return false;
//bool bGlowingOld = gslc_ElemGetGlow(pGui, pElemRef);
bool bIndexed = false;
int16_t nItemSavedSel = pListbox->nItemSavedSel;
int16_t nItemCurSelOld = pListbox->nItemCurSel;
int16_t nItemCurSel = XLISTBOX_SEL_NONE;
bool bSelTrack = false;
bool bSelSave = false;
bool bSelInItem = true;
switch (eTouch) {
case GSLC_TOUCH_DOWN_IN:
// Start glowing as must be over it
gslc_ElemSetGlow(pGui, pElemRef, true);
// User pressed inside elem: start selection
bSelTrack = true;
break;
case GSLC_TOUCH_MOVE_IN:
gslc_ElemSetGlow(pGui, pElemRef, true);
// Track changes in selection
bSelTrack = true;
break;
case GSLC_TOUCH_MOVE_OUT:
gslc_ElemSetGlow(pGui, pElemRef, false);
// User has dragged to outside elem: deselect
bSelTrack = true;
break;
case GSLC_TOUCH_UP_IN:
// End glow
gslc_ElemSetGlow(pGui, pElemRef, false);
// User released inside elem.
// Save selection.
// If selection is same as previous: toggle it
bSelTrack = true;
bSelSave = true;
break;
case GSLC_TOUCH_UP_OUT:
// End glow
gslc_ElemSetGlow(pGui, pElemRef, false);
// User released outside elem: leave selection as-is
bSelTrack = true;
bSelSave = true; // Save SEL_NONE
break;
case GSLC_TOUCH_SET_REL:
case GSLC_TOUCH_SET_ABS:
bIndexed = true;
gslc_ElemSetGlow(pGui,pElemRef,true);
// Keyboard / pin control
bSelTrack = true;
bSelSave = true;
break;
default:
return false;
break;
}
uint8_t nCols = pListbox->nCols;
uint8_t nRows = pListbox->nRows;
// If we need to update the Listbox selection, calculate the value
// and perform the update
if (bSelTrack) {
if (bIndexed) {
// The selection is changed by direct control (eg. keyboard)
// instead of touch coordinates
// FIXME: Change the following to be either absolute or relative
// value assignment instead of inc/dec. Then the user code can
// define what the magnitude and direction should be.
if (eTouch == GSLC_TOUCH_SET_REL) {
// Overload the "nRelY" parameter as an increment value
nItemCurSel = nItemCurSelOld + nRelY;
}
else if (eTouch == GSLC_TOUCH_SET_ABS) {
// Overload the "nRelY" parameter as an absolute value
nItemCurSel = nRelY;
}
gslc_ElemXListboxSetSel(pGui, pElemRef, nItemCurSel);
}
else {
// Determine which item we are tracking
int16_t nItemOuterW, nItemOuterH;
int16_t nDispR, nDispC;
int16_t nItemR, nItemC;
// Get position relative to top-left list matrix cell
nRelX -= pListbox->nMarginW;
nRelY -= pListbox->nMarginH;
// Determine spacing between matrix cells
nItemOuterW = pListbox->nItemW + pListbox->nItemGap;
nItemOuterH = pListbox->nItemH + pListbox->nItemGap;
// Determine which matrix cell we are in
nDispC = nRelX / nItemOuterW;
nDispR = nRelY / nItemOuterH;
if ((nRelX < 0) || (nRelY < 0)) {
bSelInItem = false;
}
// Determine if the selection was inside the range of displayed items
if ((nDispR < 0) || (nDispR >= nRows) || (nDispC < 0) || (nDispC >= nCols)) {
bSelInItem = false;
}
if (bSelInItem) {
// We have confirmed that the selected cell is
// within the visible display range. Now translate
// the display cell index to the absolute list cell index
// by taking into account the scroll position.
// - Note that nItemTop is always pointing to an
// item index at the start of a row
nItemC = nDispC;
nItemR = pListbox->nItemTop + nDispR;
// Now we have identified the cell within the list matrix
nItemCurSel = nItemR * nCols + nItemC;
// Confirm we haven't selected out of range
if (nItemCurSel >= pListbox->nItemCnt) {
bSelInItem = false;
}
}
if (bSelInItem) {
// Now check for touch in gap region
// - First find offset from top-left of matrix cell
nRelX = nRelX % nItemOuterW;
nRelY = nRelY % nItemOuterH;
// If we are in the gap region, disable
if (nRelX > pListbox->nItemW) {
bSelInItem = false;
}
if (nRelY > pListbox->nItemH) {
bSelInItem = false;
}
}
// If we determined that the coordinate was not inside an
// element, then clear current selection
if (!bSelInItem) {
nItemCurSel = XLISTBOX_SEL_NONE;
}
// If we have committed a touch press within an item
// that was already selected, then toggle (deselect it)
if ((bSelSave) && (nItemCurSel == nItemSavedSel)) {
nItemCurSel = XLISTBOX_SEL_NONE;
}
bool bDoRedraw = false;
if (nItemCurSel != nItemCurSelOld) {
// Selection changed, so we will redraw
bDoRedraw = true;
}
if (bDoRedraw) {
// Update the selection
gslc_ElemXListboxSetSel(pGui, pElemRef, nItemCurSel);
// If any selection callback is defined, call it now
if (pListbox->pfuncXSel != NULL) {
(*pListbox->pfuncXSel)((void*)(pGui), (void*)(pElemRef), nItemCurSel);
}
// Redraw the element
// - Note that ElemXListboxSetSel() above will also request redraw
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_INC);
}
// Update the saved selection
if (bSelSave) {
pListbox->nItemSavedSel = nItemCurSel;
}
} // bIndexed
} // bSelTrack
return true;
#endif // DRV_TOUCH_NONE
}
int16_t gslc_ElemXListboxGetSel(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return XLISTBOX_SEL_NONE;
return pListbox->nItemCurSel;
}
bool gslc_ElemXListboxSetSel(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef, int16_t nItemCurSel)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return false;
bool bOk = false;
if (nItemCurSel == XLISTBOX_SEL_NONE) { bOk = true; }
if ((nItemCurSel >= 0) && (nItemCurSel < pListbox->nItemCnt)) { bOk = true; }
if (bOk) {
pListbox->nItemCurSel = nItemCurSel;
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_INC);
}
return bOk;
}
bool gslc_ElemXListboxSetScrollPos(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nScrollPos)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return false;
bool bOk = false;
if ((nScrollPos >= 0) && (nScrollPos < pListbox->nItemCnt)) {
bOk = true;
}
int16_t nCols = pListbox->nCols;
int16_t nItemCnt = pListbox->nItemCnt;
// Error handling: in case position out of bounds
if (nScrollPos >= nItemCnt) {
nScrollPos = (nItemCnt > 0) ? nItemCnt - 1 : 0;
}
// Adjust the top item index to the start of its row
// - This is done because we may have multiple columns
// per row. This ensures nItemTop points to the list
// index at the start of a row.
nScrollPos = (nScrollPos / nCols) * nCols;
pListbox->nItemTop = nScrollPos;
// Need to update all rows in display
gslc_ElemSetRedraw(pGui, pElemRef, GSLC_REDRAW_FULL);
return bOk;
}
// Assign the selection callback function for a Listbox
void gslc_ElemXListboxSetSelFunc(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,GSLC_CB_XLISTBOX_SEL funcCb)
{
gslc_tsXListbox* pListbox = (gslc_tsXListbox*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_LISTBOX, __LINE__);
if (!pListbox) return;
pListbox->pfuncXSel = funcCb;
}
// ============================================================================

329
src/guislice/XListbox.h Normal file
View file

@ -0,0 +1,329 @@
#ifndef _GUISLICE_EX_XLISTBOX_H_
#define _GUISLICE_EX_XLISTBOX_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Listbox control
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XListbox.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Listbox
// - NOTE: The XListbox element is in beta development.
// Therefore, its API is subject to change.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_LISTBOX GSLC_TYPE_BASE_EXTEND + 10
// Define constants specific to the control
#define XLISTBOX_SEL_NONE -1 // Indicator for "no selection"
#define XLISTBOX_SIZE_AUTO -1 // Indicator for "auto-size"
#define XLISTBOX_BUF_OH_R 2 // Listbox buffer overhead per row
/// Callback function for Listbox feedback
typedef bool (*GSLC_CB_XLISTBOX_SEL)(void* pvGui,void* pvElem,int16_t nSel);
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Listbox element
typedef struct {
// Config
uint8_t* pBufItems; ///< Buffer containing items
uint16_t nBufItemsMax; ///< Max size of buffer containing items
uint16_t nBufItemsPos; ///< Current buffer position
int16_t nItemCnt; ///< Number of items in the list
// Style config
int8_t nCols; ///< Number of columns
int8_t nRows; ///< Number of columns (or XLSITBOX_SIZE_AUTO to calculate)
bool bNeedRecalc; ///< Determine if sizing may need recalc
int8_t nMarginW; ///< Margin inside main listbox area (X offset)
int8_t nMarginH; ///< Margin inside main listbox area (Y offset)
int16_t nItemW; ///< Width of listbox item
int16_t nItemH; ///< Height of listbox item
int8_t nItemGap; ///< Gap between listbox items
gslc_tsColor colGap; ///< Gap color
bool bItemAutoSizeW; ///< Enable auto-sizing of items (in width)
bool bItemAutoSizeH; ///< Enable auto-sizing of items (in height)
// State
int16_t nItemCurSel; ///< Currently selected item (XLISTBOX_SEL_NONE for none)
int16_t nItemCurSelLast; ///< Old selected item to redraw (XLISTBOX_SEL_NONE for none)
int16_t nItemSavedSel; ///< Persistent selected item (ie. saved selection)
int16_t nItemTop; ///< Item to show at top of list after scrolling (0 is default)
// Callbacks
GSLC_CB_XLISTBOX_SEL pfuncXSel; ///< Callback func ptr for selection update
} gslc_tsXListbox;
///
/// Create a Listbox Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining checkbox size
/// \param[in] nFontId: Font ID for item display
/// \param[in] pBufItems: Pointer to buffer that will contain list of items
/// \param[in] nBufItemsMax: Max size of buffer for list of items (pBufItems)
/// \param[in] nSelDefault: Default item to select
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXListboxCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXListbox* pXData, gslc_tsRect rElem, int16_t nFontId, uint8_t* pBufItems,
uint16_t nBufItemsMax, int16_t nSelDefault);
///
/// Configure the number of rows & columns to display in the listbox
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
/// \param[in] nRows: Number of rows (>= 1, or XLISTBOX_SIZE_AUTO to base on content)
/// \param[in] nCols: Number of columns (>= 1)
///
/// \return none
///
void gslc_ElemXListboxSetSize(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int8_t nRows, int8_t nCols);
///
/// Configure the margin inside the listbox
/// - Defines the region bewteen the element rect and the inner listbox items
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
/// \param[in] nMarginW: Set the margin (horizontal) inside the listbox (0 for none)
/// \param[in] nMarginH: Set the margin (horizontal) inside the listbox (0 for none)
///
/// \return none
///
void gslc_ElemXListboxSetMargin(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int8_t nMarginW, int8_t nMarginH);
///
/// Configure the size of the listbox items
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
/// \param[in] nItemW: Set the width of a listbox item (or -1 to auto-size)
/// \param[in] nItemH: Set the height of a listbox item
///
/// \return none
///
void gslc_ElemXListboxItemsSetSize(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nItemW, int16_t nItemH);
///
/// Configure the gap between listbox items
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
/// \param[in] nGap: Set the gap between listbox items (0 for none)
/// \param[in] colGap: Set the color of the gap between listbox items
///
/// \return none
///
void gslc_ElemXListboxItemsSetGap(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int8_t nGap, gslc_tsColor colGap);
///
/// Empty the listbox of all items
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
///
/// \return none
///
void gslc_ElemXListboxReset(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef);
///
/// Add an item to the listbox
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
/// \param[in] pStrItem: String to use when creating the listbox item
///
/// \return true if OK, false if fail (eg. insufficient buffer storage)
///
bool gslc_ElemXListboxAddItem(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, const char* pStrItem);
///
/// Insert an item in the listbox at a specific position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
/// \param[in] nInsertPos: Insertion position
/// \param[in] pStrItem: String to use when creating the listbox item
///
/// \return true if OK, false if fail (eg. insufficient buffer storage)
///
bool gslc_ElemXListboxInsertItemAt(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nInsertPos,
const char* pStrItem);
///
/// Insert an item in the listbox at a specific position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
/// \param[in] nDeletePos: Position to delete
///
/// \return true if OK, false if fail
///
bool gslc_ElemXListboxDeleteItemAt(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nDeletePos);
///
/// Get the indexed listbox item
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
/// \param[in] nItemCurSel: Item index to fetch
/// \param[out] pStrItem: Ptr to the string buffer to receive the item
/// \param[in] nStrItemLen: Maximum buffer length of pStrItem
///
/// \return true if success, false if fail (eg. can't locate item)
///
bool gslc_ElemXListboxGetItem(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nItemCurSel,
char* pStrItem, uint8_t nStrItemLen);
///
/// Get the number of items in the listbox
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Ptr to Element Reference to update
///
/// \return Number of items
///
int16_t gslc_ElemXListboxGetItemCnt(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef);
///
/// Draw a Listbox element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXListboxDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Handle touch events to Listbox element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXListboxTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY);
///
/// Get a Listbox element's current selection
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return Current Listbox selection (or -1 if none)
///
int16_t gslc_ElemXListboxGetSel(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Set a Listbox element's current selection
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nItemCurSel: Listbox item to select (or -1 for none)
///
/// \return true if success, false if fail
///
bool gslc_ElemXListboxSetSel(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nItemCurSel);
///
/// Set the Listbox scroll position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nScrollPos: Scroll the listbox so that the nScrollPos item is at the top (0 default)
///
/// \return true if success, false if fail
///
bool gslc_ElemXListboxSetScrollPos(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nScrollPos);
///
/// Assign the selection callback function for a Listbox
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] funcCb: Function pointer to selection routine (or NULL for none)
///
/// \return none
///
void gslc_ElemXListboxSetSelFunc(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,GSLC_CB_XLISTBOX_SEL funcCb);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XLISTBOX_H_

365
src/guislice/XProgress.c Normal file
View file

@ -0,0 +1,365 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XProgress.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XProgress.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Progress Bar
// - Basic progress bar with support for vertical / horizontal orientation and
// fill direction. Also provides an indicator of negative regions, depending
// on the configured range.
// ============================================================================
// Create a gauge element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw but does not track touch/click
gslc_tsElemRef* gslc_ElemXProgressCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXProgress* pXData,gslc_tsRect rElem,
int16_t nMin,int16_t nMax,int16_t nVal,gslc_tsColor colGauge,bool bVert)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXProgressCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_PROGRESS,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_CLICK_EN; // Element is not "clickable"
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN;
sElem.nGroup = GSLC_GROUP_ID_NONE;
pXData->nMin = nMin;
pXData->nMax = nMax;
pXData->nVal = nVal;
pXData->bVert = bVert;
pXData->bFlip = false;
pXData->colGauge = colGauge;
sElem.pXData = (void*)(pXData);
sElem.pfuncXDraw = &gslc_ElemXProgressDraw;
sElem.pfuncXTouch = NULL; // No need to track touches
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_GRAY;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
// Update the gauge control's current position
void gslc_ElemXProgressSetVal(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXProgressSetVal";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_PROGRESS,__LINE__);
// Update the data element
int16_t nValOld = pGauge->nVal;
pGauge->nVal = nVal;
// Element needs redraw
if (nVal != nValOld) {
// We only need an incremental redraw
// NOTE: If the user configures the indicator to be
// long enough that it overlaps some of the gauge indicators
// then a full redraw should be done instead.
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Update the gauge's fill direction
// - Setting bFlip causes the gauge to be filled in the reverse direction
// to the default
// - Default fill direction for horizontal gauges: left-to-right
// - Default fill direction for vertical gauges: bottom-to-top
void gslc_ElemXProgressSetFlip(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bFlip)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXProgressSetFlip";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
// Fetch the element's extended data structure
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_PROGRESS,__LINE__);
pGauge->bFlip = bFlip;
// Mark for redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// Redraw the gauge
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXProgressDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXProgressDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
// Fetch the element's extended data structure
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_PROGRESS,__LINE__);
gslc_ElemXProgressDrawHelp(pGui,pElemRef,eRedraw);
// Save as "last state" to support incremental erase/redraw
pGauge->nValLast = pGauge->nVal;
pGauge->bValLastValid = true;
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
return true;
}
bool gslc_ElemXProgressDrawHelp(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw)
{
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXProgress* pGauge = (gslc_tsXProgress*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_PROGRESS,__LINE__);
gslc_tsRect rTmp; // Temporary rect for drawing
gslc_tsRect rGauge; // Filled portion of gauge
gslc_tsRect rEmpty; // Empty portion of gauge
uint16_t nElemW,nElemH;
int16_t nElemX0,nElemY0,nElemX1,nElemY1;
int16_t nGaugeX0,nGaugeY0,nGaugeX1,nGaugeY1;
nElemX0 = pElem->rElem.x;
nElemY0 = pElem->rElem.y;
nElemX1 = pElem->rElem.x + pElem->rElem.w - 1;
nElemY1 = pElem->rElem.y + pElem->rElem.h - 1;
nElemW = pElem->rElem.w;
nElemH = pElem->rElem.h;
bool bVert = pGauge->bVert;
bool bFlip = pGauge->bFlip;
int16_t nMax = pGauge->nMax;
int16_t nMin = pGauge->nMin;
int16_t nRng = pGauge->nMax - pGauge->nMin;
uint32_t nScl = 1;
int16_t nGaugeMid = 0;
int16_t nLen = 0;
int16_t nTmp = 0;
int32_t nTmpL = 0;
if (nRng == 0) {
GSLC_DEBUG2_PRINT("ERROR: ElemXProgressDraw() Zero gauge range [%d,%d]\n",nMin,nMax);
return false;
}
if (bVert) {
nScl = nElemH*32768/nRng;
} else {
nScl = nElemW*32768/nRng;
}
// Calculate the control midpoint/zeropoint (for display purposes)
nTmpL = -((int32_t)nMin * (int32_t)nScl / 32768);
nGaugeMid = (int16_t)nTmpL;
// Calculate the length of the bar
// - Use long mult/divide to avoid need for floating point
nTmpL = (int32_t)(pGauge->nVal) * (int32_t)(nScl) / 32768;
nLen = (int16_t)(nTmpL);
// Define the gauge's fill rectangle region
// depending on the orientation (bVert) and whether
// the current position is negative or positive.
if (nLen >= 0) {
if (bVert) {
nGaugeY0 = nElemY0 + nGaugeMid;
nGaugeY1 = nElemY0 + nGaugeMid + nLen;
} else {
nGaugeX0 = nElemX0 + nGaugeMid;
nGaugeX1 = nElemX0 + nGaugeMid + nLen;
}
} else {
if (bVert) {
nGaugeY0 = nElemY0 + nGaugeMid + nLen;
nGaugeY1 = nElemY0 + nGaugeMid;
} else {
nGaugeX0 = nElemX0 + nGaugeMid + nLen;
nGaugeX1 = nElemX0 + nGaugeMid;
}
}
if (bVert) {
nGaugeX0 = nElemX0;
nGaugeX1 = nElemX1;
} else {
nGaugeY0 = nElemY0;
nGaugeY1 = nElemY1;
}
// Clip the region
nGaugeX0 = (nGaugeX0 < nElemX0)? nElemX0 : nGaugeX0;
nGaugeY0 = (nGaugeY0 < nElemY0)? nElemY0 : nGaugeY0;
nGaugeX1 = (nGaugeX1 > nElemX1)? nElemX1 : nGaugeX1;
nGaugeY1 = (nGaugeY1 > nElemY1)? nElemY1 : nGaugeY1;
// Support flipping of gauge directionality
// - The bFlip flag reverses the fill direction
// - Vertical gauges are flipped by default
if (bVert && !bFlip) {
nTmp = nElemY0+(nElemY1-nGaugeY1); // nTmp will be swapped into nGaugeY0
nGaugeY1 = nElemY1-(nGaugeY0-nElemY0);
nGaugeY0 = nTmp;
nGaugeMid = nElemH-nGaugeMid-1;
} else if (!bVert && bFlip) {
nTmp = nElemX0+(nElemX1-nGaugeX1); // nTmp will be swapped into nGaugeX0
nGaugeX1 = nElemX1-(nGaugeX0-nElemX0);
nGaugeX0 = nTmp;
nGaugeMid = nElemW-nGaugeMid-1;
}
#ifdef DBG_LOG
//printf("Gauge: nMin=%4d nMax=%4d nRng=%d nVal=%4d fScl=%6.3f nGaugeMid=%4d RectX=%4d RectW=%4d\n",
// nMin,nMax,nRng,pGauge->nGaugeVal,fScl,nGaugeMid,rGauge.x,rGauge.w);
#endif
// Draw a frame around the gauge
// - Only draw this during full redraw
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFrameRect(pGui, pElem->rElem, pElem->colElemFrame);
}
// To avoid flicker, we only erase the portion of the gauge
// that isn't "filled". Determine the gauge empty region and erase it
// There are two empty regions (one in negative and one in positive)
int16_t nEmptyPos;
if (bVert) {
// Empty Region #1 (negative)
nEmptyPos = (nGaugeY0 > nElemY1) ? nElemY1 : nGaugeY0;
rEmpty = (gslc_tsRect){nElemX0,nElemY0,nElemX1-nElemX0+1,nEmptyPos-nElemY0+1};
rTmp = gslc_ExpandRect(rEmpty,-1,-1);
gslc_DrawFillRect(pGui,rTmp,pElem->colElemFill);
// Empty Region #2 (positive)
nEmptyPos = (nGaugeY1 < nElemY0) ? nElemY0 : nGaugeY1;
rEmpty = (gslc_tsRect){nElemX0,nEmptyPos,nElemX1-nElemX0+1,nElemY1-nEmptyPos+1};
rTmp = gslc_ExpandRect(rEmpty,-1,-1);
gslc_DrawFillRect(pGui,rTmp,pElem->colElemFill);
} else {
// Empty Region #1 (negative)
nEmptyPos = (nGaugeX0 > nElemX1) ? nElemX1 : nGaugeX0;
rEmpty = (gslc_tsRect){nElemX0,nElemY0,nEmptyPos-nElemX0+1,nElemY1-nElemY0+1};
rTmp = gslc_ExpandRect(rEmpty,-1,-1);
gslc_DrawFillRect(pGui, rTmp, pElem->colElemFill);
// Empty Region #2 (positive)
nEmptyPos = (nGaugeX1 < nElemX0) ? nElemX0 : nGaugeX1;
rEmpty = (gslc_tsRect){nEmptyPos,nElemY0,nElemX1-nEmptyPos+1,nElemY1-nElemY0+1};
rTmp = gslc_ExpandRect(rEmpty,-1,-1);
gslc_DrawFillRect(pGui, rTmp, pElem->colElemFill);
}
// Draw the gauge fill region
rGauge = (gslc_tsRect){nGaugeX0,nGaugeY0,nGaugeX1-nGaugeX0+1,nGaugeY1-nGaugeY0+1};
rTmp = gslc_ExpandRect(rGauge,-1,-1);
gslc_DrawFillRect(pGui,rTmp,pGauge->colGauge);
// Draw the midpoint line
if (bVert) {
if (nElemY0 + nGaugeMid < nElemY1) {
gslc_DrawLine(pGui, nElemX0, nElemY0 + nGaugeMid, nElemX1, nElemY0 + nGaugeMid, pElem->colElemFrame);
}
} else {
if (nElemX0 + nGaugeMid < nElemX1) {
gslc_DrawLine(pGui, nElemX0 + nGaugeMid, nElemY0, nElemX0 + nGaugeMid, nElemY1, pElem->colElemFrame);
}
}
return true;
}
// ============================================================================

287
src/guislice/XProgress.h Normal file
View file

@ -0,0 +1,287 @@
#ifndef _GUISLICE_EX_XPROGRESS_H_
#define _GUISLICE_EX_XPROGRESS_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Progress Bar
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XProgress.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Progress Bar
// - Basic progress bar with support for vertical / horizontal orientation and
// fill direction. Also provides an indicator of negative regions, depending
// on the configured range.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_PROGRESS GSLC_TYPE_BASE_EXTEND + 60
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Gauge element
typedef struct {
// Range config
int16_t nMin; ///< Minimum control value
int16_t nMax; ///< Maximum control value
// Current value
int16_t nVal; ///< Current control value
// Previous value
int16_t nValLast; ///< Last value
bool bValLastValid; ///< Last value valid?
// Appearance config
gslc_tsColor colGauge; ///< Color of gauge fill bar
bool bVert; ///< Vertical if true, else Horizontal
bool bFlip; ///< Reverse direction of gauge
} gslc_tsXProgress;
///
/// Create a Progress Bar Element
/// - Draws a gauge element that represents a proportion (nVal)
/// between nMin and nMax.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining gauge size
/// \param[in] nMin: Minimum value of gauge for nVal comparison
/// \param[in] nMax: Maximum value of gauge for nVal comparison
/// \param[in] nVal: Starting value of gauge
/// \param[in] colGauge: Color for the gauge indicator
/// \param[in] bVert: Flag to indicate vertical vs horizontal action
/// (true = vertical, false = horizontal)
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXProgressCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXProgress* pXData,gslc_tsRect rElem,int16_t nMin,int16_t nMax,int16_t nVal,gslc_tsColor colGauge,bool bVert);
///
/// Update a Gauge element's current value
/// - Note that min & max values are assigned in create()
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nVal: New value to show in gauge
///
/// \return none
///
void gslc_ElemXProgressSetVal(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal);
///
/// Set a Gauge element's fill direction
/// - Setting bFlip reverses the default fill direction
/// - Default fill direction for horizontal gauges: left-to-right
/// - Default fill direction for vertical gauges: bottom-to-top
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] bFlip: If set, reverse direction of fill from default
///
/// \return none
///
void gslc_ElemXProgressSetFlip(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bFlip);
///
/// Draw a gauge element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXProgressDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Helper function to draw a gauge with style: progress bar
/// - Called from gslc_ElemXProgressDraw()
///
/// \param[in] pGui: Ptr to GUI
/// \param[in] pElemRef: Ptr to Element reference
/// \param[in] eRedraw: Redraw status
///
/// \return true if success, false otherwise
///
bool gslc_ElemXProgressDrawHelp(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
/// \def gslc_ElemXProgressCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,
/// nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_,bVert_)
///
/// Create a Gauge Element in Flash
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Unique element ID to assign
/// \param[in] nPage: Page ID to attach element to
/// \param[in] nX: X coordinate of element
/// \param[in] nY: Y coordinate of element
/// \param[in] nW: Width of element
/// \param[in] nH: Height of element
/// \param[in] nMin_: Minimum value of gauge for nVal comparison
/// \param[in] nMax_: Maximum value of gauge for nVal comparison
/// \param[in] nVal_: Starting value of gauge
/// \param[in] colFrame_: Color for the gauge frame
/// \param[in] colFill_: Color for the gauge background fill
/// \param[in] colGauge_: Color for the gauge indicator
/// \param[in] bVert_: Flag to indicate vertical vs horizontal action
/// (true = vertical, false = horizontal)
///
/// \return none
///
#if (GSLC_USE_PROGMEM)
#define gslc_ElemXProgressCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,\
nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_,bVert_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXProgress sGauge##nElemId; \
sGauge##nElemId.nMin = nMin_; \
sGauge##nElemId.nMax = nMax_; \
sGauge##nElemId.nVal = nVal_; \
sGauge##nElemId.nValLast = nVal_; \
sGauge##nElemId.bValLastValid = false; \
sGauge##nElemId.colGauge = colGauge_; \
sGauge##nElemId.bVert = bVert_; \
sGauge##nElemId.bFlip = false; \
static const gslc_tsElem sElem##nElemId PROGMEM = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_PROGRESS, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sGauge##nElemId), \
NULL, \
&gslc_ElemXProgressDraw, \
NULL, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#else
#define gslc_ElemXProgressCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,\
nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_,bVert_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXProgress sGauge##nElemId; \
sGauge##nElemId.nMin = nMin_; \
sGauge##nElemId.nMax = nMax_; \
sGauge##nElemId.nVal = nVal_; \
sGauge##nElemId.nValLast = nVal_; \
sGauge##nElemId.bValLastValid = false; \
sGauge##nElemId.colGauge = colGauge_; \
sGauge##nElemId.bVert = bVert_; \
sGauge##nElemId.bFlip = false; \
static const gslc_tsElem sElem##nElemId = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_PROGRESS, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sGauge##nElemId), \
NULL, \
&gslc_ElemXProgressDraw, \
NULL, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_CONST | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XPROGRESS_H_

363
src/guislice/XRadial.c Normal file
View file

@ -0,0 +1,363 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XRadial.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XRadial.h"
#include <stdio.h>
#include <math.h> // For sin/cos
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Radial Gauge
// - A circular gauge that can be used to show direction or other
// rotational values. Tick marks can be optionally drawn
// around the gauge.
// - Size, color and fill of the needle can be configured.
// ============================================================================
// Create a radial gauge element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw but does not track touch/click
gslc_tsElemRef* gslc_ElemXRadialCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXRadial* pXData,gslc_tsRect rElem,
int16_t nMin,int16_t nMax,int16_t nVal,gslc_tsColor colGauge)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRadialCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_RADIAL,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_CLICK_EN; // Element is not "clickable"
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN;
sElem.nGroup = GSLC_GROUP_ID_NONE;
pXData->nMin = nMin;
pXData->nMax = nMax;
pXData->nVal = nVal;
pXData->bFlip = false;
pXData->colGauge = colGauge;
pXData->colTick = GSLC_COL_GRAY;
pXData->nTickCnt = 8;
pXData->nTickLen = 5;
pXData->nIndicLen = 10; // Dummy default to be overridden
pXData->nIndicTip = 3; // Dummy default to be overridden
pXData->bIndicFill = false;
sElem.pXData = (void*)(pXData);
sElem.pfuncXDraw = &gslc_ElemXRadialDraw;
sElem.pfuncXTouch = NULL; // No need to track touches
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_GRAY;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
void gslc_ElemXRadialSetIndicator(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor colGauge,
uint16_t nIndicLen,uint16_t nIndicTip,bool bIndicFill)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRadialSetIndicator";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsXRadial* pGauge = (gslc_tsXRadial*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RADIAL,__LINE__);
// Update the config
pGauge->colGauge = colGauge;
pGauge->nIndicLen = nIndicLen;
pGauge->nIndicTip = nIndicTip;
pGauge->bIndicFill = bIndicFill;
// Just in case we were called at runtime, mark as needing redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXRadialSetTicks(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor colTick,uint16_t nTickCnt,uint16_t nTickLen)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRadialSetTicks";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsXRadial* pGauge = (gslc_tsXRadial*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RADIAL,__LINE__);
// Update the config
pGauge->colTick = colTick;
pGauge->nTickCnt = nTickCnt;
pGauge->nTickLen = nTickLen;
// Just in case we were called at runtime, mark as needing redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// Update the gauge control's current position
void gslc_ElemXRadialSetVal(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRadialSetVal";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsXRadial* pGauge = (gslc_tsXRadial*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RADIAL,__LINE__);
// Update the data element
int16_t nValOld = pGauge->nVal;
pGauge->nVal = nVal;
// Element needs redraw
if (nVal != nValOld) {
// We only need an incremental redraw
// NOTE: If the user configures the indicator to be
// long enough that it overlaps some of the gauge indicators
// then a full redraw should be done instead.
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Update the gauge's rotation direction
// - Setting bFlip reverses the rotation direction
// - Default rotation is clockwise. When bFlip is set, uses counter-clockwise
void gslc_ElemXRadialSetFlip(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bFlip)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRadialSetFlip";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
// Fetch the element's extended data structure
gslc_tsXRadial* pGauge = (gslc_tsXRadial*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RADIAL,__LINE__);
if (pGauge == NULL) {
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXRadialSetFlip(%s) pXData is NULL\n","");
return;
}
pGauge->bFlip = bFlip;
// Mark for redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// Redraw the gauge
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXRadialDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRadialDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
// Fetch the element's extended data structure
gslc_tsXRadial* pGauge = (gslc_tsXRadial*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RADIAL,__LINE__);
gslc_ElemXRadialDrawRadial(pGui,pElemRef,eRedraw);
// Save as "last state" to support incremental erase/redraw
pGauge->nValLast = pGauge->nVal;
pGauge->bValLastValid = true;
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
return true;
}
void gslc_ElemXRadialDrawRadialHelp(gslc_tsGui* pGui,int16_t nX,int16_t nY,uint16_t nArrowLen,uint16_t nArrowSz,int16_t n64Ang,bool bFill,gslc_tsColor colFrame)
{
int16_t nTipX,nTipY;
int16_t nBaseX1,nBaseY1,nBaseX2,nBaseY2;
int16_t nTipBaseX,nTipBaseY;
gslc_PolarToXY(nArrowLen,n64Ang,&nTipX,&nTipY);
gslc_PolarToXY(nArrowLen-nArrowSz,n64Ang,&nTipBaseX,&nTipBaseY);
gslc_PolarToXY(nArrowSz,n64Ang-90*64,&nBaseX1,&nBaseY1);
gslc_PolarToXY(nArrowSz,n64Ang+90*64,&nBaseX2,&nBaseY2);
// FIXME: There appears to be a wrapping bug in the trigonometry
// calculations associated with the bottom-right corner
// of the pointer body when angles approach 359 degrees.
if (!bFill) {
// Framed
gslc_DrawLine(pGui,nX+nBaseX1,nY+nBaseY1,nX+nBaseX1+nTipBaseX,nY+nBaseY1+nTipBaseY,colFrame);
gslc_DrawLine(pGui,nX+nBaseX2,nY+nBaseY2,nX+nBaseX2+nTipBaseX,nY+nBaseY2+nTipBaseY,colFrame);
gslc_DrawLine(pGui,nX+nBaseX1+nTipBaseX,nY+nBaseY1+nTipBaseY,nX+nTipX,nY+nTipY,colFrame);
gslc_DrawLine(pGui,nX+nBaseX2+nTipBaseX,nY+nBaseY2+nTipBaseY,nX+nTipX,nY+nTipY,colFrame);
gslc_DrawLine(pGui,nX+nBaseX1,nY+nBaseY1,nX+nBaseX2,nY+nBaseY2,colFrame);
} else {
// Filled
gslc_tsPt asPt[4];
// Main body of pointer
asPt[0] = (gslc_tsPt){nX+nBaseX1,nY+nBaseY1};
asPt[1] = (gslc_tsPt){nX+nBaseX1+nTipBaseX,nY+nBaseY1+nTipBaseY};
asPt[2] = (gslc_tsPt){nX+nBaseX2+nTipBaseX,nY+nBaseY2+nTipBaseY};
asPt[3] = (gslc_tsPt){nX+nBaseX2,nY+nBaseY2};
gslc_DrawFillQuad(pGui,asPt,colFrame);
// Tip of pointer
asPt[0] = (gslc_tsPt){nX+nBaseX1+nTipBaseX,nY+nBaseY1+nTipBaseY};
asPt[1] = (gslc_tsPt){nX+nTipX,nY+nTipY};
asPt[2] = (gslc_tsPt){nX+nBaseX2+nTipBaseX,nY+nBaseY2+nTipBaseY};
gslc_DrawFillTriangle(pGui,asPt[0].x,asPt[0].y,asPt[1].x,asPt[1].y,asPt[2].x,asPt[2].y,colFrame);
}
}
bool gslc_ElemXRadialDrawRadial(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw)
{
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXRadial* pGauge = (gslc_tsXRadial*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RADIAL,__LINE__);
uint16_t nElemW,nElemH,nElemRad;
int16_t nElemX0,nElemY0,nElemX1,nElemY1;
int16_t nElemMidX,nElemMidY;
nElemX0 = pElem->rElem.x;
nElemY0 = pElem->rElem.y;
nElemX1 = pElem->rElem.x + pElem->rElem.w - 1;
nElemY1 = pElem->rElem.y + pElem->rElem.h - 1;
nElemMidX = (nElemX0+nElemX1)/2;
nElemMidY = (nElemY0+nElemY1)/2;
nElemW = pElem->rElem.w;
nElemH = pElem->rElem.h;
nElemRad = (nElemW>=nElemH)? nElemH/2 : nElemW/2;
int16_t nMax = pGauge->nMax;
int16_t nMin = pGauge->nMin;
int16_t nRng = pGauge->nMax - pGauge->nMin;
int16_t nVal = pGauge->nVal;
int16_t nValLast = pGauge->nValLast;
bool bValLastValid = pGauge->bValLastValid;
uint16_t nTickLen = pGauge->nTickLen;
uint16_t nTickAng = 360 / pGauge->nTickCnt;
uint16_t nArrowLen = pGauge->nIndicLen;
uint16_t nArrowSize = pGauge->nIndicTip;
bool bFill = pGauge->bIndicFill;
int16_t n64Ang,n64AngLast;
int16_t nInd;
if (nRng == 0) {
GSLC_DEBUG2_PRINT("ERROR: ElemXRadialDraw() Zero range [%d,%d]\n",nMin,nMax);
return false;
}
// Support reversing of direction
// TODO: Clean up excess integer typecasting
if (pGauge->bFlip) {
n64Ang = (int32_t)(nMax - nVal )* 360*64 /nRng;
n64AngLast = (int32_t)(nMax - nValLast)* 360*64 /nRng;
} else {
n64Ang = (int32_t)(nVal - nMin)* 360*64 /nRng;
n64AngLast = (int32_t)(nValLast - nMin)* 360*64 /nRng;
}
// Clear old
if (bValLastValid) {
gslc_ElemXRadialDrawRadialHelp(pGui,nElemMidX,nElemMidY,nArrowLen,nArrowSize,n64AngLast,bFill,pElem->colElemFill);
}
// Draw frame
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFillCircle(pGui,nElemMidX,nElemMidY,nElemRad,pElem->colElemFill); // Erase first
gslc_DrawFrameCircle(pGui,nElemMidX,nElemMidY,nElemRad,pElem->colElemFrame);
for (nInd=0;nInd<360;nInd+=nTickAng) {
gslc_DrawLinePolar(pGui,nElemMidX,nElemMidY,nElemRad-nTickLen,nElemRad,nInd*64,pGauge->colTick);
}
}
// Draw pointer
gslc_ElemXRadialDrawRadialHelp(pGui,nElemMidX,nElemMidY,nArrowLen,nArrowSize,n64Ang,bFill,pGauge->colGauge);
return true;
}
// ============================================================================

327
src/guislice/XRadial.h Normal file
View file

@ -0,0 +1,327 @@
#ifndef _GUISLICE_EX_XRADIAL_H_
#define _GUISLICE_EX_XRADIAL_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Radial control
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XRadial.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Radial Gauge
// - A circular gauge that can be used to show direction or other
// rotational values. Tick marks can be optionally drawn
// around the gauge.
// - Size, color and fill of the needle can be configured.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_RADIAL GSLC_TYPE_BASE_EXTEND + 61
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Gauge element
typedef struct {
// Range config
int16_t nMin; ///< Minimum control value
int16_t nMax; ///< Maximum control value
// Current value
int16_t nVal; ///< Current control value
// Previous value
int16_t nValLast; ///< Last value
bool bValLastValid; ///< Last value valid?
// Appearance config
gslc_tsColor colGauge; ///< Color of gauge fill bar
gslc_tsColor colTick; ///< Color of gauge tick marks
uint16_t nTickCnt; ///< Number of gauge tick marks
uint16_t nTickLen; ///< Length of gauge tick marks
bool bFlip; ///< Reverse direction of gauge
uint16_t nIndicLen; ///< Indicator length
uint16_t nIndicTip; ///< Size of tip at end of indicator
bool bIndicFill; ///< Fill the indicator if true
} gslc_tsXRadial;
///
/// Create a Radial Gauge Element
/// - Draws a gauge element that represents a proportion (nVal)
/// between nMin and nMax.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining gauge size
/// \param[in] nMin: Minimum value of gauge for nVal comparison
/// \param[in] nMax: Maximum value of gauge for nVal comparison
/// \param[in] nVal: Starting value of gauge
/// \param[in] colGauge: Color for the gauge indicator
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXRadialCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXRadial* pXData,gslc_tsRect rElem,int16_t nMin,int16_t nMax,int16_t nVal,gslc_tsColor colGauge);
///
/// Configure the appearance of the Gauge indicator
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] colGauge: Color of the indicator
/// \param[in] nIndicLen: Length of the indicator
/// \param[in] nIndicTip: Size of the indicator tip
/// \param[in] bIndicFill: Fill in the indicator if true
///
/// \return none
///
void gslc_ElemXRadialSetIndicator(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor colGauge,
uint16_t nIndicLen,uint16_t nIndicTip,bool bIndicFill);
///
/// Configure the appearance of the Gauge ticks
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] colTick: Color of the gauge ticks
/// \param[in] nTickCnt: Number of ticks to draw around / along gauge
/// \param[in] nTickLen: Length of the tick marks to draw
///
/// \return none
///
void gslc_ElemXRadialSetTicks(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor colTick,uint16_t nTickCnt,uint16_t nTickLen);
///
/// Update a Gauge element's current value
/// - Note that min & max values are assigned in create()
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nVal: New value to show in gauge
///
/// \return none
///
void gslc_ElemXRadialSetVal(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal);
///
/// Set a Gauge element's rotation direction
/// - Setting bFlip reverses the rotation direction
/// - Default rotation is clockwise. When bFlip is set, uses counter-clockwise
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] bFlip: If set, reverse direction of rotation from default
///
/// \return none
///
void gslc_ElemXRadialSetFlip(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bFlip);
///
/// Draw a gauge element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXRadialDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Helper function to draw a gauge with style: radial
/// - Called from gslc_ElemXRadialDraw()
///
/// \param[in] pGui: Ptr to GUI
/// \param[in] pElemRef: Ptr to Element reference
/// \param[in] eRedraw: Redraw status
///
/// \return true if success, false otherwise
///
bool gslc_ElemXRadialDrawRadial(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
/// \def gslc_ElemXRadialCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,
/// nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_)
///
/// Create a Gauge Element in Flash
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Unique element ID to assign
/// \param[in] nPage: Page ID to attach element to
/// \param[in] nX: X coordinate of element
/// \param[in] nY: Y coordinate of element
/// \param[in] nW: Width of element
/// \param[in] nH: Height of element
/// \param[in] nMin_: Minimum value of gauge for nVal comparison
/// \param[in] nMax_: Maximum value of gauge for nVal comparison
/// \param[in] nVal_: Starting value of gauge
/// \param[in] colFrame_: Color for the gauge frame
/// \param[in] colFill_: Color for the gauge background fill
/// \param[in] colGauge_: Color for the gauge indicator
///
/// \return none
///
#if (GSLC_USE_PROGMEM)
#define gslc_ElemXRadialCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,\
nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXRadial sGauge##nElemId; \
sGauge##nElemId.nMin = nMin_; \
sGauge##nElemId.nMax = nMax_; \
sGauge##nElemId.nVal = nVal_; \
sGauge##nElemId.nValLast = nVal_; \
sGauge##nElemId.bValLastValid = false; \
sGauge##nElemId.colGauge = colGauge_; \
sGauge##nElemId.colTick = GSLC_COL_GRAY; \
sGauge##nElemId.nTickCnt = 8; \
sGauge##nElemId.nTickLen = 5; \
sGauge##nElemId.bFlip = false; \
sGauge##nElemId.nIndicLen = 10; \
sGauge##nElemId.nIndicTip = 3; \
sGauge##nElemId.bIndicFill = false; \
static const gslc_tsElem sElem##nElemId PROGMEM = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_RADIAL, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sGauge##nElemId), \
NULL, \
&gslc_ElemXRadialDraw, \
NULL, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#else
#define gslc_ElemXRadialCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,\
nMin_,nMax_,nVal_,colFrame_,colFill_,colGauge_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXRadial sGauge##nElemId; \
sGauge##nElemId.nMin = nMin_; \
sGauge##nElemId.nMax = nMax_; \
sGauge##nElemId.nVal = nVal_; \
sGauge##nElemId.nValLast = nVal_; \
sGauge##nElemId.bValLastValid = false; \
sGauge##nElemId.colGauge = colGauge_; \
sGauge##nElemId.colTick = GSLC_COL_GRAY; \
sGauge##nElemId.nTickCnt = 8; \
sGauge##nElemId.nTickLen = 5; \
sGauge##nElemId.bFlip = false; \
sGauge##nElemId.nIndicLen = 10; \
sGauge##nElemId.nIndicTip = 3; \
sGauge##nElemId.bIndicFill = false; \
static const gslc_tsElem sElem##nElemId = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_RADIAL, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sGauge##nElemId), \
NULL, \
&gslc_ElemXRadialDraw, \
NULL, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_CONST | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XRADIAL_H_

300
src/guislice/XRamp.c Normal file
View file

@ -0,0 +1,300 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XRamp.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XRamp.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Ramp Gauge
// - Demonstration of a gradient ramp (green-yellow-red) visual
// control similar to certain linear tachometers.
// - The ramp rises up and to the right according to the
// current value.
// - Note that this element is mainly intended as a demonstration
// example. Additional APIs would be recommended to make it
// more configurable.
// ============================================================================
// Create a gauge element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw but does not track touch/click
gslc_tsElemRef* gslc_ElemXRampCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXRamp* pXData,gslc_tsRect rElem,
int16_t nMin,int16_t nMax,int16_t nVal,gslc_tsColor colGauge,bool bVert)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRampCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_RAMP,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_CLICK_EN; // Element is not "clickable"
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN;
sElem.nGroup = GSLC_GROUP_ID_NONE;
pXData->nMin = nMin;
pXData->nMax = nMax;
pXData->nVal = nVal;
sElem.pXData = (void*)(pXData);
sElem.pfuncXDraw = &gslc_ElemXRampDraw;
sElem.pfuncXTouch = NULL; // No need to track touches
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_GRAY;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
// Update the gauge control's current position
void gslc_ElemXRampSetVal(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRampSetVal";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsXRamp* pGauge = (gslc_tsXRamp*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RAMP,__LINE__);
// Update the data element
int16_t nValOld = pGauge->nVal;
pGauge->nVal = nVal;
// Element needs redraw
if (nVal != nValOld) {
// We only need an incremental redraw
// NOTE: If the user configures the indicator to be
// long enough that it overlaps some of the gauge indicators
// then a full redraw should be done instead.
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Redraw the gauge
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXRampDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRampDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
// Fetch the element's extended data structure
gslc_tsXRamp* pGauge = (gslc_tsXRamp*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RAMP,__LINE__);
gslc_ElemXRampDrawHelp(pGui,pElemRef,eRedraw);
// Save as "last state" to support incremental erase/redraw
pGauge->nValLast = pGauge->nVal;
pGauge->bValLastValid = true;
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
return true;
}
bool gslc_ElemXRampDrawHelp(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw)
{
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXRamp* pGauge = (gslc_tsXRamp*)gslc_GetXDataFromRef(pGui,pElemRef,GSLC_TYPEX_RAMP,__LINE__);
uint16_t nElemW,nElemH;
int16_t nElemX0,nElemY1;
nElemX0 = pElem->rElem.x;
nElemY1 = pElem->rElem.y + pElem->rElem.h - 1;
nElemW = pElem->rElem.w;
nElemH = pElem->rElem.h;
int16_t nMax = pGauge->nMax;
int16_t nMin = pGauge->nMin;
int16_t nRng = pGauge->nMax - pGauge->nMin;
int16_t nVal = pGauge->nVal;
int16_t nValLast = pGauge->nValLast;
bool bValLastValid = pGauge->bValLastValid;
int16_t nInd;
if (nRng == 0) {
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXRampDrawHelp() Zero range [%d,%d]\n",nMin,nMax);
return false;
}
uint32_t nSclFX;
uint16_t nHeight;
int32_t nHeightTmp;
uint16_t nHeightBot;
uint16_t nX;
uint16_t nColInd;
// Calculate region to draw or clear
bool bModeErase;
int16_t nValStart;
int16_t nValEnd;
if ((eRedraw == GSLC_REDRAW_INC) && (!bValLastValid)) {
// - If the request was incremental (GSLC_REDRAW_INC) but
// the last value wasn't marked as valid (!bValLastValid)
// then we want to force a full redraw.
// - We don't expect to enter here since bValLastValid
// should always be set after we perform our first
// redraw.
eRedraw = GSLC_REDRAW_FULL;
}
if (eRedraw == GSLC_REDRAW_FULL) {
// If we haven't drawn anything before, draw full range from zero
bModeErase = false;
nValStart = 0;
nValEnd = nVal;
} else {
if (nVal >= nValLast) {
// As we are advancing the control, we just draw the new range
bModeErase = false;
nValStart = nValLast;
nValEnd = nVal;
} else {
// Since we are retracting the control, we erase the new range
bModeErase = true;
nValStart = nVal;
nValEnd = nValLast;
}
}
// Calculate the scaled gauge position
// - TODO: Also support reversing of direction
int16_t nPosXStart,nPosXEnd;
nPosXStart = (nValStart - nMin)*nElemW/nRng;
nPosXEnd = (nValEnd - nMin)*nElemW/nRng;
nSclFX = (uint32_t)nElemH*32767/(nElemW*nElemW);
for (nX=nPosXStart;nX<nPosXEnd;nX++) {
nInd = nElemW-nX;
nHeightTmp = nSclFX * nInd*nInd /32767;
nHeight = nElemH-nHeightTmp;
if (nHeight >= 20) {
nHeightBot = nHeight-20;
} else {
nHeightBot = 0;
}
gslc_tsColor nCol;
uint16_t nSteps = 10;
uint16_t nGap = 3;
if (nSteps == 0) {
nColInd = nX*1000/nElemW;
nCol = gslc_ColorBlend3(GSLC_COL_GREEN,GSLC_COL_YELLOW,GSLC_COL_RED,500,nColInd);
} else {
uint16_t nBlockLen,nSegLen,nSegInd,nSegOffset,nSegStart;
nBlockLen = (nElemW-(nSteps-1)*nGap)/nSteps;
nSegLen = nBlockLen + nGap;
nSegInd = nX/nSegLen;
nSegOffset = nX % nSegLen;
nSegStart = nSegInd * nSegLen;
if (nSegOffset <= nBlockLen) {
// Inside block
nColInd = (uint32_t)nSegStart*1000/nElemW;
nCol = gslc_ColorBlend3(GSLC_COL_GREEN,GSLC_COL_YELLOW,GSLC_COL_RED,500,nColInd);
} else {
// Inside gap
// - No draw
nCol = pElem->colElemFill;
}
}
if (bModeErase) {
nCol = pElem->colElemFill;
}
gslc_DrawLine(pGui,nElemX0+nX,nElemY1-nHeightBot,nElemX0+nX,nElemY1-nHeight,nCol);
}
return true;
}
// ============================================================================

264
src/guislice/XRamp.h Normal file
View file

@ -0,0 +1,264 @@
#ifndef _GUISLICE_EX_XRAMP_H_
#define _GUISLICE_EX_XRAMP_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Ramp gauge
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XRamp.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Ramp Gauge
// - Demonstration of a gradient ramp (green-yellow-red) visual
// control similar to certain linear tachometers.
// - The ramp rises up and to the right according to the
// current value.
// - Note that this element is mainly intended as a demonstration
// example. Additional APIs would be recommended to make it
// more configurable.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_RAMP GSLC_TYPE_BASE_EXTEND + 62
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Gauge element
typedef struct {
// Range config
int16_t nMin; ///< Minimum control value
int16_t nMax; ///< Maximum control value
// Current value
int16_t nVal; ///< Current control value
// Previous value
int16_t nValLast; ///< Last value
bool bValLastValid; ///< Last value valid?
// Appearance config
} gslc_tsXRamp;
///
/// Create a Ramp Gauge Element
/// - Draws a gauge element that represents a proportion (nVal)
/// between nMin and nMax.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining gauge size
/// \param[in] nMin: Minimum value of gauge for nVal comparison
/// \param[in] nMax: Maximum value of gauge for nVal comparison
/// \param[in] nVal: Starting value of gauge
/// \param[in] colGauge: Color for the gauge indicator
/// \param[in] bVert: Flag to indicate vertical vs horizontal action
/// (true = vertical, false = horizontal)
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXRampCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXRamp* pXData,gslc_tsRect rElem,int16_t nMin,int16_t nMax,int16_t nVal,gslc_tsColor colGauge,bool bVert);
///
/// Update a Gauge element's current value
/// - Note that min & max values are assigned in create()
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nVal: New value to show in gauge
///
/// \return none
///
void gslc_ElemXRampSetVal(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal);
///
/// Draw a gauge element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXRampDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Helper function to draw a gauge with style: ramp
/// - Called from gslc_ElemXRampDraw()
///
/// \param[in] pGui: Ptr to GUI
/// \param[in] pElemRef: Ptr to Element reference
/// \param[in] eRedraw: Redraw status
///
/// \return true if success, false otherwise
///
bool gslc_ElemXRampDrawHelp(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_teRedrawType eRedraw);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
/// \def gslc_ElemXRampCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,
/// nMin_,nMax_,nVal_,colFrame_,colFill_)
///
/// Create a Gauge Element in Flash
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Unique element ID to assign
/// \param[in] nPage: Page ID to attach element to
/// \param[in] nX: X coordinate of element
/// \param[in] nY: Y coordinate of element
/// \param[in] nW: Width of element
/// \param[in] nH: Height of element
/// \param[in] nMin_: Minimum value of gauge for nVal comparison
/// \param[in] nMax_: Maximum value of gauge for nVal comparison
/// \param[in] nVal_: Starting value of gauge
/// \param[in] colFrame_: Color for the gauge frame
/// \param[in] colFill_: Color for the gauge background fill
///
/// \return none
///
#if (GSLC_USE_PROGMEM)
#define gslc_ElemXRampCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,\
nMin_,nMax_,nVal_,colFrame_,colFill_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXRamp sGauge##nElemId; \
sGauge##nElemId.nMin = nMin_; \
sGauge##nElemId.nMax = nMax_; \
sGauge##nElemId.nVal = nVal_; \
sGauge##nElemId.nValLast = nVal_; \
sGauge##nElemId.bValLastValid = false; \
static const gslc_tsElem sElem##nElemId PROGMEM = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_RAMP, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sGauge##nElemId), \
NULL, \
&gslc_ElemXRampDraw, \
NULL, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#else
#define gslc_ElemXRampCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,\
nMin_,nMax_,nVal_,colFrame_,colFill_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXRamp sGauge##nElemId; \
sGauge##nElemId.nMin = nMin_; \
sGauge##nElemId.nMax = nMax_; \
sGauge##nElemId.nVal = nVal_; \
sGauge##nElemId.nValLast = nVal_; \
sGauge##nElemId.bValLastValid = false; \
static const gslc_tsElem sElem##nElemId = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_RAMP, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sGauge##nElemId), \
NULL, \
&gslc_ElemXRampDraw, \
NULL, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_CONST | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XRAMP_H_

413
src/guislice/XRingGauge.c Normal file
View file

@ -0,0 +1,413 @@
// =======================================================================
// GUIslice library (Ring control)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XRingGauge.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XRingGauge.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Ring gauge
// ============================================================================
// Create a text element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw and touch
gslc_tsElemRef* gslc_ElemXRingGaugeCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXRingGauge* pXData, gslc_tsRect rElem, char* pStrBuf, uint8_t nStrBufMax, int16_t nFontId)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXRingGaugeCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_RING,rElem,pStrBuf,nStrBufMax,nFontId);
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_BLUE;
sElem.colElemText = GSLC_COL_YELLOW;
sElem.nFeatures = GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN;
sElem.eTxtAlign = GSLC_ALIGN_MID_MID;
sElem.nGroup = GSLC_GROUP_ID_NONE;
// Provide default config
pXData->nValMin = 0;
pXData->nValMax = 100;
pXData->nAngStart = 0;
pXData->nAngRange = 360;
pXData->nThickness = 10;
pXData->nQuality = 72; // 360/72=5 degree segments
pXData->bGradient = false;
pXData->nSegGap = 0;
pXData->colRing1 = GSLC_COL_BLUE_LT4;
pXData->colRing2 = GSLC_COL_RED;
pXData->colRingRemain = (gslc_tsColor) { 0, 0, 48 };
pXData->nVal = 0;
pXData->nValLast = 0;
pXData->acStrLast[0] = 0;
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXRingGaugeDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = NULL;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
// Redraw the element
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXRingGaugeDraw(void* pvGui, void* pvElemRef, gslc_teRedrawType eRedraw)
{
gslc_tsGui* pGui = (gslc_tsGui*)pvGui;
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)pvElemRef;
gslc_tsElem* pElem = gslc_GetElemFromRefD(pGui, pElemRef, __LINE__);
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return false;
// --------------------------------------------------------------------------
// Init for default drawing
// --------------------------------------------------------------------------
bool bGlowEn, bGlowing, bGlowNow;
int16_t nElemX, nElemY;
uint16_t nElemW, nElemH;
nElemX = pElem->rElem.x;
nElemY = pElem->rElem.y;
nElemW = pElem->rElem.w;
nElemH = pElem->rElem.h;
bGlowEn = pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN; // Does the element support glow state?
bGlowing = gslc_ElemGetGlow(pGui, pElemRef); // Element should be glowing (if enabled)
bGlowNow = bGlowEn & bGlowing; // Element is currently glowing
int16_t nVal = pXRingGauge->nVal;
int16_t nValLast = pXRingGauge->nValLast;
int16_t nValMin = pXRingGauge->nValMin;
int16_t nValMax = pXRingGauge->nValMax;
int16_t nValRange = (nValMax == nValMin)? 1 : (nValMax - nValMin); // Guard against div/0
int16_t nAngStart = pXRingGauge->nAngStart;
int16_t nAngRange = pXRingGauge->nAngRange;
//int16_t nAngEnd = nAngStart + nAngRange;
int16_t nQuality = pXRingGauge->nQuality;
gslc_tsColor colRingActive1 = pXRingGauge->colRing1;
gslc_tsColor colRingActive2 = pXRingGauge->colRing2;
gslc_tsColor colRingInactive = pXRingGauge->colRingRemain;
bool bGradient = pXRingGauge->bGradient;
gslc_tsColor colBg = pElem->colElemFill; // Background color used for text clearance
// Calculate the ring center and radius
int16_t nMidX = nElemX + nElemW / 2;
int16_t nMidY = nElemY + nElemH / 2;
int16_t nRad2 = (nElemW < nElemH) ? nElemW / 2 : nElemH / 2;
int16_t nRad1 = nRad2 - pXRingGauge->nThickness;
// --------------------------------------------------------------------------
// Determine whether we should draw the full range (full redraw)
// or a smaller, updated region (incremental redraw)
bool bInc = (eRedraw == GSLC_REDRAW_INC) ? true : false;
int16_t nDrawStart = 0;
int16_t nDrawVal = 0;
int16_t nDrawEnd = 0;
bool bDrawActive = false;
bool bDrawInactive = false;
if (bInc) {
if (nVal > nValLast) {
// Incremental redraw: adding value, so draw with active color
bDrawActive = true;
nDrawStart = (int32_t)(nValLast - nValMin) * nAngRange / nValRange;
nDrawVal = (int32_t)(nVal - nValMin) * nAngRange / nValRange;
}
else {
// Incremental redraw: reducing value, so draw with inactive color
bDrawInactive = true;
nDrawVal = (int32_t)(nVal - nValMin) * nAngRange / nValRange;
nDrawEnd = (int32_t)(nValLast - nValMin) * nAngRange / nValRange;
}
} else {
// Full redraw: draw both active and inactive regions
bDrawActive = true;
bDrawInactive = true;
nDrawStart = 0;
nDrawVal = (int32_t)(nVal - nValMin) * nAngRange / nValRange;
nDrawEnd = nAngRange;
}
#if defined(DBG_REDRAW)
GSLC_DEBUG2_PRINT("\n\nRingDraw: Val=%d ValLast=%d ValRange=%d PosMin=%d PosMax=%d Q=%d\n", nVal, nValLast, nValRange, nValMin, nValMax, nQuality);
GSLC_DEBUG2_PRINT("RingDraw: Inc=%d ValLast=%d Val=%d Ang=%d..%d (Range=%d)\n", bInc,nValLast,nVal,nAngStart, nAngStart+nAngRange,nAngRange); //CAL!
GSLC_DEBUG2_PRINT("RingDraw: DrawActive=%d DrawInactive=%d DrawStart=%d DrawVal=%d DrawEnd=%d\n",bDrawActive,bDrawInactive,nDrawStart,nDrawVal,nDrawEnd);//CAL!
#endif
// Adjust for start of angular range
nDrawStart += nAngStart;
nDrawVal += nAngStart;
nDrawEnd += nAngStart;
if (bDrawActive) {
if (bGradient) {
#if defined(DBG_REDRAW)
GSLC_DEBUG2_PRINT("RingDraw: ActiveG start=%d end=%d astart=%d arange=%d\n", nDrawStart, nDrawVal,nAngStart,nAngRange);
#endif
gslc_DrawFillGradSector(pGui, nQuality, nMidX, nMidY,
nRad1, nRad2, colRingActive1, colRingActive2, nDrawStart, nDrawVal, nAngStart, nAngRange);
} else {
#if defined(DBG_REDRAW)
GSLC_DEBUG2_PRINT("RingDraw: Active start=%d end=%d\n", nDrawStart, nDrawVal);
#endif
gslc_DrawFillSector(pGui, nQuality, nMidX, nMidY,
nRad1, nRad2, colRingActive1, nDrawStart, nDrawVal);
}
}
if (bDrawInactive) {
#if defined(DBG_REDRAW)
GSLC_DEBUG2_PRINT("RingDraw: Inactive start=%d end=%d\n", nDrawEnd, nDrawVal);
#endif
// Since we are erasing, we will reverse the redraw direction (swap Val & End)
gslc_DrawFillSector(pGui, nQuality, nMidX, nMidY,
nRad1, nRad2, colRingInactive, nDrawEnd, nDrawVal);
}
// --------------------------------------------------------------------------
// Text overlays
// --------------------------------------------------------------------------
// Draw text string if defined
if (pElem->pStrBuf) {
gslc_tsColor colTxt = (bGlowNow)? pElem->colElemTextGlow : pElem->colElemText;
int8_t nMarginX = pElem->nTxtMarginX;
int8_t nMarginY = pElem->nTxtMarginY;
// Erase old string content using "background" color
if (strlen(pXRingGauge->acStrLast) != 0) {
gslc_DrawTxtBase(pGui, pXRingGauge->acStrLast, pElem->rElem, pElem->pTxtFont, pElem->eTxtFlags,
pElem->eTxtAlign, colBg, GSLC_COL_BLACK, nMarginX, nMarginY);
}
// Draw new string content
gslc_DrawTxtBase(pGui, pElem->pStrBuf, pElem->rElem, pElem->pTxtFont, pElem->eTxtFlags,
pElem->eTxtAlign, colTxt, GSLC_COL_BLACK, nMarginX, nMarginY);
// Save a copy of the new string content so we can support future erase
strncpy(pXRingGauge->acStrLast, pElem->pStrBuf, XRING_STR_MAX);
pXRingGauge->acStrLast[XRING_STR_MAX - 1] = 0; // Force null terminator
} // pStrBuf
// Save the position to enable future incremental calculations
pXRingGauge->nValLast = pXRingGauge->nVal;
// --------------------------------------------------------------------------
// Mark the element as no longer requiring redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
return true;
}
void gslc_ElemXRingGaugeSetVal(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nVal)
{
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return;
int16_t nValOld;
// Clip position
if (nVal < pXRingGauge->nValMin) { nVal = pXRingGauge->nValMin; }
if (nVal > pXRingGauge->nValMax) { nVal = pXRingGauge->nValMax; }
// Update
nValOld = pXRingGauge->nVal;
pXRingGauge->nVal = nVal;
// Only update if changed
if (nVal != nValOld) {
// Mark for redraw
// - Only need incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
void gslc_ElemXRingGaugeSetValRange(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nValMin, int16_t nValMax)
{
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return;
pXRingGauge->nValMin = nValMin;
pXRingGauge->nValMax = nValMax;
// Mark for full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXRingGaugeSetAngleRange(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nStart, int16_t nRange, bool bClockwise)
{
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return;
pXRingGauge->nAngStart = nStart;
pXRingGauge->nAngRange = nRange;
nRange = (nRange == 0) ? 1 : nRange; // Guard against div/0
// TODO: Support bClockwise
// Mark for full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXRingGaugeSetThickness(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int8_t nThickness)
{
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return;
pXRingGauge->nThickness = nThickness;
// Mark for full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXRingGaugeSetColorActiveFlat(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colActive)
{
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return;
pXRingGauge->bGradient = false;
pXRingGauge->colRing1 = colActive;
pXRingGauge->colRing2 = colActive;
// Mark for full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXRingGaugeSetColorActiveGradient(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colStart, gslc_tsColor colEnd)
{
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return;
pXRingGauge->bGradient = true;
pXRingGauge->colRing1 = colStart;
pXRingGauge->colRing2 = colEnd;
// Mark for full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXRingGaugeSetColorInactive(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colInactive)
{
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return;
pXRingGauge->colRingRemain = colInactive;
// Mark for full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
void gslc_ElemXRingGaugeSetQuality(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nSegments)
{
gslc_tsXRingGauge* pXRingGauge = (gslc_tsXRingGauge*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_RING, __LINE__);
if (!pXRingGauge) return;
nSegments = (nSegments == 0) ? 72 : nSegments; // Guard against div/0 with default
pXRingGauge->nQuality = nSegments;
// Mark for full redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
// ============================================================================

268
src/guislice/XRingGauge.h Normal file
View file

@ -0,0 +1,268 @@
#ifndef _GUISLICE_EX_XRING_H_
#define _GUISLICE_EX_XRING_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: XRingGauge
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XRingGauge.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: XRingGauge
// - Creates display element similar to a donut-chart.
// - The element has an outer and inner radius to create a ring appearance.
// - The ring has an angular range defined by SetAngleRange, which means that
// the ring can be configured to cover a full circle or just a portion of
// a circle.
// - SetAngleRange defines the starting angle and direction of fill.
// - When drawing the ring within the angular range, it is composed of
// an active region (the angular region from the start to the current
// position value) and an inactive region (from the current value to the
// end of the angular range). The inactive region can be hidden (by
// setting it to the background color).
// - A text value can be drawn in the center of the ring, typically to
// show the current value. The color defined by SetColorBackground() is
// used when redrawing the text.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_RING GSLC_TYPE_BASE_EXTEND + 23
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
#define XRING_STR_MAX 10
/// Extended data for XRingGauge element
typedef struct {
// Config
int16_t nValMin;
int16_t nValMax;
int16_t nAngStart;
int16_t nAngRange;
// Style config
int16_t nQuality;
int8_t nThickness;
bool bGradient;
uint8_t nSegGap;
gslc_tsColor colRing1;
gslc_tsColor colRing2;
gslc_tsColor colRingRemain;
// State
int16_t nVal; ///< Current position value
int16_t nValLast; ///< Previous position value
char acStrLast[XRING_STR_MAX];
// Callbacks
} gslc_tsXRingGauge;
///
/// Create an XRingGauge element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: The square box that bounds the ring element. If a rectangular region is
/// provided, then the ring control will be centered in the long axis.
/// \param[in] pStrBuf: String buffer to use for gauge inner text
/// \param[in] nStrBufMax: Maximum length of string buffer (pStrBuf)
/// \param[in] nFontId: Font ID to use for text display
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXRingGaugeCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXRingGauge* pXData, gslc_tsRect rElem, char* pStrBuf, uint8_t nStrBufMax, int16_t nFontId);
///
/// Draw the template element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXRingGaugeDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Set an Ring Gauge current indicator value
///
/// Updates the current value of the ring gauge. The active region will be drawn up
/// to the position defined by nVal within the value range defined by SetValRange(nMin,nMax).
/// A SetVal() close to nMin will cause a very small active region to be drawn and a large
/// remainder drawn in the inactive color, whereas a SetVal() close to nMax will cause a
/// more complete active region to be drawn.When SetVal() equals nMax, the entire angular
/// range will be drawn in the active color (and no inactive region).
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nVal: New position value
///
/// \return none
///
void gslc_ElemXRingGaugeSetVal(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nVal);
///
/// Defines the angular range of the gauge, including both the active
/// and inactive regions.
///
/// - nStart defines the angle at the beginning of the active region.
/// - The current position marks the end of the active region and the
/// beginning of the inactive region.
/// - nRange defines the angular range from the start of the active
/// region to the end of the inactive region. In most cases, a
/// range of 360 degrees is used.
/// - All angles are measured in units of degrees.
/// - Angles are measured with 0 at the top, 90 towards the right,
/// 180 towards the bottom, 270 towards the left, etc.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nStart: Define angle of start of active region (measured in degrees)
/// \param[in] nRange: Define angular range from strt of active region
/// to end of the inactive region (measured in degrees)
/// \param[in] bClockwise: Defines the direction in which the active
/// region grows (true for clockwise) [FORCED TRUE, FOR FUTURE IMPLEMENTATION]
///
/// \return none
///
void gslc_ElemXRingGaugeSetAngleRange(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nStart, int16_t nRange, bool bClockwise);
/// Defines the range of values that may be passed into SetVal(), used to
/// scale the input to SetVal().
/// - Default is 0..100.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nValMin: Minimum value
/// \param[in] nValMax: Maximum value
///
/// \return none
///
void gslc_ElemXRingGaugeSetValRange(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int16_t nValMin, int16_t nValMax);
/// Defines the thickness of the ring arcs. More specifically, it defines the reduction
/// in radius from the outer radius to the inner radius in pixels.
/// - Default thickness is 10 pixels
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nThickness: Thickness of ring
///
/// \return none
///
void gslc_ElemXRingGaugeSetThickness(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, int8_t nThickness);
/// Sets the quality of the ring drawing by defining the number of segments that are used when
/// rendering a 360 degree gauge.The larger the number, the more segments are used and the smoother the curve.
/// A larger ring gauge may need a higher quality number to maintain a smoothed curve appearance.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nSegments: Number of arc segments to render a complete circle. The
/// higher the value, the smoother the ring.
/// Note that 360/nSegments should be an integer result,
/// thus the allowable quality settings are: 360 (max quality),
/// 180, 120, 90, 72, 60, 45, 40, 36 (low quality), etc.
///
/// \return none
///
void gslc_ElemXRingGaugeSetQuality(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, uint16_t nSegments);
/// Defines the color of the inactive region to be a flat (constant) color.
/// The inactive color is often set to be the same as the background but it can
/// be set to a different color to indicate the remainder of the value range that is yet to be filled.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] colInactive: Color of inactive region
///
/// \return none
///
void gslc_ElemXRingGaugeSetColorInactive(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colInactive);
/// Defines the color of the active region to be a flat (constant) color.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] colActive: Color of active region
///
/// \return none
///
void gslc_ElemXRingGaugeSetColorActiveFlat(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colActive);
/// Defines the color of the active region to be a gradient using two color stops. The
/// active region will be filled according to the proportion between nMin and nMax.
/// The gradient is defined by a linear RGB blend between the two color stops(colStart and colEnd)
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] colStart: Starting color of gradient fill
/// \param[in] colEnd: Ending color of gradient fill
///
/// \return none
///
void gslc_ElemXRingGaugeSetColorActiveGradient(gslc_tsGui* pGui, gslc_tsElemRef* pElemRef, gslc_tsColor colStart, gslc_tsColor colEnd);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XRING_H_

530
src/guislice/XSeekbar.c Normal file
View file

@ -0,0 +1,530 @@
// =======================================================================
// GUIslice library extension: Seekbar control
// - Paul Conti
// - Seekbar is a modern Slider Control with Android like style
// - Based on Calvin Hass's Slider control
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XSeekbar.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XSeekbar.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Seekbar
// - A linear slider control
// ============================================================================
// Create a slider element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw and touch
gslc_tsElemRef* gslc_ElemXSeekbarCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXSeekbar* pXData,gslc_tsRect rElem,int16_t nPosMin,int16_t nPosMax,int16_t nPos,
uint8_t nProgressW,uint8_t nRemainW,uint8_t nThumbSz,
gslc_tsColor colProgress,gslc_tsColor colRemain,gslc_tsColor colThumb,bool bVert)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSeekbarCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_SEEKBAR,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures &= ~GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_GLOW_EN;
sElem.nGroup = GSLC_GROUP_ID_NONE;
// Range check on nPos
if (nPos < nPosMin) { nPos = nPosMin; }
if (nPos > nPosMax) { nPos = nPosMax; }
pXData->bVert = bVert;
pXData->nPosMin = nPosMin;
pXData->nPosMax = nPosMax;
pXData->nPos = nPos;
pXData->nProgressW = nProgressW;
pXData->nRemainW = nRemainW;
pXData->nThumbSz = nThumbSz;
pXData->colProgress = colProgress;
pXData->colRemain = colRemain;
pXData->colThumb = colThumb;
pXData->bTrimThumb = false;
pXData->colTrim = GSLC_COL_BLACK;
pXData->bFrameThumb = false;
pXData->colFrame = GSLC_COL_BLACK;
pXData->nTickDiv = 0;
pXData->nTickLen = 0;
pXData->colTick = GSLC_COL_BLACK;
pXData->pfuncXPos = NULL;
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXSeekbarDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = &gslc_ElemXSeekbarTouch;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
void gslc_ElemXSeekbarSetStyle(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,
bool bTrimThumb,gslc_tsColor colTrim,bool bFrameThumb,gslc_tsColor colFrame,uint16_t nTickDiv,
int16_t nTickLen,gslc_tsColor colTick)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSeekbarSetStyle";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSeekbar* pSeekbar = (gslc_tsXSeekbar*)(pElem->pXData);
pSeekbar->bTrimThumb = bTrimThumb;
pSeekbar->colTrim = colTrim;
pSeekbar->bFrameThumb = bFrameThumb;
pSeekbar->colFrame = colFrame;
pSeekbar->nTickDiv = nTickDiv;
pSeekbar->nTickLen = nTickLen;
pSeekbar->colTick = colTick;
// Update
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
int gslc_ElemXSeekbarGetPos(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSeekbarGetPos";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return 0;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSeekbar* pSeekbar = (gslc_tsXSeekbar*)(pElem->pXData);
return pSeekbar->nPos;
}
// Update the slider control's current state
void gslc_ElemXSeekbarSetPos(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nPos)
{
if ((pGui == NULL) || (pElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSeekbarSetPos";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSeekbar* pSeekbar = (gslc_tsXSeekbar*)(pElem->pXData);
int16_t nPosOld;
// Clip position
if (nPos < pSeekbar->nPosMin) { nPos = pSeekbar->nPosMin; }
if (nPos > pSeekbar->nPosMax) { nPos = pSeekbar->nPosMax; }
// Update
nPosOld = pSeekbar->nPos;
pSeekbar->nPos = nPos;
// Only update if changed
if (nPos != nPosOld) {
// If any position callback is defined, call it now
if (pSeekbar->pfuncXPos != NULL) {
(*pSeekbar->pfuncXPos)((void*)(pGui),(void*)(pElemRef),nPos);
}
// Mark for redraw
// - Only need incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Assign the position callback function for a slider
void gslc_ElemXSeekbarSetPosFunc(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,GSLC_CB_XSEEKBAR_POS funcCb)
{
if ((pElemRef == NULL) || (funcCb == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSeekbarSetPosFunc";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSeekbar* pSeekbar = (gslc_tsXSeekbar*)(pElem->pXData);
pSeekbar->pfuncXPos = funcCb;
}
// Redraw the slider
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXSeekbarDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSeekbarDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
// Fetch the element's extended data structure
gslc_tsXSeekbar* pSeekbar;
pSeekbar = (gslc_tsXSeekbar*)(pElem->pXData);
if (pSeekbar == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXSeekbarDraw(%s) pXData is NULL\n","");
return false;
}
bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
int16_t nPos = pSeekbar->nPos;
int16_t nPosMin = pSeekbar->nPosMin;
int16_t nPosMax = pSeekbar->nPosMax;
uint8_t nProgressW = pSeekbar->nProgressW;
uint8_t nRemainW = pSeekbar->nRemainW;
uint8_t nThumbSz = pSeekbar->nThumbSz;
bool bVert = pSeekbar->bVert;
bool bTrimThumb = pSeekbar->bTrimThumb;
bool bFrameThumb = pSeekbar->bFrameThumb;
uint16_t nTickDiv = pSeekbar->nTickDiv;
int16_t nTickLen = pSeekbar->nTickLen;
gslc_tsColor colProgress = pSeekbar->colProgress;
gslc_tsColor colRemain = pSeekbar->colRemain;
gslc_tsColor colThumb = pSeekbar->colThumb;
gslc_tsColor colFrame = pSeekbar->colFrame;
gslc_tsColor colTrim = pSeekbar->colTrim;
gslc_tsColor colTick = pSeekbar->colTick;
// Range check on nPos
if (nPos < nPosMin) { nPos = nPosMin; }
if (nPos > nPosMax) { nPos = nPosMax; }
int16_t nX0,nY0,nX1,nY1,nXMid,nYMid;
nX0 = pElem->rElem.x;
nY0 = pElem->rElem.y;
nX1 = pElem->rElem.x + pElem->rElem.w - 1;
nY1 = pElem->rElem.y + pElem->rElem.h - 1;
nXMid = (nX0+nX1)/2;
nYMid = (nY0+nY1)/2;
// Scale the current position
int16_t nPosRng = nPosMax-nPosMin;
// TODO: Check for nPosRng=0, reversed min/max
int16_t nPosOffset = nPos-nPosMin;
// Provide some margin so thumb doesn't exceed control bounds
int16_t nMargin = nThumbSz;
int16_t nCtrlRng;
if (!bVert) {
nCtrlRng = (nX1-nMargin)-(nX0+nMargin);
} else {
nCtrlRng = (nY1-nMargin)-(nY0+nMargin);
}
int16_t nCtrlPos = (nPosOffset*nCtrlRng/nPosRng)+nMargin;
int16_t nCtrlX0,nCtrlY0;
gslc_tsRect rThumb;
if (!bVert) {
nCtrlX0 = nX0+nCtrlPos-nThumbSz;
nCtrlY0 = nYMid-nThumbSz;
} else {
nCtrlX0 = nXMid-nThumbSz;
nCtrlY0 = nY0+nCtrlPos-nThumbSz;
}
rThumb.x = nCtrlX0;
rThumb.y = nCtrlY0;
rThumb.w = 2*nThumbSz;
rThumb.h = 2*nThumbSz;
// Draw the thumb control
// work out size of thumb circle
int16_t nLeftX = rThumb.x+nThumbSz;
int16_t nLeftY = rThumb.y + nThumbSz;
// Draw the background
// - TODO: To reduce flicker on unbuffered displays, one could consider
// redrawing only the thumb (last drawn position) with fill and
// then redraw other portions. This would prevent the
// track / ticks from flickering needlessly. A full redraw would
// be required if it was first draw action.
gslc_DrawFillRect(pGui,pElem->rElem,(bGlow)?pElem->colElemFillGlow:pElem->colElemFill);
// Draw the progress part of track
if (!bVert) {
gslc_tsRect rTrack = {
nX0+nMargin,
nYMid-(nProgressW/2),
nCtrlPos,
nProgressW
};
gslc_DrawFillRect(pGui,rTrack,colProgress);
} else {
gslc_tsRect rTrack = {
nXMid-(nProgressW/2),
nY0+nMargin,
nProgressW,
nCtrlPos
};
gslc_DrawFillRect(pGui,rTrack,colProgress);
}
// test for thumb trim color
if (bTrimThumb) {
// two color thumb
gslc_DrawFillCircle(pGui,nLeftX,nLeftY,nThumbSz,colTrim);
gslc_DrawFillCircle(pGui,nLeftX,nLeftY,3,colThumb);
} else {
// one solid color thumb
gslc_DrawFillCircle(pGui,nLeftX,nLeftY,nThumbSz,colProgress);
}
if (bFrameThumb) {
gslc_DrawFrameCircle(pGui,nLeftX,nLeftY,nThumbSz,colFrame);
}
// Draw the remaining part of track
if (!bVert) {
if (nRemainW == 1) {
gslc_DrawLine(pGui,nX0+nMargin,nYMid,nX1-nMargin,nYMid,colRemain);
} else {
gslc_tsRect rRemain = {
nX0+nMargin+nCtrlPos,
nYMid-(nRemainW/2),
nX1 - (nX0 + nCtrlPos+ nMargin*2) +1,
nRemainW
};
gslc_DrawFillRect(pGui,rRemain,colRemain);
}
} else {
if (nRemainW == 1) {
gslc_DrawLine(pGui,nXMid,nY0+nMargin,nXMid,nY1-nMargin,colRemain);
} else {
gslc_tsRect rRemain = {
nXMid-(nRemainW/2),
nY0+nMargin+nCtrlPos,
nRemainW,
(nY1-nMargin)-(nY0+nMargin+nCtrlPos)
};
gslc_DrawFillRect(pGui,rRemain,colRemain);
}
}
// Draw any ticks - we need to do this last or the ticks get over written
// - Need at least one tick segment
if (nTickDiv>=1) {
uint16_t nTickInd;
int16_t nTickOffset;
for (nTickInd=0;nTickInd<=nTickDiv;nTickInd++) {
nTickOffset = nTickInd * nCtrlRng / nTickDiv;
if (!bVert) {
gslc_DrawLine(pGui,nX0+nMargin+nTickOffset,nYMid-(nTickLen/2),
nX0+nTickOffset+nMargin,nYMid+(nTickLen/2),colTick);
} else {
gslc_DrawLine(pGui,nXMid+(nTickLen/2),nY0+nTickOffset+nMargin,nXMid-(nTickLen/2),
nY0+nTickOffset+nMargin,colTick);
}
}
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
// This callback function is called by gslc_ElemSendEventTouch()
// after any touch event
bool gslc_ElemXSeekbarTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSeekbarTouch";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
gslc_tsGui* pGui = NULL;
gslc_tsElemRef* pElemRef = NULL;
gslc_tsElem* pElem = NULL;
gslc_tsXSeekbar* pSeekbar = NULL;
// Typecast the parameters to match the GUI
pGui = (gslc_tsGui*)(pvGui);
pElemRef = (gslc_tsElemRef*)(pvElemRef);
pElem = gslc_GetElemFromRef(pGui,pElemRef);
pSeekbar = (gslc_tsXSeekbar*)(pElem->pXData);
bool bGlowingOld = gslc_ElemGetGlow(pGui,pElemRef);
int16_t nPosRng;
int16_t nPos = 0;
bool bUpdatePos = false;
bool bIndexed = false;
switch(eTouch) {
case GSLC_TOUCH_DOWN_IN:
// Start glowing as must be over it
gslc_ElemSetGlow(pGui,pElemRef,true);
bUpdatePos = true;
break;
case GSLC_TOUCH_MOVE_IN:
gslc_ElemSetGlow(pGui,pElemRef,true);
bUpdatePos = true;
break;
case GSLC_TOUCH_MOVE_OUT:
gslc_ElemSetGlow(pGui,pElemRef,false);
bUpdatePos = true;
break;
case GSLC_TOUCH_UP_IN:
// End glow
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
case GSLC_TOUCH_UP_OUT:
// End glow
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
case GSLC_TOUCH_SET_REL:
case GSLC_TOUCH_SET_ABS:
bIndexed = true;
gslc_ElemSetGlow(pGui,pElemRef,true);
bUpdatePos = true;
break;
default:
return false;
break;
}
// If we need to update the slider position, calculate the value
// and perform the update
if (bUpdatePos) {
if (bIndexed) {
// The position is changed by direct control (eg. keyboard)
// instead of touch coordinates
// FIXME: Change the following to be either absolute or relative
// value assignment instead of inc/dec. Then the user code can
// define what the magnitude and direction should be.
if (eTouch == GSLC_TOUCH_SET_REL) {
// Overload the "nRelY" parameter
nPos = pSeekbar->nPos;
nPos += nRelY;
nPos = (nPos > pSeekbar->nPosMax)? pSeekbar->nPosMax : nPos;
nPos = (nPos < pSeekbar->nPosMin)? pSeekbar->nPosMin : nPos;
} else if (eTouch == GSLC_TOUCH_SET_ABS) {
// Overload the "nRelY" parameter
nPos = nRelY;
nPos = (nPos > pSeekbar->nPosMax)? pSeekbar->nPosMax : nPos;
nPos = (nPos < pSeekbar->nPosMin)? pSeekbar->nPosMin : nPos;
}
} else {
// Perform additional range checking
nRelX = (nRelX < 0)? 0 : nRelX;
nRelY = (nRelY < 0)? 0 : nRelY;
// Calc new position
nPosRng = pSeekbar->nPosMax - pSeekbar->nPosMin;
if (!pSeekbar->bVert) {
nPos = (nRelX * nPosRng / pElem->rElem.w) + pSeekbar->nPosMin;
} else {
nPos = (nRelY * nPosRng / pElem->rElem.h) + pSeekbar->nPosMin;
}
}
// Update the slider
gslc_ElemXSeekbarSetPos(pGui,pElemRef,nPos);
}
// If the slider changed state, redraw
if (gslc_ElemGetGlow(pGui,pElemRef) != bGlowingOld) {
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
return true;
#endif // !DRV_TOUCH_NONE
}
// ============================================================================

336
src/guislice/XSeekbar.h Normal file
View file

@ -0,0 +1,336 @@
#ifndef _GUISLICE_EX_XSEEKBAR_H_
#define _GUISLICE_EX_XSEEKBAR_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Seekbar control
// - Paul Conti
// - Seekbar is a modern Slider Control with Android like style
// - Based on Calvin Hass's Slider control
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XSeekbar.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Seekbar
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_SEEKBAR GSLC_TYPE_BASE_EXTEND + 6
/// Callback function for slider feedback
typedef bool (*GSLC_CB_XSEEKBAR_POS)(void* pvGui,void* pvElem,int16_t nPos);
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Seekbar element
typedef struct {
// Config
bool bVert; ///< Orientation: true if vertical, else horizontal
uint8_t nProgressW; ///< Width of progress track
uint8_t nRemainW; ///< Width of remaining track
uint8_t nThumbSz; ///< Size of the thumb control
int16_t nPosMin; ///< Minimum position value of the slider
int16_t nPosMax; ///< Maximum position value of the slider
// Style config
gslc_tsColor colProgress; ///< Style: color of progress fill bar
gslc_tsColor colRemain; ///< Style: color remaining fill bar
gslc_tsColor colThumb; ///< Style: color of thumb
uint16_t nTickDiv; ///< Style: number of tickmark divisions (0 for none)
int16_t nTickLen; ///< Style: length of tickmarks
gslc_tsColor colTick; ///< Style: color of ticks
bool bTrimThumb; ///< Style: show a trim color for thumb
gslc_tsColor colTrim; ///< Style: color of trim
bool bFrameThumb; ///< Style: draw frame around thumb
gslc_tsColor colFrame; ///< Style: color of trim
// State
int16_t nPos; ///< Current position value of the slider
// Callbacks
GSLC_CB_XSEEKBAR_POS pfuncXPos; ///< Callback func ptr for position update
} gslc_tsXSeekbar;
///
/// Create a Seekbar Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining checkbox size
/// \param[in] nPosMin: Minimum position value
/// \param[in] nPosMax: Maximum position value
/// \param[in] nPos: Starting position value
/// \param[in] nProgressW: Width of progress track
/// \param[in] nRemainW: Width of remaining track
/// \param[in] nThumbSz: Size of the thumb control
/// \param[in] colProgress: Color of progress fill bar
/// \param[in] colRemain: Color remaining fill bar
/// \param[in] colThumb: Color for the thumb indicator
/// \param[in] bVert: Orientation (true for vertical)
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXSeekbarCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXSeekbar* pXData,gslc_tsRect rElem,int16_t nPosMin,int16_t nPosMax,int16_t nPos,
uint8_t nProgressW,uint8_t nRemainW,uint8_t nThumbSz,
gslc_tsColor colProgress,gslc_tsColor colRemain,gslc_tsColor colThumb,bool bVert);
///
/// Set a Seekbar element's style, this includes thumb customizations and tick marks
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] bTrimThumb: Show a colored trim for thumb?
/// \param[in] colTrim: Color of thumb trim
/// \param[in] bFrameThumb: Show a frame around thumb?
/// \param[in] colFrame: Color of thumb frame
/// \param[in] nTickDiv: Number of tick divisions to show (0 for none)
/// \param[in] nTickLen: Length of tick marks
/// \param[in] colTick: Color of ticks
///
/// \return none
///
void gslc_ElemXSeekbarSetStyle(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,
bool bTrimThumb,gslc_tsColor colTrim,bool bFrameThumb,gslc_tsColor colFrame,uint16_t nTickDiv,
int16_t nTickLen,gslc_tsColor colTick);
///
/// Get a Seekbar element's current position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return Current slider position
///
int gslc_ElemXSeekbarGetPos(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Set a Seekbar element's current position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nPos: New position value
///
/// \return none
///
void gslc_ElemXSeekbarSetPos(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nPos);
///
/// Assign the position callback function for a slider
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] funcCb: Function pointer to position routine (or NULL for none)
///
/// \return none
///
void gslc_ElemXSeekbarSetPosFunc(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,GSLC_CB_XSEEKBAR_POS funcCb);
///
/// Draw a Seekbar element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSeekbarDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Handle touch events to Seekbar element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSeekbarTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
/// \def gslc_ElemXSeekbarCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,
/// nPosMin_,nPosMax_,nPos_,nThumbSz_,bVert_,colFrame_,colFill_)
///
/// Create a Seekbar Element in Flash
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Unique element ID to assign
/// \param[in] nPage: Page ID to attach element to
/// \param[in] nX: X coordinate of element
/// \param[in] nY: Y coordinate of element
/// \param[in] nW: Width of element
/// \param[in] nH: Height of element
/// \param[in] nPosMin_: Minimum position value
/// \param[in] nPosMax_: Maximum position value
/// \param[in] nPos_: Starting position value
/// \param[in] nProgressW_: Width of progress track
/// \param[in] nRemainW_: Width of remaining track
/// \param[in] nThumbSz_: Size of the thumb control
/// \param[in] colProgress_: Color of progress fill bar
/// \param[in] colRemain_: Color remaining fill bar
/// \param[in] colThumb_: Color for the thumb indicator
/// \param[in] bVert_: Orientation (true for vertical)
/// \param[in] colFrame_: Color of the element frame
/// \param[in] colFill_: Color of the element fill
///
/// \return none
///
#if (GSLC_USE_PROGMEM)
#define gslc_ElemXSeekbarCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,nPosMin_,nPosMax_, \
nPos_,nProgressW_,nRemainW_,nThumbSz_,colProgress_,colRemain_,colThumb_,bVert_, \
colFrame_,colFill_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_CLICK_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXSeekbar sSeekbar##nElemId; \
sSeekbar##nElemId.bVert = bVert_; \
sSeekbar##nElemId.nProgressW = nProgressW_; \
sSeekbar##nElemId.nRemainW = nRemainW_; \
sSeekbar##nElemId.nThumbSz = nThumbSz_; \
sSeekbar##nElemId.nPosMin = nPosMin_; \
sSeekbar##nElemId.nPosMax = nPosMax_; \
sSeekbar##nElemId.colProgress = colProgress_; \
sSeekbar##nElemId.colRemain = colRemain_; \
sSeekbar##nElemId.colThumb = colThumb_; \
sSeekbar##nElemId.nPos = nPos_; \
sSeekbar##nElemId.pfuncXPos = NULL; \
static const gslc_tsElem sElem##nElemId PROGMEM = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_SEEKBAR, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sSeekbar##nElemId), \
NULL, \
&gslc_ElemXSeekbarDraw, \
&gslc_ElemXSeekbarTouch, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#else
#define gslc_ElemXSeekbarCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,nPosMin_,nPosMax_, \
nPos_,nProgressW_,nRemainW_,nThumbSz_,colProgress_,colRemain_,colThumb_,bVert_, \
colFrame_,colFill_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_CLICK_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXSeekbar sSeekbar##nElemId; \
sSeekbar##nElemId.bVert = bVert_; \
sSeekbar##nElemId.nProgressW = nProgressW_; \
sSeekbar##nElemId.nRemainW = nRemainW_; \
sSeekbar##nElemId.nThumbSz = nThumbSz_; \
sSeekbar##nElemId.nPosMin = nPosMin_; \
sSeekbar##nElemId.nPosMax = nPosMax_; \
sSeekbar##nElemId.colProgress = colProgress_; \
sSeekbar##nElemId.colRemain = colRemain_; \
sSeekbar##nElemId.colThumb = colThumb_; \
sSeekbar##nElemId.nPos = nPos_; \
sSeekbar##nElemId.pfuncXPos = NULL; \
static const gslc_tsElem sElem##nElemId = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_SEEKBAR, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sSeekbar##nElemId), \
NULL, \
&gslc_ElemXSeekbarDraw, \
&gslc_ElemXSeekbarTouch, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_CONST | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XSEEKBAR_H_

384
src/guislice/XSelNum.c Normal file
View file

@ -0,0 +1,384 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XSelNum.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XSelNum.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
#if (GSLC_FEATURE_COMPOUND)
// ============================================================================
// Extended Element: SelNum
// - SelNum (Select Number) element demonstrates a simple up/down counter
// - This is a compound element containing two buttons and
// a text area to represent the current count
// ============================================================================
// Private sub Element ID definitions
static const int16_t SELNUM_ID_BTN_INC = 100;
static const int16_t SELNUM_ID_BTN_DEC = 101;
static const int16_t SELNUM_ID_TXT = 102;
// Create a compound element
// - For now just two buttons and a text area
gslc_tsElemRef* gslc_ElemXSelNumCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXSelNum* pXData,gslc_tsRect rElem,int8_t nFontId)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSelNumCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
// Initialize composite element
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_SELNUM,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN; // Don't need to glow outer element
sElem.nGroup = GSLC_GROUP_ID_NONE;
pXData->nCounter = 0;
// Determine the maximum number of elements that we can store
// in the sub-element array. We do this at run-time with sizeof()
// instead of using #define to avoid polluting the global namespace.
int16_t nSubElemMax = sizeof(pXData->asElem) / sizeof(pXData->asElem[0]);
// NOTE: The count parameters in CollectReset() must match the size of
// the asElem[] array. It is used for bounds checking when we
// add new elements.
// NOTE: We only use RAM for subelement storage
gslc_CollectReset(&pXData->sCollect,pXData->asElem,nSubElemMax,pXData->asElemRef,nSubElemMax);
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXSelNumDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = &gslc_ElemXSelNumTouch;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
// Now create the sub elements
// - Ensure page is set to GSLC_PAGE_NONE so that
// we create the element struct but not add it to a specific page.
// - When we create an element with GSLC_PAGE_NONE it is
// saved in the GUI's temporary element storage.
// - When we have finished creating / styling the element, we then
// copy it into the permanent sub-element storage
// - The element IDs assigned to the sub-elements are
// arbitrary (with local scope in the compound element),
// so they don't need to be unique globally across the GUI.
gslc_tsElemRef* pElemRefTmp = NULL;
gslc_tsElem* pElemTmp = NULL;
gslc_tsElemRef* pElemRef = NULL;
// Determine offset coordinate of compound element so that we can
// specify relative positioning during the sub-element Create() operations.
int16_t nOffsetX = rElem.x;
int16_t nOffsetY = rElem.y;
gslc_tsRect rSubElem;
rSubElem = (gslc_tsRect) { nOffsetX+40,nOffsetY+10,30,30 };
rSubElem = gslc_ExpandRect(rSubElem, -1, -1);
#if (GSLC_LOCAL_STR)
pElemRefTmp = gslc_ElemCreateBtnTxt(pGui,SELNUM_ID_BTN_INC,GSLC_PAGE_NONE,
rSubElem,"+",0,nFontId,&gslc_ElemXSelNumClick);
#else
strncpy(pXData->acElemTxt[0],"+",SELNUM_STR_LEN-1);
pElemRefTmp = gslc_ElemCreateBtnTxt(pGui,SELNUM_ID_BTN_INC,GSLC_PAGE_NONE,
rSubElem,pXData->acElemTxt[0],SELNUM_STR_LEN,
nFontId,&gslc_ElemXSelNumClick);
#endif
gslc_ElemSetCol(pGui,pElemRefTmp,(gslc_tsColor){0,0,192},(gslc_tsColor){0,0,128},(gslc_tsColor){0,0,224});
gslc_ElemSetTxtCol(pGui,pElemRefTmp,GSLC_COL_WHITE);
pElemTmp = gslc_GetElemFromRef(pGui,pElemRefTmp);
gslc_CollectElemAdd(pGui,&pXData->sCollect,pElemTmp,GSLC_ELEMREF_DEFAULT);
rSubElem = (gslc_tsRect) { nOffsetX+80,nOffsetY+10,30,30 };
rSubElem = gslc_ExpandRect(rSubElem, -1, -1);
#if (GSLC_LOCAL_STR)
pElemRefTmp = gslc_ElemCreateBtnTxt(pGui,SELNUM_ID_BTN_DEC,GSLC_PAGE_NONE,
rSubElem,"-",0,nFontId,&gslc_ElemXSelNumClick);
#else
strncpy(pXData->acElemTxt[1],"-",SELNUM_STR_LEN-1);
pElemRefTmp = gslc_ElemCreateBtnTxt(pGui,SELNUM_ID_BTN_DEC,GSLC_PAGE_NONE,
rSubElem,pXData->acElemTxt[1],SELNUM_STR_LEN,
nFontId,&gslc_ElemXSelNumClick);
#endif
gslc_ElemSetCol(pGui,pElemRefTmp,(gslc_tsColor){0,0,192},(gslc_tsColor){0,0,128},(gslc_tsColor){0,0,224});
gslc_ElemSetTxtCol(pGui,pElemRefTmp,GSLC_COL_WHITE);
pElemTmp = gslc_GetElemFromRef(pGui,pElemRefTmp);
gslc_CollectElemAdd(pGui,&pXData->sCollect,pElemTmp,GSLC_ELEMREF_DEFAULT);
rSubElem = (gslc_tsRect) { nOffsetX+10,nOffsetY+10,20,30 };
rSubElem = gslc_ExpandRect(rSubElem, -1, -1);
#if (GSLC_LOCAL_STR)
pElemRefTmp = gslc_ElemCreateTxt(pGui,SELNUM_ID_TXT,GSLC_PAGE_NONE,
rSubElem,"0",0,nFontId);
#else
strncpy(pXData->acElemTxt[2],"0",SELNUM_STR_LEN-1);
pElemRefTmp = gslc_ElemCreateTxt(pGui,SELNUM_ID_TXT,GSLC_PAGE_NONE,
rSubElem,pXData->acElemTxt[2],SELNUM_STR_LEN,nFontId);
#endif
pElemTmp = gslc_GetElemFromRef(pGui,pElemRefTmp);
gslc_CollectElemAdd(pGui,&pXData->sCollect,pElemTmp,GSLC_ELEMREF_DEFAULT);
// Now proceed to add the compound element to the page
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
// Now propagate the parent relationship to enable a cascade
// of redrawing from low-level elements to the top
gslc_CollectSetParent(pGui,&pXData->sCollect,pElemRef);
return pElemRef;
} else {
GSLC_DEBUG2_PRINT("ERROR: ElemXSelNumCreate(%s) Compound elements inside compound elements not supported\n","");
return NULL;
// TODO: For now, disable compound elements within
// compound elements. If we want to enable this, we
// would probably use the temporary element reference
// the GUI.
// Save as temporary element for further processing
//pGui->sElemTmp = sElem; // Need fixing
//return &(pGui->sElemTmp); // Need fixing
}
}
// Redraw the compound element
// - When drawing a compound element, we clear the background
// and then redraw the sub-element collection.
bool gslc_ElemXSelNumDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSelNum* pSelNum = (gslc_tsXSelNum*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_SELNUM, __LINE__);
if (!pSelNum) return false;
bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
// Draw the compound element fill (background)
// - Should only need to do this in full redraw
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFillRect(pGui,pElem->rElem,(bGlow)?pElem->colElemFillGlow:pElem->colElemFill);
}
// Draw the sub-elements
// - For now, force redraw of entire compound element
gslc_tsCollect* pCollect = &pSelNum->sCollect;
gslc_tsEvent sEvent = gslc_EventCreate(pGui,GSLC_EVT_DRAW,GSLC_EVTSUB_DRAW_FORCE,(void*)(pCollect),NULL);
gslc_CollectEvent(pGui,sEvent);
// Optionally, draw a frame around the compound element
// - This could instead be done by creating a sub-element
// of type box.
// - We don't need to show any glowing of the compound element
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFrameRect(pGui, pElem->rElem, (bGlow) ? pElem->colElemFrameGlow : pElem->colElemFrame);
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
// Fetch the current value of the element's counter
int gslc_ElemXSelNumGetCounter(gslc_tsGui* pGui,gslc_tsXSelNum* pSelNum)
{
if ((pGui == NULL) || (pSelNum == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSelNumGetCounter";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return 0;
}
return pSelNum->nCounter;
}
void gslc_ElemXSelNumSetCounter(gslc_tsGui* pGui,gslc_tsXSelNum* pSelNum,int16_t nCount)
{
if (pSelNum == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSelNumSetCounter";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
pSelNum->nCounter = nCount;
// Determine new counter text
// FIXME: Consider replacing the printf() with an optimized function to
// conserve RAM. Potentially leverage GSLC_DEBUG2_PRINT().
char acStrNew[GSLC_LOCAL_STR_LEN];
snprintf(acStrNew,GSLC_LOCAL_STR_LEN,"%hd",pSelNum->nCounter);
// Update the element
gslc_tsElemRef* pElemRef = gslc_CollectFindElemById(pGui,&pSelNum->sCollect,SELNUM_ID_TXT);
gslc_ElemSetTxtStr(pGui,pElemRef,acStrNew);
}
// Handle the compound element main functionality
// - This routine is called by gslc_ElemEvent() to handle
// any click events that resulted from the touch tracking process.
// - The code here will generally represent the core
// functionality of the compound element and any communication
// between sub-elements.
// - pvElemRef is a void pointer to the element ref being tracked. From
// the pElemRefParent member we can get the parent/compound element
// data structures.
bool gslc_ElemXSelNumClick(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRefD(pGui, pElemRef, __LINE__);
if (!pElem) return false;
// Fetch the parent of the clicked element which is the compound
// element itself. This enables us to access the extra control data.
gslc_tsElemRef* pElemRefParent = pElem->pElemRefParent;
if (pElemRefParent == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXSelNumClick(%s) parent ElemRef ptr NULL\n","");
return false;
}
gslc_tsElem* pElemParent = gslc_GetElemFromRef(pGui,pElemRefParent);
gslc_tsXSelNum* pSelNum = (gslc_tsXSelNum*)(pElemParent->pXData);
if (pSelNum == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXSelNumClick() element (ID=%d) has NULL pXData\n",pElem->nId);
return false;
}
// Begin the core compound element functionality
int nCounter = pSelNum->nCounter;
// Handle the various button presses
if (eTouch == GSLC_TOUCH_UP_IN) {
// Get the tracked element ID
gslc_tsElemRef* pElemRefTracked = pSelNum->sCollect.pElemRefTracked;
gslc_tsElem* pElemTracked = gslc_GetElemFromRef(pGui,pElemRefTracked);
int nSubElemId = pElemTracked->nId;
if (nSubElemId == SELNUM_ID_BTN_INC) {
// Increment button
if (nCounter < 100) {
nCounter++;
}
gslc_ElemXSelNumSetCounter(pGui,pSelNum,nCounter);
} else if (nSubElemId == SELNUM_ID_BTN_DEC) {
// Decrement button
if (nCounter > 0) {
nCounter--;
}
gslc_ElemXSelNumSetCounter(pGui,pSelNum,nCounter);
}
} // eTouch
return true;
#endif // !DRV_TOUCH_NONE
}
bool gslc_ElemXSelNumTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXSelNum* pSelNum = (gslc_tsXSelNum*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_SELNUM, __LINE__);
if (!pSelNum) return false;
// Get Collection
gslc_tsCollect* pCollect = &pSelNum->sCollect;
return gslc_CollectTouchCompound(pvGui, pvElemRef, eTouch, nRelX, nRelY, pCollect);
#endif // !DRV_TOUCH_NONE
}
#endif // GSLC_FEATURE_COMPOUND
// ============================================================================

177
src/guislice/XSelNum.h Normal file
View file

@ -0,0 +1,177 @@
#ifndef _GUISLICE_EX_XSELNUM_H_
#define _GUISLICE_EX_XSELNUM_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Select Number (SelNum) control
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XSelNum.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if (GSLC_FEATURE_COMPOUND)
// ============================================================================
// Extended Element: SelNum (Number Selector)
// - Demonstration of a compound element consisting of
// a counter text field along with an increment and
// decrement button.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_SELNUM GSLC_TYPE_BASE_EXTEND + 3
#define SELNUM_STR_LEN 6
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for SelNum element
typedef struct {
// Core functionality for SelNum
int16_t nCounter; ///< Counter for demo purposes
// Internal sub-element members
gslc_tsCollect sCollect; ///< Collection management for sub-elements
gslc_tsElemRef asElemRef[4]; ///< Storage for sub-element references
gslc_tsElem asElem[4]; ///< Storage for sub-elements
#if (GSLC_LOCAL_STR == 0)
// If elements don't provide their own internal string buffer, then
// we need to provide storage here in the extended data of the compound
// element. For now, simply provide a fixed-length memory buffer.
char acElemTxt[4][SELNUM_STR_LEN]; ///< Storage for strings
#endif
} gslc_tsXSelNum;
///
/// Create a SelNum Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining element size
/// \param[in] nFontId: Font ID to use for drawing the element
///
/// \return Pointer to Element or NULL if failure
///
gslc_tsElemRef* gslc_ElemXSelNumCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXSelNum* pXData,gslc_tsRect rElem,int8_t nFontId);
///
/// Draw a SelNum element on the screen
/// - Called during redraw
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSelNumDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Get the current counter associated with SelNum
///
/// \param[in] pGui: Ptr to GUI
/// \param[in] pSelNum: Ptr to Element
///
/// \return Current counter value
///
int gslc_ElemXSelNumGetCounter(gslc_tsGui* pGui,gslc_tsXSelNum* pSelNum);
///
/// Set the current counter associated with SelNum
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pSelNum: Ptr to Element
/// \param[in] nCount: New counter value
///
/// \return none
///
void gslc_ElemXSelNumSetCounter(gslc_tsGui* pGui,gslc_tsXSelNum* pSelNum,int16_t nCount);
///
/// Handle a click event within the SelNum
/// - This is called internally by the SelNum touch handler
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nX: Touch X coord
/// \param[in] nY: Touch Y coord
///
/// \return none
///
bool gslc_ElemXSelNumClick(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY);
///
/// Handle touch (up,down,move) events to SelNum element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSelNumTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY);
#endif // GLSC_COMPOUND
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XSELNUM_H_

478
src/guislice/XSlider.c Normal file
View file

@ -0,0 +1,478 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XSlider.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XSlider.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Slider
// - A linear slider control
// ============================================================================
// Create a slider element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw and touch
gslc_tsElemRef* gslc_ElemXSliderCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXSlider* pXData,gslc_tsRect rElem,int16_t nPosMin,int16_t nPosMax,int16_t nPos,
uint16_t nThumbSz,bool bVert)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSliderCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_SLIDER,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures &= ~GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_GLOW_EN;
sElem.nGroup = GSLC_GROUP_ID_NONE;
// Range check on nPos
if (nPos < nPosMin) { nPos = nPosMin; }
if (nPos > nPosMax) { nPos = nPosMax; }
pXData->nPosMin = nPosMin;
pXData->nPosMax = nPosMax;
pXData->nPos = nPos;
pXData->nThumbSz = nThumbSz;
pXData->bVert = bVert;
pXData->bTrim = false;
pXData->colTrim = GSLC_COL_BLACK;
pXData->nTickDiv = 0;
pXData->pfuncXPos = NULL;
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXSliderDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = &gslc_ElemXSliderTouch;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
void gslc_ElemXSliderSetStyle(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,
bool bTrim,gslc_tsColor colTrim,uint16_t nTickDiv,
int16_t nTickLen,gslc_tsColor colTick)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSliderSetStyle";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSlider* pSlider = (gslc_tsXSlider*)(pElem->pXData);
pSlider->bTrim = bTrim;
pSlider->colTrim = colTrim;
pSlider->nTickDiv = nTickDiv;
pSlider->nTickLen = nTickLen;
pSlider->colTick = colTick;
// Update
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
int gslc_ElemXSliderGetPos(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSliderGetPos";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return 0;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSlider* pSlider = (gslc_tsXSlider*)(pElem->pXData);
return pSlider->nPos;
}
// Update the slider control's current state
void gslc_ElemXSliderSetPos(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nPos)
{
if ((pGui == NULL) || (pElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSliderSetPos";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSlider* pSlider = (gslc_tsXSlider*)(pElem->pXData);
int16_t nPosOld;
// Clip position
if (nPos < pSlider->nPosMin) { nPos = pSlider->nPosMin; }
else if (nPos > pSlider->nPosMax) { nPos = pSlider->nPosMax; }
// Update
nPosOld = pSlider->nPos;
pSlider->nPos = nPos;
// Only update if changed
if (nPos != nPosOld) {
// If any position callback is defined, call it now
if (pSlider->pfuncXPos != NULL) {
(*pSlider->pfuncXPos)((void*)(pGui),(void*)(pElemRef),nPos);
}
// Mark for redraw
// - Only need incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Assign the position callback function for a slider
void gslc_ElemXSliderSetPosFunc(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,GSLC_CB_XSLIDER_POS funcCb)
{
if ((pElemRef == NULL) || (funcCb == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSliderSetPosFunc";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSlider* pSlider = (gslc_tsXSlider*)(pElem->pXData);
pSlider->pfuncXPos = funcCb;
}
// Redraw the slider
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXSliderDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSliderDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
// Fetch the element's extended data structure
gslc_tsXSlider* pSlider;
pSlider = (gslc_tsXSlider*)(pElem->pXData);
if (pSlider == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXSliderDraw(%s) pXData is NULL\n","");
return false;
}
bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
int16_t nPos = pSlider->nPos;
int16_t nPosMin = pSlider->nPosMin;
int16_t nPosMax = pSlider->nPosMax;
bool bVert = pSlider->bVert;
int16_t nThumbSz = pSlider->nThumbSz;
bool bTrim = pSlider->bTrim;
gslc_tsColor colTrim = pSlider->colTrim;
uint16_t nTickDiv = pSlider->nTickDiv;
int16_t nTickLen = pSlider->nTickLen;
gslc_tsColor colTick = pSlider->colTick;
// Range check on nPos
if (nPos < nPosMin) { nPos = nPosMin; }
else if (nPos > nPosMax) { nPos = nPosMax; }
int16_t nX0,nY0,nX1,nY1,nXMid,nYMid;
nX0 = pElem->rElem.x;
nY0 = pElem->rElem.y;
nX1 = pElem->rElem.x + pElem->rElem.w - 1;
nY1 = pElem->rElem.y + pElem->rElem.h - 1;
nXMid = (nX0+nX1)/2;
nYMid = (nY0+nY1)/2;
// Scale the current position
int16_t nPosRng = nPosMax-nPosMin;
// TODO: Check for nPosRng=0, reversed min/max
int16_t nPosOffset = nPos-nPosMin;
// Provide some margin so thumb doesn't exceed control bounds
// TODO: Handle nCtrlRng <= 0
int16_t nMargin = nThumbSz;
int16_t nCtrlRng;
if (!bVert) {
nCtrlRng = (nX1-nMargin)-(nX0+nMargin);
} else {
nCtrlRng = (nY1-nMargin)-(nY0+nMargin);
}
int16_t nCtrlPos = (int16_t)((int32_t)nPosOffset * (int32_t)nCtrlRng / (int32_t)nPosRng) + nMargin;
// Draw the background
// - TODO: To reduce flicker on unbuffered displays, one could consider
// redrawing only the thumb (last drawn position) with fill and
// then redraw other portions. This would prevent the
// track / ticks from flickering needlessly. A full redraw would
// be required if it was first draw action.
gslc_DrawFillRect(pGui,pElem->rElem,(bGlow)?pElem->colElemFillGlow:pElem->colElemFill);
// Draw any ticks
// - Need at least one tick segment
if (nTickDiv>=1) {
uint16_t nTickInd;
int16_t nTickOffset;
for (nTickInd=0;nTickInd<=nTickDiv;++nTickInd) {
nTickOffset = (int16_t)((int32_t)nTickInd * (int32_t)nCtrlRng / (int32_t)nTickDiv);
if (!bVert) {
gslc_DrawLine(pGui,nX0+nMargin+ nTickOffset,nYMid,
nX0+nMargin + nTickOffset,nYMid+nTickLen,colTick);
} else {
gslc_DrawLine(pGui,nXMid,nY0+nMargin+ nTickOffset,
nXMid+nTickLen,nY0+nMargin + nTickOffset,colTick);
}
}
}
// Draw the track
if (!bVert) {
// Make the track highlight during glow
gslc_DrawLine(pGui,nX0+nMargin,nYMid,nX1-nMargin,nYMid,
bGlow? pElem->colElemFrameGlow : pElem->colElemFrame);
// Optionally draw a trim line
if (bTrim) {
gslc_DrawLine(pGui,nX0+nMargin,nYMid+1,nX1-nMargin,nYMid+1,colTrim);
}
} else {
// Make the track highlight during glow
gslc_DrawLine(pGui,nXMid,nY0+nMargin,nXMid,nY1-nMargin,
bGlow? pElem->colElemFrameGlow : pElem->colElemFrame);
// Optionally draw a trim line
if (bTrim) {
gslc_DrawLine(pGui,nXMid+1,nY0+nMargin,nXMid+1,nY1-nMargin,colTrim);
}
}
int16_t nCtrlX0,nCtrlY0;
gslc_tsRect rThumb;
if (!bVert) {
nCtrlX0 = nX0+nCtrlPos-nThumbSz;
nCtrlY0 = nYMid-nThumbSz;
} else {
nCtrlX0 = nXMid-nThumbSz;
nCtrlY0 = nY0+nCtrlPos-nThumbSz;
}
rThumb.x = nCtrlX0;
rThumb.y = nCtrlY0;
rThumb.w = 2*nThumbSz;
rThumb.h = 2*nThumbSz;
// Draw the thumb control
gslc_DrawFillRect(pGui,rThumb,(bGlow)?pElem->colElemFillGlow:pElem->colElemFill);
gslc_DrawFrameRect(pGui,rThumb,(bGlow)?pElem->colElemFrameGlow:pElem->colElemFrame);
if (bTrim) {
gslc_tsRect rThumbTrim;
rThumbTrim = gslc_ExpandRect(rThumb,-1,-1);
gslc_DrawFrameRect(pGui,rThumbTrim,pSlider->colTrim);
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
// This callback function is called by gslc_ElemSendEventTouch()
// after any touch event
bool gslc_ElemXSliderTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSliderTouch";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
gslc_tsGui* pGui = NULL;
gslc_tsElemRef* pElemRef = NULL;
gslc_tsElem* pElem = NULL;
gslc_tsXSlider* pSlider = NULL;
// Typecast the parameters to match the GUI
pGui = (gslc_tsGui*)(pvGui);
pElemRef = (gslc_tsElemRef*)(pvElemRef);
pElem = gslc_GetElemFromRef(pGui,pElemRef);
pSlider = (gslc_tsXSlider*)(pElem->pXData);
bool bGlowingOld = gslc_ElemGetGlow(pGui,pElemRef);
int16_t nPosRng;
int16_t nPos = 0;
bool bUpdatePos = false;
bool bIndexed = false;
switch(eTouch) {
case GSLC_TOUCH_DOWN_IN:
// Start glowing as must be over it
gslc_ElemSetGlow(pGui,pElemRef,true);
bUpdatePos = true;
break;
case GSLC_TOUCH_MOVE_IN:
gslc_ElemSetGlow(pGui,pElemRef,true);
bUpdatePos = true;
break;
case GSLC_TOUCH_MOVE_OUT:
gslc_ElemSetGlow(pGui,pElemRef,false);
bUpdatePos = true;
break;
case GSLC_TOUCH_UP_IN:
// End glow
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
case GSLC_TOUCH_UP_OUT:
// End glow
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
case GSLC_TOUCH_SET_REL:
case GSLC_TOUCH_SET_ABS:
bIndexed = true;
gslc_ElemSetGlow(pGui,pElemRef,true);
bUpdatePos = true;
break;
default:
return false;
break;
}
// If we need to update the slider position, calculate the value
// and perform the update
if (bUpdatePos) {
if (bIndexed) {
// The position is changed by direct control (eg. keyboard)
// instead of touch coordinates
// FIXME: Change the following to be either absolute or relative
// value assignment instead of inc/dec. Then the user code can
// define what the magnitude and direction should be.
if (eTouch == GSLC_TOUCH_SET_REL) {
// Overload the "nRelY" parameter
nPos = pSlider->nPos;
nPos += nRelY;
nPos = (nPos > pSlider->nPosMax)? pSlider->nPosMax : nPos;
nPos = (nPos < pSlider->nPosMin)? pSlider->nPosMin : nPos;
} else if (eTouch == GSLC_TOUCH_SET_ABS) {
// Overload the "nRelY" parameter
nPos = nRelY;
nPos = (nPos > pSlider->nPosMax)? pSlider->nPosMax : nPos;
nPos = (nPos < pSlider->nPosMin)? pSlider->nPosMin : nPos;
}
} else {
// Perform additional range checking
nRelX = (nRelX < 0)? 0 : nRelX;
nRelY = (nRelY < 0)? 0 : nRelY;
// Calc new position
nPosRng = pSlider->nPosMax - pSlider->nPosMin;
if (!pSlider->bVert) {
nPos = (int16_t)((int32_t)nRelX * (int32_t)nPosRng / (int32_t)pElem->rElem.w) + pSlider->nPosMin;
} else {
nPos = (int16_t)((int32_t)nRelY * (int32_t)nPosRng / (int32_t)pElem->rElem.h) + pSlider->nPosMin;
}
}
// Update the slider
gslc_ElemXSliderSetPos(pGui,pElemRef,nPos);
}
// If the slider changed state, redraw
if (gslc_ElemGetGlow(pGui,pElemRef) != bGlowingOld) {
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
return true;
#endif // !DRV_TOUCH_NONE
}
// ============================================================================

315
src/guislice/XSlider.h Normal file
View file

@ -0,0 +1,315 @@
#ifndef _GUISLICE_EX_XSLIDER_H_
#define _GUISLICE_EX_XSLIDER_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Slider control
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XSlider.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Slider
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_SLIDER GSLC_TYPE_BASE_EXTEND + 2
/// Callback function for slider feedback
typedef bool (*GSLC_CB_XSLIDER_POS)(void* pvGui,void* pvElem,int16_t nPos);
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Slider element
typedef struct {
// Config
bool bVert; ///< Orientation: true if vertical, else horizontal
int16_t nThumbSz; ///< Size of the thumb control
int16_t nPosMin; ///< Minimum position value of the slider
int16_t nPosMax; ///< Maximum position value of the slider
// Style config
uint16_t nTickDiv; ///< Style: number of tickmark divisions (0 for none)
int16_t nTickLen; ///< Style: length of tickmarks
gslc_tsColor colTick; ///< Style: color of ticks
bool bTrim; ///< Style: show a trim color
gslc_tsColor colTrim; ///< Style: color of trim
// State
int16_t nPos; ///< Current position value of the slider
// Callbacks
GSLC_CB_XSLIDER_POS pfuncXPos; ///< Callback func ptr for position update
} gslc_tsXSlider;
///
/// Create a Slider Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining checkbox size
/// \param[in] nPosMin: Minimum position value
/// \param[in] nPosMax: Maximum position value
/// \param[in] nPos: Starting position value
/// \param[in] nThumbSz: Size of the thumb control
/// \param[in] bVert: Orientation (true for vertical)
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXSliderCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXSlider* pXData,gslc_tsRect rElem,int16_t nPosMin,int16_t nPosMax,int16_t nPos,
uint16_t nThumbSz,bool bVert);
///
/// Set a Slider element's current position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] bTrim: Show a colored trim?
/// \param[in] colTrim: Color of trim
/// \param[in] nTickDiv: Number of tick divisions to show (0 for none)
/// \param[in] nTickLen: Length of tickmarks
/// \param[in] colTick: Color of ticks
///
/// \return none
///
void gslc_ElemXSliderSetStyle(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,
bool bTrim,gslc_tsColor colTrim,uint16_t nTickDiv,
int16_t nTickLen,gslc_tsColor colTick);
///
/// Get a Slider element's current position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return Current slider position
///
int gslc_ElemXSliderGetPos(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Set a Slider element's current position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nPos: New position value
///
/// \return none
///
void gslc_ElemXSliderSetPos(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,int16_t nPos);
///
/// Assign the position callback function for a slider
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] funcCb: Function pointer to position routine (or NULL for none)
///
/// \return none
///
void gslc_ElemXSliderSetPosFunc(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,GSLC_CB_XSLIDER_POS funcCb);
///
/// Draw a Slider element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSliderDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Handle touch events to Slider element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSliderTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
/// \def gslc_ElemXSliderCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,
/// nPosMin_,nPosMax_,nPos_,nThumbSz_,bVert_,colFrame_,colFill_)
///
/// Create a Slider Element in Flash
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Unique element ID to assign
/// \param[in] nPage: Page ID to attach element to
/// \param[in] nX: X coordinate of element
/// \param[in] nY: Y coordinate of element
/// \param[in] nW: Width of element
/// \param[in] nH: Height of element
/// \param[in] nPosMin_: Minimum position value
/// \param[in] nPosMax_: Maximum position value
/// \param[in] nPos_: Starting position value
/// \param[in] nThumbSz_: Size of the thumb control
/// \param[in] bVert_: Orientation (true for vertical)
/// \param[in] colFrame_: Color of the element frame
/// \param[in] colFill_: Color of the element fill
///
/// \return none
///
#if (GSLC_USE_PROGMEM)
#define gslc_ElemXSliderCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH, \
nPosMin_,nPosMax_,nPos_,nThumbSz_,bVert_,colFrame_,colFill_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_CLICK_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXSlider sSlider##nElemId; \
sSlider##nElemId.bVert = bVert_; \
sSlider##nElemId.nThumbSz = nThumbSz_; \
sSlider##nElemId.nPosMin = nPosMin_; \
sSlider##nElemId.nPosMax = nPosMax_; \
sSlider##nElemId.nTickDiv = 0; \
sSlider##nElemId.nTickLen = 0; \
sSlider##nElemId.colTick = GSLC_COL_WHITE; \
sSlider##nElemId.bTrim = false; \
sSlider##nElemId.colTrim = GSLC_COL_BLACK; \
sSlider##nElemId.nPos = nPos_; \
sSlider##nElemId.pfuncXPos = NULL; \
static const gslc_tsElem sElem##nElemId PROGMEM = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_SLIDER, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sSlider##nElemId), \
NULL, \
&gslc_ElemXSliderDraw, \
&gslc_ElemXSliderTouch, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#else
#define gslc_ElemXSliderCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH, \
nPosMin_,nPosMax_,nPos_,nThumbSz_,bVert_,colFrame_,colFill_) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_CLICK_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXSlider sSlider##nElemId; \
sSlider##nElemId.bVert = bVert_; \
sSlider##nElemId.nThumbSz = nThumbSz_; \
sSlider##nElemId.nPosMin = nPosMin_; \
sSlider##nElemId.nPosMax = nPosMax_; \
sSlider##nElemId.nTickDiv = 0; \
sSlider##nElemId.nTickLen = 0; \
sSlider##nElemId.colTick = GSLC_COL_WHITE; \
sSlider##nElemId.bTrim = false; \
sSlider##nElemId.colTrim = GSLC_COL_BLACK; \
sSlider##nElemId.nPos = nPos_; \
sSlider##nElemId.pfuncXPos = NULL; \
static const gslc_tsElem sElem##nElemId = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_SLIDER, \
(gslc_tsRect){nX,nY,nW,nH}, \
GSLC_GROUP_ID_NONE, \
colFrame_,colFill_,colFrame_,colFill_, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sSlider##nElemId), \
NULL, \
&gslc_ElemXSliderDraw, \
&gslc_ElemXSliderTouch, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_CONST | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XSLIDER_H_

425
src/guislice/XSpinner.c Normal file
View file

@ -0,0 +1,425 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XSpinner.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XSpinner.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
#if (GSLC_FEATURE_COMPOUND)
// ============================================================================
// Extended Element: Spinner
// - Spinner element demonstrates a simple up/down counter
// - This is a compound element containing two buttons and
// a text area to represent the current value
// ============================================================================
// Private sub Element ID definitions
static const int16_t SPINNER_ID_BTN_INC = 100;
static const int16_t SPINNER_ID_BTN_DEC = 101;
static const int16_t SPINNER_ID_TXT = 102;
// Create a compound element
// - For now just two buttons and a text area
gslc_tsElemRef* gslc_ElemXSpinnerCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage, gslc_tsXSpinner* pXData,
gslc_tsRect rElem, int16_t nMin, int16_t nMax, int16_t nVal, int16_t nIncr,
int8_t nFontId, int8_t nButtonSz, GSLC_CB_INPUT cbInput)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSpinnerCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL, FUNCSTR);
return NULL;
}
// determine size of our text box
// first calculate number of digits required
int16_t nTxtBoxW,nTxtBoxH,nBtnPosY;
char acTxtNum[XSPINNER_STR_LEN];
// FIXME: Consider replacing the sprintf() with an optimized function to
// conserve RAM. Potentially leverage GSLC_DEBUG2_PRINT().
nTxtBoxW = rElem.w - 2 * (nButtonSz);
// Determine the maximum width of a digit, we will use button size for height.
// now we can work out our rectangle
nTxtBoxH = rElem.h;
nBtnPosY = rElem.y + (rElem.h - nButtonSz) / 2;
// set our intial value for our text field
snprintf(acTxtNum, XSPINNER_STR_LEN - 1, "%d", nVal);
gslc_tsElem sElem;
// Initialize composite element
sElem = gslc_ElemCreate(pGui, nElemId, nPage, GSLC_TYPEX_SPINNER, rElem, NULL, 0, GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN; // Don't need to glow outer element
sElem.nGroup = GSLC_GROUP_ID_NONE;
pXData->nCounter = nVal;
pXData->nMin = nMin;
pXData->nMax = nMax;
pXData->nIncr = nIncr;
pXData->pfuncXInput = cbInput;
// Initialize the collection of sub-elements within the compound element
// - XSELNUM_COMP_CNT defines the maximum number of sub-elements we have allocated space for
// - Note that this example shows RAM being used for storage
gslc_CollectReset(&pXData->sCollect, pXData->asElem, XSPINNER_COMP_CNT, pXData->asElemRef, XSPINNER_COMP_CNT);
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXSpinnerDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = &gslc_ElemXSpinnerTouch;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
// Now create the sub elements
// - Ensure page is set to GSLC_PAGE_NONE so that
// we create the element struct but not add it to a specific page.
// - When we create an element with GSLC_PAGE_NONE it is
// saved in the GUI's temporary element storage.
// - When we have finished creating / styling the element, we then
// copy it into the permanent sub-element storage
// - The element IDs assigned to the sub-elements are
// arbitrary (with local scope in the compound element),
// so they don't need to be unique globally across the GUI.
gslc_tsElemRef* pElemRefTmp = NULL;
gslc_tsElem* pElemTmp = NULL;
gslc_tsElemRef* pElemRef = NULL;
// Determine offset coordinate of compound element so that we can
// specify relative positioning during the sub-element Create() operations.
int16_t nOffsetX = rElem.x;
int16_t nOffsetY = rElem.y;
gslc_tsRect rSubElem;
// Create button sub-element
strcpy(pXData->acIncr,"\030");
rSubElem = (gslc_tsRect) { nOffsetX+nTxtBoxW, nBtnPosY, nButtonSz, nButtonSz };
rSubElem = gslc_ExpandRect(rSubElem, -1, -1);
pElemRefTmp = gslc_ElemCreateBtnTxt(pGui, SPINNER_ID_BTN_INC, GSLC_PAGE_NONE,
rSubElem, pXData->acIncr, 2, nFontId, &gslc_ElemXSpinnerClick);
gslc_ElemSetCol(pGui, pElemRefTmp, (gslc_tsColor) { 0, 0, 192 }, (gslc_tsColor) { 0, 0, 128 }, (gslc_tsColor) { 0, 0, 224 });
gslc_ElemSetTxtCol(pGui, pElemRefTmp, GSLC_COL_WHITE);
pElemTmp = gslc_GetElemFromRef(pGui, pElemRefTmp);
gslc_CollectElemAdd(pGui, &pXData->sCollect, pElemTmp, GSLC_ELEMREF_DEFAULT);
// Create button sub-element
strcpy(pXData->acDecr,"\031");
rSubElem = (gslc_tsRect) { nOffsetX+nTxtBoxW+nButtonSz, nBtnPosY, nButtonSz, nButtonSz };
rSubElem = gslc_ExpandRect(rSubElem, -1, -1);
pElemRefTmp = gslc_ElemCreateBtnTxt(pGui, SPINNER_ID_BTN_DEC, GSLC_PAGE_NONE,
rSubElem, pXData->acDecr, 2, nFontId, &gslc_ElemXSpinnerClick);
gslc_ElemSetCol(pGui, pElemRefTmp, (gslc_tsColor) { 0, 0, 192 }, (gslc_tsColor) { 0, 0, 128 }, (gslc_tsColor) { 0, 0, 224 });
gslc_ElemSetTxtCol(pGui, pElemRefTmp, GSLC_COL_WHITE);
pElemTmp = gslc_GetElemFromRef(pGui, pElemRefTmp);
gslc_CollectElemAdd(pGui, &pXData->sCollect, pElemTmp, GSLC_ELEMREF_DEFAULT);
// Create dynamic text sub-element
// - Note that we are using string storage in the extended element data
// structure (pXData).
rSubElem = (gslc_tsRect) { nOffsetX, nOffsetY, nTxtBoxW, nTxtBoxH };
rSubElem = gslc_ExpandRect(rSubElem, -1, -1);
strncpy(pXData->acElemTxt[0], acTxtNum, XSPINNER_STR_LEN - 1);
pElemRefTmp = gslc_ElemCreateTxt(pGui, SPINNER_ID_TXT, GSLC_PAGE_NONE,
rSubElem, pXData->acElemTxt[0], XSPINNER_STR_LEN, nFontId);
gslc_ElemSetTxtAlign(pGui, pElemRefTmp, GSLC_ALIGN_MID_MID);
pElemTmp = gslc_GetElemFromRef(pGui, pElemRefTmp);
gslc_CollectElemAdd(pGui, &pXData->sCollect, pElemTmp, GSLC_ELEMREF_DEFAULT);
// Now proceed to add the compound element to the page
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui, nPage, &sElem, GSLC_ELEMREF_DEFAULT);
// save our ElemRef for the callback
pXData->pElemRef = pElemRef;
// Now propagate the parent relationship to enable a cascade
// of redrawing from low-level elements to the top
gslc_CollectSetParent(pGui, &pXData->sCollect, pElemRef);
return pElemRef;
}
else {
GSLC_DEBUG2_PRINT("ERROR: ElemXSpinnerCreate(%s) Compound elements inside compound elements not supported\n", "");
return NULL;
// TODO: For now, disable compound elements within
// compound elements. If we want to enable this, we
// would probably use the temporary element reference
// the GUI.
// Save as temporary element for further processing
//pGui->sElemTmp = sElem; // Need fixing
//return &(pGui->sElemTmp); // Need fixing
}
}
bool gslc_ElemXSpinnerSetChars(void* pvGui,gslc_tsElemRef* pElemRef,uint8_t cIncr, uint8_t cDecr)
{
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElem* pElem = gslc_GetElemFromRefD(pGui, pElemRef, __LINE__);
if (!pElem) return false;
gslc_tsXSpinner* pSpinner = (gslc_tsXSpinner*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_SPINNER, __LINE__);
if (!pSpinner) return false;
char acIncr[2] = { cIncr, '\0'};
char acDecr[2] = { cDecr, '\0'};
gslc_tsElemRef* pElemRefUp = gslc_CollectFindElemById(pGui,&pSpinner->sCollect,SPINNER_ID_BTN_INC);
gslc_ElemSetTxtStr(pGui,pElemRefUp,acIncr);
gslc_tsElemRef* pElemRefDown = gslc_CollectFindElemById(pGui,&pSpinner->sCollect,SPINNER_ID_BTN_DEC);
gslc_ElemSetTxtStr(pGui,pElemRefDown,acDecr);
return true;
}
// Redraw the compound element
// - When drawing a compound element, we clear the background
// and then redraw the sub-element collection.
bool gslc_ElemXSpinnerDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsXSpinner* pSpinner = (gslc_tsXSpinner*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_SPINNER, __LINE__);
if (!pSpinner) return false;
bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
// Draw the compound element fill (background)
// - Should only need to do this in full redraw
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFillRect(pGui,pElem->rElem,(bGlow)?pElem->colElemFillGlow:pElem->colElemFill);
}
// Draw the sub-elements
// - For now, force redraw of entire compound element
gslc_tsCollect* pCollect = &pSpinner->sCollect;
if (eRedraw != GSLC_REDRAW_NONE) {
uint8_t sEventSubType = GSLC_EVTSUB_DRAW_NEEDED;
if (eRedraw == GSLC_REDRAW_FULL) {
sEventSubType = GSLC_EVTSUB_DRAW_FORCE;
}
gslc_tsEvent sEvent = gslc_EventCreate(pGui, GSLC_EVT_DRAW, sEventSubType, (void*)(pCollect), NULL);
gslc_CollectEvent(pGui, sEvent);
}
// Optionally, draw a frame around the compound element
// - This could instead be done by creating a sub-element
// of type box.
// - We don't need to show any glowing of the compound element
if (eRedraw == GSLC_REDRAW_FULL) {
gslc_DrawFrameRect(pGui, pElem->rElem, (bGlow) ? pElem->colElemFrameGlow : pElem->colElemFrame);
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
// Fetch the current value of the element's counter
int gslc_ElemXSpinnerGetCounter(gslc_tsGui* pGui,gslc_tsXSpinner* pSpinner)
{
if ((pGui == NULL) || (pSpinner == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSpinnerGetCounter";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return 0;
}
return pSpinner->nCounter;
}
void gslc_ElemXSpinnerSetCounter(gslc_tsGui* pGui,gslc_tsXSpinner* pSpinner,int16_t nCount)
{
if (pSpinner == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXSpinnerSetCounter";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
pSpinner->nCounter = nCount;
// Determine new counter text
// FIXME: Consider replacing the printf() with an optimized function to
// conserve RAM. Potentially leverage GSLC_DEBUG2_PRINT().
char acStrNew[GSLC_LOCAL_STR_LEN];
snprintf(acStrNew,GSLC_LOCAL_STR_LEN,"%hd",pSpinner->nCounter);
// Update the element
gslc_tsElemRef* pElemRef = gslc_CollectFindElemById(pGui,&pSpinner->sCollect,SPINNER_ID_TXT);
gslc_ElemSetTxtStr(pGui,pElemRef,acStrNew);
}
// Handle the compound element main functionality
// - This routine is called by gslc_ElemEvent() to handle
// any click events that resulted from the touch tracking process.
// - The code here will generally represent the core
// functionality of the compound element and any communication
// between sub-elements.
// - pvElemRef is a void pointer to the element ref being tracked. From
// the pElemRefParent member we can get the parent/compound element
// data structures.
bool gslc_ElemXSpinnerClick(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRefD(pGui, pElemRef, __LINE__);
if (!pElem) return false;
// Fetch the parent of the clicked element which is the compound
// element itself. This enables us to access the extra control data.
gslc_tsElemRef* pElemRefParent = pElem->pElemRefParent;
if (pElemRefParent == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXSpinnerClick(%s) parent ElemRef ptr NULL\n","");
return false;
}
gslc_tsElem* pElemParent = gslc_GetElemFromRef(pGui,pElemRefParent);
gslc_tsXSpinner* pSpinner = (gslc_tsXSpinner*)(pElemParent->pXData);
if (pSpinner == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXSpinnerClick() element (ID=%d) has NULL pXData\n",pElem->nId);
return false;
}
// Begin the core compound element functionality
int nCounter = pSpinner->nCounter;
// Handle the various button presses
GSLC_CB_INPUT pfuncXInput = NULL;
if (eTouch == GSLC_TOUCH_UP_IN) {
// Get the tracked element ID
gslc_tsElemRef* pElemRefTracked = pSpinner->sCollect.pElemRefTracked;
gslc_tsElem* pElemTracked = gslc_GetElemFromRef(pGui,pElemRefTracked);
int nSubElemId = pElemTracked->nId;
if (nSubElemId == SPINNER_ID_BTN_INC) {
// Increment button
if (nCounter < pSpinner->nMax) {
nCounter += pSpinner->nIncr;
}
gslc_ElemXSpinnerSetCounter(pGui,pSpinner,nCounter);
} else if (nSubElemId == SPINNER_ID_BTN_DEC) {
// Decrement button
if (nCounter > pSpinner->nMin) {
nCounter -= pSpinner->nIncr;
}
gslc_ElemXSpinnerSetCounter(pGui,pSpinner,nCounter);
}
// Invoke the callback function
pfuncXInput = pSpinner->pfuncXInput;
if (pfuncXInput != NULL) {
(*pfuncXInput)(pvGui, (void*)(pSpinner->pElemRef), XSPINNER_CB_STATE_UPDATE, NULL);
}
} // eTouch
return true;
#endif // !DRV_TOUCH_NONE
}
bool gslc_ElemXSpinnerTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXSpinner* pSpinner = (gslc_tsXSpinner*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_SPINNER, __LINE__);
if (!pSpinner) return false;
// Get Collection
gslc_tsCollect* pCollect = &pSpinner->sCollect;
return gslc_CollectTouchCompound(pvGui, pvElemRef, eTouch, nRelX, nRelY, pCollect);
#endif // !DRV_TOUCH_NONE
}
#endif // GSLC_FEATURE_COMPOUND
// ============================================================================

210
src/guislice/XSpinner.h Normal file
View file

@ -0,0 +1,210 @@
#ifndef _GUISLICE_EX_XSPINNER_H_
#define _GUISLICE_EX_XSPINNER_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Spinner (number selection) control
// - Extension by Paul Conti, Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XSpinner.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if (GSLC_FEATURE_COMPOUND)
// ============================================================================
// Extended Element: Spinner (Number Selector)
// - Demonstration of a compound element consisting of
// a counter text field along with an increment and
// decrement button.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_SPINNER GSLC_TYPE_BASE_EXTEND + 15
// Define the max number of sub-elements in compound element
#define XSPINNER_COMP_CNT 3
// Define the max string length to allocate for dynamic text elements
#define XSPINNER_STR_LEN 8
// Define the status for GSLC_CB_INPUT callback
#define XSPINNER_CB_STATE_UPDATE 3
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Spinner element
typedef struct {
// Core functionality for Spinner
int16_t nMin; ///< Minimum control value
int16_t nMax; ///< Maximum control value
int16_t nIncr; ///< Increment by value
int16_t nCounter; ///< Current value
GSLC_CB_INPUT pfuncXInput; ///< Callback func ptr for input ready
gslc_tsElemRef* pElemRef; ///< Save our ElemRef for the callback
// Internal sub-element members
gslc_tsCollect sCollect; ///< Collection management for sub-elements
gslc_tsElemRef asElemRef[XSPINNER_COMP_CNT]; ///< Storage for sub-element references
gslc_tsElem asElem[XSPINNER_COMP_CNT]; ///< Storage for sub-elements
// Provide storage for any dynamic text elements
// Simple example here uses fixed-length character array
char acElemTxt[1][XSPINNER_STR_LEN]; ///< Storage for strings
char acIncr[2]; ///< Increment character string
char acDecr[2]; ///< Decrement character string
} gslc_tsXSpinner;
///
/// Create a Spinner Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining overall size
/// \param[in] nMin: Minimum value of Spinner
/// \param[in] nMax: Maximum value of Spinner
/// \param[in] nVal: Starting value of Spinner
/// \param[in] nIncr: Increment Spinner by this value
/// \param[in] nFontId: Font ID to use for drawing the element
/// \param[in] nButtonSz: Size of individual buttons
/// \param[in] cbInput: Callback for touch events
///
/// \return Pointer to Element or NULL if failure
///
gslc_tsElemRef* gslc_ElemXSpinnerCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage, gslc_tsXSpinner* pXData,
gslc_tsRect rElem, int16_t nMin, int16_t nMax, int16_t nVal, int16_t nIncr,
int8_t nFontId, int8_t nButtonSz, GSLC_CB_INPUT cbInput);
///
/// Set Up and Down characters for the Spinner element
/// - Called during redraw
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pElemRef: Ptr to ElementRef
/// \param[in] cIncr: Character to use to indicate incrementing the spinner
/// \param[in] cDecr: Character to use to indicate decrementing the spinner
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSpinnerSetChars(void* pvGui,gslc_tsElemRef* pElemRef,uint8_t cIncr, uint8_t cDecr);
///
/// Draw a Spinner element on the screen
/// - Called during redraw
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSpinnerDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Get the current counter associated with Spinner
///
/// \param[in] pGui: Ptr to GUI
/// \param[in] pSpinner: Ptr to Element
///
/// \return Current counter value
///
int gslc_ElemXSpinnerGetCounter(gslc_tsGui* pGui,gslc_tsXSpinner* pSpinner);
///
/// Set the current counter associated with Spinner
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pSpinner: Ptr to Element
/// \param[in] nCount: New counter value
///
/// \return none
///
void gslc_ElemXSpinnerSetCounter(gslc_tsGui* pGui,gslc_tsXSpinner* pSpinner,int16_t nCount);
///
/// Handle a click event within the Spinner
/// - This is called internally by the Spinner touch handler
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nX: Touch X coord
/// \param[in] nY: Touch Y coord
///
/// \return none
///
bool gslc_ElemXSpinnerClick(void* pvGui,void *pvElemRef,gslc_teTouch eTouch,int16_t nX,int16_t nY);
///
/// Handle touch (up,down,move) events to Spinner element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXSpinnerTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY);
#endif // GLSC_COMPOUND
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XSPINNER_H_

307
src/guislice/XTemplate.c Normal file
View file

@ -0,0 +1,307 @@
// =======================================================================
// GUIslice library (extensions template)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XTemplate.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XTemplate.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Template example
// - Demonstrates minimum code to implement an extended element
// ============================================================================
// Create a text element and add it to the GUI element list
// - Defines default styling for the element
// - Defines callback for redraw and touch
gslc_tsElemRef* gslc_ElemXTemplateCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXTemplate* pXData, gslc_tsRect rElem, char* pStrBuf, uint8_t nStrBufMax, int16_t nFontId)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTemplateCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_TEMPLATE,rElem,pStrBuf,nStrBufMax,nFontId);
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_GRAY;
sElem.colElemText = GSLC_COL_WHITE;
sElem.colElemTextGlow = GSLC_COL_WHITE;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_GLOW_EN;
sElem.eTxtAlign = GSLC_ALIGN_MID_LEFT;
sElem.nGroup = GSLC_GROUP_ID_NONE;
// TODO:
// - Initialize any pxData members with the constructor parameters
// eg. pXData->nPos = nPos;
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXTemplateDraw;
// Specify the custom touch tracking callback
sElem.pfuncXTouch = &gslc_ElemXTemplateTouch;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
// Redraw the element
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXTemplateDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTemplateDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
// Fetch the element's extended data structure
gslc_tsXTemplate* pTemplate = (gslc_tsXTemplate*)(pElem->pXData);
if (pTemplate == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXTemplateDraw(%s) pXData is NULL\n","");
return false;
}
// Template example drawing routine implements formatted text field
// --------------------------------------------------------------------------
// Init for default drawing
// --------------------------------------------------------------------------
bool bGlowEn,bGlowing,bGlowNow;
//int16_t nElemX,nElemY;
//uint16_t nElemW,nElemH;
//nElemX = pElem->rElem.x;
//nElemY = pElem->rElem.y;
//nElemW = pElem->rElem.w;
//nElemH = pElem->rElem.h;
bGlowEn = pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN; // Does the element support glow state?
bGlowing = gslc_ElemGetGlow(pGui,pElemRef); // Element should be glowing (if enabled)
bGlowNow = bGlowEn & bGlowing; // Element is currently glowing
gslc_tsColor colBg = GSLC_COL_BLACK;
// --------------------------------------------------------------------------
// Background
// --------------------------------------------------------------------------
// Fill in the background
gslc_tsRect rElemInner = pElem->rElem;
// - If both fill and frame are enabled then contract
// the fill region slightly so that we don't overdraw
// the frame (prevent unnecessary flicker).
if ((pElem->nFeatures & GSLC_ELEM_FEA_FILL_EN) && (pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN)) {
// NOTE: If the region is already too small to shrink (eg. w=1 or h=1)
// then a zero dimension box will be returned by ExpandRect() and not drawn
rElemInner = gslc_ExpandRect(rElemInner,-1,-1);
}
// - This also changes the fill color if selected and glow state is enabled
if (pElem->nFeatures & GSLC_ELEM_FEA_FILL_EN) {
if (bGlowEn && bGlowing) {
colBg = pElem->colElemFillGlow;
} else {
colBg = pElem->colElemFill;
}
if (pElem->nFeatures & GSLC_ELEM_FEA_ROUND_EN) {
gslc_DrawFillRoundRect(pGui, rElemInner, pGui->nRoundRadius, colBg);
} else {
gslc_DrawFillRect(pGui, rElemInner, colBg);
}
} else {
// TODO: If unfilled, then we might need
// to redraw the background layer(s)
}
// --------------------------------------------------------------------------
// Frame
// --------------------------------------------------------------------------
// Frame the region
if (pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN) {
if (pElem->nFeatures & GSLC_ELEM_FEA_ROUND_EN) {
gslc_DrawFrameRoundRect(pGui, pElem->rElem, pGui->nRoundRadius, pElem->colElemFrame);
} else {
gslc_DrawFrameRect(pGui, pElem->rElem, pElem->colElemFrame);
}
}
// --------------------------------------------------------------------------
// Text overlays
// --------------------------------------------------------------------------
// Draw text string if defined
if (pElem->pStrBuf) {
gslc_tsColor colTxt = (bGlowNow)? pElem->colElemTextGlow : pElem->colElemText;
int8_t nMarginX = pElem->nTxtMarginX;
int8_t nMarginY = pElem->nTxtMarginY;
gslc_DrawTxtBase(pGui, pElem->pStrBuf, pElem->rElem, pElem->pTxtFont, pElem->eTxtFlags,
pElem->eTxtAlign, colTxt, colBg, nMarginX, nMarginY);
}
// --------------------------------------------------------------------------
// Mark the element as no longer requiring redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
return true;
}
// This callback function is called by gslc_ElemSendEventTouch()
// after any touch event
bool gslc_ElemXTemplateTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
// Define any specific touch handling here
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTemplateTouch";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
gslc_tsGui* pGui = NULL;
gslc_tsElemRef* pElemRef = NULL;
//gslc_tsElem* pElem = NULL;
//gslc_tsXTemplate* pTemplate = NULL;
// Typecast the parameters to match the GUI
pGui = (gslc_tsGui*)(pvGui);
pElemRef = (gslc_tsElemRef*)(pvElemRef);
//pElem = gslc_GetElemFromRef(pGui,pElemRef);
//pTemplate = (gslc_tsXTemplate*)(pElem->pXData);
bool bGlowingOld = gslc_ElemGetGlow(pGui,pElemRef);
// Example touch handler simply tracks "glowing" status
switch(eTouch) {
case GSLC_TOUCH_DOWN_IN:
// Start glowing as must be over it
gslc_ElemSetGlow(pGui,pElemRef,true);
break;
case GSLC_TOUCH_MOVE_IN:
gslc_ElemSetGlow(pGui,pElemRef,true);
break;
case GSLC_TOUCH_MOVE_OUT:
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
case GSLC_TOUCH_UP_IN:
// End glow
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
case GSLC_TOUCH_UP_OUT:
// End glow
gslc_ElemSetGlow(pGui,pElemRef,false);
break;
case GSLC_TOUCH_SET_REL:
case GSLC_TOUCH_SET_ABS:
gslc_ElemSetGlow(pGui,pElemRef,true);
break;
default:
return false;
break;
}
// If the slider changed state, redraw
if (gslc_ElemGetGlow(pGui,pElemRef) != bGlowingOld) {
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_FULL);
}
return true;
#endif // !DRV_TOUCH_NONE
}
// ============================================================================

128
src/guislice/XTemplate.h Normal file
View file

@ -0,0 +1,128 @@
#ifndef _GUISLICE_EX_XTEMPLATE_H_
#define _GUISLICE_EX_XTEMPLATE_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Example template
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XTemplate.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Example template
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_TEMPLATE GSLC_TYPE_BASE_EXTEND + 12
/// Callback function for slider feedback
//typedef bool (*GSLC_CB_XTEMPLATE_POS)(void* pvGui,void* pvElem,int16_t nPos);
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Slider element
typedef struct {
// Config
// Style config
// State
//int16_t nPos; ///< Example position value
// Callbacks
//GSLC_CB_XTEXTEX_POS pfuncXPos; ///< Callback func ptr for position update
} gslc_tsXTemplate;
///
/// Create an Extended Text Field Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining element size
/// \param[in] pStrBuf: Ptr to string buffer
/// \param[in] nStrBufMax: Maximum buffer alength allocated to pStrBuf
/// \param[in] nFontId: ID of font to use for text output
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXTemplateCreate(gslc_tsGui* pGui, int16_t nElemId, int16_t nPage,
gslc_tsXTemplate* pXData, gslc_tsRect rElem, char* pStrBuf, uint8_t nStrBufMax, int16_t nFontId);
///
/// Draw the template element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXTemplateDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Handle touch events to template element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element ref (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXTemplateTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XTEMPLATE_H_

663
src/guislice/XTextbox.c Normal file
View file

@ -0,0 +1,663 @@
// =======================================================================
// GUIslice library (extensions)
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XTextbox.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
#include "XTextbox.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
gslc_tsElemRef* gslc_ElemXTextboxCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXTextbox* pXData,gslc_tsRect rElem,int16_t nFontId,char* pBuf,
uint16_t nBufRows,uint16_t nBufCols)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTextboxCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_TEXTBOX,rElem,NULL,0,nFontId);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures &= ~GSLC_ELEM_FEA_GLOW_EN;
// Default group assignment. Can override later with ElemSetGroup()
sElem.nGroup = GSLC_GROUP_ID_NONE;
// Define other extended data
pXData->pBuf = pBuf;
pXData->nMarginX = 5;
pXData->nMarginY = 5;
pXData->bWrapEn = true;
pXData->nCurPosX = 0;
pXData->nCurPosY = 0;
pXData->nBufRows = nBufRows;
pXData->nBufCols = nBufCols;
pXData->nBufPosX = 0;
pXData->nBufPosY = 0;
pXData->nWndRowStart = 0;
pXData->nRedrawRow = XTEXTBOX_REDRAW_ALL;
// Clear the buffer
memset(pBuf,0,nBufRows*nBufCols*sizeof(char));
// Precalculate certain parameters
// Determine the maximum size of a character
// - For now, assume we are using a monospaced font and derive
// text pixel coords from the size of a worst-case character.
int16_t nChOffsetX, nChOffsetY, nChOffsetTmp;
uint16_t nChSzW,nChSzH,nChSzTmp;
// Fetch X & Y sizing and offsets independently, based on characters that
// are likely to maximize the ascenders / descenders / width attributes
char acMonoH[3] = "p$";
char acMonoW[2] = "W";
gslc_DrvGetTxtSize(pGui, sElem.pTxtFont, (char*)&acMonoH, sElem.eTxtFlags, &nChOffsetTmp, &nChOffsetY, &nChSzTmp, &nChSzH);
gslc_DrvGetTxtSize(pGui, sElem.pTxtFont, (char*)&acMonoW, sElem.eTxtFlags, &nChOffsetX, &nChOffsetTmp, &nChSzW, &nChSzTmp);
pXData->nWndCols = (rElem.w - (2*pXData->nMarginX)) / nChSzW;
pXData->nWndRows = (rElem.h - (2*pXData->nMarginY)) / nChSzH;
// Adjust margin to correct for character offsets
pXData->nMarginX -= nChOffsetX;
pXData->nMarginY -= nChOffsetY;
pXData->nChSizeX = nChSzW;
pXData->nChSizeY = nChSzH;
// Determine if scrollbar should be enabled
if (pXData->nWndRows >= pXData->nBufRows) {
// Disable scrollbar as the window is larger
// than the number of rows in the buffer
pXData->bScrollEn = false;
pXData->nScrollPos = 0;
} else {
// Scrollbar is enabled
pXData->bScrollEn = true;
pXData->nScrollPos = 0;
}
sElem.pXData = (void*)(pXData);
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXTextboxDraw;
sElem.pfuncXTouch = NULL;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
void gslc_ElemXTextboxReset(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTextboxReset";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsXTextbox* pBox;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXTextbox*)(pElem->pXData);
// Reset the positional state
pBox->nCurPosX = 0;
pBox->nCurPosY = 0;
pBox->nBufPosX = 0;
pBox->nBufPosY = 0;
pBox->nWndRowStart = 0;
// Clear the buffer
memset(pBox->pBuf,0,pBox->nBufRows*pBox->nBufCols*sizeof(char));
// Set the redraw flag
// - Only need incremental redraw
pBox->nRedrawRow = XTEXTBOX_REDRAW_ALL; // All-row update
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
// Advance the buffer writer to the next line
// The window is also shifted if we are eating the first row
void gslc_ElemXTextboxLineWrAdv(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTextboxLineWrAdv";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
gslc_tsXTextbox* pBox;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXTextbox*)(pElem->pXData);
pBox->nBufPosX = 0;
pBox->nBufPosY++;
// Wrap the pointers around end of buffer
pBox->nBufPosY = pBox->nBufPosY % pBox->nBufRows;
// Did the buffer write pointer start to encroach upon
// the visible window region? If so, shift the window
if (pBox->nBufPosY == pBox->nWndRowStart) {
// Advance the window (with wrap if needed)
pBox->nWndRowStart = (pBox->nWndRowStart + 1) % pBox->nBufRows;
// Ensure all rows get redrawn
pBox->nRedrawRow = XTEXTBOX_REDRAW_ALL;
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
void gslc_ElemXTextboxScrollSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,uint8_t nScrollPos,uint8_t nScrollMax)
{
gslc_tsXTextbox* pBox;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXTextbox*)(pElem->pXData);
// Ensure scrollbar is enabled
if (!pBox->bScrollEn) {
// Scrollbar is disabled, so ignore
return;
}
// Check for out-of-range values
if (nScrollPos > nScrollMax) {
GSLC_DEBUG2_PRINT("ERROR: ElemXTextboxScrollSet() pos [%u] exceeds max [%u]\n", nScrollPos,nScrollMax);
// Force the position to the max
nScrollPos = nScrollMax;
}
// Assign proportional value based on visible window region
uint16_t nScrollPosOld = pBox->nScrollPos;
pBox->nScrollPos = nScrollPos * (pBox->nBufRows - pBox->nWndRows) / nScrollMax;
// Set the redraw flag
// - Only need incremental redraw
// - Only redraw if changed actual scroll row
if (pBox->nScrollPos != nScrollPosOld) {
// Ensure all rows get redrawn
pBox->nRedrawRow = XTEXTBOX_REDRAW_ALL;
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Write a character to the buffer
// - Advance the write ptr, wrap if needed
// - If encroach upon buffer read ptr, then drop the oldest line from the buffer
// NOTE: This should not be called with newline char!
void gslc_ElemXTextboxBufAdd(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,unsigned char chNew,bool bAdvance)
{
gslc_tsXTextbox* pBox;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXTextbox*)(pElem->pXData);
// Ensure that we haven't gone past end of line
// - Note that we have to leave one extra byte for the line terminator (NULL)
if ((pBox->nBufPosX+1) >= pBox->nBufCols) {
if (pBox->bWrapEn) {
// Perform line wrap
// - Force a null at the end of the current line first
pBox->pBuf[pBox->nBufPosY * pBox->nBufCols + (pBox->nBufCols-1)] = 0;
gslc_ElemXTextboxLineWrAdv(pGui,pElemRef);
} else {
// Ignore the write
return;
}
}
uint16_t nBufPos = pBox->nBufPosY * pBox->nBufCols + pBox->nBufPosX;
// Add the character
pBox->pBuf[nBufPos] = chNew;
// Only trigger redraw if we have output a printable character
// to the display buffer. For simplicity, just check for
// null for now.
// TODO: Consider checking isprint()
if (chNew == 0) {
// Don't update redraw on non-printing characters
} else {
// Mark this specific row as needing redraw
if (pBox->nRedrawRow == XTEXTBOX_REDRAW_NONE) {
// No redraw was pending, so mark single row pending
pBox->nRedrawRow = pBox->nBufPosY;
}
else if (pBox->nRedrawRow == XTEXTBOX_REDRAW_ALL) {
// All-row redraw was pending, so no change
}
else {
// Single row redraw was pending
if (pBox->nRedrawRow != pBox->nBufPosY) {
// But the pending row differs from the current row,
// so promote redraw to all lines
pBox->nRedrawRow = XTEXTBOX_REDRAW_ALL;
}
else {
// Pending row is the same, so no change
}
}
} // chNew
// Optionally advance the pointer
// - The only time we don't advance is if we added NULL
// but note that in some special commands there may be
// zero values added, so we still need to advance these
if (bAdvance) {
pBox->nBufPosX++;
}
}
void gslc_ElemXTextboxColSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor nCol)
{
#if (GSLC_FEATURE_XTEXTBOX_EMBED == 0)
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXTextboxColSet() not enabled. Requires GSLC_FEATURE_XTEXTBOX_EMBED=1 %s\n","");
return;
#else
gslc_tsXTextbox* pBox = NULL;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXTextbox*)(pElem->pXData);
// Ensure that there are enough free columns in current
// buffer row to accommodate the color code (4 bytes)
if (pBox->nBufPosX +4 >= pBox->nBufCols) {
// Not enough space for the code, so ignore it
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXTextboxColSet() not enough cols [Pos=%u Cols=%u]\n",pBox->nBufPosX,pBox->nBufCols);
return;
}
gslc_ElemXTextboxBufAdd(pGui,pElemRef,GSLC_XTEXTBOX_CODE_COL_SET,true);
gslc_ElemXTextboxBufAdd(pGui,pElemRef,nCol.r,true);
gslc_ElemXTextboxBufAdd(pGui,pElemRef,nCol.g,true);
gslc_ElemXTextboxBufAdd(pGui,pElemRef,nCol.b,true);
#endif
}
void gslc_ElemXTextboxColReset(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
#if (GSLC_FEATURE_XTEXTBOX_EMBED == 0)
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXTextboxColReset() not enabled. Requires GSLC_FEATURE_XTEXTBOX_EMBED=1 %s\n","");
return;
#else
gslc_tsXTextbox* pBox = NULL;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXTextbox*)(pElem->pXData);
(void)pBox; // Unused
gslc_ElemXTextboxBufAdd(pGui,pElemRef,GSLC_XTEXTBOX_CODE_COL_RESET,true);
#endif
}
void gslc_ElemXTextboxWrapSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bWrapEn)
{
gslc_tsXTextbox* pBox = NULL;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
pBox = (gslc_tsXTextbox*)(pElem->pXData);
pBox->bWrapEn = bWrapEn;
}
void gslc_ElemXTextboxAdd(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,char* pTxt)
{
// Warn the user about mode compatibility
#if (GSLC_FEATURE_XTEXTBOX_EMBED)
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
//gslc_tsXTextbox* pBox = (gslc_tsXTextbox*)(pElem->pXData);
static bool bWarned = false; // Warn only once
bool bEncUtf8 = ((pElem->eTxtFlags & GSLC_TXT_ENC) == GSLC_TXT_ENC_UTF8);
if ((!bWarned) && (bEncUtf8)) {
// Continue to render the text, but issue warning to the user
GSLC_DEBUG2_PRINT("WARNING: ElemXTextboxAdd(%s) UTF-8 encoding not supported in GSLC_FEATURE_XTEXTBOX_EMBED=1 mode\n","");
bWarned = true;
}
#endif
// Add null-terminated string to the bottom of the buffer
// If the string exceeds the buffer length then it will wrap
// back to the beginning.
// TODO: Ensure that buffer wrap doesn't encroach upon visible region!
// TODO: Assert (pBox)
bool bDone = false;
uint16_t nTxtPos = 0;
unsigned char chNext;
if (pTxt == NULL) { bDone = true; }
while (!bDone) {
chNext = pTxt[nTxtPos];
nTxtPos++;
if (chNext == 0) {
// Reached terminator character
// Add terminator to buffer but don't advance write pointer
// since we want next write to overwrite this
gslc_ElemXTextboxBufAdd(pGui,pElemRef,0,false);
bDone = true;
continue;
}
// FIXME: It is possible that the following check may no longer be
// appropriate when using UTF-8 encoding mode.
if (chNext == '\n') {
// Terminate the line
gslc_ElemXTextboxBufAdd(pGui,pElemRef,0,false);
// Advance the writer by one line
gslc_ElemXTextboxLineWrAdv(pGui,pElemRef);
} else {
// TODO: Check to see if we are in mask/truncate state
// Note that this routine also handles line wrap
gslc_ElemXTextboxBufAdd(pGui,pElemRef,chNext,true);
}
}
// Set the redraw flag
// - Only need incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
bool gslc_ElemXTextboxDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
if ((pvGui == NULL) || (pvElemRef == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTextboxDraw";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return false;
}
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
gslc_tsColor colBg = GSLC_COL_BLACK;
// Fetch the element's extended data structure
gslc_tsXTextbox* pBox;
pBox = (gslc_tsXTextbox*)(pElem->pXData);
if (pBox == NULL) {
GSLC_DEBUG2_PRINT("ERROR: ElemXTextboxDraw(%s) pXData is NULL\n","");
return false;
}
bool bGlow = (pElem->nFeatures & GSLC_ELEM_FEA_GLOW_EN) && gslc_ElemGetGlow(pGui,pElemRef);
bool bFrameEn = (pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN);
// Draw the frame
if (eRedraw == GSLC_REDRAW_FULL) {
if (bFrameEn) {
gslc_DrawFrameRect(pGui,pElem->rElem,pElem->colElemFrame);
}
}
// Clear the background (inset from frame)
// - Only do this if we need to redraw all rows
if ((eRedraw == GSLC_REDRAW_FULL) || (pBox->nRedrawRow == XTEXTBOX_REDRAW_ALL)) {
gslc_tsRect rInner = gslc_ExpandRect(pElem->rElem, -1, -1);
colBg = (bGlow) ? pElem->colElemFillGlow : pElem->colElemFill;
gslc_DrawFillRect(pGui, rInner, colBg);
}
uint16_t nBufPos = 0;
uint16_t nTxtPixX;
uint16_t nTxtPixY;
gslc_tsColor colTxt;
//bool bEncUtf8;
bool bRedrawLine;
// Determine what encoding method is used for text
// Not used at the moment
//bEncUtf8 = ((pElem->eTxtFlags & GSLC_TXT_ENC) == GSLC_TXT_ENC_UTF8);
// Initialize color state
colTxt = pElem->colElemText;
// Calculate the starting row for the window
uint16_t nWndRowStartScr = pBox->nWndRowStart;
// Only correct for scrollbar position if enabled
if (pBox->bScrollEn) {
nWndRowStartScr = (pBox->nWndRowStart + pBox->nScrollPos) % pBox->nBufRows;
}
#if (GSLC_FEATURE_XTEXTBOX_EMBED == 0)
// Normal mode support (no embedded text color)
// - This mode is much faster and is able to support UTF-8 text encoding
uint8_t nCurY = 0;
uint8_t nOutRow = 0;
uint8_t nMaxRow = 0;
nMaxRow = (pBox->nBufRows < pBox->nWndRows)? pBox->nBufRows : pBox->nWndRows;
for (nOutRow=0;nOutRow<nMaxRow;nOutRow++) {
bRedrawLine = true; // Default to drawing the row
// Calculate row offset after accounting for buffer wrap
// and current window starting offset
uint16_t nRowCur = nWndRowStartScr + nOutRow;
nRowCur = nRowCur % pBox->nBufRows;
// If we are doing incremental redraw and only a single
// row has been marked as requiring redraw, then skip
// all other rows
if (eRedraw == GSLC_REDRAW_INC) {
if ((pBox->nRedrawRow >= 0) && (pBox->nRedrawRow != nRowCur)) {
// Single-row redraw, but we are not on that row, so skip
bRedrawLine = false;
}
}
// NOTE: At the start of buffer fill where we have
// only written a couple rows, we don't stop reading
// across all of the rows. We are dependent upon
// the reset to initialize all rows with NULL terminator
// so that we don't show garbage.
if (bRedrawLine) {
nBufPos = nRowCur * pBox->nBufCols;
nTxtPixX = pElem->rElem.x + pBox->nMarginX + 0 * pBox->nChSizeX;
nTxtPixY = pElem->rElem.y + pBox->nMarginY + nCurY * pBox->nChSizeY;
#if (DRV_OVERRIDE_TXT_ALIGN)
gslc_DrvDrawTxtAlign(pGui,nTxtPixX,nTxtPixY,nTxtPixX,nTxtPixY,GSLC_ALIGN_TOP_LEFT,pElem->pTxtFont,
(char*)&(pBox->pBuf[nBufPos]),pElem->eTxtFlags,colTxt,colBg);
#else
gslc_DrvDrawTxt(pGui, nTxtPixX, nTxtPixY, pElem->pTxtFont, (char*)&(pBox->pBuf[nBufPos]), pElem->eTxtFlags, colTxt, colBg);
#endif
}
nCurY++;
} // nOutRow
#else
// Embedded color mode support
// - This mode supports inline changing of text color
// - However, it does not support UTF-8 character encoding
// - It is also slower since rendering is per-character
enum {TBOX_NORM, TBOX_COL_SET};
int16_t eTBoxState = TBOX_NORM;
uint16_t nTBoxStateCnt = 0;
unsigned char chNext;
uint8_t nCurX = 0;
uint8_t nCurY = 0;
uint8_t nOutRow = 0;
uint8_t nOutCol = 0;
uint8_t nMaxCol = 0;
uint8_t nMaxRow = 0;
bool bRowDone = false;
nMaxCol = (pBox->nBufCols < pBox->nWndCols)? pBox->nBufCols : pBox->nWndCols;
nMaxRow = (pBox->nBufRows < pBox->nWndRows)? pBox->nBufRows : pBox->nWndRows;
for (nOutRow=0;nOutRow<nMaxRow;nOutRow++) {
bRedrawLine = true; // Default to drawing the row
// Calculate row offset after accounting for buffer wrap
// and current window starting offset
uint16_t nRowCur = nWndRowStartScr + nOutRow;
nRowCur = nRowCur % pBox->nBufRows;
// If we are doing incremental redraw and only a single
// row has been marked as requiring redraw, then skip
// all other rows
if (eRedraw == GSLC_REDRAW_INC) {
if ((pBox->nRedrawRow >= 0) && (pBox->nRedrawRow != nRowCur)) {
// Single-row redraw, but we are not on that row, so skip
bRedrawLine = false;
}
}
bRowDone = false;
nCurX = 0;
for (nOutCol=0;(!bRowDone)&&(bRedrawLine)&&(nOutCol<nMaxCol);nOutCol++) {
// NOTE: At the start of buffer fill where we have
// only written a couple rows, we don't stop reading
// across all of the rows. We are dependent upon
// the reset to initialize all rows with NULL terminator
// so that we don't show garbage.
nBufPos = nRowCur * pBox->nBufCols + nOutCol;
chNext = pBox->pBuf[nBufPos];
if (eTBoxState == TBOX_NORM) {
if (chNext == 0) {
// Reached early terminator
bRowDone = true;
continue;
} else if (chNext == GSLC_XTEXTBOX_CODE_COL_SET) {
// Set color (enter FSM)
eTBoxState = TBOX_COL_SET;
nTBoxStateCnt = 0;
} else if (chNext == GSLC_XTEXTBOX_CODE_COL_RESET) {
// Reset color
colTxt = pElem->colElemText;
} else {
// Render the character
// TODO: Optimize by coalescing all characters in row before calling DrvDrawTxt
// - Note that this would make it harder to change aspects (such as color)
// in mid-line.
char acChToDraw[2] = "";
acChToDraw[0] = chNext;
acChToDraw[1] = 0;
nTxtPixX = pElem->rElem.x + pBox->nMarginX + nCurX * pBox->nChSizeX;
nTxtPixY = pElem->rElem.y + pBox->nMarginY + nCurY * pBox->nChSizeY;
#if (DRV_OVERRIDE_TXT_ALIGN)
gslc_DrvDrawTxtAlign(pGui,nTxtPixX,nTxtPixY,nTxtPixX,nTxtPixY,GSLC_ALIGN_TOP_LEFT,pElem->pTxtFont,
(char*)&acChToDraw,pElem->eTxtFlags,colTxt,colBg);
#else
gslc_DrvDrawTxt(pGui,nTxtPixX,nTxtPixY,pElem->pTxtFont,(char*)&acChToDraw,pElem->eTxtFlags,colTxt,colBg);
#endif
nCurX++;
}
} else if (eTBoxState == TBOX_COL_SET) {
nTBoxStateCnt++;
if (nTBoxStateCnt == 1) { colTxt.r = chNext; }
else if (nTBoxStateCnt == 2) { colTxt.g = chNext; }
else if (nTBoxStateCnt == 3) {
colTxt.b = chNext;
eTBoxState = TBOX_NORM;
}
} // eTBoxState
} // nOutCol
nCurY++;
} // nOutRow
#endif // GSLC_FEATURE_XTEXTBOX_EMBED
// Clear the redraw flag
pBox->nRedrawRow = XTEXTBOX_REDRAW_NONE;
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
// ============================================================================

221
src/guislice/XTextbox.h Normal file
View file

@ -0,0 +1,221 @@
#ifndef _GUISLICE_EX_XTEXTBOX_H_
#define _GUISLICE_EX_XTEXTBOX_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Textbox control
// - Calvin Hass
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XTextbox.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Textbox
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_TEXTBOX GSLC_TYPE_BASE_EXTEND + 4
/// Definitions for textbox special inline codes
#define GSLC_XTEXTBOX_CODE_COL_SET 187
#define GSLC_XTEXTBOX_CODE_COL_RESET 188
#define XTEXTBOX_REDRAW_NONE -1
#define XTEXTBOX_REDRAW_ALL -2
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Textbox element
typedef struct {
// Config
char* pBuf; ///< Ptr to the text buffer (circular buffer))
int8_t nMarginX; ///< Margin for text area within element rect (X)
int8_t nMarginY; ///< Margin for text area within element rect (Y)
bool bWrapEn; ///< Enable for line wrapping
uint16_t nBufRows; ///< Number of rows in buffer
uint16_t nBufCols; ///< Number of columns in buffer
bool bScrollEn; ///< Enable for scrollbar
uint16_t nScrollPos; ///< Current scrollbar position
// Precalculated params
uint8_t nChSizeX; ///< Width of characters (pixels)
uint8_t nChSizeY; ///< Height of characters (pixels)
uint8_t nWndCols; ///< Window X size
uint8_t nWndRows; ///< Window Y size
// Current status
uint8_t nCurPosX; ///< Cursor X position
uint8_t nCurPosY; ///< Cursor Y position
uint8_t nBufPosX; ///< Buffer X position
uint8_t nBufPosY; ///< Buffer Y position
uint8_t nWndRowStart; ///< First row of current window
// Redraw
int16_t nRedrawRow; ///< Specific row to update in redraw (if not -1)
} gslc_tsXTextbox;
///
/// Create a Textbox Element
/// - The textbox is a scrolling window designed for displaying multi-line
/// text using a monospaced font. A character buffer is defined by nBufRows*nBufCols
/// to capture the added text. If the allocation buffer is larger than the
/// display size (defined by rElem), then a scrollbar will be shown.
/// - Support for changing color within a row can be enabled with GSLC_FEATURE_XTEXTBOX_EMBED 1
/// - Note that each color change command will consume 4 of the available "column" bytes.
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining textbox size
/// \param[in] nFontId: Font ID to use for text area
/// \param[in] pBuf: Ptr to text buffer (already allocated)
/// with size (nBufRows*nBufCols) chars
/// \param[in] nBufRows: Number of rows in buffer
/// \param[in] nBufCols: Number of columns in buffer (incl special codes)
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXTextboxCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXTextbox* pXData,gslc_tsRect rElem,int16_t nFontId,char* pBuf,
uint16_t nBufRows,uint16_t nBufCols);
/// Reset the contents of the textbox
/// - Clears the buffer and resets the position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return none
///
void gslc_ElemXTextboxReset(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Draw a Textbox element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXTextboxDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
/// Add a text string to the textbox
/// - If it includes a newline then the buffer will
/// advance to the next row
/// - If wrap has been enabled, then a newline will
/// be forced
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] pTxt Pointer to text string (null-terminated)
///
/// \return none
///
void gslc_ElemXTextboxAdd(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,char* pTxt);
///
/// Insert a color set code into the current buffer position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nCol: Color to assign for next text written to textbox
///
/// \return none
///
void gslc_ElemXTextboxColSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,gslc_tsColor nCol);
///
/// Insert a color reset code into the current buffer position
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return none
///
void gslc_ElemXTextboxColReset(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Enable or disable line wrap within textbox
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] bWrapEn: Enable line wrap if true
///
/// \return none
///
void gslc_ElemXTextboxWrapSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bWrapEn);
///
/// Set the textbox scroll position (nScrollPos) as a fraction of
/// nScrollMax
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] nScrollPos: New scroll position
/// \param[in] nScrollMax: Maximum scroll position
///
/// \return none
///
void gslc_ElemXTextboxScrollSet(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,uint8_t nScrollPos,uint8_t nScrollMax);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XTEXTBOX_H_

453
src/guislice/XTogglebtn.c Normal file
View file

@ -0,0 +1,453 @@
// =======================================================================
// GUIslice library extension: Toggle button control
// - Paul Conti
// - Toggle button with Android and iOS like styles
// - Based on Calvin Hass' Checkbox control
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass and Paul Conti
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XTogglebtn.c
// GUIslice library
#include "GUIslice.h"
#include "GUIslice_drv.h"
//#include "XTogglebtn.h"
#include "XTogglebtn.h"
#include <stdio.h>
#if (GSLC_USE_PROGMEM)
#include <avr/pgmspace.h>
#endif
// ----------------------------------------------------------------------------
// Error Messages
// ----------------------------------------------------------------------------
extern const char GSLC_PMEM ERRSTR_NULL[];
extern const char GSLC_PMEM ERRSTR_PXD_NULL[];
// ----------------------------------------------------------------------------
// Extended element definitions
// ----------------------------------------------------------------------------
//
// - This file extends the core GUIslice functionality with
// additional widget types
//
// ----------------------------------------------------------------------------
// ============================================================================
// Extended Element: Togglebtn
// - Togglebtn
// Acts much like a checkbox but with styles that are similar to iOS and
// Android slider buttons.
// ============================================================================
// Create a togglebtn element and add it to the GUI element list
gslc_tsElemRef* gslc_ElemXTogglebtnCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXTogglebtn* pXData,gslc_tsRect rElem,
gslc_tsColor colThumb,gslc_tsColor colOnState,gslc_tsColor colOffState,
bool bCircular,bool bChecked,GSLC_CB_TOUCH cbTouch)
{
if ((pGui == NULL) || (pXData == NULL)) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTogglebtnCreate";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsElem sElem;
gslc_tsElemRef* pElemRef = NULL;
sElem = gslc_ElemCreate(pGui,nElemId,nPage,GSLC_TYPEX_TOGGLEBTN,rElem,NULL,0,GSLC_FONT_NONE);
sElem.nFeatures |= GSLC_ELEM_FEA_FRAME_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_FILL_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_CLICK_EN;
sElem.nFeatures |= GSLC_ELEM_FEA_GLOW_EN;
// Define other extended data
sElem.pXData = (void*)(pXData);
pXData->bOn = bChecked; // save on/off status
pXData->pfunctUser = cbTouch; // save user's callback in our extra data
pXData->nMyPageId = nPage; // save our page id for group by access later, if needed.
pXData->colThumb = colThumb; // save thumb color
pXData->colOnState = colOnState; // save on color
pXData->colOffState = colOffState; // save off color
pXData->bCircular = bCircular; // save button style
// Specify the custom drawing callback
sElem.pfuncXDraw = &gslc_ElemXTogglebtnDraw;
// Specify the custom touch handler
sElem.pfuncXTouch = &gslc_ElemXTogglebtnTouch;
sElem.colElemFill = GSLC_COL_BLACK;
sElem.colElemFillGlow = GSLC_COL_BLACK;
sElem.colElemFrame = GSLC_COL_GRAY;
sElem.colElemFrameGlow = GSLC_COL_WHITE;
if (nPage != GSLC_PAGE_NONE) {
pElemRef = gslc_ElemAdd(pGui,nPage,&sElem,GSLC_ELEMREF_DEFAULT);
return pElemRef;
#if (GSLC_FEATURE_COMPOUND)
} else {
// Save as temporary element
pGui->sElemTmp = sElem;
pGui->sElemRefTmp.pElem = &(pGui->sElemTmp);
pGui->sElemRefTmp.eElemFlags = GSLC_ELEMREF_DEFAULT | GSLC_ELEMREF_REDRAW_FULL;
return &(pGui->sElemRefTmp);
#endif
}
return NULL;
}
bool gslc_ElemXTogglebtnGetState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
gslc_tsXTogglebtn* pTogglebtn = (gslc_tsXTogglebtn*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_TOGGLEBTN, __LINE__);
if (!pTogglebtn) return false;
return pTogglebtn->bOn;
}
// Helper routine for gslc_ElemXTogglebtnSetState()
// - Updates the togglebtn control's state but does
// not touch any other controls in the group
void gslc_ElemXTogglebtnSetStateHelp(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bOn)
{
gslc_tsXTogglebtn* pTogglebtn = (gslc_tsXTogglebtn*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_TOGGLEBTN, __LINE__);
if (!pTogglebtn) return;
// Update our data element
bool bStateOld = pTogglebtn->bOn;
pTogglebtn->bOn = bOn;
// Element needs redraw
if (bOn != bStateOld) {
// Only need an incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
}
// Update the togglebtn control's state. If it's part of a group
// then also update the state of all other buttons in the group.
void gslc_ElemXTogglebtnSetState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bOn)
{
gslc_tsXTogglebtn* pTogglebtn = (gslc_tsXTogglebtn*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_TOGGLEBTN, __LINE__);
if (!pTogglebtn) return;
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
int16_t nGroup = pElem->nGroup;
int16_t nElemId = pElem->nId;
if (bOn && pElem->nGroup != GSLC_GROUP_ID_NONE) {
// If we are selecting a button that is already
// selected, then skip further update events.
// NOTE: This check is not very efficient, but it avoids
// the creation of extra events.
gslc_tsElemRef* pTmpRef = gslc_ElemXTogglebtnFindSelected(pGui, nGroup);
if (pTmpRef == pElemRef) {
// Same element, so skip
return;
}
// Proceed to deselect any other selected items in the group.
// Note that SetState calls itself to deselect other items so it
// is important to qualify this logic with bOn=true
int16_t nCurInd;
int16_t nCurId;
gslc_tsElem* pCurElem = NULL;
gslc_tsElemRef* pCurElemRef = NULL;
int16_t nCurGroup;
/*
* The elements must be grouped on the same layer but do not need to be on the current
* page. This allows us to place grouped elements on the base page.
* p conti.
*/
// Find our page layer
gslc_tsPage* pPage = gslc_PageFindById(pGui, pTogglebtn->nMyPageId);
if (pPage == NULL) {
GSLC_DEBUG2_PRINT("ERROR: gslc_ElemXTogglebtnSetState() can't find page (ID=%d)\n",
pTogglebtn->nMyPageId);
return;
}
gslc_tsCollect* pCollect = &pPage->sCollect;
for (nCurInd=0;nCurInd<pCollect->nElemRefCnt;nCurInd++) {
// Fetch extended data
pCurElemRef = &pCollect->asElemRef[nCurInd];
pCurElem = gslc_GetElemFromRef(pGui,pCurElemRef);
// NOTE: Sorry but I have no idea what this FIXME is talking about - p conti
// FIXME: Handle pCurElemRef->eElemFlags
nCurId = pCurElem->nId;
nCurGroup = pCurElem->nGroup;
// If this is in a different group, ignore it
if (nCurGroup != nGroup) {
continue;
}
// Is this our element? If so, ignore the deselect operation
if (nCurId == nElemId) {
continue;
}
// Deselect all other elements
gslc_ElemSetGlow(pGui,pCurElemRef,false); // trurn off glow state
gslc_ElemXTogglebtnSetStateHelp(pGui,pCurElemRef,false);
} // nInd
} // bOn
// Set the state of the current element
gslc_ElemXTogglebtnSetStateHelp(pGui,pElemRef,bOn);
}
// Toggle the togglebtn control's state
void gslc_ElemXTogglebtnToggleState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef)
{
if (pElemRef == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTogglebtnToggleState";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return;
}
// Toggle the data element value
bool bStateNew = (gslc_ElemXTogglebtnGetState(pGui,pElemRef))? false : true;
gslc_ElemXTogglebtnSetState(pGui,pElemRef,bStateNew);
}
void gslc_ElemXTogglebtnDrawCircularHelp(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsXTogglebtn* pTogglebtn)
{
// frame enabled?
bool bFrameEn = pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN;
// Work out the sizes of the inner rectangles
gslc_tsRect rInner = gslc_ExpandRect(pElem->rElem,-1,-1);
// work out our circle positions
uint16_t nRadius = rInner.h / 2;
int16_t nLeftX = rInner.x + nRadius;
int16_t nLeftY = rInner.y + nRadius;
int16_t nRightX = rInner.x + pElem->rElem.w - nRadius -1;
int16_t nRightY = rInner.y + nRadius;
if (pTogglebtn->bOn) {
// draw our main body
gslc_DrawFillRoundRect(pGui,rInner,rInner.h,pTogglebtn->colOnState);
// place thumb on left-hand side
gslc_DrawFillCircle(pGui,nLeftX,nLeftY,nRadius-1,pTogglebtn->colThumb);
if (bFrameEn) {
gslc_DrawFrameRoundRect(pGui,pElem->rElem,pElem->rElem.h,pElem->colElemFrame);
gslc_DrawFrameCircle(pGui,nLeftX,nLeftY,nRadius,pElem->colElemFrame);
}
} else {
// draw our main body
gslc_DrawFillRoundRect(pGui,rInner,rInner.h,pTogglebtn->colOffState);
// place thumb on right-hand side
gslc_DrawFillCircle(pGui,nRightX,nRightY,nRadius-1,pTogglebtn->colThumb);
if (bFrameEn) {
gslc_DrawFrameRoundRect(pGui,pElem->rElem,pElem->rElem.h,pElem->colElemFrame);
gslc_DrawFrameCircle(pGui,nRightX,nRightY,nRadius,pElem->colElemFrame);
}
}
}
void gslc_ElemXTogglebtnDrawRectangularHelp(gslc_tsGui* pGui,gslc_tsElem* pElem,gslc_tsXTogglebtn* pTogglebtn)
{
// frame enabled?
bool bFrameEn = pElem->nFeatures & GSLC_ELEM_FEA_FRAME_EN;
// Work out the sizes of the inner rectangles
gslc_tsRect rSquare = {
pElem->rElem.x,
pElem->rElem.y,
pElem->rElem.h, // force a square
pElem->rElem.h
};
gslc_tsRect rInner = gslc_ExpandRect(pElem->rElem,-1,-1);
if (pTogglebtn->bOn) {
gslc_DrawFillRect(pGui,rInner,pTogglebtn->colOnState);
// place thumb on left-hand side
gslc_DrawFillRect(pGui,rSquare,pTogglebtn->colThumb);
if (bFrameEn) {
gslc_DrawFrameRect(pGui,pElem->rElem,pElem->colElemFrame);
gslc_DrawFrameRect(pGui,rSquare,pElem->colElemFrame);
}
} else {
gslc_DrawFillRect(pGui,rInner,pTogglebtn->colOffState);
// place thumb on right-hand side
rSquare.x = rInner.x + rInner.w - rInner.h - 1;
gslc_DrawFillRect(pGui,rSquare,pTogglebtn->colThumb);
if (bFrameEn) {
gslc_DrawFrameRect(pGui,pElem->rElem,pElem->colElemFrame);
gslc_DrawFrameRect(pGui,rSquare,pElem->colElemFrame);
}
}
}
// Redraw the togglebtn
// - Note that this redraw is for the entire element rect region
// - The Draw function parameters use void pointers to allow for
// simpler callback function definition & scalability.
bool gslc_ElemXTogglebtnDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw)
{
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXTogglebtn* pTogglebtn = (gslc_tsXTogglebtn*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_TOGGLEBTN, __LINE__);
if (!pTogglebtn) {
GSLC_DEBUG_PRINT("ERROR: gslc_ElemXTogglebtnDraw(%s) pXData is NULL\n","");
return false;
}
gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
if (pTogglebtn->bCircular) {
gslc_ElemXTogglebtnDrawCircularHelp(pGui, pElem, pTogglebtn);
} else {
gslc_ElemXTogglebtnDrawRectangularHelp(pGui, pElem, pTogglebtn);
}
// Clear the redraw flag
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_NONE);
// Mark page as needing flip
gslc_PageFlipSet(pGui,true);
return true;
}
// This callback function is called by gslc_ElemSendEventTouch()
// after any touch event
// - NOTE: Adding this touch callback is optional. Without it, we
// can still have a functional togglebtn, but doing the touch
// tracking allows us to change the glow state of the element
// dynamically, as well as updating the togglebtn state if the
// user releases over it (ie. a click event).
//
bool gslc_ElemXTogglebtnTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY)
{
#if defined(DRV_TOUCH_NONE)
return false;
#else
// Typecast the parameters to match the GUI and element types
gslc_tsGui* pGui = (gslc_tsGui*)(pvGui);
gslc_tsElemRef* pElemRef = (gslc_tsElemRef*)(pvElemRef);
gslc_tsXTogglebtn* pTogglebtn = (gslc_tsXTogglebtn*)gslc_GetXDataFromRef(pGui, pElemRef, GSLC_TYPEX_TOGGLEBTN, __LINE__);
if (!pTogglebtn) return false;
//gslc_tsElem* pElem = gslc_GetElemFromRef(pGui,pElemRef);
bool bStateOld = pTogglebtn->bOn;
switch(eTouch) {
case GSLC_TOUCH_UP_IN:
// Now that we released on element, update the state
// Togglebtn button action: toggle
gslc_ElemXTogglebtnToggleState(pGui,pElemRef);
break;
default:
return false;
break;
}
// If the togglebtn changed state, redraw and notify user
if (pTogglebtn->bOn != bStateOld) {
// Now send the callback notification
(*pTogglebtn->pfunctUser)((void*)(pGui), (void*)(pElemRef), eTouch, nRelX, nRelY);
// Incremental redraw
gslc_ElemSetRedraw(pGui,pElemRef,GSLC_REDRAW_INC);
}
return true;
#endif // !DRV_TOUCH_NONE
}
// Determine which togglebtn in the group is selected "on"
gslc_tsElemRef* gslc_ElemXTogglebtnFindSelected(gslc_tsGui* pGui,int16_t nGroupId)
{
int16_t nCurInd;
gslc_tsElemRef* pCurElemRef = NULL;
gslc_tsElem* pCurElem = NULL;
int16_t nCurType;
int16_t nCurGroup;
bool bCurSelected;
gslc_tsElemRef* pFoundElemRef = NULL;
// Operate on current page
// TODO: Support other page layers
gslc_tsPage* pPage = pGui->apPageStack[GSLC_STACK_CUR];
if (pPage == NULL) {
return NULL; // No page added yet
}
if (pGui == NULL) {
static const char GSLC_PMEM FUNCSTR[] = "ElemXTogglebtnFindChecked";
GSLC_DEBUG2_PRINT_CONST(ERRSTR_NULL,FUNCSTR);
return NULL;
}
gslc_tsCollect* pCollect = &pPage->sCollect;
for (nCurInd=0;nCurInd<pCollect->nElemCnt;nCurInd++) {
// Fetch extended data
pCurElemRef = &(pCollect->asElemRef[nCurInd]);
pCurElem = gslc_GetElemFromRef(pGui,pCurElemRef);
nCurType = pCurElem->nType;
// Only want to proceed if it is a togglebtn
if (nCurType != GSLC_TYPEX_TOGGLEBTN) {
continue;
}
nCurGroup = pCurElem->nGroup;
bCurSelected = gslc_ElemXTogglebtnGetState(pGui,pCurElemRef);
// If this is in a different group, ignore it
if (nCurGroup != nGroupId) {
continue;
}
// Did we find an element in the group that was checked?
if (bCurSelected) {
pFoundElemRef = pCurElemRef;
break;
}
} // nCurInd
return pFoundElemRef;
}
// ============================================================================

282
src/guislice/XTogglebtn.h Normal file
View file

@ -0,0 +1,282 @@
#ifndef _GUISLICE_EX_XTOGGLEBTN_H_
#define _GUISLICE_EX_XTOGGLEBTN_H_
#include "GUIslice.h"
// =======================================================================
// GUIslice library extension: Toggle button control
// - Paul Conti
// - Toggle button with Android and iOS like styles
// - Based on Calvin Hass' Checkbox control
// - https://www.impulseadventure.com/elec/guislice-gui.html
// - https://github.com/ImpulseAdventure/GUIslice
// =======================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass and Paul Conti
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =======================================================================
/// \file XTogglebtn.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ============================================================================
// Extended Element: Togglebtn
// - Togglebtn
// Acts much like a checkbox but with styles that are similar to iOS and
// Android slider buttons.
// ============================================================================
// Define unique identifier for extended element type
// - Select any number above GSLC_TYPE_BASE_EXTEND
#define GSLC_TYPEX_TOGGLEBTN GSLC_TYPE_BASE_EXTEND + 40
// Extended element data structures
// - These data structures are maintained in the gslc_tsElem
// structure via the pXData pointer
/// Extended data for Togglebtn element
typedef struct {
bool bOn; ///< Indicates if button is ON or OFF
int16_t nMyPageId; ///< We need to track our page in case of grouping elements
///< on a non current layer, like base layer
gslc_tsColor colThumb; ///< Color of thumb
gslc_tsColor colOnState; ///< Color of button in ON state
gslc_tsColor colOffState; ///< Color of button in OFF state
bool bCircular; ///< Style of the toggle button circular or rectangular
GSLC_CB_TOUCH pfunctUser; ///< User's Callback event to say element has changed
} gslc_tsXTogglebtn;
///
/// Create a Togglebtn button Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] pXData: Ptr to extended element data structure
/// \param[in] rElem: Rectangle coordinates defining togglebtn size
/// \param[in] colThumb: Color of thumb
/// \param[in] colOnState: Color to indicate on position
/// \param[in] colOffState: Color to indicate off position
/// \param[in] bCircular: Style of the toggle button circular or rectangular
/// \param[in] bChecked: Default state
/// \param[in] cbTouch: Callback for touch events
///
/// \return Pointer to Element reference or NULL if failure
///
gslc_tsElemRef* gslc_ElemXTogglebtnCreate(gslc_tsGui* pGui,int16_t nElemId,int16_t nPage,
gslc_tsXTogglebtn* pXData,gslc_tsRect rElem,
gslc_tsColor colThumb,gslc_tsColor colOnState,gslc_tsColor colOffState,
bool bCircular,bool bChecked,GSLC_CB_TOUCH cbTouch);
///
/// Get a Togglebtn element's current state
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return Current state
///
bool gslc_ElemXTogglebtnGetState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Set a Togglebtn element's current state
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
/// \param[in] bOn: New state
///
/// \return none
///
void gslc_ElemXTogglebtnSetState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef,bool bOn);
///
/// Toggle a Togglebtn element's current state
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] pElemRef: Pointer to Element reference
///
/// \return none
///
void gslc_ElemXTogglebtnToggleState(gslc_tsGui* pGui,gslc_tsElemRef* pElemRef);
///
/// Draw a Togglebtn element on the screen
/// - Called from gslc_ElemDraw()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eRedraw: Redraw mode
///
/// \return true if success, false otherwise
///
bool gslc_ElemXTogglebtnDraw(void* pvGui,void* pvElemRef,gslc_teRedrawType eRedraw);
///
/// Handle touch events to Togglebtn element
/// - Called from gslc_ElemSendEventTouch()
///
/// \param[in] pvGui: Void ptr to GUI (typecast to gslc_tsGui*)
/// \param[in] pvElemRef: Void ptr to Element reference (typecast to gslc_tsElemRef*)
/// \param[in] eTouch: Touch event type
/// \param[in] nRelX: Touch X coord relative to element
/// \param[in] nRelY: Touch Y coord relative to element
///
/// \return true if success, false otherwise
///
bool gslc_ElemXTogglebtnTouch(void* pvGui,void* pvElemRef,gslc_teTouch eTouch,int16_t nRelX,int16_t nRelY);
///
/// Find the togglebtn within a group that has been selected
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nGroupId: Group ID to search
///
/// \return Element Ptr or NULL if none selected
///
gslc_tsElemRef* gslc_ElemXTogglebtnFindSelected(gslc_tsGui* pGui,int16_t nGroupId);
// ============================================================================
// ------------------------------------------------------------------------
// Read-only element macros
// ------------------------------------------------------------------------
// Macro initializers for Read-Only Elements in Flash/PROGMEM
//
/// \def gslc_ElemXTogglebtnCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,nGroup,bRadio_,nStyle_,colCheck_,bChecked_)
///
/// Create a Togglebtn button Element
///
/// \param[in] pGui: Pointer to GUI
/// \param[in] nElemId: Element ID to assign (0..16383 or GSLC_ID_AUTO to autogen)
/// \param[in] nPage: Page ID to attach element to
/// \param[in] nX: X coordinate of element
/// \param[in] nY: Y coordinate of element
/// \param[in] nW: Width of element
/// \param[in] nH: Height of element
/// \param[in] colThumb_: Color of thumb
/// \param[in] colOnState_: Color to indicate on position
/// \param[in] colOffState_: Color to indicate off position
/// \param[in] bCircular_: Style of the toggle button circular or rectangular
/// \param[in] bChecked_: Default state
/// \param[in] cbTouch: Callback for touch events
///
/// \return none
///
#if (GSLC_USE_PROGMEM)
#define gslc_ElemXTogglebtnCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,colThumb_,colOnState_,colOffState_,bCircular_,bChecked_,cbTouch) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_CLICK_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXTogglebtn sTogglebtn##nElemId; \
sTogglebtn##nElemId.bOn = bChecked_; \
sTogglebtn##nElemId.nMyPageId = nPage; \
sTogglebtn##nElemId.colThumb = colThumb_; \
sTogglebtn##nElemId.colOnState = colOnState_; \
sTogglebtn##nElemId.colOffState = colOffState_; \
sTogglebtn##nElemId.bCircular = bCircular_; \
sTogglebtn##nElemId.pfunctUser = cbTouch; \
static const gslc_tsElem sElem##nElemId PROGMEM = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_TOGGLEBTN, \
(gslc_tsRect){nX,nY,nW,nH}, \
0, \
GSLC_COL_GRAY,GSLC_COL_BLACK,GSLC_COL_WHITE,GSLC_COL_BLACK, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sTogglebtn##nElemId), \
NULL, \
&gslc_ElemXTogglebtnDraw, \
&gslc_ElemXTogglebtnTouch, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#else
#define gslc_ElemXTogglebtnCreate_P(pGui,nElemId,nPage,nX,nY,nW,nH,colThumb_,colOnState_,colOffState_,bCircular_,bChecked_,cbTouch) \
static const uint8_t nFeatures##nElemId = GSLC_ELEM_FEA_VALID | \
GSLC_ELEM_FEA_GLOW_EN | GSLC_ELEM_FEA_CLICK_EN | GSLC_ELEM_FEA_FILL_EN; \
static gslc_tsXTogglebtn sTogglebtn##nElemId; \
sTogglebtn##nElemId.bOn = bChecked_; \
sTogglebtn##nElemId.nMyPageId = nPage; \
sTogglebtn##nElemId.colThumb = colThumb_; \
sTogglebtn##nElemId.colOnState = colOnState_; \
sTogglebtn##nElemId.colOffState = colOffState_; \
sTogglebtn##nElemId.bCircular = bCircular_; \
sTogglebtn##nElemId.pfunctUser = cbTouch; \
static const gslc_tsElem sElem##nElemId = { \
nElemId, \
nFeatures##nElemId, \
GSLC_TYPEX_TOGGLEBTN, \
(gslc_tsRect){nX,nY,nW,nH}, \
0, \
GSLC_COL_GRAY,GSLC_COL_BLACK,GSLC_COL_WHITE,GSLC_COL_BLACK, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
(gslc_tsImgRef){NULL,NULL,GSLC_IMGREF_NONE,NULL}, \
NULL, \
NULL, \
0, \
(gslc_teTxtFlags)(GSLC_TXT_DEFAULT), \
GSLC_COL_WHITE, \
GSLC_COL_WHITE, \
GSLC_ALIGN_MID_MID, \
0, \
0, \
NULL, \
(void*)(&sTogglebtn##nElemId), \
NULL, \
&gslc_ElemXTogglebtnDraw, \
&gslc_ElemXTogglebtnTouch, \
NULL, \
}; \
gslc_ElemAdd(pGui,nPage,(gslc_tsElem*)&sElem##nElemId, \
(gslc_teElemRefFlags)(GSLC_ELEMREF_SRC_PROG | GSLC_ELEMREF_VISIBLE | GSLC_ELEMREF_REDRAW_FULL));
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_EX_XTOGGLEBTN_H_

View file

@ -0,0 +1,207 @@
#ifndef _GUISLICE_CONFIG_ARD_H_
#define _GUISLICE_CONFIG_ARD_H_
// =============================================================================
// GUIslice library (example user configuration) for:
// - CPU: Arduino UNO / MEGA / etc
// - Display: ILI9341
// - Touch: None
// - Wiring: Custom breakout
// - Pinout:
//
// - Example display:
// -
//
// DIRECTIONS:
// - To use this example configuration, include in "GUIslice_config.h"
//
// WIRING:
// - As this config file is designed for a breakout board, customization
// of the Pinout in SECTION 2 will be required to match your display.
//
// =============================================================================
// - Calvin Hass
// - https://github.com/ImpulseAdventure/GUIslice
// =============================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =============================================================================
// \file GUIslice_config_ard.h
// =============================================================================
// User Configuration
// - This file can be modified by the user to match the
// intended target configuration
// =============================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// =============================================================================
// USER DEFINED CONFIGURATION
// =============================================================================
// -----------------------------------------------------------------------------
// SECTION 1: Device Mode Selection
// - The following defines the display and touch drivers
// and should not require modifications for this example config
// -----------------------------------------------------------------------------
#define DRV_DISP_ADAGFX // Adafruit-GFX library
#define DRV_DISP_ADAGFX_ILI9341 // Adafruit ILI9341
#define DRV_TOUCH_NONE // No touch enabled
// -----------------------------------------------------------------------------
// SECTION 2: Pinout
// -----------------------------------------------------------------------------
// For shields, the following pinouts are typically hardcoded
#define ADAGFX_PIN_CS 9 // Display chip select
#define ADAGFX_PIN_DC 10 // Display SPI data/command
#define ADAGFX_PIN_RST 0 // Display Reset
// Display interface type
#define ADAGFX_SPI_HW 1 // Display uses SPI interface: 1=hardware 0=software
// Display interface software SPI
// - Hardware SPI: the following definitions are unused
// - Software SPI: the following pins need to be defined
#define ADAGFX_PIN_MOSI 11
#define ADAGFX_PIN_MISO 12
#define ADAGFX_PIN_CLK 13
// SD Card
#define ADAGFX_PIN_SDCS 5 // SD card chip select (if GSLC_SD_EN=1)
// -----------------------------------------------------------------------------
// SECTION 3: Orientation
// -----------------------------------------------------------------------------
// Set Default rotation of the display
// - Values 0,1,2,3. Rotation is clockwise
#define GSLC_ROTATE 1
// -----------------------------------------------------------------------------
// SECTION 5: Diagnostics
// -----------------------------------------------------------------------------
// Error reporting
// - Set DEBUG_ERR to >0 to enable error reporting via the Serial connection
// - Enabling DEBUG_ERR increases FLASH memory consumption which may be
// limited on the baseline Arduino (ATmega328P) devices.
// - DEBUG_ERR 0 = Disable all error messaging
// - DEBUG_ERR 1 = Enable critical error messaging (eg. init)
// - DEBUG_ERR 2 = Enable verbose error messaging (eg. bad parameters, etc.)
// - For baseline Arduino UNO, recommended to disable this after one has
// confirmed basic operation of the library is successful.
#define DEBUG_ERR 1 // 1,2 to enable, 0 to disable
// Debug initialization message
// - By default, GUIslice outputs a message in DEBUG_ERR mode
// to indicate the initialization status, even during success.
// - To disable the messages during successful initialization,
// uncomment the following line.
//#define INIT_MSG_DISABLE
// -----------------------------------------------------------------------------
// SECTION 6: Optional Features
// -----------------------------------------------------------------------------
// Enable of optional features
// - For memory constrained devices such as Arduino, it is best to
// set the following features to 0 (to disable) unless they are
// required.
#define GSLC_FEATURE_COMPOUND 1 // Compound elements (eg. XSelNum)
#define GSLC_FEATURE_XTEXTBOX_EMBED 1 // XTextbox control with embedded color
#define GSLC_FEATURE_INPUT 1 // Keyboard / GPIO input control
// Enable support for SD card
// - Set to 1 to enable, 0 to disable
// - Note that the inclusion of the SD library consumes considerable
// RAM and flash memory which could be problematic for Arduino models
// with limited resources.
#define GSLC_SD_EN 0
// =============================================================================
// SECTION 10: INTERNAL CONFIGURATION
// - The following settings should not require modification by users
// =============================================================================
// -----------------------------------------------------------------------------
// Touch Handling
// -----------------------------------------------------------------------------
// Define the maximum number of touch events that are handled
// per gslc_Update() call. Normally this can be set to 1 but certain
// displays may require a greater value (eg. 30) in order to increase
// responsiveness of the touch functionality.
#define GSLC_TOUCH_MAX_EVT 1
// -----------------------------------------------------------------------------
// Misc
// -----------------------------------------------------------------------------
// Define buffer size for loading images from SD
// - A larger buffer will be faster but at the cost of RAM
#define GSLC_SD_BUFFPIXEL 50
// Enable support for graphics clipping (DrvSetClipRect)
// - Note that this will impact performance of drawing graphics primitives
#define GSLC_CLIP_EN 1
// Enable for bitmap transparency and definition of color to use
#define GSLC_BMP_TRANS_EN 1 // 1 = enabled, 0 = disabled
#define GSLC_BMP_TRANS_RGB 0xFF,0x00,0xFF // RGB color (default: MAGENTA)
#define GSLC_USE_FLOAT 1 // 1=Use floating pt library, 0=Fixed-point lookup tables
#define GSLC_DEV_TOUCH ""
#define GSLC_USE_PROGMEM 0
#define GSLC_LOCAL_STR 1 // 1=Use local strings (in element array), 0=External
#define GSLC_LOCAL_STR_LEN 30 // Max string length of text elements
// -----------------------------------------------------------------------------
// Debug diagnostic modes
// -----------------------------------------------------------------------------
// - Uncomment any of the following to enable specific debug modes
//#define DBG_LOG // Enable debugging log output
//#define DBG_TOUCH // Enable debugging of touch-presses
//#define DBG_FRAME_RATE // Enable diagnostic frame rate reporting
//#define DBG_DRAW_IMM // Enable immediate rendering of drawing primitives
//#define DBG_DRIVER // Enable graphics driver debug reporting
// =============================================================================
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_CONFIG_ARD_H_

View file

@ -0,0 +1,246 @@
#ifndef _GUISLICE_CONFIG_ARD_H_
#define _GUISLICE_CONFIG_ARD_H_
// =============================================================================
// GUIslice library (example user configuration) for:
// - CPU: Arduino UNO / MEGA / etc
// - Display: ILI9341
// - Touch: STMPE610 (Resistive)
// - Wiring: Custom breakout
// - Pinout:
//
// - Example display:
// - Adafruit 2.8" TFT LCD Shield w/ Touchscreen (resistive)
//
// DIRECTIONS:
// - To use this example configuration, include in "GUIslice_config.h"
//
// WIRING:
// - As this config file is designed for a breakout board, customization
// of the Pinout in SECTION 2 will be required to match your display.
//
// =============================================================================
// - Calvin Hass
// - https://github.com/ImpulseAdventure/GUIslice
// =============================================================================
//
// The MIT License
//
// Copyright 2016-2020 Calvin Hass
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// =============================================================================
// \file GUIslice_config_ard.h
// =============================================================================
// User Configuration
// - This file can be modified by the user to match the
// intended target configuration
// =============================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// =============================================================================
// USER DEFINED CONFIGURATION
// =============================================================================
// -----------------------------------------------------------------------------
// SECTION 1: Device Mode Selection
// - The following defines the display and touch drivers
// and should not require modifications for this example config
// -----------------------------------------------------------------------------
#define DRV_DISP_ADAGFX // Adafruit-GFX library
#define DRV_DISP_ADAGFX_ILI9341 // Adafruit ILI9341
#define DRV_TOUCH_ADA_STMPE610 // Adafruit STMPE610 touch driver
// -----------------------------------------------------------------------------
// SECTION 2: Pinout
// -----------------------------------------------------------------------------
// For shields, the following pinouts are typically hardcoded
#define ADAGFX_PIN_CS 9 // Display chip select
#define ADAGFX_PIN_DC 10 // Display SPI data/command
#define ADAGFX_PIN_RST 0 // Display Reset
// Display interface type
#define ADAGFX_SPI_HW 1 // Display uses SPI interface: 1=hardware 0=software
// Display interface software SPI
// - Hardware SPI: the following definitions are unused
// - Software SPI: the following pins need to be defined
#define ADAGFX_PIN_MOSI 11
#define ADAGFX_PIN_MISO 12
#define ADAGFX_PIN_CLK 13
// SD Card
#define ADAGFX_PIN_SDCS 5 // SD card chip select (if GSLC_SD_EN=1)
// -----------------------------------------------------------------------------
// SECTION 3: Orientation
// -----------------------------------------------------------------------------
// Set Default rotation of the display
// - Values 0,1,2,3. Rotation is clockwise
#define GSLC_ROTATE 1
// -----------------------------------------------------------------------------
// SECTION 4: Touch Handling
// - Documentation for configuring touch support can be found at:
// https://github.com/ImpulseAdventure/GUIslice/wiki/Configure-Touch-Support
// -----------------------------------------------------------------------------
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// SECTION 4A: Update your pin connections here
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Select touch device wiring method by setting one of the following to 1, others to 0
#define ADATOUCH_I2C_HW 0 // Touch controller via hardware I2C (uses ADATOUCH_I2C_ADDR)
#define ADATOUCH_SPI_HW 1 // Touch controller via hardware SPI (uses ADATOUCH_PIN_CS)
#define ADATOUCH_SPI_SW 0 // Touch controller via software SPI [not yet supported]
// Touch bus & pinout
#define ADATOUCH_I2C_ADDR 0x41 // Touch device I2C address (for ADATOUCH_I2C_HW=1)
#define ADATOUCH_PIN_CS 6 // Touch device chip select (for ADATOUCH_SPI_HW=1)
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// SECTION 4B: Update your calibration settings here
// - These values should come from the diag_ard_touch_calib sketch output
// - Please update the values to the right of ADATOUCH_X/Y_MIN/MAX_* accordingly
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Calibration settings from diag_ard_touch_calib:
// DRV_TOUCH_ADA_STMPE610 [240x320]:
#define ADATOUCH_X_MIN 150
#define ADATOUCH_X_MAX 3800
#define ADATOUCH_Y_MIN 130
#define ADATOUCH_Y_MAX 4000
#define ADATOUCH_REMAP_YX 0 // Some touch controllers may swap X & Y coords
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// SECTION 4D: Additional touch configuration
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// -----------------------------------------------------------------------------
// SECTION 5: Diagnostics
// -----------------------------------------------------------------------------
// Error reporting
// - Set DEBUG_ERR to >0 to enable error reporting via the Serial connection
// - Enabling DEBUG_ERR increases FLASH memory consumption which may be
// limited on the baseline Arduino (ATmega328P) devices.
// - DEBUG_ERR 0 = Disable all error messaging
// - DEBUG_ERR 1 = Enable critical error messaging (eg. init)
// - DEBUG_ERR 2 = Enable verbose error messaging (eg. bad parameters, etc.)
// - For baseline Arduino UNO, recommended to disable this after one has
// confirmed basic operation of the library is successful.
#define DEBUG_ERR 1 // 1,2 to enable, 0 to disable
// Debug initialization message
// - By default, GUIslice outputs a message in DEBUG_ERR mode
// to indicate the initialization status, even during success.
// - To disable the messages during successful initialization,
// uncomment the following line.
//#define INIT_MSG_DISABLE
// -----------------------------------------------------------------------------
// SECTION 6: Optional Features
// -----------------------------------------------------------------------------
// Enable of optional features
// - For memory constrained devices such as Arduino, it is best to
// set the following features to 0 (to disable) unless they are
// required.
#define GSLC_FEATURE_COMPOUND 1 // Compound elements (eg. XSelNum)
#define GSLC_FEATURE_XTEXTBOX_EMBED 1 // XTextbox control with embedded color
#define GSLC_FEATURE_INPUT 1 // Keyboard / GPIO input control
// Enable support for SD card
// - Set to 1 to enable, 0 to disable
// - Note that the inclusion of the SD library consumes considerable
// RAM and flash memory which could be problematic for Arduino models
// with limited resources.
#define GSLC_SD_EN 0
// =============================================================================
// SECTION 10: INTERNAL CONFIGURATION
// - The following settings should not require modification by users
// =============================================================================
// -----------------------------------------------------------------------------
// Touch Handling
// -----------------------------------------------------------------------------
// Define the maximum number of touch events that are handled
// per gslc_Update() call. Normally this can be set to 1 but certain
// displays may require a greater value (eg. 30) in order to increase
// responsiveness of the touch functionality.
#define GSLC_TOUCH_MAX_EVT 1
// -----------------------------------------------------------------------------
// Misc
// -----------------------------------------------------------------------------
// Define buffer size for loading images from SD
// - A larger buffer will be faster but at the cost of RAM
#define GSLC_SD_BUFFPIXEL 50
// Enable support for graphics clipping (DrvSetClipRect)
// - Note that this will impact performance of drawing graphics primitives
#define GSLC_CLIP_EN 1
// Enable for bitmap transparency and definition of color to use
#define GSLC_BMP_TRANS_EN 1 // 1 = enabled, 0 = disabled
#define GSLC_BMP_TRANS_RGB 0xFF,0x00,0xFF // RGB color (default: MAGENTA)
#define GSLC_USE_FLOAT 1 // 1=Use floating pt library, 0=Fixed-point lookup tables
#define GSLC_DEV_TOUCH ""
#define GSLC_USE_PROGMEM 0
#define GSLC_LOCAL_STR 1 // 1=Use local strings (in element array), 0=External
#define GSLC_LOCAL_STR_LEN 30 // Max string length of text elements
// -----------------------------------------------------------------------------
// Debug diagnostic modes
// -----------------------------------------------------------------------------
// - Uncomment any of the following to enable specific debug modes
//#define DBG_LOG // Enable debugging log output
//#define DBG_TOUCH // Enable debugging of touch-presses
//#define DBG_FRAME_RATE // Enable diagnostic frame rate reporting
//#define DBG_DRAW_IMM // Enable immediate rendering of drawing primitives
//#define DBG_DRIVER // Enable graphics driver debug reporting
// =============================================================================
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // _GUISLICE_CONFIG_ARD_H_