O yNodeGUI v2.0 é um framework leve para C++ baseado na biblioteca gráfica EasyX, voltado para o desenvolvimento rápido de interfaces de sistemas de gerenciamento simples. Ele oferece componentes GUI prontos, um sistema de menus hierárquicos infinitos e uma renderização fixa simplificada.
Pré‑requisitos
- Conhecimento básico de orientação a objetos e familiaridade com EasyX ou IMGUI.
- Visual Studio instalado com a biblioteca EasyX (disponível em easyx.cn).
- Configurar o projeto para usar conjunto de caracteres multibyte e, para dsitribuição, definir a opção de geração de código como MT (no modo Release).
Funcionalidades
- Componentes GUI prontos: botões, caixas redimensionáveis, grades, textos, imagens.
- Baixa curva de aprendizado: o sistema NodeGUI abstrai menus em uma árvore N‑ária, facilitando o gerenciamento de profundidade.
- Gerenciador de menus eficiente: renderização automática de menus com suporte a submenus de vários níveis.
Uso básico da API
0. Incluir o cabeçalho
#include "yNodeGUI.h"
1. Criar a tela no main
Tela canvas = { 1000, 600, 90, RGB(255,255,255) };
canvas.Mostrar(AoIniciar, AoAtualizar, AoRenderizar, true);
2. Definir os nós do menu em AoIniciar
No* raiz = new No();
GerenciadorMenu* menu = new GerenciadorMenu(raiz, &canvas);
No* m1 = new No(raiz, "Cadastro de Cursos");
No* m2 = new No(raiz, "Visualizar Cursos", browsing, true, fresh);
No* m3 = new No(raiz, "Consultar Cursos", queryshow, true, fresh_query);
No* m4 = new No(raiz, "Matrícula de Alunos");
No* m6 = new No(raiz, "Sair do Sistema", exit_system, true);
No* x1 = new No(m1, "Inserir Curso", input, true);
No* x2 = new No(m1, "Alterar Curso", set_course, true);
No* x3 = new No(m1, "Excluir Curso", del_course, true);
No* x4 = new No(m1, "Voltar", last_menu, true);
No* n1 = new No(m4, "Escolher Disciplina", choose, true);
No* n2 = new No(m4, "Ver Disciplinas Selecionadas", view_selected, true, fresh_selected);
No* n3 = new No(m4, "Voltar", last_menu, true);
3. Registrar e renderizar componentes
// Em AoRenderizar:
canvas.Ambiente(0).Registrar(id, botao);
canvas.Ambiente(0).Desenhar(id);
Exemplos completos encontram‑se no repositório de demonstração: yNodeGUI_Sample.
Estruturas básicas
O framework define seus próprios tipos Vector2, Rect, uma classe base EntidadeBase (que atribui um ID único a cada objeto) e uma interface ComponenteGUI.
// ---- Estruturas fundamentais ----
typedef struct {
int x, y;
} Vector2;
typedef struct {
Vector2 origem, fim, centro;
int largura, altura;
} Rect;
// Classe base para todo objeto instanciável
class EntidadeBase {
private:
static int contadorGlobal;
static std::set<int> idsUsados;
int idInstancia;
static int AtribuirID() {
do {
if (contadorGlobal == INT_MIN) contadorGlobal = INT_MAX;
contadorGlobal--;
} while (idsUsados.count(contadorGlobal));
idsUsados.insert(contadorGlobal);
return contadorGlobal;
}
public:
EntidadeBase() : idInstancia(AtribuirID()) {}
~EntidadeBase() { idsUsados.erase(idInstancia); }
int ObterID() const { return idInstancia; }
};
int EntidadeBase::contadorGlobal = INT_MAX;
std::set<int> EntidadeBase::idsUsados;
// Interface para componentes visuais
class ComponenteGUI : public EntidadeBase {
public:
virtual void AoRenderizar() = 0;
virtual void AoEvento(ExMessage* msg) = 0;
};
A classe Tela (Canvas)
Responsável por gerenciar a janela, o loop principal e o ambiente de renderização.
class Tela : public EntidadeBase {
private:
int largura, altura, fps, inicioQuadro, tempoQuadro;
bool ativo = true;
int deltaTempo;
COLORREF corFundo;
ExMessage mensagem;
HWND hwnd;
std::map<int, ComponenteGUI*> ambiente0, ambiente1, ambiente2, ambiente3;
std::map<int, std::map<int, ComponenteGUI*>> ambientes{{0,ambiente0},{1,ambiente1},{2,ambiente2},{3,ambiente3}};
std::map<int, std::vector<ComponenteGUI*>> coletas{{0,{}},{1,{}},{2,{}},{3,{}}};
std::queue<ComponenteGUI*> filaRender, filaEventos;
int ambienteAtual = 0;
void RenderizarTodos() { while (!filaRender.empty()) { filaRender.front()->AoRenderizar(); filaRender.pop(); } }
void TransmitirEvento(ExMessage* msg) { while (!filaEventos.empty()) { filaEventos.front()->AoEvento(msg); filaEventos.pop(); } }
protected:
void (*AoIniciar)(Tela&);
void (*AoRenderizar)(Tela&);
void (*AoAtualizar)(Tela&);
public:
// Construtor, destrutor, métodos de ambiente, registro, desenho, ciclo de vida...
void Mostrar(void inicio(Tela&), void atualizar(Tela&), void renderizar(Tela&), bool console = false) {
AoIniciar = inicio; AoAtualizar = atualizar; AoRenderizar = renderizar;
hwnd = console ? initgraph(largura, altura, EW_SHOWCONSOLE) : initgraph(largura, altura);
setbkcolor(corFundo); cleardevice();
AoIniciar(*this);
while (ativo && IsWindow(hwnd)) {
inicioQuadro = GetTickCount();
AoRenderizar(*this);
BeginBatchDraw(); cleardevice(); RenderizarTodos(); EndBatchDraw();
if (peekmessage(&mensagem)) { TransmitirEvento(&mensagem); mensagem = {}; }
else while (!filaEventos.empty()) filaEventos.pop();
AoAtualizar(*this);
deltaTempo = GetTickCount() - inicioQuadro;
if (tempoQuadro - deltaTempo > 0) Sleep(tempoQuadro - deltaTempo);
}
closegraph();
}
// Demais métodos (Encerrar, Ambiente, Registrar, Desenhar, etc.)
};
Componentes GUI exemplos
CaixaLinha (LineBox)
class CaixaLinha : public ComponenteGUI {
Rect rect, rectAmpliado;
COLORREF cor;
bool ampliado = false;
public:
void AoRenderizar() override {
setlinecolor(cor);
if (!ampliado) rectangle(rect.origem.x, rect.origem.y, rect.fim.x, rect.fim.y);
else rectangle(rectAmpliado.origem.x, rectAmpliado.origem.y, rectAmpliado.fim.x, rectAmpliado.fim.y);
}
void AoEvento(ExMessage* msg) override {
ampliado = (msg->message == WM_MOUSEMOVE && DentroRect(msg->x, msg->y, &rect));
}
CaixaLinha(Rect r, COLORREF c, int margem = 10) : rect(r), cor(c) {
rectAmpliado = CriarRectPorCentro(rect.centro, rect.largura + margem, rect.altura + margem);
}
};
Imagem (Image)
class Imagem : public ComponenteGUI {
IMAGE img;
bool corSolida = false;
COLORREF cor;
public:
Rect rect;
void AoRenderizar() override {
if (!corSolida) putimage(rect.origem.x, rect.origem.y, &img);
else { setfillcolor(cor); fillrectangle(rect.origem.x, rect.origem.y, rect.fim.x, rect.fim.y); }
}
void AoEvento(ExMessage*) override {}
Imagem(Rect r, std::string caminho) : rect(r) { loadimage(&img, caminho.c_str(), r.largura, r.altura); }
Imagem(Rect r, COLORREF c) : rect(r), cor(c), corSolida(true) {}
};
Texto (Text)
class Texto : public ComponenteGUI {
std::string estilo;
COLORREF cor;
RECT rectSistema;
public:
Rect rect;
std::string texto;
bool centralizar = true;
void AoRenderizar() override {
setbkmode(TRANSPARENT);
settextcolor(cor);
settextstyle(16, 0, estilo.c_str());
if (centralizar) drawtext(texto.c_str(), &rectSistema, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
else drawtext(texto.c_str(), &rectSistema, DT_VCENTER | DT_SINGLELINE);
}
void AoEvento(ExMessage*) override {}
void DefinirTexto(std::string txt, std::string est = "宋体", COLORREF c = -1) {
if (c != -1) cor = c;
if (est != "宋体") estilo = est;
texto = txt;
}
Texto(std::string txt, Rect r, std::string est = "宋体", COLORREF c = BLACK, bool centro = true)
: texto(txt), rect(r), estilo(est), cor(c), centralizar(centro) {
rectSistema = { r.origem.x, r.origem.y, r.fim.x, r.fim.y };
}
};
Botao (Button)
class Botao : public ComponenteGUI {
Imagem* img = nullptr;
Texto* txt = nullptr;
CaixaLinha* borda = nullptr;
std::vector<std::function<void()>> aoClicar;
public:
void AoRenderizar() override { if (img) img->AoRenderizar(); if (txt) txt->AoRenderizar(); if (borda) borda->AoRenderizar(); }
void AoEvento(ExMessage* msg) override {
if (img) img->AoEvento(msg);
if (txt) txt->AoEvento(msg);
if (borda) borda->AoEvento(msg);
if (msg->message == WM_LBUTTONDOWN && img && DentroRect(msg->x, msg->y, &(img->rect)))
for (auto& acao : aoClicar) acao();
}
void AdicionarAcao(std::function<void()> acao) { aoClicar.push_back(acao); }
void RemoverAcao(int idx) { if (idx < (int)aoClicar.size()) aoClicar.erase(aoClicar.begin() + idx); }
void LimparAcoes() { aoClicar.clear(); }
Botao(Rect r, COLORREF corFundo, std::string txt, COLORREF corTexto, COLORREF corBorda)
: img(new Imagem(r, corFundo)), txt(new Texto(txt, r, "宋体", corTexto, true)), borda(new CaixaLinha(r, corBorda)) {}
~Botao() { delete img; delete txt; delete borda; }
};
Grade (Gird)
class Grade : public ComponenteGUI {
Texto*** celulas;
Rect rect;
COLORREF corLinha, corFonte;
std::string estiloFonte;
public:
int colunas, linhas;
Rect tamCelula;
void InicializarCelulas() {
celulas = new Texto**[linhas];
for (int i = 0; i < linhas; i++)
celulas[i] = new Texto*[colunas]{nullptr};
for (int y = 0; y < linhas; y++)
for (int x = 0; x < colunas; x++)
celulas[y][x] = new Texto("", DeslocarRect({rect.origem.x + x * tamCelula.largura, rect.origem.y + y * tamCelula.altura}, tamCelula), estiloFonte, corFonte);
}
Grade(Rect r, int cols, int rows, COLORREF cor = BLACK, std::string estilo = "宋体", COLORREF corTxt = BLACK)
: rect(r), colunas(cols), linhas(rows), corLinha(cor), estiloFonte(estilo), corFonte(corTxt) {
tamCelula = CriarRectPorPonto(0, 0, r.largura / cols, r.altura / rows);
InicializarCelulas();
}
~Grade() { /* desalocação similar ao original */ }
void AoRenderizar() override {
setlinecolor(corLinha);
for (int x = 0; x <= colunas; x++) line(rect.origem.x + x * tamCelula.largura, rect.origem.y, rect.origem.x + x * tamCelula.largura, rect.fim.y);
for (int y = 0; y <= linhas; y++) line(rect.origem.x, rect.origem.y + y * tamCelula.altura, rect.fim.x, rect.origem.y + y * tamCelula.altura);
for (int y = 0; y < linhas; y++) for (int x = 0; x < colunas; x++) celulas[y][x]->AoRenderizar();
}
void AoEvento(ExMessage* msg) override {
for (int y = 0; y < linhas; y++) for (int x = 0; x < colunas; x++) celulas[y][x]->AoEvento(msg);
}
void DefinirCelula(int linha, int coluna, std::string texto, COLORREF cor = BLACK) {
celulas[linha][coluna]->DefinirTexto(texto);
}
};
ListaGrade (GirdList)
Especialização da Grade para exibir listas paginadas de objetos.