#include<iostream>
#include<raylib.h>
#include "Control/Button.hpp"
int main()
{
//加载资源
Image img = LoadImage("./res/BingWallpaper.jpg");
InitAudioDevice();
Music bgm = LoadMusicStream("./res/bgm.mp3");
Sound se = LoadSound("./res/se.mp3");
int circle_x = img.width*0.5f;
int circle_y = img.height*0.5f;
bool isBgmStop = false;
//创建窗口
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI);
InitWindow(800, 600, "title");
//Texture2D texture = LoadTextureFromImage(img);
SetTargetFPS(120);
PlayMusicStream(bgm);
Button btn(300, 300, 100, 50, 0.4f);
//Button btn(300, 300, 100, 50);
for (int i = 0;i < 10;i++)
{
int id = btn.registerEvent(EventType::MouseButtonPressed, [i](std::shared_ptr<Event> event) {
TraceLog(LOG_INFO, std::to_string((int)event->type).c_str());
});
}
for (int i = 0;i < 10;i++)
{
int id = btn.registerEvent(EventType::KeyPressed, [i](std::shared_ptr<Event> event) {
auto ev = dynamic_cast<KeyEvent*>(event.get());
TraceLog(LOG_INFO, std::to_string((int)ev->key).c_str());
});
}
btn.unRegisterEvent(EventType::MouseButtonPressed, 0);
while (!WindowShouldClose())
{
//绘图
BeginDrawing();
ClearBackground(WHITE);
//DrawTextureEx(texture, Vector2{0,0}, 0, 1, WHITE);
//绘制元素
//DrawCircle(circle_x, circle_y, 10, RED);
//DrawCircleGradient(circle_x, circle_y, 10.0f, WHITE, RED); // 中心白,边缘红
btn.setText("click me", 10);
btn.update();
EndDrawing();
}
CloseWindow();
CloseAudioDevice();
return 0;
}
#ifndef BUTTON_HPP
#define BUTTON_HPP
#include "raylib_mingw/raylib.h"
#include<string>
// #include <filesystem>
#include"Control.hpp"
class ButtonMouseEnterEvent : public Event
{
public:
ButtonMouseEnterEvent()
{
type_ = EventType::ButtonMouseEnter;
}
};
class ButtonMouseLeaveEvent : public Event
{
public:
ButtonMouseLeaveEvent()
{
type_ = EventType::ButtonMouseLeave;
}
};
class ButtonState : public IState
{
public:
ButtonState()
{}
virtual ~ButtonState() = default;
enum class Type
{
Disabled=0,
Normal,
Hover,
Pressed,
Size
};
void SetRoundness(float roundness) { roundness_ = roundness; }
float GetRoundness() { return roundness_; }
void SetBoundColor(Color boundColor) { boundColor_ = boundColor; }
Color GetBoundColor() { return boundColor_; }
void SetContentColor(Color contentColor) { contentColor_ = contentColor; }
Color GetContentColor() { return contentColor_; }
void SetBoundThick(float boundThick) { boundThick_ = boundThick; }
float GetBoundThick() { return boundThick_; }
void SetFontSize(int fontSize) { fontSize_ = fontSize; }
int GetFontSize() { return fontSize_; }
void SetText(std::string text) { text_ = text; }
std::string GetText() { return text_; }
void SetAttributes(std::string text, int fontSize, float roundness, Color boundColor, Color contentColor, float boundThick)
{
SetText(text);
SetFontSize(fontSize);
SetRoundness(roundness);
SetBoundColor(boundColor);
SetContentColor(contentColor);
SetBoundThick(boundThick);
}
//virtual int SwitchStateOnEvent(EventType eventType) = 0;
virtual int GetState() = 0;
protected:
int fontSize_;//字体大小
std::string text_;//文本
float roundness_;//圆角按钮弧度
Color boundColor_;//边框颜色
Color contentColor_;//填充颜色
float boundThick_;//边框宽度
};
class ButtonStateDisabled :public ButtonState
{
public:
ButtonStateDisabled()
{
SetContentColor(GRAY);
}
virtual int SwitchStateOnEvent(EventType eventType)
{
switch (eventType)
{
case EventType::Enable:
return (int)ButtonState::Type::Normal;
break;
default:
return (int)ButtonState::Type::Disabled;
break;
}
}
virtual int GetState()
{
return (int)ButtonState::Type::Disabled;
}
};
class ButtonStateNormal:public ButtonState
{
public:
ButtonStateNormal()
{
SetContentColor(WHITE);
}
virtual int SwitchStateOnEvent(EventType eventType) override
{
switch (eventType)
{
case EventType::Disable:
return (int)ButtonState::Type::Disabled;
break;
case EventType::ButtonMouseEnter:
return (int)ButtonState::Type::Hover;
break;
case EventType::MouseDown:
return (int)ButtonState::Type::Pressed;
break;
default:
return (int)ButtonState::Type::Normal;
break;
}
}
virtual int GetState()
{
return (int)ButtonState::Type::Normal;
}
};
class ButtonStateHover :public ButtonState
{
public:
ButtonStateHover()
{
SetContentColor(SKYBLUE);
}
virtual int SwitchStateOnEvent(EventType eventType)
{
switch (eventType)
{
case EventType::Disable:
return (int)ButtonState::Type::Disabled;
break;
case EventType::ButtonMouseLeave:
return (int)ButtonState::Type::Normal;
break;
case EventType::MouseDown:
return (int)ButtonState::Type::Pressed;
break;
default:
return (int)ButtonState::Type::Hover;
break;
}
}
virtual int GetState()
{
return (int)ButtonState::Type::Hover;
}
};
class ButtonStatePressed :public ButtonState
{
public:
ButtonStatePressed()
{
SetContentColor(DARKBLUE);
}
virtual int SwitchStateOnEvent(EventType eventType)
{
switch (eventType)
{
case EventType::Disable:
return (int)ButtonState::Type::Disabled;
break;
case EventType::MouseUp:
return (int)ButtonState::Type::Hover;
break;
case EventType::ButtonMouseLeave:
return (int)ButtonState::Type::Normal;
break;
default:
return (int)ButtonState::Type::Pressed;
break;
}
}
virtual int GetState()
{
return (int)ButtonState::Type::Pressed;
}
};
class ButtonStateFactory
{
public:
ButtonStateFactory() = default;
~ButtonStateFactory() = default;
virtual std::unique_ptr<IState> createState(ButtonState::Type type)
{
static std::unordered_map<ButtonState::Type, std::function<std::unique_ptr<IState>()>> mapButtonStateType_ButtonState
{
{ButtonState::Type::Disabled, []() { return std::make_unique<ButtonStateDisabled>(); }},
{ButtonState::Type::Normal, []() { return std::make_unique<ButtonStateNormal>(); }},
{ButtonState::Type::Hover, []() { return std::make_unique<ButtonStateHover>(); }},
{ButtonState::Type::Pressed, []() { return std::make_unique<ButtonStatePressed>(); }}
};
auto it = mapButtonStateType_ButtonState.find(type);
if (it == mapButtonStateType_ButtonState.end())
{
return std::make_unique<ButtonStatePressed>();
}
return it->second(); //it->second是一个function,需调用才可返回对象
}
};
class Button:public IControl
{
public:
Button(float posX, float posY, float width, float height, std::string text={}, int fontSize=10, float roundness = 0.0f, Color contentColor = WHITE, Color boundColor = BLACK, float boundThick = 1.0f)
:IControl(posX, posY, width, height, true, true)
{
for (int i = 0;i < (int)ButtonState::Type::Size;i++)
{
auto type = (ButtonState::Type)i;
mapAllButtonStates_[type] = std::move(ButtonStateFactory().createState(type));
}
for (int i = 0; i < (int)EventType::Size;i++)
{
RegisterEvent((EventType)i, [this,i](std::shared_ptr<Event> event) {
this->handleEventChangeState(event->GetType());
});
}
SetNormalAppearance(text,fontSize,roundness,boundColor,contentColor,boundThick);
ChangeState(ButtonState::Type::Normal);
InitFont("./font/Source-Code-Pro/SourceCodePro-Medium-10.ttf", 64);
}
Button(Rectangle rec, std::string text={}, int fontSize=10, float roundness=0.0f, Color contentColor = WHITE, Color boundColor = BLACK , float boundThick=1.0f)
:Button(rec.x, rec.y, rec.width, rec.height, text, roundness, fontSize, boundColor, contentColor, boundThick)
{}
int RegisterEvent(EventType eventType, EventHandler eventHandler)
{
return eventBus_.subscribe(eventType, eventHandler);
}
bool UnRegisterEvent(EventType eventType, int handlerId)
{
return eventBus_.unSubscribe(eventType, handlerId);
}
void Update()
{
if (!visible_)
return;
Draw();
eventBus_.pollCommonEvents();
this->pollButtonEvents();
}
void handleEventChangeState(EventType eventType)
{
//int nextButtonStateType = dynamic_cast<ButtonState*>(curState_)->SwitchStateOnEvent(eventType);
int nextButtonStateType = curState_->SwitchStateOnEvent(eventType);
if (curState_->GetState() == nextButtonStateType)
return;
ChangeState((ButtonState::Type)nextButtonStateType);
}
void ChangeState(ButtonState::Type newStateType)
{
curState_ = mapAllButtonStates_.at(newStateType).get();
//Draw(); //raylib必须每帧都重绘,不能状态改变时再重绘
}
void pollButtonEvents()
{
//这里Button独有的事件只有鼠标进入和离开
Vector2 posMouseCur = GetMousePosition();
auto recControl = Rectangle{ posX_, posY_, width_, height_ };
//TODO:对于圆角矩形的判定
bool mouseCollision = CheckCollisionPointRec(posMouseCur, recControl);
if (mouseCollision)
{
auto event = std::make_shared<ButtonMouseEnterEvent>();
eventBus_.emit(EventType::ButtonMouseEnter, event);
//TraceLog(LOG_INFO, "Hover");
}
else if (!mouseCollision)
{
auto event = std::make_shared<ButtonMouseLeaveEvent>();
eventBus_.emit(EventType::ButtonMouseLeave, event);
//TraceLog(LOG_INFO, "Presssed");
}
}
bool SetVisible(bool visible)
{
if(this->visible_ == visible)
return false;
this->visible_ = visible;
return true;
}
bool SetEnable(bool enabled)
{
if(this->enabled_ == enabled)
return false;
enabled_ = enabled;
if(this->enabled_)
{
auto event = std::make_shared<EnableEvent>();
eventBus_.emit(EventType::Enable, event);
}
else
{
auto event = std::make_shared<DisableEvent>();
eventBus_.emit(EventType::Disable, event);
}
return true;
}
bool SetTextDisabled(std::string text, int fontSize=10)
{
auto iter = mapAllButtonStates_.find(ButtonState::Type::Disabled);
if (iter == mapAllButtonStates_.end())
return false;
auto state = dynamic_cast<ButtonStateNormal*>(iter->second.get());
state->SetText(std::move(text));
state->SetFontSize(fontSize);
return true;
}
bool SetTextNormal(std::string text, int fontSize=10)
{
auto iter = mapAllButtonStates_.find(ButtonState::Type::Normal);
if (iter == mapAllButtonStates_.end())
return false;
auto state = dynamic_cast<ButtonStateNormal*>(iter->second.get());
state->SetText(std::move(text));
state->SetFontSize(fontSize);
return true;
}
bool SetTextHover(std::string text, int fontSize=10)
{
auto iter = mapAllButtonStates_.find(ButtonState::Type::Hover);
if (iter == mapAllButtonStates_.end())
return false;
auto state = dynamic_cast<ButtonStateNormal*>(iter->second.get());
state->SetText(std::move(text));
state->SetFontSize(fontSize);
return true;
}
bool SetTextPressed(std::string text, int fontSize=10)
{
auto iter = mapAllButtonStates_.find(ButtonState::Type::Pressed);
if (iter == mapAllButtonStates_.end())
return false;
auto state = dynamic_cast<ButtonStateNormal*>(iter->second.get());
state->SetText(std::move(text));
state->SetFontSize(fontSize);
return true;
}
Button& SetDisabledAppearance(std::string text, int fontSize, float roundness, Color boundColor, Color contentColor, float boundThick)
{
auto iter = mapAllButtonStates_.find(ButtonState::Type::Disabled);
if (iter == mapAllButtonStates_.end())
return *this;
dynamic_cast<ButtonStateDisabled*>(iter->second.get())->SetAttributes(text, fontSize, roundness, boundColor, contentColor, boundThick);
return *this;
}
Button& SetHoverAppearance(std::string text, int fontSize, float roundness, Color boundColor, Color contentColor, float boundThick)
{
auto iter = mapAllButtonStates_.find(ButtonState::Type::Hover);
if (iter == mapAllButtonStates_.end())
return *this;
dynamic_cast<ButtonStateHover*>(iter->second.get())->SetAttributes(text, fontSize, roundness, boundColor, contentColor, boundThick);
return *this;
}
Button& SetPressedAppearance(std::string text, int fontSize, float roundness, Color boundColor, Color contentColor, float boundThick)
{
auto iter = mapAllButtonStates_.find(ButtonState::Type::Pressed);
if (iter == mapAllButtonStates_.end())
return *this;
dynamic_cast<ButtonStatePressed*>(iter->second.get())->SetAttributes(text, fontSize, roundness, boundColor, contentColor, boundThick);
return *this;
}
// void InitFont(std::filesystem::path fontPath, int fontBaseSize)
void InitFont(std::string fontPath, int fontBaseSize)
{
// auto path = fontPath.string().c_str();
auto path = fontPath.c_str();
font_ = LoadFontEx(path, fontBaseSize, NULL, 0);
TraceLog(LOG_INFO, path);
SetTextureFilter(font_.texture, TEXTURE_FILTER_BILINEAR);
}
private:
Font font_;
std::unordered_map<ButtonState::Type, std::unique_ptr<IState>> mapAllButtonStates_;
Button& SetNormalAppearance(std::string text, int fontSize, float roundness, Color boundColor, Color contentColor, float boundThick)
{
auto iter = mapAllButtonStates_.find(ButtonState::Type::Normal);
if (iter == mapAllButtonStates_.end())
return *this;
auto state = dynamic_cast<ButtonStateNormal*>(iter->second.get());
state->SetAttributes(text, fontSize, roundness, boundColor, contentColor, boundThick);
return *this;
}
virtual void Draw() override
{
auto curState = dynamic_cast<ButtonState*>(curState_);
int tw = MeasureText(curState->GetText().c_str(), curState->GetFontSize());
if (curState->GetRoundness() == 0.0f)
{
DrawRectangleLinesEx(Rectangle{ posX_, posY_, width_, height_ }, curState->GetBoundThick(), curState->GetBoundColor());
DrawRectangle(posX_ + curState->GetBoundThick(), posY_ + curState->GetBoundThick(), width_ - curState->GetBoundThick() * 2, height_ - curState->GetBoundThick() * 2, curState->GetContentColor());
}
else
{
DrawRectangleRoundedLinesEx(Rectangle{ posX_, posY_, width_, height_ }, curState->GetRoundness(), 10, curState->GetBoundThick(), curState->GetBoundColor());
DrawRectangleRounded(Rectangle{ posX_, posY_, width_, height_ }, curState->GetRoundness(), 10, curState->GetContentColor());
}
DrawTextEx(font_, curState->GetText().c_str(), Vector2{ posX_ + (width_ - tw) * 0.5f, posY_ + (height_ - curState->GetFontSize()) * 0.5f }, (float)curState->GetFontSize(), 1.0f, BLACK);
// DrawText(curState->GetText().c_str(), posX_ + (width_ - tw) * 0.5f, posY_ + (height_ - curState->GetFontSize()) * 0.5f, curState->GetFontSize(), Color{ BLACK });
}
};
#endif // !BUTTON_HPP