Improved comments in implementation of button, checkbox, numupdown, tft, touch and screen modules/submodules.

This commit is contained in:
t-moe
2015-05-17 14:23:12 +02:00
parent e46314b760
commit 2d463366c1
9 changed files with 441 additions and 396 deletions

View File

@@ -3,187 +3,152 @@
#include "button.h" #include "button.h"
#include <string.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) 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; TOUCH_AREA_STRUCT * area = (TOUCH_AREA_STRUCT*)touchArea;
BUTTON_STRUCT* button = (BUTTON_STRUCT*)touchArea; BUTTON_STRUCT* button = (BUTTON_STRUCT*)touchArea;
unsigned int c1,c2;
unsigned char r,g,b; 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)
r=(button->bgcolor&0xF800)>>11; calculate_shadows(button->bgcolor,&c_light,&c_dark);
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;
switch(triggeredAction) switch(triggeredAction)
{ {
case PEN_DOWN: case PEN_DOWN: //If the user touches the area for the "first time"
area->hookedActions=PEN_UP|PEN_LEAVE; area->hookedActions=PEN_UP|PEN_LEAVE; //for the future we only want PEN_UP and PEN_LEAVE events
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 //Draw shadows
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.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_dark); //North
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c1); //Ost 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; break;
case PEN_UP: case PEN_UP: //If the user took the pen away, while in the area (=button pressed!)
case PEN_LEAVE: case PEN_LEAVE: //or the user "slided out" of the area
area->hookedActions=PEN_DOWN; area->hookedActions=PEN_DOWN; //for the future we only want PEN_DOWN events
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 //Draw inverse shadows
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.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_light); //North
tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c2); //Ost tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c_light);//West
if(triggeredAction==PEN_UP && button->callback!=NULL) tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c_dark); //South
button->callback(button); 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; break;
default: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 int strwidth=tft_font_width(button->font)*strlen(button->text);
unsigned char strheight=tft_font_height(button->font); unsigned char strheight=tft_font_height(button->font);
button->base.hookedActions=PEN_DOWN;
button->base.callback = buttons_cb; button->base.hookedActions=PEN_DOWN; //At first we are interested in PEN_DOWN events
if(button->base.x2==AUTO) 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); 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)) } else if((button->base.x2-button->base.x1+1)<(strwidth+2)) { //the provided width is too small to fit the entire text
return false; return false; //report error
}
if(button->base.y2==AUTO) 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); 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; 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) void gui_button_redraw(BUTTON_STRUCT* button)
{ {
unsigned int strwidth=tft_font_width(button->font)*strlen(button->text); //Calculate text dimensions and shadow colors
unsigned char strheight=tft_font_height(button->font); unsigned int strwidth=tft_font_width(button->font)*strlen(button->text);
unsigned char r,g,b; unsigned char strheight=tft_font_height(button->font);
unsigned int c; uint16_t c_light,c_dark;
r=(button->bgcolor&0xF800)>>11; calculate_shadows(button->bgcolor,&c_light,&c_dark);
g=(button->bgcolor&0x07E0)>>5;
b=(button->bgcolor&0x001F)>>0; //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); 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) tft_draw_line(button->base.x1+1,button->base.y1,button->base.x2-1,button->base.y1,c_light); //North
c=0xF800; tft_draw_line(button->base.x1,button->base.y1+1,button->base.x1,button->base.y2-1,c_light);//West
else tft_draw_line(button->base.x1+1,button->base.y2,button->base.x2-1,button->base.y2,c_dark); //South
c=(r+0x1F/BRIGHTNESS_VAL)<<11; tft_draw_line(button->base.x2,button->base.y1+1,button->base.x2,button->base.y2-1,c_dark); //East
if((g + 0x3F/BRIGHTNESS_VAL) >0x3F)
c|=0x07E0; //Draw the text
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_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); 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) 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); 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);
}
*/

View File

@@ -11,7 +11,8 @@
/** /**
* @defgroup button Button * @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); 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).
/*@}*/ /*@}*/

View File

@@ -3,29 +3,44 @@
#include "checkbox.h" #include "checkbox.h"
#include <stdio.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) /* The idea is as follows:
#define ACTIVE_COLOR RGB(251,208,123) * When the user creates a checkbox we create a touch area for that region and wait for PEN_DOWN events.
#define BORDER_COLOR RGB(29,82,129) * Once the user puts the pen down in this area we'll redraw the checkbox with different shadows (feedback)
#define BACKGROUND_COLOR WHITE * 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) void checkboxes_cb(void* touchArea, TOUCH_ACTION triggeredAction)
{ {
TOUCH_AREA_STRUCT * area = (TOUCH_AREA_STRUCT*)touchArea; TOUCH_AREA_STRUCT * area = (TOUCH_AREA_STRUCT*)touchArea;
CHECKBOX_STRUCT* checkbox = (CHECKBOX_STRUCT*)touchArea; CHECKBOX_STRUCT* checkbox = (CHECKBOX_STRUCT*)touchArea;
switch(triggeredAction) switch(triggeredAction)
{ {
case PEN_DOWN: case PEN_DOWN: //If the user touches the area for the "first time"
area->hookedActions=PEN_UP|PEN_LEAVE; 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+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); tft_draw_rectangle(checkbox->base.x1+2,checkbox->base.y1+2,checkbox->base.x2-2,checkbox->base.y2-2,ACTIVE_COLOR);
break; break;
case PEN_UP: case PEN_UP: //If the user took the pen away, while in the area (=toggle checkbox!)
checkbox->checked=!checkbox->checked; checkbox->checked=!checkbox->checked; //Toggle checkbox state
gui_checkbox_update(checkbox); gui_checkbox_update(checkbox); //redraw/overdraw tickmark
if(checkbox->callback!=NULL) if(checkbox->callback!=NULL) { //The user provided a callback
checkbox->callback(checkbox,checkbox->checked); checkbox->callback(checkbox,checkbox->checked); //Call the provided callback with the new checked state
case PEN_LEAVE: }
area->hookedActions=PEN_DOWN; // 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+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); tft_draw_rectangle(checkbox->base.x1+2,checkbox->base.y1+2,checkbox->base.x2-2,checkbox->base.y2-2,BACKGROUND_COLOR);
break; break;
@@ -35,48 +50,66 @@ void checkboxes_cb(void* touchArea, TOUCH_ACTION triggeredAction)
bool gui_checkbox_add(CHECKBOX_STRUCT* checkbox) 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; unsigned char size=0;
checkbox->base.hookedActions=PEN_DOWN; checkbox->base.hookedActions=PEN_DOWN; //At first we are interested in PEN_DOWN events
checkbox->base.callback = checkboxes_cb; 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) 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)
{ {
if((checkbox->base.y2 - checkbox->base.y1)>size) if((checkbox->base.y2 - checkbox->base.y1)>size) //height is larger than size
size = checkbox->base.y2 - checkbox->base.y1; size = checkbox->base.y2 - checkbox->base.y1; //use height as size
} }
if((size&0x01)) if(size==0) { //no size found (maybe swap x2 and x1 or y2 and y1 ?)
size++; 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.x2 = checkbox->base.x1 + size;
checkbox->base.y2 = checkbox->base.y1 + 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) 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_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); 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) 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); touch_unregister_area((TOUCH_AREA_STRUCT*)checkbox);
} }
void gui_checkbox_update(CHECKBOX_STRUCT* checkbox) void gui_checkbox_update(CHECKBOX_STRUCT* checkbox)
{ {
unsigned int c = (checkbox->checked)? checkbox->fgcolor:BACKGROUND_COLOR; unsigned int c = (checkbox->checked)? checkbox->fgcolor:BACKGROUND_COLOR; //color to use for the tickmark
unsigned int xcent = checkbox->base.x1+(checkbox->base.x2-checkbox->base.x1)*6/14;
//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 yleft = checkbox->base.y2 - (xcent- checkbox->base.x1) - 1 ;
unsigned int yright = checkbox->base.y2 - (checkbox->base.x2 - xcent) - 1 ; unsigned int yright = checkbox->base.y2 - (checkbox->base.x2 - xcent) - 1 ;
unsigned int ybot = checkbox->base.y2 - 4; 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-1,xcent,ybot -1,c);
tft_draw_line(checkbox->base.x1+3,yleft,xcent,ybot ,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); tft_draw_line(checkbox->base.x1+3,yleft+1,xcent,ybot + 1,c);

View File

@@ -10,7 +10,8 @@
/** /**
* @defgroup checkbox Checkbox * @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.
*/ */
/*@}*/ /*@}*/

View File

@@ -6,39 +6,51 @@
#include <stddef.h> //for offsetof macro #include <stddef.h> //for offsetof macro
#include <stdlib.h> //for abs #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) 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); NUMUPDOWN_STRUCT* element = button-offsetof(NUMUPDOWN_STRUCT,buttonUp);
if(element->value<element->max) {
element->value++; if(element->value<element->max) { //old value lies below the maximum
gui_numupdown_update(element); element->value++; //let's increase the value
if(element->callback!=NULL) { gui_numupdown_update(element); //and redraw everything
element->callback(element,element->value); 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) 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); NUMUPDOWN_STRUCT* element = button-offsetof(NUMUPDOWN_STRUCT,buttonDown);
if(element->value>element->min) {
element->value--; if(element->value>element->min) { //old value lies above the minimum
gui_numupdown_update(element); element->value--; //let's decrease the value
if(element->callback!=NULL) { gui_numupdown_update(element); //and redraw everything
element->callback(element,element->value); 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) { 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) uint8_t width = 1 + (val<0); //1 if positive, 2 if negative (to let space for sign)
val=abs(val); val=abs(val); //Make the number positive
while(val>=10) { while(val>=10) { //while we have two or more digits
val/=10; val/=10; //remove one digit
width++; width++; //add one character
} }
return width; return width;
} }
@@ -46,24 +58,24 @@ static uint8_t calc_text_width(int16_t val) {
bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown) 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->min > numupdown->max) { //min is bigger than max?
return false; //invalid parameter
if(numupdown->value > numupdown->max) {
numupdown->value = numupdown->max;
} }
if(numupdown->value < numupdown->min) {
numupdown->value = numupdown->min; if(numupdown->value < numupdown->min) { //value is smaller than min?
} else if(numupdown->value > numupdown->max) { numupdown->value = numupdown->min; //normalize value
numupdown->value = numupdown->max; } 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); uint8_t tw1 = calc_text_width(numupdown->max); //Calculate character width to render maximum value
uint8_t tw2 = calc_text_width(numupdown->min); uint8_t tw2 = calc_text_width(numupdown->min); //Calculate character width to render minimum value
if(tw2 > tw1) tw1 = tw2; if(tw2 > tw1) tw1 = tw2; //ensure tw1 contains the larger number of the two
uint8_t width= tft_font_width(0)*(tw1+1); 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.x1=numupdown->x;
numupdown->buttonDown.base.y1=numupdown->y; numupdown->buttonDown.base.y1=numupdown->y;
numupdown->buttonDown.base.x2=AUTO; numupdown->buttonDown.base.x2=AUTO;
@@ -74,6 +86,8 @@ bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown)
numupdown->buttonDown.txtcolor=WHITE; numupdown->buttonDown.txtcolor=WHITE;
numupdown->buttonDown.callback = button_down_cb; numupdown->buttonDown.callback = button_down_cb;
gui_button_add(&numupdown->buttonDown); 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.x1=numupdown->buttonDown.base.x2+width+2;
numupdown->buttonUp.base.y1=numupdown->y; numupdown->buttonUp.base.y1=numupdown->y;
numupdown->buttonUp.base.x2=AUTO; numupdown->buttonUp.base.x2=AUTO;
@@ -85,34 +99,45 @@ bool gui_numupdown_add(NUMUPDOWN_STRUCT* numupdown)
numupdown->buttonUp.callback = button_up_cb; numupdown->buttonUp.callback = button_up_cb;
gui_button_add(&numupdown->buttonUp); 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_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); 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 true;
} }
return false;
return false; //not enough touch areas left
} }
void gui_numupdown_remove(NUMUPDOWN_STRUCT* numupdown) 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->buttonUp);
gui_button_remove(&numupdown->buttonDown); 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) 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 tw1 = calc_text_width(numupdown->max);
uint8_t tw2 = calc_text_width(numupdown->min); uint8_t tw2 = calc_text_width(numupdown->min);
if(tw2 > tw1) tw1 = tw2; if(tw2 > tw1) tw1 = tw2;
uint8_t width= tft_font_width(0)*(tw1+1); 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_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); 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);
}

View File

@@ -1,32 +1,40 @@
#include "screen.h" #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_list = NULL; //Head of the linked list which stores the screen history.
static SCREEN_STRUCT* screen_current = NULL; static SCREEN_STRUCT* screen_current = NULL; //Pointer to the current screen (= tail of the list)
static volatile SCREEN_STRUCT* screen_goto = NULL; 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() { SCREEN_STRUCT* gui_screen_get_current() {
return screen_current; return screen_current;
} }
void gui_screen_update() { void gui_screen_update() {
if(screen_goto!=NULL) { //we received the task to switch the screen if(screen_goto!=NULL) { //we received the task to switch the screen
SCREEN_STRUCT* go = (SCREEN_STRUCT*) screen_goto; //Backup volatile variable SCREEN_STRUCT* go = (SCREEN_STRUCT*) screen_goto; //Backup volatile variable
screen_goto=NULL; screen_goto=NULL; //reset the "goto instruction", since we're processing it now
if(go->next!=NULL) { //we're going back if(go->next!=NULL) { //The screen is not the last in the list, so we're going back
if(go->next!=screen_current) return; //list corrupted? if(go->next!=screen_current) { //this condition should always be false
screen_current->on_leave(screen_current); return; //list corrupted?
go->next=NULL; }
} else { //we're going forward 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 if(screen_current!=NULL) { //this is not the first screen
screen_current->on_leave(screen_current); screen_current->on_leave(screen_current); //let the current screen free/unregister it's resources
screen_current->next = go; screen_current->next = go; //append the new screen to the end of the list
} else { //first screen ever seen } else { //first screen ever seen
screen_list=go; screen_list=go; //set the new screen as list-head
} }
} }
go->on_enter(go); go->on_enter(go); //let the new screen allocate/register it's resources
screen_current =go; screen_current = go; //the new screen is now the current screen. Transition done
} }
if(screen_current!=NULL) { //A screen has been set if(screen_current!=NULL) { //A screen has been set
@@ -37,16 +45,18 @@ void gui_screen_update() {
bool gui_screen_navigate(SCREEN_STRUCT* screen) { bool gui_screen_navigate(SCREEN_STRUCT* screen) {
if(screen==NULL) return false; if(screen==NULL) { //invalid argument passed
screen->next = NULL; return false;
screen_goto=screen; //send message to main loop, to switch the screen }
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; return true;
} }
bool gui_screen_back() { 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* current = screen_list;
SCREEN_STRUCT* last = NULL; SCREEN_STRUCT* last = NULL;
//Find second last element in list //Find second last element in list
@@ -54,15 +64,8 @@ bool gui_screen_back() {
last = current; last = current;
current = current->next; current = current->next;
} }
if(last==NULL) return false; //There's only a single screen. if(last==NULL) return false; //There's only a single screen, there's no going back here
if(current!=screen_current) return false; //List corrupted? 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 screen_goto=last; //"send message" to main loop, to switch the screen
return true; return true;
} }

View File

@@ -12,6 +12,7 @@
/** /**
* @defgroup screen Screen * @defgroup screen Screen
* The Screen Submodule provides an api to navigate between different "screens" on the UI. * 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. * The implemented screens of the application are documented in the \ref screens module.
*/ */
/*@}*/ /*@}*/

View File

@@ -5,6 +5,16 @@
#include <stdio.h> #include <stdio.h>
#include "filesystem.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() { bool tft_init() {
return ll_tft_init(); return ll_tft_init();
@@ -52,35 +62,41 @@ uint8_t tft_font_width(uint8_t fontnum) {
return ll_tft_font_width(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) { 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; if(font>=ll_tft_num_fonts()) return; //invalid font index
for(int i=0; i<strlen(text); i++) { 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]); ll_tft_draw_char(x,y,color,bgcolor, font, text[i]); //draw the char
x+=ll_tft_font_width(font); 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, ...) { 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_list args;
va_start (args, format); va_start (args, format); //start the varg-list
vsprintf(buffer,format,args); vsprintf(buffer,format,args); //let vsprintf render the formatted string
tft_print_line(x,y,color,bgcolor,font,buffer); tft_print_line(x,y,color,bgcolor,font,buffer); //print the string as normal text
va_end(args); va_end(args); //end the varg-list
} }
bool tft_draw_bitmap_file_unscaled(uint16_t x, uint16_t y, const char* filename) { 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); FILE_HANDLE* file = filesystem_file_open(filename); //try to open the file
if(file==NULL) { if(file==NULL) { //file opening failed
return false; return false;
} }
unsigned char info[54]; 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); filesystem_file_close(file);
return false; return false; //reading the header failed
} }
// extract image height and width from header // 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 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); filesystem_file_read(file,data,row_padded); //read row into buffer
for(int j = 0; j < width*depth; j += depth) for(int j = 0; j < width*depth; j += depth) //for each pixel
{ {
unsigned char a,r,g,b; unsigned char a,r,g,b;
if(depth==4) { if(depth==4) { //a,r,g,b 8bit each
a = data[j]; a = data[j];
r = data[j+1]; r = data[j+1];
g = data[j+2]; g = data[j+2];
b = data[j+3]; b = data[j+3];
} else if (depth==3) { } else if (depth==3) { // b,g,r, 8bit each
a = 255; a = 255;
r = data[j+2]; r = data[j+2];
g = data[j+1]; 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) { 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)); tft_draw_pixel(x+j/depth,y+height-1-i,RGB(r,g,b));
} }
} }

View File

@@ -2,11 +2,23 @@
#include "ll_touch.h" #include "ll_touch.h"
#include <stdio.h> #include <stdio.h>
#define NUM_AREAS 50 //Number of Structs Reserved in Memory for TouchAreas (e.g Buttons) /* The idea is as follows:
TOUCH_AREA_STRUCT* areas[NUM_AREAS] = {NULL}; * 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; /* Possible improvements:
volatile TOUCH_STATE oldState=TOUCH_UP; * 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() { bool touch_init() {
return ll_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 touch_add_raw_event(uint16_t touchX, uint16_t touchY, TOUCH_STATE state) {
bool penDown = (state==TOUCH_DOWN); //Update current and old position/state
bool oldPenDown = (oldState==TOUCH_DOWN); bool penDown = (state==TOUCH_DOWN);
oldState=state; bool oldPenDown = (oldState==TOUCH_DOWN);
oldState=state;
pos.x=touchX; pos.x=touchX;
pos.y=touchY; 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; return true;
} }
bool touch_have_empty(unsigned char num) bool touch_have_empty(unsigned char num)
{ {
for(unsigned char i=0; i<NUM_AREAS; i++) //go through pointer array and check for free spaces
{ for(unsigned char i=0; i<NUM_AREAS; i++)
if(areas[i]==NULL) num--; {
if(num==0) return true; 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; }
return false; //not enough free spaces found
}
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;
} }
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++) //go through pointer array and check for free space
{ for(unsigned char i=0; i<NUM_AREAS; i++)
if(areas[i]==area) {
{ if(areas[i]==NULL) //free space found
areas[i]=NULL; {
break; 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;
}
}
} }