discoverpixy
button.c
Go to the documentation of this file.
1 /**************************************************************************************************************************************
2 * Project: discoverpixy
3 * Website: https://github.com/t-moe/discoverpixy
4 * Authors: Aaron Schmocker, Timo Lang
5 * Institution: BFH Bern University of Applied Sciences
6 * File: common/gui/button.c
7 *
8 * Version History:
9 * Date Autor Email SHA Changes
10 * 2015-04-27 timolang@gmail.com 7c9eabc Added button support.
11 * 2015-05-17 timolang@gmail.com 2d46336 Improved comments in implementation of button, checkbox, numupdown, tft, touch and screen modules/submodules.
12 *
13 **************************************************************************************************************************************/
14 
15 #include "tft.h"
16 #include "touch.h"
17 #include "button.h"
18 #include <string.h>
19 
20 /* The Idea is as follows:
21  * When the user add's a button we create a touch area for that region and wait for PEN_DOWN events.
22  * Once the user puts the pen down in this area we'll redraw the button with different shadows (feedback)
23  * and we'll now wait on PEN_UP or PEN_LEAVE events.
24  * If the user takes the pen away while in the area (PEN_UP), we call the provided user callback
25  * Otherwise (PEN_LEAVE) we only restore the initial shadows
26  */
27 
28 /* Possible improvements:
29  * Move the button by 1 pixel while he is pressed, to create a "full 3d" experience
30  * Add events for the case when the button is pressed for a long time, without release
31  */
32 
33 //Method to calculate the shadow colors used to create the "3d" effect
34 void calculate_shadows(uint16_t bgcolor, uint16_t* light_shadow, uint16_t* dark_shadow)
35 {
36 #define BRIGHTNESS_VAL 3 //How much the Brightness is in/decreased for button shadows (3 -> Add/Subtract 1/3 off Full Value)
37 
38  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)
39  uint8_t r, g, b;
40 
41  //separate the channels of the 16-bit rgb565 color
42  r = (bgcolor & 0xF800) >> 11;
43  g = (bgcolor & 0x07E0) >> 5;
44  b = (bgcolor & 0x001F) >> 0;
45 
46  //For the light shadow color:
47  if ((r + 0x1F / BRIGHTNESS_VAL) > 0x1F) { //Adding one third would exceed the maximum of the red channel
48  c_light = 0xF800; //Use full red
49  } else { //adding one third to the red channel is fine
50  c_light = (r + 0x1F / BRIGHTNESS_VAL) << 11; //Use same red as in the background, but add one third
51  }
52 
53  if ((g + 0x3F / BRIGHTNESS_VAL) > 0x3F) { //same for the green channel
54  c_light |= 0x07E0;
55  } else {
56  c_light |= (g + 0x3F / BRIGHTNESS_VAL) << 5;
57  }
58 
59  if ((b + 0x1F / BRIGHTNESS_VAL) > 0x1F) { //and the blue channel
60  c_light |= 0x0018;
61  } else {
62  c_light |= (b + 0x1F / BRIGHTNESS_VAL) << 0;
63  }
64 
65  //For the dark shadow color
66  if (r > (0x1F / BRIGHTNESS_VAL)) { //Subtracting one third would NOT exceed the minimum of the red channel
67  c_dark = (r - 0x1F / BRIGHTNESS_VAL) << 11; //Use same red as in the background, but subtract one third
68  } else { //Subtracting one third would give us a number below zero
69  c_dark = 0x0000; //use no red channel
70  }
71 
72  if (g > (0x3F / BRIGHTNESS_VAL)) { //Same for the green channel
73  c_dark |= (g - 0x3F / BRIGHTNESS_VAL) << 5;
74  }
75 
76  if (b > (0x1F / BRIGHTNESS_VAL)) { //and the blue channel
77  c_dark |= (b - 0x1F / BRIGHTNESS_VAL) << 0;
78  }
79 
80  //Assign the calculated shadows to out parameters
81  if (light_shadow != NULL) {
82  *light_shadow = c_light;
83  }
84 
85  if (dark_shadow != NULL) {
86  *dark_shadow = c_dark;
87  }
88 
89 }
90 
91 //Callback which is called when the user touches the touch-area we created for the button
92 void buttons_cb(void* touchArea, TOUCH_ACTION triggeredAction)
93 {
94  TOUCH_AREA_STRUCT* area = (TOUCH_AREA_STRUCT*)touchArea;
95  BUTTON_STRUCT* button = (BUTTON_STRUCT*)touchArea;
96 
97  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)
98  calculate_shadows(button->bgcolor, &c_light, &c_dark);
99 
100  switch (triggeredAction) {
101  case PEN_DOWN: //If the user touches the area for the "first time"
102  area->hookedActions = PEN_UP | PEN_LEAVE; //for the future we only want PEN_UP and PEN_LEAVE events
103 
104  //Draw shadows
105  tft_draw_line(button->base.x1 + 1, button->base.y1, button->base.x2 - 1, button->base.y1, c_dark); //North
106  tft_draw_line(button->base.x1, button->base.y1 + 1, button->base.x1, button->base.y2 - 1, c_dark); //West
107  tft_draw_line(button->base.x1 + 1, button->base.y2, button->base.x2 - 1, button->base.y2, c_light); //South
108  tft_draw_line(button->base.x2, button->base.y1 + 1, button->base.x2, button->base.y2 - 1, c_light); //East
109  break;
110 
111  case PEN_UP: //If the user took the pen away, while in the area (=button pressed!)
112  case PEN_LEAVE: //or the user "slided out" of the area
113  area->hookedActions = PEN_DOWN; //for the future we only want PEN_DOWN events
114 
115  //Draw inverse shadows
116  tft_draw_line(button->base.x1 + 1, button->base.y1, button->base.x2 - 1, button->base.y1, c_light); //North
117  tft_draw_line(button->base.x1, button->base.y1 + 1, button->base.x1, button->base.y2 - 1, c_light); //West
118  tft_draw_line(button->base.x1 + 1, button->base.y2, button->base.x2 - 1, button->base.y2, c_dark); //South
119  tft_draw_line(button->base.x2, button->base.y1 + 1, button->base.x2, button->base.y2 - 1, c_dark); //East
120 
121  if (triggeredAction == PEN_UP && button->callback != NULL) { //If the button got "pressed" instead of left, and the user provided a callback
122  button->callback(button); //execute the user callback
123  }
124 
125  break;
126 
127  default:
128  break;
129  }
130 }
131 
132 
134 {
135  if (touch_have_empty(1)) { //Check if the touch module can handle one additional area
136  //Calculate width and height of the button text
137  unsigned int strwidth = tft_font_width(button->font) * strlen(button->text);
138  unsigned char strheight = tft_font_height(button->font);
139 
140  button->base.hookedActions = PEN_DOWN; //At first we are interested in PEN_DOWN events
141  button->base.callback = buttons_cb; //Use our own callback for the touch area events
142 
143  if (button->base.x2 == AUTO) { //The user wants us to calculate the button width automatically
144  //Use string width + half of a character width as button width
145  button->base.x2 = button->base.x1 - 1 + strwidth + (tft_font_width(button->font) / 2);
146  } else if ((button->base.x2 - button->base.x1 + 1) < (strwidth + 2)) { //the provided width is too small to fit the entire text
147  return false; //report error
148  }
149 
150  if (button->base.y2 == AUTO) { //The user wants us to calculate the button height automatically
151  //Use one and a half character heights as button height
152  button->base.y2 = button->base.y1 - 1 + strheight + (strheight / 2);
153  } else if ((button->base.y2 - button->base.y1 + 1) < (strheight + 2)) { //the provided height is too small to fit the text
154  return false;
155  }
156 
157  gui_button_redraw(button); //call the redraw method, which will take care of drawing the entire button
158  return touch_register_area(&button->base); //Register the touch area and receive events for this button, from now on
159  }
160 
161  return false; //no more touch areas left
162 }
163 
165 {
166  //Calculate text dimensions and shadow colors
167  unsigned int strwidth = tft_font_width(button->font) * strlen(button->text);
168  unsigned char strheight = tft_font_height(button->font);
169  uint16_t c_light, c_dark;
170  calculate_shadows(button->bgcolor, &c_light, &c_dark);
171 
172  //Draw the background and the 4 lines (shadow colors)
173  tft_fill_rectangle(button->base.x1 + 1, button->base.y1 + 1, button->base.x2 - 1, button->base.y2 - 1, button->bgcolor);
174  tft_draw_line(button->base.x1 + 1, button->base.y1, button->base.x2 - 1, button->base.y1, c_light); //North
175  tft_draw_line(button->base.x1, button->base.y1 + 1, button->base.x1, button->base.y2 - 1, c_light); //West
176  tft_draw_line(button->base.x1 + 1, button->base.y2, button->base.x2 - 1, button->base.y2, c_dark); //South
177  tft_draw_line(button->base.x2, button->base.y1 + 1, button->base.x2, button->base.y2 - 1, c_dark); //East
178 
179  //Draw the text
180  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);
181 
182 }
183 
185 {
186  //We only need to unregister the touch area, as we have not allocated anything else
188 }
TOUCH_ACTION
Definition: touch.h:52
uint8_t tft_font_height(uint8_t fontnum)
Definition: tft.c:87
bool touch_register_area(TOUCH_AREA_STRUCT *area)
Definition: touch.c:181
const char * text
The label of the button.
Definition: button.h:61
uint8_t tft_font_width(uint8_t fontnum)
Definition: tft.c:92
void gui_button_redraw(BUTTON_STRUCT *button)
Definition: button.c:164
uint16_t txtcolor
The 16-bit text color.
Definition: button.h:59
#define AUTO
Use this value instead of x2, y2 in the BUTTON_STRUCT to autocalculate the button width/height...
Definition: button.h:65
Receive an event when the pen goes down inside the region.
Definition: touch.h:54
Receive an event when the pen goes up inside the region.
Definition: touch.h:55
void tft_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
Definition: tft.c:50
uint16_t y1
Top Left Y-Coordinate of Area.
Definition: touch.h:75
bool gui_button_add(BUTTON_STRUCT *button)
Definition: button.c:133
void tft_print_line(uint16_t x, uint16_t y, uint16_t color, uint16_t bgcolor, uint8_t font, const char *text)
Definition: tft.c:98
uint16_t x1
Top Left X-Coordinate of Area.
Definition: touch.h:74
uint16_t bgcolor
The 16-bit background color of the button.
Definition: button.h:57
TOUCH_AREA_STRUCT base
Basic geometry of the button. You only need to set the x1, y1, x2, y2 members of this struct...
Definition: button.h:56
void touch_unregister_area(TOUCH_AREA_STRUCT *area)
Definition: touch.c:195
uint16_t y2
Bottom Right Y-Coordinate of Area.
Definition: touch.h:77
void buttons_cb(void *touchArea, TOUCH_ACTION triggeredAction)
Definition: button.c:92
TOUCH_CALLBACK callback
Callback which is executed when an event occurred in this Area.
Definition: touch.h:78
uint16_t x2
Bottom Right X-Coordinate of Area.
Definition: touch.h:76
void gui_button_remove(BUTTON_STRUCT *button)
Definition: button.c:184
void calculate_shadows(uint16_t bgcolor, uint16_t *light_shadow, uint16_t *dark_shadow)
Definition: button.c:34
void tft_fill_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
Definition: tft.c:67
TOUCH_ACTION hookedActions
Actions to listen to.
Definition: touch.h:73
BUTTON_CALLBACK callback
Callback which is executed when the button is pressed.
Definition: button.h:58
bool touch_have_empty(unsigned char num)
Definition: touch.c:165
uint8_t font
The number of the font to use.
Definition: button.h:60
#define BRIGHTNESS_VAL
Receive an event when the pen leaves the region (pen was inside region before)
Definition: touch.h:57