按钮
2022-5-29 ~ 2023-2-20
(0)
程序截图
操作方法
点击按钮操作。
更新说明
原先版本仅支持 Unicode 字符集,此次经过修改可支持多字节字符集。
原先按钮仅支持文字,此次按钮可支持图片,需传入 normal 状态下图片的路径,touch 状态下图片的路径,press 状态下图片的路径,图片可支持透明通道,会自动缩放。不过用图片时需要传几张图片才能演示,不能代码复制进去就能跑起来。
2023/2/20 修了一些 bug 如更改字体大小按钮尺寸会改变的问题
函数说明
// 添加一个按钮进按钮管理者,由于插入的按钮是拷贝不是引用,所以返回一个插入的按钮指针供操作按钮
Button* ButtonManager::AddButton(Button button)
{
this->buttonlist.push_back(button);
return &buttonlist.back();
}
// Vec2 构造函数
Vec2(double xx, double yy) :x(xx), y(yy) {};
// Rect 构造函数
Rect(Vec2 position, Vec2 size) :size(size), position(position) {}
// 按钮构造函数
// boundingbox 是边框,报错按钮位置和尺寸的信息,buttontext 是按钮文字信息,releaseFunc 是松开按钮后调用的函数,releaseParam 是 releaseFunc 的参数
Button(Rect boundingbox, std::wstring buttontext, std::function<int(void*)> releaseFunc, void* releaseParam) :
boundingbox(boundingbox), buttontext(buttontext), releaseFunc(releaseFunc), releaseParam(releaseParam) {}
// 按钮用构造函数生成
ButtonManager manager;
manager.AddButton(Button(Rect(Vec2(WIDTH / 2.0, HEIGHT / 2.0), Vec2(WIDTH / 2.0, HEIGHT / 2.0)), L"",
[](void* param)
{
if (param == nullptr)return 1;
Button* button = (Button*)param;
WCHAR arr[128];
InputBox(arr, 128, L"请输入按钮内的文字", L"输入框", button->buttontext.c_str());
button->buttontext = arr;
double width = 0, height = 0;
InputBox(arr, 128, L"请输入按钮的宽度", L"输入框", L"");
swscanf_s(arr, TEXT("%lf"), &width);
InputBox(arr, 128, L"请输入按钮的高度", L"输入框", L"");
swscanf_s(arr, TEXT("%lf"), &height);
if (width != 0.0 && height != 0.0)
button->boundingbox.size = Vec2(width, height);
return 0;
}, nullptr));
// 接受鼠标信息
void ButtonManager::ReceiveMessage(ExMessage* msg)
{
for (Button& button : this->buttonlist)
{
button.receiver(msg);
}
}
// 画按钮
void ButtonManager::DrawButton()
{
for (Button& button : this->buttonlist)
{
button.DrawButton();
}
}
几个重要参数
Button 类里有两个重要参数,textsize 和 textareasize,textsize 控制字体高度,textareasize 控制按钮内文字所能占据的区域在按钮区域中的占比。改 textsize 字体会改变大小,改 textareasize 字体所能占据的区域变大或变小。
class Button
{
private:
double textsize = 20;
double textareasize = 0.9;
Vec2 defaultsize = Vec2(textwidth(L"...") / textareasize, textheight(L"...") / textareasize);
Vec2 defaulttext = Vec2(textwidth(L"..."), textheight(L"..."));
State nowstate = State::general;
void DrawButton_General();
void DrawButton_Touch();
void DrawButton_Press();
void DrawButton_Forbidden();
bool isPress = false;
public:
Button() :boundingbox(), buttontext() {}
Button(Rect boundingbox, std::wstring buttontext, std::function<int(void*)> releaseFunc, void* releaseParam) :
boundingbox(boundingbox), buttontext(buttontext), releaseFunc(releaseFunc), releaseParam(releaseParam) {}
std::wstring buttontext;
Rect boundingbox;
std::function<int(void*)> releaseFunc = nullptr;
void* releaseParam = nullptr;
void DrawButton();
void receiver(ExMessage* msg);
void ForbidButton() { this->nowstate = State::forbidden; } // 禁用按钮
void RefreshButton() { this->nowstate = State::general; } // 恢复按钮
void SetTextSize(double size)
{
textsize = size;
defaultsize = Vec2(textsize * 1.5 / textareasize, textsize / textareasize);
defaulttext = Vec2(textsize * 1.5, textsize);
}
void SetTextAreaSize(double size)
{
textareasize = size;
defaultsize = Vec2(textsize * 1.5 / textareasize, textsize / textareasize);
defaulttext = Vec2(textsize * 1.5, textsize);
}
};
宏 TCW_GUI_BUTTON_MYSELF,当按钮回调函数调用时,若回调函数的参数为 TCW_GUI_BUTTON_MYSELF 也即为空,则参数默认为按钮自身的指针。
#define TCW_GUI_BUTTON_MYSELF 0
void Button::DrawButton()
{
switch (this->nowstate)
{
case State::general:
DrawButton_General();
break;
case State::touch:
DrawButton_Touch();
break;
case State::press:
DrawButton_Press();
break;
case State::release:
DrawButton_Touch();
if (releaseFunc != nullptr)
{
if (releaseParam == TCW_GUI_BUTTON_MYSELF)releaseFunc(this);
else releaseFunc(releaseParam);
}
this->nowstate = State::touch;
break;
case State::forbidden:
DrawButton_Forbidden();
break;
default:
break;
}
}
写这个按钮要注意字体宽度不要设置,传 0 就可以,不然会拉伸字体。
代码实现
TCW_GUI.h
#pragma once
#include<graphics.h>
#include<string>
#include<list>
#include<functional>
#include<stdio.h>
#pragma comment( lib, "MSIMG32.LIB")
#define TCW_GUI_BUTTON_MYSELF 0
#ifdef UNICODE
#define STRING std::wstring
#define SPRINTF swprintf_s
#define SSCANF swscanf_s
#else
#define STRING std::string
#define SPRINTF sprintf
#define SSCANF sscanf
#endif // UNICODE
namespace TCW_GUI
{
enum class State
{
general = 0,
touch = 1,
press = 2,
release = 3,
forbidden = 4
};
enum class DrawDecision
{
text = 0,
graph = 1
};
class Vec2
{
public:
double x, y;
Vec2() :x(0), y(0) {}
Vec2(double xx, double yy) :x(xx), y(yy) {};
Vec2 operator+(Vec2 num)
{
return Vec2(x + num.x, y + num.y);
}
Vec2 operator-(Vec2 num)
{
return Vec2(x - num.x, y - num.y);
}
Vec2 operator/(double num)
{
return Vec2(x / num, y / num);
}
Vec2 operator*(double num)
{
return Vec2(x * num, y * num);
}
};
class Rect
{
public:
Rect() :size(), position() {}
Rect(Vec2 position, Vec2 size) :size(size), position(position) {}
Vec2 size;
Vec2 position;
bool isInRect(Vec2 point)
{
Vec2 left_top = position - size / 2.0;
Vec2 right_buttom = position + size / 2.0;
if (point.x >= left_top.x && point.y >= left_top.y &&
point.x <= right_buttom.x && point.y <= right_buttom.y)return true;
return false;
}
};
class Button
{
private:
double textsize = 20;
double textareasize = 0.9;
Vec2 defaultsize;
Vec2 defaulttext;
State nowstate = State::general;
DrawDecision drawdecision = DrawDecision::text;
void DrawButton_General();
void DrawButton_Touch();
void DrawButton_Press();
void DrawButton_Forbidden();
bool isPress = false;
public:
Button() :boundingbox(), buttontext()
{
settextstyle(textsize, 0, TEXT("微软雅黑"));
Vec2 defaultsize = Vec2(textwidth(TEXT("...")) / textareasize, textheight(TEXT("...")) / textareasize);
Vec2 defaulttext = Vec2(textwidth(TEXT("...")), textheight(TEXT("...")));
}
Button(Rect boundingbox, STRING buttontext, std::function<int(void*)> releaseFunc, void* releaseParam) :
boundingbox(boundingbox), buttontext(buttontext), releaseFunc(releaseFunc), releaseParam(releaseParam)
{
drawdecision = DrawDecision::text;
settextstyle(textsize, 0, TEXT("微软雅黑"));
Vec2 defaultsize = Vec2(textwidth(TEXT("...")) / textareasize, textheight(TEXT("...")) / textareasize);
Vec2 defaulttext = Vec2(textwidth(TEXT("...")), textheight(TEXT("...")));
}
Button(Rect boundingbox, STRING graphsrc_normal, STRING graphsrc_touch, STRING graphsrc_press,
std::function<int(void*)> releaseFunc, void* releaseParam) :
boundingbox(boundingbox), graphsrc_normal(graphsrc_normal), graphsrc_touch(graphsrc_touch), graphsrc_press(graphsrc_press),
releaseFunc(releaseFunc), releaseParam(releaseParam)
{
drawdecision = DrawDecision::graph;
}
STRING buttontext;
STRING graphsrc_normal;
STRING graphsrc_touch;
STRING graphsrc_press;
Rect boundingbox;
std::function<int(void*)> releaseFunc = nullptr;
void* releaseParam = nullptr;
void DrawButton();
void DrawButton_Text(State state);
void DrawButton_Graph(State state);
void receiver(ExMessage* msg);
void ForbidButton() { this->nowstate = State::forbidden; } // 禁用按钮
void RefreshButton() { this->nowstate = State::general; } // 恢复按钮
void SetTextSize(double size)
{
textsize = size;
defaultsize = Vec2(textsize * 1.5 / textareasize, textsize / textareasize);
defaulttext = Vec2(textsize * 1.5, textsize);
}
void SetTextAreaSize(double size)
{
textareasize = size;
defaultsize = Vec2(textsize * 1.5 / textareasize, textsize / textareasize);
defaulttext = Vec2(textsize * 1.5, textsize);
}
};
class ButtonManager
{
std::list<Button> buttonlist;
public:
Button* AddButton(Button button);
void ReceiveMessage(ExMessage* msg);
void DrawButton();
};
void Rectangle_TCW(Vec2 left_top, Vec2 right_buttom)
{
rectangle(left_top.x, left_top.y, right_buttom.x, right_buttom.y);
}
void Fillrectangle_TCW(Vec2 left_top, Vec2 right_buttom)
{
fillrectangle(left_top.x, left_top.y, right_buttom.x, right_buttom.y);
}
void Outtextxy_TCW(Vec2 position, const TCHAR* str)
{
outtextxy(position.x, position.y, str);
}
void Button::DrawButton_Text(State state)
{
LOGFONT log;
COLORREF textcol;
COLORREF linecol;
COLORREF fillcol;
int bkmode;
gettextstyle(&log);
bkmode = getbkmode();
textcol = gettextcolor();
linecol = getlinecolor();
fillcol = getfillcolor();
// 默认数值
settextstyle(textsize, 0, TEXT("微软雅黑"));
settextcolor(BLACK);
setbkmode(TRANSPARENT);
setlinecolor(BLACK);
setfillcolor(WHITE);
switch (state)
{
case TCW_GUI::State::general:
break;
case TCW_GUI::State::touch:
setfillcolor(RGB(240, 240, 240));
break;
case TCW_GUI::State::press:
setfillcolor(RGB(240, 240, 240));
break;
case TCW_GUI::State::release:
break;
case TCW_GUI::State::forbidden:
settextcolor(RGB(128, 128, 128));
break;
default:
break;
}
Vec2 size_button = Vec2(this->boundingbox.size * textareasize);
Vec2 size_text = Vec2(textwidth(this->buttontext.c_str()), textsize);
if (boundingbox.size.x > defaultsize.x && boundingbox.size.y > defaultsize.y) // 比最小值大
{
Rectangle_TCW(this->boundingbox.position - this->boundingbox.size / 2.0,
this->boundingbox.position + this->boundingbox.size / 2.0);
Fillrectangle_TCW(this->boundingbox.position - this->boundingbox.size / 2.0,
this->boundingbox.position + this->boundingbox.size / 2.0);
if (size_button.x >= size_text.x && size_button.y >= size_text.y) // 字数没超
{
switch (state)
{
case TCW_GUI::State::general:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
break;
case TCW_GUI::State::touch:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
break;
case TCW_GUI::State::press:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0 + Vec2(2, 2), buttontext.c_str());
break;
case TCW_GUI::State::release:
break;
case TCW_GUI::State::forbidden:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
break;
default:
break;
}
}
else // 字数超了
{
int wordnum = size_button.x / textwidth(buttontext.c_str()) * buttontext.size() - 2;
STRING realstr = buttontext.substr(0, wordnum);
realstr += TEXT("...");
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, realstr.c_str());
switch (state)
{
case TCW_GUI::State::general:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, realstr.c_str());
break;
case TCW_GUI::State::touch:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, realstr.c_str());
break;
case TCW_GUI::State::press:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0 + Vec2(2, 2), realstr.c_str());
break;
case TCW_GUI::State::release:
break;
case TCW_GUI::State::forbidden:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, realstr.c_str());
break;
default:
break;
}
}
}
else // 比最小值小
{
Rectangle_TCW(this->boundingbox.position - this->defaultsize / 2.0,
this->boundingbox.position + this->defaultsize / 2.0);
Fillrectangle_TCW(this->boundingbox.position - this->defaultsize / 2.0,
this->boundingbox.position + this->defaultsize / 2.0);
if (defaulttext.x >= size_text.x && defaulttext.y >= size_text.y) // 字宽比三个点小
{
switch (state)
{
case TCW_GUI::State::general:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
break;
case TCW_GUI::State::touch:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
break;
case TCW_GUI::State::press:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0 + Vec2(2, 2), buttontext.c_str());
break;
case TCW_GUI::State::release:
break;
case TCW_GUI::State::forbidden:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
break;
default:
break;
}
}
else // 字宽比三个点大
{
switch (state)
{
case TCW_GUI::State::general:
Outtextxy_TCW(this->boundingbox.position - defaulttext / 2.0, TEXT("..."));
break;
case TCW_GUI::State::touch:
Outtextxy_TCW(this->boundingbox.position - defaulttext / 2.0, TEXT("..."));
break;
case TCW_GUI::State::press:
Outtextxy_TCW(this->boundingbox.position - size_text / 2.0 + Vec2(2, 2), TEXT("..."));
break;
case TCW_GUI::State::release:
break;
case TCW_GUI::State::forbidden:
Outtextxy_TCW(this->boundingbox.position - defaulttext / 2.0, TEXT("..."));
break;
default:
break;
}
}
}
settextstyle(&log);
settextcolor(textcol);
setbkmode(bkmode);
setlinecolor(linecol);
setfillcolor(fillcol);
}
void Button::DrawButton_Graph(State state)
{
IMAGE img;
switch (state)
{
case TCW_GUI::State::general:
loadimage(&img, (this->graphsrc_normal).c_str());
break;
case TCW_GUI::State::touch:
loadimage(&img, (this->graphsrc_touch).c_str());
break;
case TCW_GUI::State::press:
loadimage(&img, (this->graphsrc_press).c_str());
break;
case TCW_GUI::State::release:
break;
case TCW_GUI::State::forbidden:
loadimage(&img, (this->graphsrc_normal).c_str());
break;
default:
break;
}
IMAGE* srcimg = &img;
HDC dstDC = GetImageHDC(NULL);
HDC srcDC = GetImageHDC(srcimg);
int w = srcimg->getwidth();
int h = srcimg->getheight();
// 结构体的第三个成员表示额外的透明度,0 表示全透明,255 表示不透明。
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
// 使用 Windows GDI 函数实现半透明位图
Vec2 left_top = this->boundingbox.position - this->boundingbox.size / 2.0;
AlphaBlend(dstDC, left_top.x, left_top.y, boundingbox.size.x, boundingbox.size.y, srcDC, 0, 0, w, h, bf);
}
void Button::DrawButton_General()
{
switch (this->drawdecision)
{
case TCW_GUI::DrawDecision::text:DrawButton_Text(State::general);
break;
case TCW_GUI::DrawDecision::graph:DrawButton_Graph(State::general);
break;
default:
break;
}
}
void Button::DrawButton_Touch()
{
switch (this->drawdecision)
{
case TCW_GUI::DrawDecision::text:DrawButton_Text(State::touch);
break;
case TCW_GUI::DrawDecision::graph:DrawButton_Graph(State::touch);
break;
default:
break;
}
}
void Button::DrawButton_Press()
{
switch (this->drawdecision)
{
case TCW_GUI::DrawDecision::text:DrawButton_Text(State::press);
break;
case TCW_GUI::DrawDecision::graph:DrawButton_Graph(State::press);
break;
default:
break;
}
}
void Button::DrawButton_Forbidden()
{
switch (drawdecision)
{
case TCW_GUI::DrawDecision::text:DrawButton_Text(State::forbidden);
break;
case TCW_GUI::DrawDecision::graph:DrawButton_Graph(State::forbidden);
break;
default:
break;
}
}
void Button::DrawButton()
{
switch (this->nowstate)
{
case State::general:
DrawButton_General();
break;
case State::touch:
DrawButton_Touch();
break;
case State::press:
DrawButton_Press();
break;
case State::release:
DrawButton_Touch();
if (releaseFunc != nullptr)
{
if (releaseParam == TCW_GUI_BUTTON_MYSELF)releaseFunc(this);
else releaseFunc(releaseParam);
}
this->nowstate = State::touch;
break;
case State::forbidden:
DrawButton_Forbidden();
break;
default:
break;
}
}
void Button::receiver(ExMessage* msg)
{
if (this->nowstate == State::forbidden)return;
// 先 general 后 touch 再 press 一个 release 后重新 general
if (!isPress && !this->boundingbox.isInRect(Vec2(msg->x, msg->y)))
{
this->nowstate = State::general;
}
else if (!isPress && this->boundingbox.isInRect(Vec2(msg->x, msg->y)))
{
if (!msg->lbutton)
this->nowstate = State::touch;
else if (this->nowstate == State::touch)
{
isPress = true;
this->nowstate = State::press;
}
}
else if (isPress && this->boundingbox.isInRect(Vec2(msg->x, msg->y)))
{
if (!msg->lbutton)
{
isPress = false;
this->nowstate = State::release;
}
else this->nowstate = State::press;
}
else if (isPress && !this->boundingbox.isInRect(Vec2(msg->x, msg->y)))
{
if (!msg->lbutton)
{
isPress = false;
this->nowstate = State::general;
}
else this->nowstate = State::press;
}
}
Button* ButtonManager::AddButton(Button button)
{
this->buttonlist.push_back(button);
return &buttonlist.back();
}
void ButtonManager::ReceiveMessage(ExMessage* msg)
{
for (Button& button : this->buttonlist)
{
button.receiver(msg);
}
}
void ButtonManager::DrawButton()
{
for (Button& button : this->buttonlist)
{
button.DrawButton();
}
}
}
示例程序:
#include"TCW_GUI.h"
#include<time.h>
#define WIDTH 640
#define HEIGHT 480
int SrandCircle(void* param)
{
int radius = rand() % 200 + 1;
int x = rand() % WIDTH;
int y = rand() % HEIGHT;
circle(x, y, radius);
return 0;
}
void Rectangle_Middle_Size(int middle_x, int middle_y, int size_width, int size_height)
{
int left = middle_x - (size_width >> 1);
int top = middle_y - (size_height >> 1);
int right = middle_x + (size_width >> 1);
int buttom = middle_y + (size_height >> 1);
rectangle(left, top, right, buttom);
}
int SrandRectangle(void* param)
{
int middle_x = rand() % WIDTH;
int middle_y = rand() % HEIGHT;
int size_width = rand() % 300 + 2;
int size_height = rand() % 200 + 2;
Rectangle_Middle_Size(middle_x, middle_y, size_width, size_height);
return 0;
}
int ClearDrawArea(void* param)
{
cleardevice();
return 0;
}
int main()
{
srand((unsigned)time(NULL));
initgraph(WIDTH, HEIGHT);
setbkcolor(BROWN);
cleardevice();
ExMessage msg;
bool isExit = false;
int margin = 10;
TCW_GUI::ButtonManager manager;
manager.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(WIDTH - 50 - margin, 20 + margin),
TCW_GUI::Vec2(100, 40)), TEXT("随机画圆"), SrandCircle, nullptr));
manager.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(WIDTH - 50 - margin, 60 + margin * 2),
TCW_GUI::Vec2(100, 40)), TEXT("随机画矩形"), SrandRectangle, nullptr));
manager.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(WIDTH - 50 - margin, 100 + margin * 3),
TCW_GUI::Vec2(100, 40)), TEXT("清空绘图区域"), ClearDrawArea, nullptr));
while (!isExit)
{
if (peekmessage(&msg, EM_MOUSE))
{
manager.ReceiveMessage(&msg);
manager.DrawButton();
}
}
EndBatchDraw();
closegraph();
return 0;
}
添加评论
取消回复