【raylib】绘制button

#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
posted @ 2026-01-19 16:56  仰望星河Leon  阅读(5)  评论(0)    收藏  举报