我正在用 C++ wxWidgets 编写一个应用程序。我正在尝试创建一个自定义按钮类,但有两个问题:
代码:
gridButton.h
#pragma once
#include "wx/wx.h"
enum gbSTYLE {
NUM,
OP,
EXTRA
};
class gridButton : public wxWindow
{
bool pressed;
bool hovered;
wxString text;
static const int width = 50, height = 50;
wxFont* gbFont = new wxFont(20, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
wxFONTWEIGHT_LIGHT, false, "Lato");
// RICORDATI DI CANCELLARE font_Button DALLA CLASSE MAIN QUANDO HAI FINITO
wxBrush* background;
wxColour* textColor;
wxBrush* hoveredBackground;
wxColour* hoveredTextColor;
wxBrush* pressedBackground;
wxColour* pressedTextColor;
public:
gridButton(wxFrame* parent, wxWindowID id, wxString text, gbSTYLE style);
~gridButton();
void applyStyle(gbSTYLE style);
void paintEvent(wxPaintEvent& evt);
void paintNow();
void render(wxDC& dc);
void mouseMoved(wxMouseEvent& evt);
void mouseDown(wxMouseEvent& evt);
void mouseWheelMoved(wxMouseEvent& evt);
void mouseReleased(wxMouseEvent& evt);
void rightClick(wxMouseEvent& evt);
void mouseLeftWindow(wxMouseEvent& evt);
void mouseEnterWindow(wxMouseEvent& evt);
void keyPressed(wxKeyEvent& evt);
void keyReleased(wxKeyEvent& evt);
DECLARE_EVENT_TABLE()
};
gridButton.cpp
#include "gridButton.h"
BEGIN_EVENT_TABLE(gridButton, wxWindow)
EVT_MOTION(gridButton::mouseMoved)
EVT_LEFT_DOWN(gridButton::mouseDown)
EVT_LEFT_UP(gridButton::mouseReleased)
EVT_RIGHT_DOWN(gridButton::rightClick)
EVT_ENTER_WINDOW(gridButton::mouseEnterWindow)
EVT_LEAVE_WINDOW(gridButton::mouseLeftWindow)
EVT_KEY_DOWN(gridButton::keyPressed)
EVT_KEY_UP(gridButton::keyReleased)
EVT_MOUSEWHEEL(gridButton::mouseWheelMoved)
EVT_PAINT(gridButton::paintEvent)
END_EVENT_TABLE()
// funzione costruttrice (wxFULL_REPAINT_ON_RESIZE per evitare un glitch grafico quando si ridimensiona la finestra)
gridButton::gridButton(wxFrame* parent, wxWindowID id, wxString text, gbSTYLE style)
: wxWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
{
SetMinSize(wxSize(width, height));
this->text = text;
hovered = false;
pressed = false;
applyStyle(style);
}
// funzione distruttrice
gridButton::~gridButton()
{
delete gbFont;
delete background;
delete textColor;
delete hoveredBackground;
delete hoveredTextColor;
delete pressedBackground;
delete pressedTextColor;
}
void gridButton::applyStyle(gbSTYLE style)
{
switch (style)
{
default:
background = new wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID);
textColor = new wxColour(25, 25, 25);
hoveredBackground = new wxBrush(wxColour(125, 125, 125), wxBRUSHSTYLE_SOLID);
hoveredTextColor = new wxColour(185, 185, 185);
pressedBackground = new wxBrush(wxColour(25, 25, 25), wxBRUSHSTYLE_SOLID);
pressedTextColor = new wxColour(200, 200, 200);
break;
case NUM:
background = new wxBrush(wxColour(98, 101, 138), wxBRUSHSTYLE_SOLID);
textColor = new wxColour(190, 197, 200);
hoveredBackground = new wxBrush(wxColour(66, 69, 96), wxBRUSHSTYLE_SOLID);
hoveredTextColor = new wxColour(36, 38, 52);
pressedBackground = new wxBrush(wxColour(16, 17, 23), wxBRUSHSTYLE_SOLID);
pressedTextColor = new wxColour(128, 134, 192);
break;
}
}
//chiamato da EVT_PAINT ogni frame
void gridButton::paintEvent(wxPaintEvent& evt)
{
wxPaintDC dc(this);
render(dc);
}
//eseguire un render quando si vuole
void gridButton::paintNow()
{
wxClientDC dc(this);
render(dc);
}
//renderizzazione, qui si mette la grafica
void gridButton::render(wxDC& dc)
{
int w = this->GetSize().GetWidth();
int h = this->GetSize().GetHeight();
if (pressed) {
dc.SetBrush(*pressedBackground);
dc.SetTextForeground(*pressedTextColor);
}
else if (hovered) {
dc.SetBrush(*hoveredBackground);
dc.SetTextForeground(*hoveredTextColor);
}
else {
dc.SetBrush(*background);
dc.SetTextForeground(*textColor);
}
dc.DrawRectangle(0, 0, w, h);
dc.SetFont(*gbFont);
wxSize textSize = GetTextExtent(text);
dc.DrawText(text, w / 2 - (textSize.GetWidth()), h / 2 - (textSize.GetHeight()));
}
void gridButton::mouseDown(wxMouseEvent& evt)
{
pressed = true;
paintNow();
}
void gridButton::mouseReleased(wxMouseEvent& evt)
{
pressed = false;
paintNow();
}
void gridButton::mouseEnterWindow(wxMouseEvent& evt)
{
hovered = true;
paintNow();
}
void gridButton::mouseLeftWindow(wxMouseEvent& evt)
{
hovered = false;
pressed = false;
paintNow();
}
// eventi inutilizzati
void gridButton::mouseMoved(wxMouseEvent& evt) {}
void gridButton::mouseWheelMoved(wxMouseEvent& evt) {}
void gridButton::rightClick(wxMouseEvent& evt) {}
void gridButton::keyPressed(wxKeyEvent& evt) {}
void gridButton::keyReleased(wxKeyEvent& evt) {}
这就是我布局网格的方式(现在看起来很糟糕,因为我仍在处理按钮本身):
#include "main.h"
void Main::AddButtons()
{
int w = 5, h = 4;
MainGrid = new wxGridSizer(w, h, 0, 0);
gridButton* test = new gridButton(this, wxID_ANY, "(", NUM);
MainGrid->Add(test, 0, wxEXPAND);
AddOpButton(4);
for (int i = 0; i < 2; i++) MainGrid->AddSpacer(1);
for (int i = 7; i < 10; i++) AddNumButton(i);
AddOpButton(3);
for (int i = 4; i < 7; i++) AddNumButton(i);
AddOpButton(2);
for (int i = 1; i < 4; i++) AddNumButton(i);
AddOpButton(1);
MainGrid->AddSpacer(1);
AddNumButton(0);
//dot
wxButton* dot = new wxButton(this, wxID_ANY, ".");
dot->SetFont(*font_Button);
dot->SetBackgroundColour(wxColour(200, 200, 200));
dot->SetForegroundColour(wxColour(125, 125, 125));
dot->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &Main::OnDotClicked, this);
MainGrid->Add(dot, 0, wxEXPAND);
AddOpButton(0);
MainSizer->Add(MainGrid, 10, wxEXPAND);
}
void Main::AddNumButton(int n)
{
gridButton* btn = new gridButton(this, BUTTON_NUM + n, wxString(std::to_string(n)), NUM);
btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &Main::OnNumClicked, this);
MainGrid->Add(btn, 0, wxEXPAND);
}
void Main::AddOpButton(int n)
{
std::string s(1, ops[n]);
gridButton* btn = new gridButton(this, BUTTON_OP + n, wxString(s), OP);
btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &Main::OnOpClicked, this);
MainGrid->Add(btn, 0, wxEXPAND);
}
它应该触发的事件:
void Main::OnNumClicked(wxCommandEvent& evt)
{
int id = evt.GetId() - BUTTON_NUM;
DoNumClick(id);
evt.Skip();
}
void Main::DoNumClick(int id)
{
char c = '0' + id;
char exp[EXPBUFFER];
strcpy(exp, MainText->GetValue());
if (strlen(exp) == 1 && exp[0] == '0')
{
exp[0] = c;
MainText->SetLabel(exp);
}
else
MainText->AppendText(std::to_string(id));
}
我是 wxWidgets 的新手,不胜感激。
答案 0 :(得分:1)
我认为额外的灰色边框只是网格中剩余的额外像素。要去除剩余的像素,您可以改用 flex grid sizer,并使所有行和列都灵活。
void Main::AddButtons()
{
MainGrid = new wxFlexGridSizer(w, h, 0, 0);
for ( int i = 0 ; i < w ; ++ i)
{
MainGrid->AddGrowableRow(i);
}
for ( int j = 0 ; j < h ; ++j )
{
MainGrid->AddGrowableCol(j);
}
...
要去掉按钮本身周围的矩形,而不是绘制矩形,您可以设置背景画笔并调用Clear()
方法。 Clear()
方法使用背景画笔完全绘制窗口。
void gridButton::render(wxDC& dc)
{
int w = this->GetSize().GetWidth();
int h = this->GetSize().GetHeight();
if (pressed) {
dc.SetBackground(*pressedBackground);
dc.SetTextForeground(*pressedTextColor);
}
else if (hovered) {
dc.SetBackground(*hoveredBackground);
dc.SetTextForeground(*hoveredTextColor);
}
else {
dc.SetBackground(*background);
dc.SetTextForeground(*textColor);
}
dc.Clear();
您之前看到的边框是用默认笔绘制的矩形的边缘。
我有一个小小的建议。我会存储颜色而不是画笔本身。每个画笔都是一个 gdi 对象,在 Windows 上,gdi objects available 的数量是有限制的。
例如,如果您已经存储了pressedBackgroundColor,您可以像这样在堆栈上创建一个画笔:
if (pressed) {
wxBrush temp(*pressedBackgroundColor));
dc.SetBackground(temp);
或者你可以像这样隐式创建画笔:
if (pressed) {
dc.SetBackground(*pressedBackgroundColor);
这无疑是一个轻微的反优化,因为必须在每次调用 render
方法时创建画笔。因此,如果您不担心 Windows 上的 gdi 限制,请随时忽略这一点。
回答第一个问题。您需要为自定义按钮手动生成按钮按下。最简单的方法是使用 mouseReleased
方法。要生成按钮事件,该方法需要执行以下操作:
void gridButton::mouseReleased(wxMouseEvent& evt)
{
pressed = false;
paintNow();
// Create a button event and send it for processing.
wxCommandEvent event(wxEVT_BUTTON);
event.SetId(GetId());
event.SetEventObject(this);
// Set any other data needed here.
ProcessWindowEvent(event);
}
为了避免与原生按钮混淆,您还可以使用 wxDECLARE_EVENT
和 wxDEFINE_EVENT
macros 创建自己的事件类型。 documentation 中有更多详细信息。