Improved comments in implementation of button, checkbox, numupdown, tft, touch and screen modules/submodules.
This commit is contained in:
@@ -3,187 +3,152 @@
|
||||
#include "button.h"
|
||||
#include <string.h>
|
||||
|
||||
#define BRIGHTNESS_VAL 3 //How much the Brightness is in/decreased for button shadows (3 -> Add 1/3 off Full Value)
|
||||
/* The Idea is as follows:
|
||||
* When the user add's a button we create a touch area for that region and wait for PEN_DOWN events.
|
||||
* Once the user puts the pen down in this area we'll redraw the button with different shadows (feedback)
|
||||
* and we'll now wait on PEN_UP or PEN_LEAVE events.
|
||||
* If the user takes the pen away while in the area (PEN_UP), we call the provided user callback
|
||||
* Otherwise (PEN_LEAVE) we only restore the initial shadows
|
||||
*/
|
||||
|
||||
/* Possible improvements:
|
||||
* Move the button by 1 pixel while he is pressed, to create a "full 3d" experience
|
||||
* Add events for the case when the button is pressed for a long time, without release
|
||||
*/
|
||||
|
||||
//Method to calculate the shadow colors used to create the "3d" effect
|
||||
void calculate_shadows(uint16_t bgcolor, uint16_t* light_shadow, uint16_t* dark_shadow) {
|
||||
#define BRIGHTNESS_VAL 3 //How much the Brightness is in/decreased for button shadows (3 -> Add/Subtract 1/3 off Full Value)
|
||||
|
||||
uint16_t c_light,c_dark; //c_light and c_dark will be filled with a lighter and a darker color as the background color (for the shadows)
|
||||
uint8_t r,g,b;
|
||||
|
||||
//separate the channels of the 16-bit rgb565 color
|
||||
r=(bgcolor&0xF800)>>11;
|
||||
g=(bgcolor&0x07E0)>>5;
|
||||
b=(bgcolor&0x001F)>>0;
|
||||
|
||||
//For the light shadow color:
|
||||
if((r + 0x1F/BRIGHTNESS_VAL) > 0x1F) //Adding one third would exceed the maximum of the red channel
|
||||
c_light=0xF800; //Use full red
|
||||
else //adding one third to the red channel is fine
|
||||
c_light=(r+0x1F/BRIGHTNESS_VAL)<<11; //Use same red as in the background, but add one third
|
||||
if((g + 0x3F/BRIGHTNESS_VAL) > 0x3F) //same for the green channel
|
||||
c_light|=0x07E0;
|
||||
else
|
||||
c_light|=(g+0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if((b + 0x1F/BRIGHTNESS_VAL) > 0x1F) //and the blue channel
|
||||
c_light|=0x0018;
|
||||
else
|
||||
c_light|=(b+0x1F/BRIGHTNESS_VAL)<<0;
|
||||
|
||||
//For the dark shadow color
|
||||
if(r > (0x1F/BRIGHTNESS_VAL)) //Subtracting one third would NOT exceed the minimum of the red channel
|
||||
c_dark=(r-0x1F/BRIGHTNESS_VAL)<<11; //Use same red as in the background, but subtract one third
|
||||
else //Subtracting one third would give us a number below zero
|
||||
c_dark=0x0000; //use no red channel
|
||||
if(g > (0x3F/BRIGHTNESS_VAL)) //Same for the green channel
|
||||
c_dark|=(g-0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if(b > (0x1F/BRIGHTNESS_VAL)) //and the blue channel
|
||||
c_dark|=(b-0x1F/BRIGHTNESS_VAL)<<0;
|
||||
|
||||
//Assign the calculated shadows to out parameters
|
||||
if(light_shadow!=NULL) *light_shadow = c_light;
|
||||
if(dark_shadow!=NULL) *dark_shadow = c_dark;
|
||||
|
||||
}
|
||||
|
||||
//Callback which is called when the user touches the touch-area we created for the button
|
||||
void buttons_cb(void* touchArea, TOUCH_ACTION triggeredAction)
|
||||
//Method shared between normal Buttons and Bitmap Buttons-> Look at comment in headerfile for explanation.
|
||||
{
|
||||
TOUCH_AREA_STRUCT * area = (TOUCH_AREA_STRUCT*)touchArea;
|
||||
BUTTON_STRUCT* button = (BUTTON_STRUCT*)touchArea;
|
||||
unsigned int c1,c2;
|
||||
unsigned char r,g,b;
|
||||
r=(button->bgcolor&0xF800)>>11;
|
||||
g=(button->bgcolor&0x07E0)>>5;
|
||||
b=(button->bgcolor&0x001F)>>0;
|
||||
if((r + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c1=0xF800;
|
||||
else
|
||||
c1=(r+0x1F/BRIGHTNESS_VAL)<<11;
|
||||
if((g + 0x3F/BRIGHTNESS_VAL) >0x3F)
|
||||
c1|=0x07E0;
|
||||
else
|
||||
c1|=(g+0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if((b + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c1|=0x0018;
|
||||
else
|
||||
c1|=(b+0x1F/BRIGHTNESS_VAL)<<0;
|
||||
if(r > (0x1F/BRIGHTNESS_VAL))
|
||||
c2=(r-0x1F/BRIGHTNESS_VAL)<<11;
|
||||
else
|
||||
c2=0x0000;
|
||||
if(g > (0x3F/BRIGHTNESS_VAL))
|
||||
c2|=(g-0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if(b > (0x1F/BRIGHTNESS_VAL))
|
||||
c2|=(b-0x1F/BRIGHTNESS_VAL)<<0;
|
||||
|
||||
uint16_t c_light,c_dark; //c_light and c_dark will be filled with a lighter and a darker color as the background color (for the shadows)
|
||||
calculate_shadows(button->bgcolor,&c_light,&c_dark);
|
||||
|
||||
switch(triggeredAction)
|
||||
{
|
||||
case PEN_DOWN:
|
||||
area->hookedActions=PEN_UP|PEN_LEAVE;
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c2); //Nord
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c2);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c1); //S<EFBFBD>d
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c1); //Ost
|
||||
case PEN_DOWN: //If the user touches the area for the "first time"
|
||||
area->hookedActions=PEN_UP|PEN_LEAVE; //for the future we only want PEN_UP and PEN_LEAVE events
|
||||
|
||||
//Draw shadows
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_dark); //North
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c_dark);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c_light); //South
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c_light); //East
|
||||
break;
|
||||
case PEN_UP:
|
||||
case PEN_LEAVE:
|
||||
area->hookedActions=PEN_DOWN;
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c1); //Nord
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c1);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c2); //S<EFBFBD>d
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c2); //Ost
|
||||
if(triggeredAction==PEN_UP && button->callback!=NULL)
|
||||
button->callback(button);
|
||||
case PEN_UP: //If the user took the pen away, while in the area (=button pressed!)
|
||||
case PEN_LEAVE: //or the user "slided out" of the area
|
||||
area->hookedActions=PEN_DOWN; //for the future we only want PEN_DOWN events
|
||||
|
||||
//Draw inverse shadows
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_light); //North
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c_light);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c_dark); //South
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c_dark); //East
|
||||
|
||||
if(triggeredAction==PEN_UP && button->callback!=NULL) //If the button got "pressed" instead of left, and the user provided a callback
|
||||
button->callback(button); //execute the user callback
|
||||
break;
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool gui_button_add(BUTTON_STRUCT* button)//Registers a button (fill Struct first). Return false if no more Space in the Pointertable (-->Change NUM_AREAS).
|
||||
bool gui_button_add(BUTTON_STRUCT* button)
|
||||
{
|
||||
if(touch_have_empty(1))
|
||||
if(touch_have_empty(1)) //Check if the touch module can handle one additional area
|
||||
{
|
||||
//Calculate width and height of the button text
|
||||
unsigned int strwidth=tft_font_width(button->font)*strlen(button->text);
|
||||
unsigned char strheight=tft_font_height(button->font);
|
||||
button->base.hookedActions=PEN_DOWN;
|
||||
button->base.callback = buttons_cb;
|
||||
if(button->base.x2==AUTO)
|
||||
button->base.x2= button->base.x1 -1 + strwidth+(tft_font_width(button->font)/2);
|
||||
else if((button->base.x2-button->base.x1+1)<(strwidth+2))
|
||||
return false;
|
||||
|
||||
if(button->base.y2==AUTO)
|
||||
button->base.hookedActions=PEN_DOWN; //At first we are interested in PEN_DOWN events
|
||||
button->base.callback = buttons_cb; //Use our own callback for the touch area events
|
||||
|
||||
if(button->base.x2==AUTO) { //The user wants us to calculate the button width automatically
|
||||
//Use string width + half of a character width as button width
|
||||
button->base.x2= button->base.x1 -1 + strwidth+(tft_font_width(button->font)/2);
|
||||
} else if((button->base.x2-button->base.x1+1)<(strwidth+2)) { //the provided width is too small to fit the entire text
|
||||
return false; //report error
|
||||
}
|
||||
|
||||
if(button->base.y2==AUTO) { //The user wants us to calculate the button height automatically
|
||||
//Use one and a half character heights as button height
|
||||
button->base.y2=button->base.y1 -1 +strheight+(strheight/2);
|
||||
else if((button->base.y2-button->base.y1+1)<(strheight+2))
|
||||
} else if((button->base.y2-button->base.y1+1)<(strheight+2)) { //the provided height is too small to fit the text
|
||||
return false;
|
||||
gui_button_redraw(button);
|
||||
return touch_register_area(&button->base);
|
||||
}
|
||||
gui_button_redraw(button); //call the redraw method, which will take care of drawing the entire button
|
||||
return touch_register_area(&button->base); //Register the touch area and receive events for this button, from now on
|
||||
}
|
||||
return false;
|
||||
|
||||
return false; //no more touch areas left
|
||||
}
|
||||
|
||||
void gui_button_redraw(BUTTON_STRUCT* button)
|
||||
{
|
||||
unsigned int strwidth=tft_font_width(button->font)*strlen(button->text);
|
||||
unsigned char strheight=tft_font_height(button->font);
|
||||
unsigned char r,g,b;
|
||||
unsigned int c;
|
||||
r=(button->bgcolor&0xF800)>>11;
|
||||
g=(button->bgcolor&0x07E0)>>5;
|
||||
b=(button->bgcolor&0x001F)>>0;
|
||||
//Calculate text dimensions and shadow colors
|
||||
unsigned int strwidth=tft_font_width(button->font)*strlen(button->text);
|
||||
unsigned char strheight=tft_font_height(button->font);
|
||||
uint16_t c_light,c_dark;
|
||||
calculate_shadows(button->bgcolor,&c_light,&c_dark);
|
||||
|
||||
//Draw the background and the 4 lines (shadow colors)
|
||||
tft_fill_rectangle(button->base.x1+1,button->base.y1+1,button->base.x2-1,button->base.y2-1,button->bgcolor);
|
||||
if((r + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c=0xF800;
|
||||
else
|
||||
c=(r+0x1F/BRIGHTNESS_VAL)<<11;
|
||||
if((g + 0x3F/BRIGHTNESS_VAL) >0x3F)
|
||||
c|=0x07E0;
|
||||
else
|
||||
c|=(g+0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if((b + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c|=0x0018;
|
||||
else
|
||||
c|=(b+0x1F/BRIGHTNESS_VAL)<<0;
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c); //Nord
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c);//West
|
||||
if(r > (0x1F/BRIGHTNESS_VAL))
|
||||
c=(r-0x1F/BRIGHTNESS_VAL)<<11;
|
||||
else
|
||||
c=0x0000;
|
||||
if(g > (0x3F/BRIGHTNESS_VAL))
|
||||
c|=(g-0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if(b > (0x1F/BRIGHTNESS_VAL))
|
||||
c|=(b-0x1F/BRIGHTNESS_VAL)<<0;
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c); //S<>d
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c); //Ost
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_light); //North
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c_light);//West
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c_dark); //South
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c_dark); //East
|
||||
|
||||
//Draw the text
|
||||
tft_print_line(button->base.x1+(button->base.x2-button->base.x1+1-strwidth)/2,button->base.y1+(button->base.y2-button->base.y1+1-strheight)/2,button->txtcolor,button->bgcolor,button->font,button->text);
|
||||
|
||||
}
|
||||
|
||||
void gui_button_remove(BUTTON_STRUCT* button)
|
||||
{
|
||||
//We only need to unregister the touch area, as we have not allocated anything else
|
||||
touch_unregister_area((TOUCH_AREA_STRUCT*)button);
|
||||
}
|
||||
|
||||
/*
|
||||
bool guiAddBitmapButton (BITMAPBUTTON_STRUCT* button)
|
||||
{
|
||||
if(touchHaveEmpty(1))
|
||||
{
|
||||
button->base.hookedActions=PEN_DOWN;
|
||||
button->base.callback = buttons_cb;
|
||||
if(button->base.x2==AUTO)
|
||||
button->base.x2= button->base.x1 -1 + button->imgwidth + button->imgwidth/4;
|
||||
else if((button->base.x2-button->base.x1+1)<(button->imgwidth+2))
|
||||
return false;
|
||||
|
||||
if(button->base.y2==AUTO)
|
||||
button->base.y2=button->base.y1 -1 +button->imgheight + button->imgheight/4;
|
||||
else if((button->base.y2-button->base.y1+1)<(button->imgheight+2))
|
||||
return false;
|
||||
guiRedrawBitmapButton(button);
|
||||
return touchRegisterArea(&button->base);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void guiRedrawBitmapButton(BITMAPBUTTON_STRUCT* button)
|
||||
{
|
||||
|
||||
unsigned char r,g,b;
|
||||
unsigned int c;
|
||||
r=(button->bgcolor&0xF800)>>11;
|
||||
g=(button->bgcolor&0x07E0)>>5;
|
||||
b=(button->bgcolor&0x001F)>>0;
|
||||
tftFillRectangle(button->base.x1+1,button->base.y1+1,button->base.x2-1,button->base.y2-1,button->bgcolor);
|
||||
if((r + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c=0xF800;
|
||||
else
|
||||
c=(r+0x1F/BRIGHTNESS_VAL)<<11;
|
||||
if((g + 0x3F/BRIGHTNESS_VAL) >0x3F)
|
||||
c|=0x07E0;
|
||||
else
|
||||
c|=(g+0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if((b + 0x1F/BRIGHTNESS_VAL) >0x1F)
|
||||
c|=0x0018;
|
||||
else
|
||||
c|=(b+0x1F/BRIGHTNESS_VAL)<<0;
|
||||
tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c); //Nord
|
||||
tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c);//West
|
||||
if(r > (0x1F/BRIGHTNESS_VAL))
|
||||
c=(r-0x1F/BRIGHTNESS_VAL)<<11;
|
||||
else
|
||||
c=0x0000;
|
||||
if(g > (0x3F/BRIGHTNESS_VAL))
|
||||
c|=(g-0x3F/BRIGHTNESS_VAL)<<5;
|
||||
if(b > (0x1F/BRIGHTNESS_VAL))
|
||||
c|=(b-0x1F/BRIGHTNESS_VAL)<<0;
|
||||
tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c); //S<>d
|
||||
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c); //Ost
|
||||
tftDrawBitmapUnscaledStreamedRaw(button->base.x1+(button->base.x2-button->base.x1+1-button->imgwidth)/2,button->base.y1+(button->base.y2-button->base.y1+1-button->imgheight)/2,button->imgwidth,button->imgheight,button->filename);
|
||||
}
|
||||
void guiRemoveBitmapButton(BITMAPBUTTON_STRUCT* button)
|
||||
{
|
||||
touchUnregisterArea((TOUCH_AREA_STRUCT*)button);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
|
||||
/**
|
||||
* @defgroup button Button
|
||||
* The Button Gui-Element
|
||||
* The Button Gui-Element is a clickable, rectangular box with a label inside.
|
||||
* When it is pressed and released you will be notified via the provided callback.
|
||||
*/
|
||||
|
||||
/*@}*/
|
||||
@@ -64,26 +65,6 @@ void gui_button_remove(BUTTON_STRUCT* button);
|
||||
*/
|
||||
void gui_button_redraw(BUTTON_STRUCT* button);
|
||||
|
||||
/*
|
||||
bool guiAddBitmapButton(BITMAPBUTTON_STRUCT* button);
|
||||
void guiRemoveBitmapButton(BITMAPBUTTON_STRUCT* button);
|
||||
void guiRedrawBitmapButton(BITMAPBUTTON_STRUCT* button);
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
typedef struct {
|
||||
TOUCH_AREA_STRUCT base;
|
||||
unsigned int bgcolor;
|
||||
BUTTON_CALLBACK callback; //Callback
|
||||
unsigned char imgwidth;
|
||||
unsigned char imgheight;
|
||||
char* filename;
|
||||
} BITMAPBUTTON_STRUCT;
|
||||
*/
|
||||
//Notice that the first 3 Members are Equal, so it's possible to cast it to a BUTTON_STRUCT even if it's a BITMAPBUTTON_STRUCT (when changeing only the first 3 Members).
|
||||
|
||||
|
||||
|
||||
/*@}*/
|
||||
|
||||
|
||||
@@ -3,29 +3,44 @@
|
||||
#include "checkbox.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define BRIGHTNESS_VAL 2 //How much the Brightness is in/decreased for checkbox shadows (3 -> Add 1/3 off Full Value)
|
||||
#define ACTIVE_COLOR RGB(251,208,123)
|
||||
#define BORDER_COLOR RGB(29,82,129)
|
||||
#define BACKGROUND_COLOR WHITE
|
||||
/* The idea is as follows:
|
||||
* When the user creates a checkbox we create a touch area for that region and wait for PEN_DOWN events.
|
||||
* Once the user puts the pen down in this area we'll redraw the checkbox with different shadows (feedback)
|
||||
* and we'll now wait on PEN_UP or PEN_LEAVE events.
|
||||
* If the user takes the pen away while in the area (PEN_UP), we toggle the checkbox and we call the provided user callback
|
||||
* Otherwise (PEN_LEAVE) we only restore the initial shadows
|
||||
*/
|
||||
|
||||
|
||||
#define ACTIVE_COLOR RGB(251,208,123) //shadow color (inside of border)
|
||||
#define BORDER_COLOR RGB(29,82,129) //1px border color
|
||||
#define BACKGROUND_COLOR WHITE //Background color
|
||||
|
||||
//Callback which is called when the user touches the touch-area we created for the checkbox
|
||||
void checkboxes_cb(void* touchArea, TOUCH_ACTION triggeredAction)
|
||||
{
|
||||
TOUCH_AREA_STRUCT * area = (TOUCH_AREA_STRUCT*)touchArea;
|
||||
CHECKBOX_STRUCT* checkbox = (CHECKBOX_STRUCT*)touchArea;
|
||||
switch(triggeredAction)
|
||||
{
|
||||
case PEN_DOWN:
|
||||
area->hookedActions=PEN_UP|PEN_LEAVE;
|
||||
case PEN_DOWN: //If the user touches the area for the "first time"
|
||||
area->hookedActions=PEN_UP|PEN_LEAVE; //for the future we only want PEN_UP and PEN_LEAVE events
|
||||
|
||||
//Draw active shadows
|
||||
tft_draw_rectangle(checkbox->base.x1+1,checkbox->base.y1+1,checkbox->base.x2-1,checkbox->base.y2-1,ACTIVE_COLOR);
|
||||
tft_draw_rectangle(checkbox->base.x1+2,checkbox->base.y1+2,checkbox->base.x2-2,checkbox->base.y2-2,ACTIVE_COLOR);
|
||||
break;
|
||||
case PEN_UP:
|
||||
checkbox->checked=!checkbox->checked;
|
||||
gui_checkbox_update(checkbox);
|
||||
if(checkbox->callback!=NULL)
|
||||
checkbox->callback(checkbox,checkbox->checked);
|
||||
case PEN_LEAVE:
|
||||
area->hookedActions=PEN_DOWN;
|
||||
case PEN_UP: //If the user took the pen away, while in the area (=toggle checkbox!)
|
||||
checkbox->checked=!checkbox->checked; //Toggle checkbox state
|
||||
gui_checkbox_update(checkbox); //redraw/overdraw tickmark
|
||||
if(checkbox->callback!=NULL) { //The user provided a callback
|
||||
checkbox->callback(checkbox,checkbox->checked); //Call the provided callback with the new checked state
|
||||
}
|
||||
// no break statement here!
|
||||
case PEN_LEAVE: //if the user "slided out" of the area
|
||||
area->hookedActions=PEN_DOWN; //for the future we only want PEN_DOWN events
|
||||
|
||||
//Draw inactive shadows
|
||||
tft_draw_rectangle(checkbox->base.x1+1,checkbox->base.y1+1,checkbox->base.x2-1,checkbox->base.y2-1,BACKGROUND_COLOR);
|
||||
tft_draw_rectangle(checkbox->base.x1+2,checkbox->base.y1+2,checkbox->base.x2-2,checkbox->base.y2-2,BACKGROUND_COLOR);
|
||||
break;
|
||||
@@ -35,48 +50,66 @@ void checkboxes_cb(void* touchArea, TOUCH_ACTION triggeredAction)
|
||||
|
||||
bool gui_checkbox_add(CHECKBOX_STRUCT* checkbox)
|
||||
{
|
||||
if(touch_have_empty(1))
|
||||
if(touch_have_empty(1)) //Check if the touch module can handle one additional area
|
||||
{
|
||||
unsigned char size=0;
|
||||
checkbox->base.hookedActions=PEN_DOWN;
|
||||
checkbox->base.callback = checkboxes_cb;
|
||||
checkbox->base.hookedActions=PEN_DOWN; //At first we are interested in PEN_DOWN events
|
||||
checkbox->base.callback = checkboxes_cb; //Use our own callback for the touch area events
|
||||
|
||||
//Check the size of the checkbox
|
||||
if(checkbox->base.x2>checkbox->base.x1)
|
||||
size = checkbox->base.x2 - checkbox->base.x1;
|
||||
size = checkbox->base.x2 - checkbox->base.x1; //use width a as size
|
||||
if(checkbox->base.y2>checkbox->base.y1)
|
||||
{
|
||||
if((checkbox->base.y2 - checkbox->base.y1)>size)
|
||||
size = checkbox->base.y2 - checkbox->base.y1;
|
||||
if((checkbox->base.y2 - checkbox->base.y1)>size) //height is larger than size
|
||||
size = checkbox->base.y2 - checkbox->base.y1; //use height as size
|
||||
}
|
||||
if((size&0x01))
|
||||
size++;
|
||||
if(size==0) { //no size found (maybe swap x2 and x1 or y2 and y1 ?)
|
||||
return false; //signal error
|
||||
}
|
||||
if((size&0x01)) //the size is an odd number
|
||||
size++; //make size an even number
|
||||
|
||||
//Correct x2,y2 so that the checkbox is quadratic
|
||||
checkbox->base.x2 = checkbox->base.x1 + size;
|
||||
checkbox->base.y2 = checkbox->base.y1 + size;
|
||||
gui_checkbox_redraw(checkbox);
|
||||
return touch_register_area(&checkbox->base);
|
||||
|
||||
gui_checkbox_redraw(checkbox);//Call redraw method, which will take care of the drawing of the entire checkbox
|
||||
|
||||
return touch_register_area(&checkbox->base); //Register the touch area and receive events for this checkbox, from now on
|
||||
}
|
||||
return false;
|
||||
|
||||
return false; //no more touch areas left
|
||||
}
|
||||
|
||||
void gui_checkbox_redraw(CHECKBOX_STRUCT* checkbox)
|
||||
{
|
||||
//Draw background and border
|
||||
tft_fill_rectangle(checkbox->base.x1+1,checkbox->base.y1+1,checkbox->base.x2-1,checkbox->base.y2-1,BACKGROUND_COLOR);
|
||||
tft_draw_rectangle(checkbox->base.x1,checkbox->base.y1,checkbox->base.x2,checkbox->base.y2,BORDER_COLOR);
|
||||
if(checkbox->checked)
|
||||
gui_checkbox_update(checkbox);
|
||||
|
||||
if(checkbox->checked) { //checkbox is currently checked
|
||||
gui_checkbox_update(checkbox); //Call update method which will draw the tickmark
|
||||
}
|
||||
}
|
||||
|
||||
void gui_checkbox_remove(CHECKBOX_STRUCT* checkbox)
|
||||
{
|
||||
//We only need to unregister the touch area, as we have not allocated anything else
|
||||
touch_unregister_area((TOUCH_AREA_STRUCT*)checkbox);
|
||||
}
|
||||
|
||||
void gui_checkbox_update(CHECKBOX_STRUCT* checkbox)
|
||||
{
|
||||
unsigned int c = (checkbox->checked)? checkbox->fgcolor:BACKGROUND_COLOR;
|
||||
unsigned int xcent = checkbox->base.x1+(checkbox->base.x2-checkbox->base.x1)*6/14;
|
||||
unsigned int c = (checkbox->checked)? checkbox->fgcolor:BACKGROUND_COLOR; //color to use for the tickmark
|
||||
|
||||
//helper points inside the checkbox
|
||||
unsigned int xcent = checkbox->base.x1+(checkbox->base.x2-checkbox->base.x1)*6/14;
|
||||
unsigned int yleft = checkbox->base.y2 - (xcent- checkbox->base.x1) - 1 ;
|
||||
unsigned int yright = checkbox->base.y2 - (checkbox->base.x2 - xcent) - 1 ;
|
||||
unsigned int ybot = checkbox->base.y2 - 4;
|
||||
|
||||
//Draw tickmark as a 3pixel wide line
|
||||
tft_draw_line(checkbox->base.x1+3,yleft-1,xcent,ybot -1,c);
|
||||
tft_draw_line(checkbox->base.x1+3,yleft,xcent,ybot ,c);
|
||||
tft_draw_line(checkbox->base.x1+3,yleft+1,xcent,ybot + 1,c);
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
/**
|
||||
* @defgroup checkbox Checkbox
|
||||
* The Checkbox Gui-Element
|
||||
* The Checkbox Gui-Element is a clickable, rectangular box with an optional tickmark inside of it.
|
||||
* When the checkbox is pressed and released it's tick state changes and you will be notified via the provided callback.
|
||||
*/
|
||||
/*@}*/
|
||||
|
||||
|
||||
@@ -6,39 +6,51 @@
|
||||
#include <stddef.h> //for offsetof macro
|
||||
#include <stdlib.h> //for abs
|
||||
|
||||
/* The idea is as follows:
|
||||
* When the user add's a numupdown we create two buttons, one with a plus and one with a minus sign in it
|
||||
* When the user presses one of the buttons we check and increase the value and execute the provided user callback
|
||||
*/
|
||||
|
||||
#define BASE_COLOR RGB(90,90,90)
|
||||
|
||||
#define BASE_COLOR RGB(90,90,90) //Background color for the whole element
|
||||
|
||||
//Callback which is called when the user presses the "plus" button
|
||||
void button_up_cb(void* button)
|
||||
{
|
||||
//Get the pointer to the numupdown: subtract the offset of the buttonUp member in the struct from the button pointer
|
||||
NUMUPDOWN_STRUCT* element = button-offsetof(NUMUPDOWN_STRUCT,buttonUp);
|
||||
if(element->value<element->max) {
|
||||
element->value++;
|
||||
gui_numupdown_update(element);
|
||||
if(element->callback!=NULL) {
|
||||
element->callback(element,element->value);
|
||||
|
||||
if(element->value<element->max) { //old value lies below the maximum
|
||||
element->value++; //let's increase the value
|
||||
gui_numupdown_update(element); //and redraw everything
|
||||
if(element->callback!=NULL) { //the user provided a callback
|
||||
element->callback(element,element->value); //Call the user callback with the new value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Callback which is called when the user presses the "minus" button
|
||||
void button_down_cb(void* button)
|
||||
{
|
||||
//Get the pointer to the numupdown: subtract the offset of the buttonDown member in the struct from the button pointer
|
||||
NUMUPDOWN_STRUCT* element = button-offsetof(NUMUPDOWN_STRUCT,buttonDown);
|
||||
if(element->value>element->min) {
|
||||
element->value--;
|
||||
gui_numupdown_update(element);
|
||||
if(element->callback!=NULL) {
|
||||
element->callback(element,element->value);
|
||||
|
||||
if(element->value>element->min) { //old value lies above the minimum
|
||||
element->value--; //let's decrease the value
|
||||
gui_numupdown_update(element); //and redraw everything
|
||||
if(element->callback!=NULL) { //the user provided a callback
|
||||
element->callback(element,element->value); //Call the user callback with the new value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Method to calculate the number of characters needed to print the provided number in decimal notation (with optional sign)
|
||||
static uint8_t calc_text_width(int16_t val) {
|
||||
uint8_t width = 1 + (val<0); //1 if positive, 2 if negative (to let space for sign)
|
||||
val=abs(val);
|
||||
while(val>=10) {
|
||||
val/=10;
|
||||
width++;
|
||||
val=abs(val); //Make the number positive
|
||||
while(val>=10) { //while we have two or more digits
|
||||
val/=10; //remove one digit
|
||||
width++; //add one character
|
||||
}
|
||||
return width;
|
||||
}
|
||||
@@ -46,24 +58,24 @@ static uint8_t calc_text_width(int16_t val) {
|
||||
|
||||
bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
if(touch_have_empty(2)) //We require 2 TouchAreas (for Buttons)
|
||||
if(touch_have_empty(2)) //Check if the touch module can handle two additional areas
|
||||
{
|
||||
if(numupdown->min > numupdown->max) return false;
|
||||
|
||||
if(numupdown->value > numupdown->max) {
|
||||
numupdown->value = numupdown->max;
|
||||
}
|
||||
if(numupdown->value < numupdown->min) {
|
||||
numupdown->value = numupdown->min;
|
||||
} else if(numupdown->value > numupdown->max) {
|
||||
numupdown->value = numupdown->max;
|
||||
if(numupdown->min > numupdown->max) { //min is bigger than max?
|
||||
return false; //invalid parameter
|
||||
}
|
||||
|
||||
uint8_t tw1 = calc_text_width(numupdown->max);
|
||||
uint8_t tw2 = calc_text_width(numupdown->min);
|
||||
if(tw2 > tw1) tw1 = tw2;
|
||||
uint8_t width= tft_font_width(0)*(tw1+1);
|
||||
if(numupdown->value < numupdown->min) { //value is smaller than min?
|
||||
numupdown->value = numupdown->min; //normalize value
|
||||
} else if(numupdown->value > numupdown->max) { //value is bigger than max?
|
||||
numupdown->value = numupdown->max; //normalize value
|
||||
}
|
||||
|
||||
uint8_t tw1 = calc_text_width(numupdown->max); //Calculate character width to render maximum value
|
||||
uint8_t tw2 = calc_text_width(numupdown->min); //Calculate character width to render minimum value
|
||||
if(tw2 > tw1) tw1 = tw2; //ensure tw1 contains the larger number of the two
|
||||
uint8_t width= tft_font_width(0)*(tw1+1); //Calculate width of the number area
|
||||
|
||||
//Add "minus" button to the left side of the number area
|
||||
numupdown->buttonDown.base.x1=numupdown->x;
|
||||
numupdown->buttonDown.base.y1=numupdown->y;
|
||||
numupdown->buttonDown.base.x2=AUTO;
|
||||
@@ -74,6 +86,8 @@ bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown)
|
||||
numupdown->buttonDown.txtcolor=WHITE;
|
||||
numupdown->buttonDown.callback = button_down_cb;
|
||||
gui_button_add(&numupdown->buttonDown);
|
||||
|
||||
//Add "plus" button to the right side of the number area
|
||||
numupdown->buttonUp.base.x1=numupdown->buttonDown.base.x2+width+2;
|
||||
numupdown->buttonUp.base.y1=numupdown->y;
|
||||
numupdown->buttonUp.base.x2=AUTO;
|
||||
@@ -85,34 +99,45 @@ bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown)
|
||||
numupdown->buttonUp.callback = button_up_cb;
|
||||
gui_button_add(&numupdown->buttonUp);
|
||||
|
||||
//Draw background and label of the number area
|
||||
tft_fill_rectangle(numupdown->buttonDown.base.x2+2,numupdown->y,numupdown->buttonDown.base.x2+width,numupdown->buttonUp.base.y2,BASE_COLOR);
|
||||
tft_print_formatted(numupdown->buttonDown.base.x2+2+tft_font_width(0)/2,numupdown->y+tft_font_height(0)/2,numupdown->fgcolor,BASE_COLOR,0,"%*d",tw1,numupdown->value);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return false; //not enough touch areas left
|
||||
}
|
||||
|
||||
void gui_numupdown_remove(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
{
|
||||
//remove the two buttons, we have no other allocated resources
|
||||
gui_button_remove(&numupdown->buttonUp);
|
||||
gui_button_remove(&numupdown->buttonDown);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gui_numupdown_redraw(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
//redraw the two buttons
|
||||
gui_button_redraw(&numupdown->buttonUp);
|
||||
gui_button_redraw(&numupdown->buttonDown);
|
||||
|
||||
//call update method which will take care of the number-area rendering
|
||||
gui_numupdown_update(numupdown);
|
||||
}
|
||||
|
||||
void gui_numupdown_update(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
{
|
||||
//Calculate the number area width again (see above)
|
||||
uint8_t tw1 = calc_text_width(numupdown->max);
|
||||
uint8_t tw2 = calc_text_width(numupdown->min);
|
||||
if(tw2 > tw1) tw1 = tw2;
|
||||
uint8_t width= tft_font_width(0)*(tw1+1);
|
||||
|
||||
//Draw background and label of the number area
|
||||
tft_fill_rectangle(numupdown->buttonDown.base.x2+2,numupdown->y,numupdown->buttonDown.base.x2+width,numupdown->buttonUp.base.y2,BASE_COLOR);
|
||||
tft_print_formatted(numupdown->buttonDown.base.x2+2+tft_font_width(0)/2,numupdown->y+tft_font_height(0)/2,numupdown->fgcolor,BASE_COLOR,0,"%*d",tw1,numupdown->value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gui_numupdown_redraw(NUMUPDOWN_STRUCT* numupdown)
|
||||
{
|
||||
gui_button_redraw(&numupdown->buttonUp);
|
||||
gui_button_redraw(&numupdown->buttonDown);
|
||||
gui_numupdown_update(numupdown);
|
||||
}
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
#include "screen.h"
|
||||
|
||||
/* The idea is as follows:
|
||||
* We only call screen callbacks from the gui_screen_update() method, which is called from the applications main loop.
|
||||
* Instructions to switch the screen will be delayed until the gui_screen_update() method is called again.
|
||||
* This makes it safe to change the screen from an touch interrupt (e.g. button callback)
|
||||
*/
|
||||
|
||||
static SCREEN_STRUCT* screen_list = NULL;
|
||||
static SCREEN_STRUCT* screen_current = NULL;
|
||||
static volatile SCREEN_STRUCT* screen_goto = NULL;
|
||||
static SCREEN_STRUCT* screen_list = NULL; //Head of the linked list which stores the screen history.
|
||||
static SCREEN_STRUCT* screen_current = NULL; //Pointer to the current screen (= tail of the list)
|
||||
static volatile SCREEN_STRUCT* screen_goto = NULL; //Screen we should navigate to once we enter the gui_screen_update() method again
|
||||
|
||||
SCREEN_STRUCT* gui_screen_get_current() {
|
||||
return screen_current;
|
||||
}
|
||||
|
||||
|
||||
void gui_screen_update() {
|
||||
if(screen_goto!=NULL) { //we received the task to switch the screen
|
||||
SCREEN_STRUCT* go = (SCREEN_STRUCT*) screen_goto; //Backup volatile variable
|
||||
screen_goto=NULL;
|
||||
if(go->next!=NULL) { //we're going back
|
||||
if(go->next!=screen_current) return; //list corrupted?
|
||||
screen_current->on_leave(screen_current);
|
||||
go->next=NULL;
|
||||
} else { //we're going forward
|
||||
screen_goto=NULL; //reset the "goto instruction", since we're processing it now
|
||||
if(go->next!=NULL) { //The screen is not the last in the list, so we're going back
|
||||
if(go->next!=screen_current) { //this condition should always be false
|
||||
return; //list corrupted?
|
||||
}
|
||||
screen_current->on_leave(screen_current); //let the current screen free/unregister it's resources
|
||||
go->next=NULL; //remove the current screen from the list
|
||||
} else { //we're going forward (to a new screen)
|
||||
if(screen_current!=NULL) { //this is not the first screen
|
||||
screen_current->on_leave(screen_current);
|
||||
screen_current->next = go;
|
||||
} else { //first screen ever seen
|
||||
screen_list=go;
|
||||
}
|
||||
screen_current->on_leave(screen_current); //let the current screen free/unregister it's resources
|
||||
screen_current->next = go; //append the new screen to the end of the list
|
||||
} else { //first screen ever seen
|
||||
screen_list=go; //set the new screen as list-head
|
||||
}
|
||||
}
|
||||
go->on_enter(go);
|
||||
screen_current =go;
|
||||
go->on_enter(go); //let the new screen allocate/register it's resources
|
||||
screen_current = go; //the new screen is now the current screen. Transition done
|
||||
}
|
||||
|
||||
if(screen_current!=NULL) { //A screen has been set
|
||||
@@ -37,16 +45,18 @@ void gui_screen_update() {
|
||||
|
||||
|
||||
bool gui_screen_navigate(SCREEN_STRUCT* screen) {
|
||||
if(screen==NULL) return false;
|
||||
screen->next = NULL;
|
||||
screen_goto=screen; //send message to main loop, to switch the screen
|
||||
if(screen==NULL) { //invalid argument passed
|
||||
return false;
|
||||
}
|
||||
screen->next = NULL; //this will become the new tail of the list, so the next pointer must be NULL
|
||||
screen_goto=screen; //"send message" to main loop, to switch the screen
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool gui_screen_back() {
|
||||
if(screen_list==NULL) return false;
|
||||
if(screen_list==NULL) { //the list head is emtpy, nothing to go back to
|
||||
return false;
|
||||
}
|
||||
SCREEN_STRUCT* current = screen_list;
|
||||
SCREEN_STRUCT* last = NULL;
|
||||
//Find second last element in list
|
||||
@@ -54,15 +64,8 @@ bool gui_screen_back() {
|
||||
last = current;
|
||||
current = current->next;
|
||||
}
|
||||
if(last==NULL) return false; //There's only a single screen.
|
||||
if(current!=screen_current) return false; //List corrupted?
|
||||
screen_goto=last; //send message to main loop, to switch the screen
|
||||
if(last==NULL) return false; //There's only a single screen, there's no going back here
|
||||
if(current!=screen_current) return false; //The last entry in the list is not the current screen. List corrupted?
|
||||
screen_goto=last; //"send message" to main loop, to switch the screen
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
/**
|
||||
* @defgroup screen Screen
|
||||
* The Screen Submodule provides an api to navigate between different "screens" on the UI.
|
||||
* Each screen must provide an enter, update and a leave method; which will be called from this module at the right time.
|
||||
* The implemented screens of the application are documented in the \ref screens module.
|
||||
*/
|
||||
/*@}*/
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
#include <stdio.h>
|
||||
#include "filesystem.h"
|
||||
|
||||
/* The idea is as follows:
|
||||
* Most of the tft_* functions can be forwarded to the lowlevel implementation.
|
||||
* The exceptions are commented below.
|
||||
* Make sure to have a look at the doxygen comments for the lowlevel functions and for the tft_* functions
|
||||
*/
|
||||
|
||||
/* Possible improvements:
|
||||
* For formatted printing implement putchar, instead of writing into a buffer and drawing that buffer afterwards
|
||||
*/
|
||||
|
||||
bool tft_init() {
|
||||
return ll_tft_init();
|
||||
|
||||
@@ -52,35 +62,41 @@ uint8_t tft_font_width(uint8_t fontnum) {
|
||||
return ll_tft_font_width(fontnum);
|
||||
}
|
||||
|
||||
//Print line can be done with multiple calls to draw_char
|
||||
void tft_print_line(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, uint8_t font, const char* text) {
|
||||
if(font>=ll_tft_num_fonts()) return;
|
||||
for(int i=0; i<strlen(text); i++) {
|
||||
ll_tft_draw_char(x,y,color,bgcolor, font, text[i]);
|
||||
x+=ll_tft_font_width(font);
|
||||
if(font>=ll_tft_num_fonts()) return; //invalid font index
|
||||
for(int i=0; i<strlen(text); i++) { //for each char in the line
|
||||
ll_tft_draw_char(x,y,color,bgcolor, font, text[i]); //draw the char
|
||||
x+=ll_tft_font_width(font); //and increase the x position
|
||||
}
|
||||
}
|
||||
|
||||
//Printing a formatted line can be done by printing the line in a buffer using "sprintf" and then calling print_line
|
||||
void tft_print_formatted(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, uint8_t font, const char* format, ...) {
|
||||
static char buffer[256]; //not sure if that's the best solution. It would propbably better to implement putchar and use vprintf
|
||||
static char buffer[128]; //buffer to save the formatted text into
|
||||
|
||||
//Since we have variable arguments, we need to forward them. We have to use vsprintf instead of sprintf for that.
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
vsprintf(buffer,format,args);
|
||||
tft_print_line(x,y,color,bgcolor,font,buffer);
|
||||
va_end(args);
|
||||
va_start (args, format); //start the varg-list
|
||||
vsprintf(buffer,format,args); //let vsprintf render the formatted string
|
||||
tft_print_line(x,y,color,bgcolor,font,buffer); //print the string as normal text
|
||||
va_end(args); //end the varg-list
|
||||
}
|
||||
|
||||
bool tft_draw_bitmap_file_unscaled(uint16_t x, uint16_t y, const char* filename) {
|
||||
//Copied and modified from: http://stackoverflow.com/a/17040962/2606757
|
||||
//This method reads a .bmp file from the filesystem and tries to draw it.
|
||||
//Note: The bmp implementation is not complete, it has some limitations and it makes assumptions. See doxygen comment for this method.
|
||||
//Source Copied and adapted from: http://stackoverflow.com/a/17040962/2606757
|
||||
|
||||
FILE_HANDLE* file = filesystem_file_open(filename);
|
||||
if(file==NULL) {
|
||||
FILE_HANDLE* file = filesystem_file_open(filename); //try to open the file
|
||||
if(file==NULL) { //file opening failed
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char info[54];
|
||||
if(filesystem_file_read(file,info,54)!=F_OK) {
|
||||
if(filesystem_file_read(file,info,54)!=F_OK) { //try to read the 54 byte header
|
||||
filesystem_file_close(file);
|
||||
return false;
|
||||
return false; //reading the header failed
|
||||
}
|
||||
|
||||
// extract image height and width from header
|
||||
@@ -91,22 +107,22 @@ bool tft_draw_bitmap_file_unscaled(uint16_t x, uint16_t y, const char* filename)
|
||||
|
||||
filesystem_file_seek(file,*(uint32_t*)&info[10]); //seek to the place where img data begins
|
||||
|
||||
uint32_t row_padded = (width*depth + 3) & (~3); //row size aligned to 4 bytes
|
||||
uint32_t row_padded = (width*depth + 3) & (~3); //row size must be aligned to 4 bytes
|
||||
|
||||
unsigned char data [row_padded];
|
||||
unsigned char data [row_padded]; //allocate space for one row (incl. padding)
|
||||
|
||||
for(int i = 0; i < height; i++)
|
||||
for(int i = 0; i < height; i++) //for each row
|
||||
{
|
||||
filesystem_file_read(file,data,row_padded);
|
||||
for(int j = 0; j < width*depth; j += depth)
|
||||
filesystem_file_read(file,data,row_padded); //read row into buffer
|
||||
for(int j = 0; j < width*depth; j += depth) //for each pixel
|
||||
{
|
||||
unsigned char a,r,g,b;
|
||||
if(depth==4) {
|
||||
if(depth==4) { //a,r,g,b 8bit each
|
||||
a = data[j];
|
||||
r = data[j+1];
|
||||
g = data[j+2];
|
||||
b = data[j+3];
|
||||
} else if (depth==3) {
|
||||
} else if (depth==3) { // b,g,r, 8bit each
|
||||
a = 255;
|
||||
r = data[j+2];
|
||||
g = data[j+1];
|
||||
@@ -114,6 +130,7 @@ bool tft_draw_bitmap_file_unscaled(uint16_t x, uint16_t y, const char* filename)
|
||||
}
|
||||
|
||||
if(a!=0) {
|
||||
//bmp's are stored "bottom-up", so we start drawing at the bottom
|
||||
tft_draw_pixel(x+j/depth,y+height-1-i,RGB(r,g,b));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,23 @@
|
||||
#include "ll_touch.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define NUM_AREAS 50 //Number of Structs Reserved in Memory for TouchAreas (e.g Buttons)
|
||||
TOUCH_AREA_STRUCT* areas[NUM_AREAS] = {NULL};
|
||||
/* The idea is as follows:
|
||||
* The user can add "touch-areas" which basically represent a rectangles on the screen.
|
||||
* Once the user touches such a rectangle with the pen, we forward events to his provided callback.
|
||||
* Touch events are provided to us from the low level implementation via touch_add_raw_event().
|
||||
* We then need to check which touch areas are effected by that event
|
||||
*/
|
||||
|
||||
volatile POINT_STRUCT pos;
|
||||
volatile TOUCH_STATE oldState=TOUCH_UP;
|
||||
/* Possible improvements:
|
||||
* Exchange pointer-list "areas" with a linked list. This would ensure that we can always accept new regions
|
||||
* Implement calibration stuff, and calculate the real coordinates out of the data provided in touch_add_raw_event()
|
||||
*/
|
||||
|
||||
#define NUM_AREAS 50 //Number of Touch Areas we can manage
|
||||
TOUCH_AREA_STRUCT* areas[NUM_AREAS] = {NULL}; //list with pointers to all managed touch area's
|
||||
|
||||
volatile POINT_STRUCT pos; //the last touch point
|
||||
volatile TOUCH_STATE oldState=TOUCH_UP; //the last touch state
|
||||
|
||||
bool touch_init() {
|
||||
return ll_touch_init();
|
||||
@@ -14,117 +26,124 @@ bool touch_init() {
|
||||
|
||||
|
||||
bool touch_add_raw_event(uint16_t touchX, uint16_t touchY, TOUCH_STATE state) {
|
||||
bool penDown = (state==TOUCH_DOWN);
|
||||
bool oldPenDown = (oldState==TOUCH_DOWN);
|
||||
oldState=state;
|
||||
//Update current and old position/state
|
||||
bool penDown = (state==TOUCH_DOWN);
|
||||
bool oldPenDown = (oldState==TOUCH_DOWN);
|
||||
oldState=state;
|
||||
pos.x=touchX;
|
||||
pos.y=touchY;
|
||||
if(penDown)
|
||||
{
|
||||
// tftDrawPixel(touchX,touchY,WHITE);
|
||||
if(!oldPenDown) //First Touch
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
if(areas[z]!=NULL && touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2 )
|
||||
{
|
||||
areas[z]->flags=1; //PenInside=1
|
||||
if(areas[z]->hookedActions & PEN_DOWN)
|
||||
areas[z]->callback(areas[z],PEN_DOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
else //Second, Third
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
if(areas[z]!=NULL )
|
||||
{
|
||||
if(touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2)
|
||||
{
|
||||
if(areas[z]->flags==0) //PenInside ==0
|
||||
{
|
||||
areas[z]->flags=1; //PenInside=1
|
||||
if(areas[z]->hookedActions & PEN_ENTER)
|
||||
areas[z]->callback(areas[z],PEN_ENTER);
|
||||
}
|
||||
}
|
||||
else if(areas[z]->flags) //PenInside==1
|
||||
{
|
||||
areas[z]->flags=0; //PenInside=0
|
||||
if(areas[z]->hookedActions & PEN_LEAVE)
|
||||
areas[z]->callback(areas[z],PEN_LEAVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
if(areas[z]!=NULL && areas[z]->hookedActions&PEN_MOVE)
|
||||
{
|
||||
if(touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2)
|
||||
{
|
||||
areas[z]->callback(areas[z],PEN_MOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(oldPenDown) //Was the pen ever down (or was it a too short touch)
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
if(areas[z]!=NULL && touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2 )
|
||||
{
|
||||
areas[z]->flags=0; //PenInside = 0;
|
||||
if(areas[z]->hookedActions & PEN_UP)
|
||||
areas[z]->callback(areas[z],PEN_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
touchX=0xFFFF;
|
||||
touchY=0xFFFF;
|
||||
}
|
||||
|
||||
if(penDown) //pen is down now
|
||||
{
|
||||
//tft_draw_pixel(touchX,touchY,WHITE);
|
||||
if(!oldPenDown) //pen wasn't down before (positive edge) => First Touch
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
//Check if pos is inside area
|
||||
if(areas[z]!=NULL && touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2 )
|
||||
{
|
||||
areas[z]->flags=1; //Save PenInside=1
|
||||
if(areas[z]->hookedActions & PEN_DOWN) //The user wants to receive pen down events
|
||||
areas[z]->callback(areas[z],PEN_DOWN); //Send event to user callback
|
||||
}
|
||||
}
|
||||
}
|
||||
else //Pen was down before => Second, Third event in row
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
if(areas[z]!=NULL )
|
||||
{
|
||||
//Check if pos is inside area
|
||||
if(touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2)
|
||||
{
|
||||
if(areas[z]->flags==0) //Pen was not inside before (PenInside==0)
|
||||
{
|
||||
areas[z]->flags=1; //Pen is inside now (PenInside=1)
|
||||
if(areas[z]->hookedActions & PEN_ENTER) //The user wants to receive pen enter events
|
||||
areas[z]->callback(areas[z],PEN_ENTER);
|
||||
}
|
||||
}
|
||||
else if(areas[z]->flags) //Pos not inside area, but it was before (PenInside==1)
|
||||
{
|
||||
areas[z]->flags=0; //Pen is no longer inside (PenInside=0)
|
||||
if(areas[z]->hookedActions & PEN_LEAVE) //The user wants to receive pen leave events
|
||||
areas[z]->callback(areas[z],PEN_LEAVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
if(areas[z]!=NULL && (areas[z]->hookedActions&PEN_MOVE)) //User want's to receive pen move events
|
||||
{
|
||||
//Check if pos is inside area
|
||||
if(touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2)
|
||||
{
|
||||
areas[z]->callback(areas[z],PEN_MOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else //pen is not down now
|
||||
{
|
||||
if(oldPenDown) //but it was down before (negative edge)
|
||||
{
|
||||
for(int z=0; z < NUM_AREAS; z++) // For every touch area
|
||||
{
|
||||
//Check if pos is inside area
|
||||
if(areas[z]!=NULL && touchX >= areas[z]->x1 && touchX <= areas[z]->x2 && touchY >= areas[z]->y1 && touchY <= areas[z]->y2 )
|
||||
{
|
||||
areas[z]->flags=0; //The pen is no longer inside (PenInside = 0);
|
||||
if(areas[z]->hookedActions & PEN_UP) //user want's to receive pen up events
|
||||
areas[z]->callback(areas[z],PEN_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touch_have_empty(unsigned char num)
|
||||
{
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==NULL) num--;
|
||||
if(num==0) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
bool touch_register_area(TOUCH_AREA_STRUCT* area) //Registers an Area (fill Struct first). Return false if no more Space in the Pointertable (-->Change NUM_AREAS).
|
||||
{
|
||||
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==NULL)
|
||||
{
|
||||
area->flags=0;
|
||||
areas[i]=area;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
//go through pointer array and check for free spaces
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==NULL) num--; //a free space was found, we need one less
|
||||
if(num==0) return true; //enough free spaces found
|
||||
}
|
||||
return false; //not enough free spaces found
|
||||
}
|
||||
|
||||
void touch_unregister_area(TOUCH_AREA_STRUCT* area)//Unregisters an Area
|
||||
bool touch_register_area(TOUCH_AREA_STRUCT* area)
|
||||
{
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==area)
|
||||
{
|
||||
areas[i]=NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//go through pointer array and check for free space
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==NULL) //free space found
|
||||
{
|
||||
area->flags=0; //we start with empty flags (PenInside=0)
|
||||
areas[i]=area; //save pointer into list
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; //no free space found
|
||||
}
|
||||
|
||||
void touch_unregister_area(TOUCH_AREA_STRUCT* area)
|
||||
{
|
||||
if(area==NULL) return;
|
||||
|
||||
//go through pointer array and find the area to remove
|
||||
for(unsigned char i=0; i<NUM_AREAS; i++)
|
||||
{
|
||||
if(areas[i]==area) //area found in pointer array at pos i
|
||||
{
|
||||
areas[i]=NULL; //set pointer in list to NULL again
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user