EGE专栏:EGE专栏
上一篇:EGE绘图之四 Gif动图播放
下一篇:EGE绘图之五 按钮(下)
目录
- 一、按钮功能
-
- 1. 普通按钮
- 2. 可选按钮
-
- 2.1 单选按钮 (Radio Button)
- 2.2 复选框 (Check Box)
- 3. 多功能按钮
- 二、按钮样式
- 三、 按钮点击
-
- 1. 点击按钮确定
- 2.点击判断按钮
-
-
- 2.1 点击判断矩形区域
-
- 2.1.1 表示矩形区域
- 2.1.2 矩形区域内点的判断
- 2.1.3 矩形区域点击判断示例
- 2.2 点击判断圆形区域
-
- 2.2.1 表示圆形区域
- 2.2.2 圆形区域内点的判断
- 2.2.3 点击圆区确定示例
- 2.3 点击判断圆角矩形区域
-
- 2.3.1 表示圆角矩形区域
- 2.3.2 圆角矩形区域内点的判断
- 2.3.3 点击圆角矩形区确定示例
-
- 3. 点击检测多个按钮
-
- 3.1 多个按钮检测示例
一、按钮功能
响应按钮最基本的功能是响应,再有便是,长按事件可能不会回应。
1. 普通按钮
一般情况下,普通按钮只响应点击和长按事件,事件触发后,进行相应的动作。 
2. 可选按钮
可以添加选择状态和操作,按钮在和。一般由点击事件或长按事件触发。
当多个可选按钮组成一个组时,根据可选行为的不同,一般分为 和 。
2.1 单选按钮 (Radio Button)
在按钮组中,单选按钮最多只能选择一个。当选择按钮时,按钮组中的其他按钮应处于非选择状态。(类似于常见的单选题)
2.2 复选框 (Check Box)
在按钮组中,每个按钮都可以独立设置为选择或非选择状态。(类似于常见的多选题)
如下所示:
- 上面一排为Radio Button,每一列都是一组,一组只能选择一个。
- 下面一排为Check Box, 每一为一组,一组种可选择多个。
3. 多功能按钮
根据点击和长按事件,按钮可以添加不同的属性来响应不同的动作。 以下是带进度条和执行结果的按钮,弹出和收入菜单的按钮,可根据需要定制。
二、按钮样式
以下是一些按钮的常见风格
- CSS按钮|菜鸟教程
- Buttons
还有各种按钮特效 用户界面中按钮的基本类型
三、 按钮点击
1. 点击按钮确定
在GUI界面中按钮是最常见的,通过鼠标悬停,点击,拖动等执行不同的动作。
然后解释如何定制按钮来执行鼠标点击。
鼠标点击事件之前已经解释过了。只需按下并抬起鼠标消息即可。指定按钮时,应添加区分左、中、右键的判断。
按下鼠标左键:
if (msg.is_left() && msg.is_down()) { 按下///鼠标左键 } 当前的按钮是:
- :只确定鼠标点击的按钮,而不执行实际按钮点击动作
- :点击动作执行相应的按钮。
当然,,也可以,点击按钮直接执行,而不是等到鼠标按钮抬起。这可以简化代码,并且的情况。
鼠标主要用于这里始终确定按键点击。
2.点击判断按钮
当鼠标点击时,需要判断按钮的点击判断是否在内部。也就是说,断一个点是否在区域内。
通常按钮的形状是矩形、圆形和圆角矩形,这些形状最多。 按钮和不一定重叠。矩形区域和圆形区域域更容易判断,圆形矩形更复杂,因此,一些圆形矩形按钮,小圆角,仍然用矩形区域来判断。
2.1 点击判断矩形区域
2.1.1 表示矩形区域
矩形区域有多种表示方式,但都表示同一区域,可以简单转换。 :
- 左上角位置(x, y), 宽 width, 高 height
- 左上角位置(left, top), 右下角位置(right, bottom) 这两种方法的区别在于,一种是右下角,另一种是宽高,适用于不同的情况。转换很简单,关系是 w i d t h = r i g h t l e f t h e i g h t = b o t t o m t o p width = right - left \ height = bottom - top width=rightleftheight=bottomtop 根据等式稍微变换计算。
2.1.2 矩形区域内点的判断
矩形区域内的判断点, 在 满足以下关系: l e f t x < r i g h t t o p y < b o t t o m left leqslant x < right \ top leqslant y < bottom leftx<righttopy<bottom 用代码表示
(left <= x) && (x < right) && (top <= y) && (y < bottom)
2.1.3 矩形区点击判断示例
#include lt;graphics.h>
#include <math.h>
// 矩形按钮
struct RectButton
{
int x, y;
int width, height;
};
// 判断点(x, y) 是否在按钮点击区域内
bool insideRectButton(const RectButton* button, int x, int y);
// 绘制按钮
void drawRectButton(const RectButton* button);
void draw();
//定义按钮,确定区域
RectButton button = {
220, 200, /* x, y */
200, 80, /* width, height */
};
int clickCount = 0;
int main()
{
initgraph(640, 480, INIT_RENDERMANUAL);
setbkcolor(WHITE);
ege_enable_aa(true);
bool clickButton = false;
bool redraw = true;
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
mouse_msg msg = getmouse();
//判断鼠标左键点击(左键按下确定位置,抬起为执行时刻)
if (msg.is_left()) {
if (msg.is_down()) {
//检测点击的按钮
clickButton = insideRectButton(&button, msg.x, msg.y);
}
else {
//左键抬起,点击动作执行
if (clickButton)
{
clickButton = false;
redraw = true;
clickCount++;
}
}
}
}
//绘制
if (redraw) {
cleardevice();
draw();
redraw = false;
}
}
return 0;
}
bool insideRectButton(const RectButton* button, int x, int y)
{
return (x >= button->x) && (y >= button->y)
&& (x < button->x + button->width)
&& (y < button->y + button->height);
}
void drawRectButton(const RectButton* button)
{
setfillcolor(EGERGB(0x1E, 0x90, 0xFF));
bar(button->x, button->y, button->x + button->width, button->y + button->height);
}
void draw()
{
drawRectButton(&button);
setcolor(BLACK);
setfont(24, 0, "");
xyprintf(240, 360, "点击按钮次数:%d", clickCount);
}
2.2 圆形区域的点击判定
2.2.1 圆形区域的表示
圆形区域有两种表示方法:
- 圆心和半径
- 用包围矩形区域的表述 这两种转换关系也很简单
{ x = l e f t + r i g h t 2 y = t o p + b o t t o m 2 r a d i u s = r i g h t l e f t 2 egin{cases} x= rac{left+right}{2}\ y= rac{top+bottom}{2}\ radius= rac{right-left}{2}\ end{cases} x=2left+righty=2top+bottomradius=2rightleft
{ l e f t = x r a d i u s t o p = y r a d i u s w i d t h = 2 r a d i u s h e i g h t = 2 r a d i u s left{ egin{aligned} left&=x-radius\ top&=y-radius\ width&=2cdot radius\ height&=2cdot radius\ end{aligned} ight. lefttopwidthheight=xradius=yradius=2radius=2radius
2.2.2 点在圆形区域内的判定
如果点 P ( x , y ) P(x, y) P(x,y)在一个圆心为 C ( x 0 , y 0 ) C(x_0,y_0) C(x0,y0),半径为 r r r的 圆内,有以下关系: 点 P P P与圆心 C C C的距离 d = ( x x 0 ) 2 + ( y y 0 ) 2 r d =sqrt{left( x-x_0 ight) ^2+left( y-y_0 ight) ^2}leqslant r d=(xx0)2+(yy0)2 r 由于开方运行复杂,可将两边同时平方,不等号方向保持不变,可得: ( x x 0 ) 2 + ( y y 0 ) 2 r a d i u s 2 left( x-x_0 ight) ^2+left( y-y_0 ight) ^2leqslant radius^2 (xx0)2+(yy0)2radius2
用代码表示:
//计算点与圆心的xy差值
int dx = x - x0;
int dy = y - y0;
//(点到圆心距离的平方) 小于等于 (半径的平方)
(dx * dx + dy * dy) <= (r * r)
2.2.3 圆形区域点击判定示例
#include <graphics.h>
#include <math.h>
// 圆形按钮
struct CircleButton
{
int x, y;
int radius;
};
// 判断点(x, y) 是否在按钮点击区域内
bool insideCircleButton(const CircleButton* button, int x, int y);
// 绘制按钮
void drawCircleButton(const CircleButton* button);
void draw();
//定义按钮,确定区域
CircleButton button = {
320, 240, /* x, y */
100, /* radius */
};
int clickCount = 0;
int main()
{
initgraph(640, 480, INIT_RENDERMANUAL);
setbkcolor(WHITE);
ege_enable_aa(true);
bool clickButton = false;
bool redraw = true;
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
mouse_msg msg = getmouse();
//判断鼠标左键点击(左键按下确定位置,抬起为执行时刻)
if (msg.is_left()) {
if (msg.is_down()) {
//检测点击的按钮
clickButton = insideCircleButton(&button, msg.x, msg.y);
}
else {
//左键抬起,点击动作执行
if (clickButton)
{
clickButton = false;
redraw = true;
clickCount++;
}
}
}
}
//绘制
if (redraw) {
cleardevice();
draw();
redraw = false;
}
}
return 0;
}
bool insideCircleButton(const CircleButton* button, int x, int y)
{
int dx = x - button->x, dy = y - button->y;
return (dx * dx + dy * dy) <= (button->radius * button->radius);
}
void drawCircleButton(const CircleButton* button)
{
setfillcolor(EGEARGB(0xFF, 0x1E, 0x90, 0xFF));
//高级绘图函数
ege_fillellipse(button->x - button->radius, button->y - button->radius,
2 * button->radius, 2 * button->radius);
//或者使用下面的普通绘图函数
//fillellipse(button->x, button->y, button->radius, button->radius);
}
void draw()
{
drawCircleButton(&button);
setcolor(BLACK);
setfont(24, 0, "");
xyprintf(240, 360, "点击按钮次数:%d", clickCount);
}
2.3 圆角矩形区域的点击判定
圆角矩形即矩形的四个角不再是直角,取而代之的是四个90°的圆弧。一般圆角矩形四个圆角的半径都是相等的,有些可以不相等,这里的圆角矩形是前者。
2.3.1 圆角矩形区域的表示
取圆角矩形的包围矩形的位置和大小参数,额外一个参数设定圆角的半径。圆角的半径最大不能大于短边的一半。
2.3.2 点在圆角矩形区域内的判定
首先,如果点在圆角矩形区域内部,则肯定满足点在圆角矩形外部包围矩形内部的条件。 如果点在圆角矩形区域内部,需要两个条件同时成立。
点P 位于 圆角矩形包围矩形 的内部。
(left <= x) && (x < right) && (top <= y) && (y < bottom)
与矩形不同的是,即使位于包围矩形内,但是点也可能在圆角处。因此增加在圆角处的判断: 由对称性质,以圆角矩形中心为原点,可以将圆角矩形分成四个区域,并对称映射到第一象限,如下图所示。
当点位于图中所示时,可以根据来判断。
在满足条件1的情况下:
- 当点P位于圆角处时,点P位于圆角所在的圆内。
- 当点P不在圆角处。
下图为判断流程:
比较复杂的是在圆角处的判断: 当 x w 2 r xgeqslant rac{w}{2}-r x2wr 且 y h 2 r ygeqslant rac{h}{2}-r y2hr 时,点P在圆角矩形的圆角处。此时,如果点P在圆角矩形内,则满足关系: ( x ( w 2 r ) ) 2 + ( y ( h 2 r ) ) 2 r 2 left( x-left( rac{w}{2}-r ight) ight) ^2+left( y-left( rac{h}{2}-r ight) ight) ^2leqslant ,r^2 (x(2wr))2+(y(2hr))2r2
2.3.3 圆角矩形区域点击判定示例
#include <graphics.h>
#include <math.h>
// 圆角矩形按钮
struct RoundRectButton
{
int x, y;
int width, height;
float radius;
};
// 判断点(x, y) 是否在按钮点击区域内
bool insideRoundRectButton(const RoundRectButton* button, int x, int y);
// 绘制按钮
void drawRoundRectButton(const RoundRectButton* button);
void draw();
//定义按钮,确定区域
RoundRectButton button = {
320 - 100, 240 - 80, /* x, y */
200, 160, /* width, height */
40, /* cornerRadius */
};
int clickCount = 0;
int main()
{
initgraph(640, 480, INIT_RENDERMANUAL);
setbkcolor(WHITE);
ege_enable_aa(true);
bool clickButton = false;
bool redraw = true;
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
mouse_msg msg = getmouse();
//判断鼠标左键点击(左键按下确定位置,抬起为执行时刻)
if (msg.is_left()) {
if (msg.is_down()) {
//检测点击的按钮
clickButton = insideRoundRectButton(&button, msg.x, msg.y);
}
else {
//左键抬起,点击动作执行
if (clickButton)
{
clickButton = false;
redraw = true;
clickCount++;
}
}
}
}
//绘制
if (redraw) {
cleardevice();
draw();
redraw = false;
}
}
return 0;
}
bool insideRoundRectButton(const RoundRectButton* button, int x, int y)
{
bool inside = false;
// 点在包围矩形内
if ((x >= button->x) && (y >= button->y)
&& (x < button->x + button->width)
&& (y < button->y + button->height)
) {
float centerx = button->x + button->width / 2.0f;
float centery = button->y + button->height / 2.0f;
float dx = (float)fabs(x - centerx);
float dy = (float)fabs(y - centery);
float interWidth = button->width / 2.0f - button->radius;
float interHeight = button->height / 2.0f - button->radius;
// 点不在圆角空白处
if (! ((dx > interWidth)
&& (dy > interHeight)
&& ((dx - interWidth) * (dx - interWidth) + (dy - interHeight) * (dy - interHeight)
> button->radius * button->radius)
)
) {
inside = true;
}
}
return inside;
}
void drawRoundRectButton(const RoundRectButton* button)
{
setfillcolor(EGEARGB(0xFF, 0x1E, 0x90, 0xFF));
ege_fillrect((float)(button->x + button->radius), (float)(button->y),
(float)(button->width - 2 * button->radius), float(button->height)
);
ege_fillrect((float)(button->x), (float)(button->y + button->radius),
(float)(button->radius), (float)(button->height - 2 * button->radius)
);
ege_fillrect((float)(button->x + button->width - button->radius),
(float)(button->y + button->radius),
(float)(button->radius), (float)(button->height - 2 * button->radius)
);
float diameter = 2 * button->radius;
float dx = button->width - diameter;
float dy = button->height - diameter;
ege_fillpie((float)(button->x + dx), (float)(button->y + dy), diameter, diameter, 0.0f, 90.0f);
ege_fillpie((float)(button->x), (float)(button->y + dy), diameter, diameter, 90.0f, 90.0f);
ege_fillpie((float)(button->x), (float)(button->y), diameter, diameter, 180.0f, 90.0f);
ege_fillpie((float)(button->x + dx), (float)(button->y), diameter, diameter, 270.0f, 90.0f);
}
void draw()
{
drawRoundRectButton(&button);
setcolor(BLACK);
setfont(24, 0, "");
xyprintf(240, 360, "点击按钮次数:%d", clickCount);
}
3. 多个按钮的点击检测
上面按钮都是定义成结构体,当存在多个按钮的时候,当鼠标点击时,简单的方法就是按一定的顺序去遍历这些按钮,逐一进行点击判断。当检测到有一个按钮被点击到,则执行动作,剩余不再检测。
为了方便,这里创建一个Button数组,逐一检查。当然,这样的前提是按钮没有相互重叠,不然重叠后,是需要按照一定的顺序的。 当然,这样子效率会低一些,对于十几个按钮是没有问题的,如果是大量按钮,就需要使用算法来处理了,这里就不再深入。
for (int i = 0; i < buttonArrayLength; i++) {
// 点击位置在按钮内
if (insideButton(&buttonArray[i], x, y)) {
clickButtonId = i;
break; //退出,已经检测到,后面的按钮不再检测
}
}
3.1 多个按钮检测示例
下面为多个按钮的点击检测示例。
#include <graphics.h>
// 圆形按钮
struct CircleButton
{
int x, y; /* 圆心*/
int radius; /* 半径*/
};
// 判断点(x, y)是否在按钮点击区域内部
bool insideButton(const CircleButton* button, int x, int y);
// 绘制所有按钮
void drawCircleButton(const CircleButton buttonArray[], int length);
// 查找 (x, y) 所在的按钮,返回按钮ID, 没有返回 -1
int searchButton(int x, int y, const CircleButton buttonArray[], int length);
// 绘制
void draw();
#define BUTTON_SIZE 8
#define BUTTON_ID_NONE -1
//定义按钮,确定区域
CircleButton buttonArray[BUTTON_SIZE];
// 按下按钮ID
int clickButtonId = BUTTON_ID_NONE;
int main()
{
initgraph(640, 480, INIT_RENDERMANUAL);
setbkcolor(WHITE);
setbkmode(TRANSPARENT);
ege_enable_aa(true);
for (int i = 0; i < BUTTON_SIZE; i++) {
buttonArray[i].x = (i % 2 * 2 + 1) * 640 / 4;
buttonArray[i].y = (i / 2) * 320 / 3 + 60;
buttonArray[i].radius = 50;
}
clickButtonId = BUTTON_ID_NONE;
bool redraw = true;
int btnId = BUTTON_ID_NONE;
for (; is_run(); delay_fps(60)) {
while (mousemsg()) {
mouse_msg msg = getmouse();
// 判断鼠标左键按下(左键按下确定位置,同时判断是否为按钮区域
// 抬起则解除按下状态
if (msg.is_left()) {
if (msg.is_down()) {
// 检查是否有按钮被按下
btnId = searchButton(msg.x, msg.y, buttonArray, BUTTON_SIZE);
}
else {
//左键抬起,执行动作(这里显示设置点击按钮ID)
if (btnId != clickButtonId) {
clickButtonId = btnId;
redraw = true;
}
}
}
}
// 判断是否需要重绘,减少不必要的绘制操作
if (redraw) {
cleardevice();
draw();
redraw = false;
}
}
return 0;
}
bool insideButton(const CircleButton* button, int x, int y)
{
int dx = x - button->x, dy = y - button->y;
return (dx * dx + dy * dy) <= (button->radius * button->radius);
}
void drawCircleButton(const CircleButton buttonArray[], int length)
{
setfillcolor(EGEARGB(0xFF, 0x1E, 0x90, 0xFF));
setcolor(WHITE);
settextjustify(CENTER_TEXT, CENTER_TEXT);
setfont(36, 0, "");
for (int i = 0; i < length; i++) {
//高级绘图函数
ege_fillellipse(buttonArray[i].x - buttonArray[i].radius,
buttonArray[i].y - buttonArray[i].radius,
2 * buttonArray[i].radius,
2 * buttonArray[i].radius);
xyprintf(buttonArray[i].x, buttonArray[i].y, "%d", i);
}
}
int searchButton(int x, int y, const CircleButton buttonArray[], int length)
{
int buttonId = BUTTON_ID_NONE;
for (int i = 0; i < length; i++) {
if (insideButton(&buttonArray[i], x, y)) {
buttonId = i;
break; //退出,已经检测到,后面的按钮不再检测
}
}
return buttonId;
}
void draw()
{
//绘制
drawCircleButton(buttonArray, BUTTON_SIZE);
setcolor(BLACK);
setfont(24, 0, "");
settextjustify(LEFT_TEXT, TOP_TEXT);
xyprintf(240, 360, "点击按钮ID:%d", clickButtonId);
}
EGE专栏:EGE专栏
上一篇:EGE绘图之四 Gif动图播放
下一篇:EGE绘图之五 按钮(下)