refactor(ui): 移除UI组件及相关功能代码
移除所有UI组件代码,包括按钮、复选框、单选框、进度条、滑动条、标签、文本和基础Widget类 删除对象池预热功能及相关代码 清理UI组件相关的头文件引用
This commit is contained in:
parent
c84aab70ed
commit
60ef7ab63f
|
|
@ -101,7 +101,6 @@ private:
|
|||
void mainLoop();
|
||||
void update();
|
||||
void render();
|
||||
void prewarmObjectPools();
|
||||
|
||||
// 配置
|
||||
AppConfig config_;
|
||||
|
|
|
|||
|
|
@ -35,16 +35,6 @@
|
|||
#include <scene/shape.h>
|
||||
#include <scene/sprite.h>
|
||||
|
||||
// UI
|
||||
#include <ui/button.h>
|
||||
#include <ui/check_box.h>
|
||||
#include <ui/label.h>
|
||||
#include <ui/progress_bar.h>
|
||||
#include <ui/radio_button.h>
|
||||
#include <ui/slider.h>
|
||||
#include <ui/text.h>
|
||||
#include <ui/widget.h>
|
||||
|
||||
// Event
|
||||
#include <event/event.h>
|
||||
#include <event/event_dispatcher.h>
|
||||
|
|
|
|||
|
|
@ -1,261 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <graphics/font.h>
|
||||
#include <graphics/texture.h>
|
||||
#include <platform/window.h>
|
||||
#include <ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// 图片缩放模式
|
||||
enum class ImageScaleMode {
|
||||
Original, // 使用原图大小
|
||||
Stretch, // 拉伸填充
|
||||
ScaleFit, // 等比缩放,保持完整显示
|
||||
ScaleFill // 等比缩放,填充整个区域(可能裁剪)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 基础按钮类
|
||||
// ============================================================================
|
||||
class Button : public Widget {
|
||||
public:
|
||||
Button();
|
||||
explicit Button(const std::string &text);
|
||||
~Button() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Button> create();
|
||||
static Ptr<Button> create(const std::string &text);
|
||||
static Ptr<Button> create(const std::string &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字内容
|
||||
// ------------------------------------------------------------------------
|
||||
void setText(const std::string &text);
|
||||
const std::string &getText() const { return text_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内边距
|
||||
// ------------------------------------------------------------------------
|
||||
void setPadding(const Vec2 &padding);
|
||||
void setPadding(float x, float y);
|
||||
Vec2 getPadding() const { return padding_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 纯色背景设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundColor(const Color &normal, const Color &hover,
|
||||
const Color &pressed);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边框设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBorder(const Color &color, float width);
|
||||
float getBorderWidth() const { return borderWidth_; }
|
||||
Color getBorderColor() const { return borderColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 图片背景设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundImage(Ptr<Texture> normal, Ptr<Texture> hover = nullptr,
|
||||
Ptr<Texture> pressed = nullptr);
|
||||
void setBackgroundImage(Ptr<Texture> texture, const Rect &rect);
|
||||
|
||||
/**
|
||||
* @brief 为切换按钮的两种状态设置图片背景
|
||||
* @param offNormal 关闭状态的普通图片
|
||||
* @param onNormal 开启状态的普通图片
|
||||
* @param offHover 关闭状态的悬停图片(可选)
|
||||
* @param onHover 开启状态的悬停图片(可选)
|
||||
* @param offPressed 关闭状态的按下图片(可选)
|
||||
* @param onPressed 开启状态的按下图片(可选)
|
||||
*/
|
||||
void setStateBackgroundImage(Ptr<Texture> offNormal, Ptr<Texture> onNormal,
|
||||
Ptr<Texture> offHover = nullptr,
|
||||
Ptr<Texture> onHover = nullptr,
|
||||
Ptr<Texture> offPressed = nullptr,
|
||||
Ptr<Texture> onPressed = nullptr);
|
||||
|
||||
void setBackgroundImageScaleMode(ImageScaleMode mode);
|
||||
void setCustomSize(const Vec2 &size);
|
||||
void setCustomSize(float width, float height);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 圆角矩形设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setCornerRadius(float radius);
|
||||
float getCornerRadius() const { return cornerRadius_; }
|
||||
void setRoundedCornersEnabled(bool enabled);
|
||||
bool isRoundedCornersEnabled() const { return roundedCornersEnabled_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 鼠标光标设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setHoverCursor(CursorShape cursor);
|
||||
CursorShape getHoverCursor() const { return hoverCursor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Alpha遮罩点击检测
|
||||
// ------------------------------------------------------------------------
|
||||
void setUseAlphaMaskForHitTest(bool enabled);
|
||||
bool isUseAlphaMaskForHitTest() const { return useAlphaMaskForHitTest_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 点击回调
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnClick(Function<void()> callback);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 切换模式支持(Toggle Button)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置是否为切换模式
|
||||
* @param enabled true 表示启用切换模式,点击时自动切换 on/off 状态
|
||||
*/
|
||||
void setToggleMode(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 获取当前是否为切换模式
|
||||
* @return true 表示处于切换模式
|
||||
*/
|
||||
bool isToggleMode() const { return toggleMode_; }
|
||||
|
||||
/**
|
||||
* @brief 设置当前状态(仅切换模式有效)
|
||||
* @param on true 表示开启状态,false 表示关闭状态
|
||||
*/
|
||||
void setOn(bool on);
|
||||
|
||||
/**
|
||||
* @brief 获取当前状态
|
||||
* @return true 表示开启状态,false 表示关闭状态
|
||||
*/
|
||||
bool isOn() const { return isOn_; }
|
||||
|
||||
/**
|
||||
* @brief 切换当前状态
|
||||
*/
|
||||
void toggle();
|
||||
|
||||
/**
|
||||
* @brief 设置状态改变回调
|
||||
* @param callback 状态改变时调用的回调函数,参数为新状态
|
||||
*/
|
||||
void setOnStateChange(Function<void(bool)> callback);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 状态文字设置(用于切换按钮)
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 为两种状态设置不同的文字
|
||||
* @param textOff 关闭状态显示的文字
|
||||
* @param textOn 开启状态显示的文字
|
||||
*/
|
||||
void setStateText(const std::string &textOff, const std::string &textOn);
|
||||
|
||||
/**
|
||||
* @brief 为两种状态设置不同的文字颜色
|
||||
* @param colorOff 关闭状态的文字颜色
|
||||
* @param colorOn 开启状态的文字颜色
|
||||
*/
|
||||
void setStateTextColor(const Color &colorOff, const Color &colorOn);
|
||||
|
||||
Rect boundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(Renderer &renderer) override;
|
||||
void drawBackgroundImage(Renderer &renderer, const Rect &rect);
|
||||
void drawRoundedRect(Renderer &renderer, const Rect &rect, const Color &color,
|
||||
float radius);
|
||||
void fillRoundedRect(Renderer &renderer, const Rect &rect, const Color &color,
|
||||
float radius);
|
||||
Vec2 calculateImageSize(const Vec2 &buttonSize, const Vec2 &imageSize);
|
||||
|
||||
// 状态访问(供子类使用)
|
||||
bool isHovered() const { return hovered_; }
|
||||
bool isPressed() const { return pressed_; }
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Vec2 padding_ = Vec2(10.0f, 6.0f);
|
||||
|
||||
// 文字颜色
|
||||
Color textColor_ = Colors::White;
|
||||
|
||||
// 纯色背景
|
||||
Color bgNormal_ = Color(0.2f, 0.2f, 0.2f, 1.0f);
|
||||
Color bgHover_ = Color(0.28f, 0.28f, 0.28f, 1.0f);
|
||||
Color bgPressed_ = Color(0.15f, 0.15f, 0.15f, 1.0f);
|
||||
|
||||
// 图片背景
|
||||
Ptr<Texture> imgNormal_;
|
||||
Ptr<Texture> imgHover_;
|
||||
Ptr<Texture> imgPressed_;
|
||||
Rect imgNormalRect_;
|
||||
Rect imgHoverRect_;
|
||||
Rect imgPressedRect_;
|
||||
ImageScaleMode scaleMode_ = ImageScaleMode::Original;
|
||||
bool useImageBackground_ = false;
|
||||
bool useTextureRect_ = false;
|
||||
|
||||
// 切换按钮状态图片
|
||||
Ptr<Texture> imgOffNormal_, imgOnNormal_;
|
||||
Ptr<Texture> imgOffHover_, imgOnHover_;
|
||||
Ptr<Texture> imgOffPressed_, imgOnPressed_;
|
||||
bool useStateImages_ = false;
|
||||
|
||||
// 边框
|
||||
Color borderColor_ = Color(0.6f, 0.6f, 0.6f, 1.0f);
|
||||
float borderWidth_ = 1.0f;
|
||||
|
||||
// 圆角矩形
|
||||
float cornerRadius_ = 8.0f;
|
||||
bool roundedCornersEnabled_ = false;
|
||||
|
||||
// 鼠标光标
|
||||
CursorShape hoverCursor_ = CursorShape::Hand;
|
||||
bool cursorChanged_ = false;
|
||||
|
||||
// Alpha遮罩点击检测
|
||||
bool useAlphaMaskForHitTest_ = false;
|
||||
|
||||
bool hovered_ = false;
|
||||
bool pressed_ = false;
|
||||
|
||||
// 切换模式相关
|
||||
bool toggleMode_ = false;
|
||||
bool isOn_ = false;
|
||||
Function<void(bool)> onStateChange_;
|
||||
|
||||
// 状态文字
|
||||
std::string textOff_, textOn_;
|
||||
bool useStateText_ = false;
|
||||
|
||||
// 状态文字颜色
|
||||
Color textColorOff_ = Colors::White;
|
||||
Color textColorOn_ = Colors::White;
|
||||
bool useStateTextColor_ = false;
|
||||
|
||||
Function<void()> onClick_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <graphics/font.h>
|
||||
#include <ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 复选框组件
|
||||
// ============================================================================
|
||||
class CheckBox : public Widget {
|
||||
public:
|
||||
CheckBox();
|
||||
~CheckBox() override = default;
|
||||
|
||||
static Ptr<CheckBox> create();
|
||||
static Ptr<CheckBox> create(const std::string &label);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 选中状态
|
||||
// ------------------------------------------------------------------------
|
||||
void setChecked(bool checked);
|
||||
bool isChecked() const { return checked_; }
|
||||
void toggle();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 标签设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setLabel(const std::string &label);
|
||||
const std::string &getLabel() const { return label_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 复选框尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setBoxSize(float size);
|
||||
float getBoxSize() const { return boxSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 间距
|
||||
// ------------------------------------------------------------------------
|
||||
void setSpacing(float spacing);
|
||||
float getSpacing() const { return spacing_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 颜色设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setCheckedColor(const Color &color);
|
||||
Color getCheckedColor() const { return checkedColor_; }
|
||||
|
||||
void setUncheckedColor(const Color &color);
|
||||
Color getUncheckedColor() const { return uncheckedColor_; }
|
||||
|
||||
void setCheckMarkColor(const Color &color);
|
||||
Color getCheckMarkColor() const { return checkMarkColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 回调设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnStateChange(Function<void(bool)> callback);
|
||||
|
||||
Rect boundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(Renderer &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
|
||||
private:
|
||||
bool checked_ = false;
|
||||
std::string label_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
|
||||
float boxSize_ = 20.0f;
|
||||
float spacing_ = 8.0f;
|
||||
|
||||
Color checkedColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
||||
Color uncheckedColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
Color checkMarkColor_ = Colors::White;
|
||||
|
||||
bool pressed_ = false;
|
||||
Function<void(bool)> onStateChange_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <graphics/font.h>
|
||||
#include <ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 文本标签组件 - 用于显示静态文本
|
||||
// 支持多行、对齐、阴影、描边等游戏常用效果
|
||||
// ============================================================================
|
||||
class Label : public Widget {
|
||||
public:
|
||||
Label();
|
||||
explicit Label(const std::string &text);
|
||||
~Label() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Label> create();
|
||||
static Ptr<Label> create(const std::string &text);
|
||||
static Ptr<Label> create(const std::string &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本内容
|
||||
// ------------------------------------------------------------------------
|
||||
void setText(const std::string &text);
|
||||
const std::string &getText() const { return text_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体大小
|
||||
// ------------------------------------------------------------------------
|
||||
void setFontSize(int size);
|
||||
int getFontSize() const { return fontSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 水平对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
enum class HorizontalAlign { Left, Center, Right };
|
||||
|
||||
void setHorizontalAlign(HorizontalAlign align);
|
||||
HorizontalAlign getHorizontalAlign() const { return hAlign_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 垂直对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
enum class VerticalAlign { Top, Middle, Bottom };
|
||||
|
||||
void setVerticalAlign(VerticalAlign align);
|
||||
VerticalAlign getVerticalAlign() const { return vAlign_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 阴影效果
|
||||
// ------------------------------------------------------------------------
|
||||
void setShadowEnabled(bool enabled);
|
||||
bool isShadowEnabled() const { return shadowEnabled_; }
|
||||
|
||||
void setShadowColor(const Color &color);
|
||||
Color getShadowColor() const { return shadowColor_; }
|
||||
|
||||
void setShadowOffset(const Vec2 &offset);
|
||||
Vec2 getShadowOffset() const { return shadowOffset_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 描边效果
|
||||
// ------------------------------------------------------------------------
|
||||
void setOutlineEnabled(bool enabled);
|
||||
bool isOutlineEnabled() const { return outlineEnabled_; }
|
||||
|
||||
void setOutlineColor(const Color &color);
|
||||
Color getOutlineColor() const { return outlineColor_; }
|
||||
|
||||
void setOutlineWidth(float width);
|
||||
float getOutlineWidth() const { return outlineWidth_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 多行文本
|
||||
// ------------------------------------------------------------------------
|
||||
void setMultiLine(bool multiLine);
|
||||
bool isMultiLine() const { return multiLine_; }
|
||||
|
||||
void setLineSpacing(float spacing);
|
||||
float getLineSpacing() const { return lineSpacing_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 最大宽度(用于自动换行)
|
||||
// ------------------------------------------------------------------------
|
||||
void setMaxWidth(float maxWidth);
|
||||
float getMaxWidth() const { return maxWidth_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 尺寸计算
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 getTextSize() const;
|
||||
float getLineHeight() const;
|
||||
|
||||
Rect boundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(Renderer &renderer) override;
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
int fontSize_ = 16;
|
||||
|
||||
HorizontalAlign hAlign_ = HorizontalAlign::Left;
|
||||
VerticalAlign vAlign_ = VerticalAlign::Top;
|
||||
|
||||
bool shadowEnabled_ = false;
|
||||
Color shadowColor_ = Color(0.0f, 0.0f, 0.0f, 0.5f);
|
||||
Vec2 shadowOffset_ = Vec2(2.0f, 2.0f);
|
||||
|
||||
bool outlineEnabled_ = false;
|
||||
Color outlineColor_ = Colors::Black;
|
||||
float outlineWidth_ = 1.0f;
|
||||
|
||||
bool multiLine_ = false;
|
||||
float lineSpacing_ = 1.0f;
|
||||
float maxWidth_ = 0.0f;
|
||||
|
||||
mutable Vec2 cachedSize_ = Vec2::Zero();
|
||||
mutable bool sizeDirty_ = true;
|
||||
|
||||
void updateCache() const;
|
||||
void drawText(Renderer &renderer, const Vec2 &position, const Color &color);
|
||||
Vec2 calculateDrawPosition() const;
|
||||
std::vector<std::string> splitLines() const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <graphics/font.h>
|
||||
#include <ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 进度条组件 - 用于显示进度/百分比
|
||||
// 适用于血条、能量条、加载进度、经验条等游戏场景
|
||||
// ============================================================================
|
||||
class ProgressBar : public Widget {
|
||||
public:
|
||||
ProgressBar();
|
||||
~ProgressBar() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<ProgressBar> create();
|
||||
static Ptr<ProgressBar> create(float min, float max, float value);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 数值范围
|
||||
// ------------------------------------------------------------------------
|
||||
void setRange(float min, float max);
|
||||
float getMin() const { return min_; }
|
||||
float getMax() const { return max_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 当前值
|
||||
// ------------------------------------------------------------------------
|
||||
void setValue(float value);
|
||||
float getValue() const { return value_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 获取百分比 (0.0 - 1.0)
|
||||
// ------------------------------------------------------------------------
|
||||
float getPercent() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 进度条方向
|
||||
// ------------------------------------------------------------------------
|
||||
enum class Direction { LeftToRight, RightToLeft, BottomToTop, TopToBottom };
|
||||
|
||||
void setDirection(Direction dir);
|
||||
Direction getDirection() const { return direction_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 颜色设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundColor(const Color &color);
|
||||
Color getBackgroundColor() const { return bgColor_; }
|
||||
|
||||
void setFillColor(const Color &color);
|
||||
Color getFillColor() const { return fillColor_; }
|
||||
|
||||
// 渐变填充色(从 fillColor_ 到 fillColorEnd_)
|
||||
void setGradientFillEnabled(bool enabled);
|
||||
bool isGradientFillEnabled() const { return gradientEnabled_; }
|
||||
|
||||
void setFillColorEnd(const Color &color);
|
||||
Color getFillColorEnd() const { return fillColorEnd_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 分段颜色(根据百分比自动切换颜色)
|
||||
// ------------------------------------------------------------------------
|
||||
void setSegmentedColorsEnabled(bool enabled);
|
||||
bool isSegmentedColorsEnabled() const { return segmentedColorsEnabled_; }
|
||||
|
||||
// 设置分段阈值和颜色,例如:>70%绿色, >30%黄色, 其他红色
|
||||
void addColorSegment(float percentThreshold, const Color &color);
|
||||
void clearColorSegments();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 圆角设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setCornerRadius(float radius);
|
||||
float getCornerRadius() const { return cornerRadius_; }
|
||||
|
||||
void setRoundedCornersEnabled(bool enabled);
|
||||
bool isRoundedCornersEnabled() const { return roundedCornersEnabled_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 边框设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBorderEnabled(bool enabled);
|
||||
bool isBorderEnabled() const { return borderEnabled_; }
|
||||
|
||||
void setBorderColor(const Color &color);
|
||||
Color getBorderColor() const { return borderColor_; }
|
||||
|
||||
void setBorderWidth(float width);
|
||||
float getBorderWidth() const { return borderWidth_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内边距(填充与边框的距离)
|
||||
// ------------------------------------------------------------------------
|
||||
void setPadding(float padding);
|
||||
float getPadding() const { return padding_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本显示
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextEnabled(bool enabled);
|
||||
bool isTextEnabled() const { return textEnabled_; }
|
||||
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// 文本格式:"{value}/{max}", "{percent}%", "{value:.1f}" 等
|
||||
void setTextFormat(const std::string &format);
|
||||
const std::string &getTextFormat() const { return textFormat_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 动画效果
|
||||
// ------------------------------------------------------------------------
|
||||
void setAnimatedChangeEnabled(bool enabled);
|
||||
bool isAnimatedChangeEnabled() const { return animatedChangeEnabled_; }
|
||||
|
||||
void setAnimationSpeed(float speed); // 每秒变化量
|
||||
float getAnimationSpeed() const { return animationSpeed_; }
|
||||
|
||||
// 延迟显示效果(如LOL血条)
|
||||
void setDelayedDisplayEnabled(bool enabled);
|
||||
bool isDelayedDisplayEnabled() const { return delayedDisplayEnabled_; }
|
||||
|
||||
void setDelayTime(float seconds);
|
||||
float getDelayTime() const { return delayTime_; }
|
||||
|
||||
void setDelayedFillColor(const Color &color);
|
||||
Color getDelayedFillColor() const { return delayedFillColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 条纹效果
|
||||
// ------------------------------------------------------------------------
|
||||
void setStripedEnabled(bool enabled);
|
||||
bool isStripedEnabled() const { return stripedEnabled_; }
|
||||
|
||||
void setStripeColor(const Color &color);
|
||||
Color getStripeColor() const { return stripeColor_; }
|
||||
|
||||
void setStripeSpeed(float speed); // 条纹移动速度
|
||||
float getStripeSpeed() const { return stripeSpeed_; }
|
||||
|
||||
Rect boundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onUpdate(float deltaTime) override;
|
||||
void onDrawWidget(Renderer &renderer) override;
|
||||
|
||||
private:
|
||||
// 数值
|
||||
float min_ = 0.0f;
|
||||
float max_ = 100.0f;
|
||||
float value_ = 50.0f;
|
||||
|
||||
// 方向
|
||||
Direction direction_ = Direction::LeftToRight;
|
||||
|
||||
// 颜色
|
||||
Color bgColor_ = Color(0.2f, 0.2f, 0.2f, 1.0f);
|
||||
Color fillColor_ = Color(0.0f, 0.8f, 0.2f, 1.0f);
|
||||
Color fillColorEnd_ = Color(0.0f, 0.6f, 0.1f, 1.0f);
|
||||
bool gradientEnabled_ = false;
|
||||
|
||||
// 分段颜色
|
||||
bool segmentedColorsEnabled_ = false;
|
||||
std::vector<std::pair<float, Color>> colorSegments_;
|
||||
|
||||
// 圆角
|
||||
float cornerRadius_ = 4.0f;
|
||||
bool roundedCornersEnabled_ = true;
|
||||
|
||||
// 边框
|
||||
bool borderEnabled_ = false;
|
||||
Color borderColor_ = Colors::White;
|
||||
float borderWidth_ = 1.0f;
|
||||
|
||||
// 内边距
|
||||
float padding_ = 2.0f;
|
||||
|
||||
// 文本
|
||||
bool textEnabled_ = false;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
std::string textFormat_ = "{percent:.0f}%";
|
||||
|
||||
// 动画
|
||||
bool animatedChangeEnabled_ = false;
|
||||
float animationSpeed_ = 100.0f;
|
||||
float displayValue_ = 50.0f; // 用于动画的显示值
|
||||
|
||||
// 延迟显示
|
||||
bool delayedDisplayEnabled_ = false;
|
||||
float delayTime_ = 0.3f;
|
||||
float delayTimer_ = 0.0f;
|
||||
float delayedValue_ = 50.0f;
|
||||
Color delayedFillColor_ = Color(1.0f, 0.0f, 0.0f, 0.5f);
|
||||
|
||||
// 条纹
|
||||
bool stripedEnabled_ = false;
|
||||
Color stripeColor_ = Color(1.0f, 1.0f, 1.0f, 0.2f);
|
||||
float stripeSpeed_ = 50.0f;
|
||||
float stripeOffset_ = 0.0f;
|
||||
|
||||
Color getCurrentFillColor() const;
|
||||
std::string formatText() const;
|
||||
void drawRoundedRect(Renderer &renderer, const Rect &rect, const Color &color,
|
||||
float radius);
|
||||
void fillRoundedRect(Renderer &renderer, const Rect &rect, const Color &color,
|
||||
float radius);
|
||||
void drawStripes(Renderer &renderer, const Rect &rect);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <graphics/font.h>
|
||||
#include <ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 单选按钮组件
|
||||
// ============================================================================
|
||||
class RadioButton : public Widget {
|
||||
public:
|
||||
RadioButton();
|
||||
~RadioButton() override = default;
|
||||
|
||||
static Ptr<RadioButton> create();
|
||||
static Ptr<RadioButton> create(const std::string &label);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 选择状态
|
||||
// ------------------------------------------------------------------------
|
||||
void setSelected(bool selected);
|
||||
bool isSelected() const { return selected_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 标签设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setLabel(const std::string &label);
|
||||
const std::string &getLabel() const { return label_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字颜色
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 圆形尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setCircleSize(float size);
|
||||
float getCircleSize() const { return circleSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 间距
|
||||
// ------------------------------------------------------------------------
|
||||
void setSpacing(float spacing);
|
||||
float getSpacing() const { return spacing_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 颜色设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setSelectedColor(const Color &color);
|
||||
Color getSelectedColor() const { return selectedColor_; }
|
||||
|
||||
void setUnselectedColor(const Color &color);
|
||||
Color getUnselectedColor() const { return unselectedColor_; }
|
||||
|
||||
void setDotColor(const Color &color);
|
||||
Color getDotColor() const { return dotColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 分组
|
||||
// ------------------------------------------------------------------------
|
||||
void setGroupId(int groupId);
|
||||
int getGroupId() const { return groupId_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 回调设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnStateChange(Function<void(bool)> callback);
|
||||
|
||||
Rect boundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(Renderer &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
|
||||
private:
|
||||
bool selected_ = false;
|
||||
std::string label_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
|
||||
float circleSize_ = 20.0f;
|
||||
float spacing_ = 8.0f;
|
||||
|
||||
Color selectedColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
||||
Color unselectedColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
Color dotColor_ = Colors::White;
|
||||
|
||||
int groupId_ = 0;
|
||||
|
||||
bool pressed_ = false;
|
||||
Function<void(bool)> onStateChange_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 单选按钮组管理器
|
||||
// ============================================================================
|
||||
class RadioButtonGroup {
|
||||
public:
|
||||
void addButton(RadioButton *button);
|
||||
void removeButton(RadioButton *button);
|
||||
void selectButton(RadioButton *button);
|
||||
RadioButton *getSelectedButton() const { return selectedButton_; }
|
||||
|
||||
void setOnSelectionChange(Function<void(RadioButton *)> callback);
|
||||
|
||||
private:
|
||||
std::vector<RadioButton *> buttons_;
|
||||
RadioButton *selectedButton_ = nullptr;
|
||||
Function<void(RadioButton *)> onSelectionChange_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <graphics/font.h>
|
||||
#include <ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 滑动条组件
|
||||
// ============================================================================
|
||||
class Slider : public Widget {
|
||||
public:
|
||||
Slider();
|
||||
~Slider() override = default;
|
||||
|
||||
static Ptr<Slider> create();
|
||||
static Ptr<Slider> create(float min, float max, float value);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 数值范围
|
||||
// ------------------------------------------------------------------------
|
||||
void setRange(float min, float max);
|
||||
float getMin() const { return min_; }
|
||||
float getMax() const { return max_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 当前值
|
||||
// ------------------------------------------------------------------------
|
||||
void setValue(float value);
|
||||
float getValue() const { return value_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 步进值
|
||||
// ------------------------------------------------------------------------
|
||||
void setStep(float step);
|
||||
float getStep() const { return step_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 方向
|
||||
// ------------------------------------------------------------------------
|
||||
void setVertical(bool vertical);
|
||||
bool isVertical() const { return vertical_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 轨道尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setTrackSize(float size);
|
||||
float getTrackSize() const { return trackSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 滑块尺寸
|
||||
// ------------------------------------------------------------------------
|
||||
void setThumbSize(float size);
|
||||
float getThumbSize() const { return thumbSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 颜色设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setTrackColor(const Color &color);
|
||||
Color getTrackColor() const { return trackColor_; }
|
||||
|
||||
void setFillColor(const Color &color);
|
||||
Color getFillColor() const { return fillColor_; }
|
||||
|
||||
void setThumbColor(const Color &color);
|
||||
Color getThumbColor() const { return thumbColor_; }
|
||||
|
||||
void setThumbHoverColor(const Color &color);
|
||||
Color getThumbHoverColor() const { return thumbHoverColor_; }
|
||||
|
||||
void setThumbPressedColor(const Color &color);
|
||||
Color getThumbPressedColor() const { return thumbPressedColor_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 显示设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setShowThumb(bool show);
|
||||
bool isShowThumb() const { return showThumb_; }
|
||||
|
||||
void setShowFill(bool show);
|
||||
bool isShowFill() const { return showFill_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本显示
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextEnabled(bool enabled);
|
||||
bool isTextEnabled() const { return textEnabled_; }
|
||||
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return textColor_; }
|
||||
|
||||
void setTextFormat(const std::string &format);
|
||||
const std::string &getTextFormat() const { return textFormat_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 回调设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnValueChange(Function<void(float)> callback);
|
||||
void setOnDragStart(Function<void()> callback);
|
||||
void setOnDragEnd(Function<void()> callback);
|
||||
|
||||
Rect boundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(Renderer &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
bool onMouseMove(const MouseEvent &event) override;
|
||||
void onMouseEnter() override;
|
||||
void onMouseLeave() override;
|
||||
|
||||
private:
|
||||
float min_ = 0.0f;
|
||||
float max_ = 100.0f;
|
||||
float value_ = 50.0f;
|
||||
float step_ = 0.0f;
|
||||
|
||||
bool vertical_ = false;
|
||||
float trackSize_ = 6.0f;
|
||||
float thumbSize_ = 16.0f;
|
||||
|
||||
Color trackColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
Color fillColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
||||
Color thumbColor_ = Color(0.8f, 0.8f, 0.8f, 1.0f);
|
||||
Color thumbHoverColor_ = Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
Color thumbPressedColor_ = Color(0.6f, 0.6f, 0.6f, 1.0f);
|
||||
|
||||
bool showThumb_ = true;
|
||||
bool showFill_ = true;
|
||||
|
||||
bool textEnabled_ = false;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color textColor_ = Colors::White;
|
||||
std::string textFormat_ = "{value:.0f}";
|
||||
|
||||
bool dragging_ = false;
|
||||
bool hovered_ = false;
|
||||
|
||||
Function<void(float)> onValueChange_;
|
||||
Function<void()> onDragStart_;
|
||||
Function<void()> onDragEnd_;
|
||||
|
||||
float valueToPosition(float value) const;
|
||||
float positionToValue(float pos) const;
|
||||
Rect getThumbRect() const;
|
||||
Rect getTrackRect() const;
|
||||
std::string formatText() const;
|
||||
float snapToStep(float value) const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/color.h>
|
||||
#include <core/types.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <graphics/font.h>
|
||||
#include <string>
|
||||
#include <ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 文本组件 - 继承自 Widget 的 UI 组件
|
||||
// ============================================================================
|
||||
class Text : public Widget {
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
// 对齐方式枚举
|
||||
// ------------------------------------------------------------------------
|
||||
enum class Alignment { Left, Center, Right };
|
||||
enum class VerticalAlignment { Top, Middle, Bottom };
|
||||
|
||||
Text();
|
||||
explicit Text(const std::string &text);
|
||||
~Text() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Text> create();
|
||||
static Ptr<Text> create(const std::string &text);
|
||||
static Ptr<Text> create(const std::string &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 格式化创建方法(类似 printf)
|
||||
// 使用示例:
|
||||
// auto text = Text::createFormat("FPS: %d", 60);
|
||||
// auto text = Text::createFormat(font, "得分: %d", score);
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Text> createFormat(const char *fmt, ...);
|
||||
static Ptr<Text> createFormat(Ptr<FontAtlas> font, const char *fmt, ...);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字内容
|
||||
// ------------------------------------------------------------------------
|
||||
void setText(const std::string &text);
|
||||
const std::string &getText() const { return text_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 格式化文本设置(类似 printf)
|
||||
// 使用示例:
|
||||
// text->setFormat("FPS: %d", 60);
|
||||
// text->setFormat("位置: (%.1f, %.1f)", x, y);
|
||||
// text->setFormat("生命值: %d/100", hp);
|
||||
// ------------------------------------------------------------------------
|
||||
void setFormat(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char buffer[256];
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
text_ = buffer;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字属性
|
||||
// ------------------------------------------------------------------------
|
||||
void setTextColor(const Color &color);
|
||||
Color getTextColor() const { return color_; }
|
||||
|
||||
void setFontSize(int size);
|
||||
int getFontSize() const { return fontSize_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
void setAlignment(Alignment align);
|
||||
Alignment getAlignment() const { return alignment_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 垂直对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
void setVerticalAlignment(VerticalAlignment align);
|
||||
VerticalAlignment getVerticalAlignment() const { return verticalAlignment_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 尺寸计算
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 getTextSize() const;
|
||||
float getLineHeight() const;
|
||||
|
||||
Rect boundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDrawWidget(Renderer &renderer) override;
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
Ptr<FontAtlas> font_;
|
||||
Color color_ = Colors::White;
|
||||
int fontSize_ = 16;
|
||||
Alignment alignment_ = Alignment::Left;
|
||||
VerticalAlignment verticalAlignment_ = VerticalAlignment::Top;
|
||||
|
||||
mutable Vec2 cachedSize_ = Vec2::Zero();
|
||||
mutable bool sizeDirty_ = true;
|
||||
|
||||
void updateCache() const;
|
||||
Vec2 calculateDrawPosition() const;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/rect.h>
|
||||
#include <core/types.h>
|
||||
#include <core/vec2.h>
|
||||
#include <platform/input.h>
|
||||
#include <scene/node.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标事件结构
|
||||
// ============================================================================
|
||||
struct MouseEvent {
|
||||
MouseButton button;
|
||||
float x;
|
||||
float y;
|
||||
int mods; // 修饰键状态
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Widget 基类 - UI 组件的基础
|
||||
// ============================================================================
|
||||
class Widget : public Node {
|
||||
public:
|
||||
Widget();
|
||||
~Widget() override = default;
|
||||
|
||||
void setSize(const Size &size);
|
||||
void setSize(float width, float height);
|
||||
Size size() const { return size_; }
|
||||
|
||||
Rect boundingBox() const override;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 鼠标事件处理(子类可重写)
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool onMousePress(const MouseEvent &event) { return false; }
|
||||
virtual bool onMouseRelease(const MouseEvent &event) { return false; }
|
||||
virtual bool onMouseMove(const MouseEvent &event) { return false; }
|
||||
virtual void onMouseEnter() {}
|
||||
virtual void onMouseLeave() {}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 启用/禁用状态
|
||||
// ------------------------------------------------------------------------
|
||||
void setEnabled(bool enabled) { enabled_ = enabled; }
|
||||
bool isEnabled() const { return enabled_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 焦点状态
|
||||
// ------------------------------------------------------------------------
|
||||
void setFocused(bool focused) { focused_ = focused; }
|
||||
bool isFocused() const { return focused_; }
|
||||
|
||||
protected:
|
||||
// 供子类使用的辅助方法
|
||||
bool isPointInside(float x, float y) const {
|
||||
return boundingBox().containsPoint(Point(x, y));
|
||||
}
|
||||
|
||||
// 子类重写此方法以支持自定义渲染
|
||||
virtual void onDrawWidget(Renderer &renderer) {}
|
||||
|
||||
// 重写 Node 的 onDraw
|
||||
void onDraw(Renderer &renderer) override;
|
||||
|
||||
private:
|
||||
Size size_ = Size::Zero();
|
||||
bool enabled_ = true;
|
||||
bool focused_ = false;
|
||||
bool hovered_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,492 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <utils/logger.h>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 对象池 - 自动管理的高性能内存池
|
||||
// 特性:
|
||||
// - 自动内存对齐
|
||||
// - 侵入式空闲链表(零额外内存开销)
|
||||
// - 线程本地缓存(减少锁竞争)
|
||||
// - 自动容量管理(自动扩展/收缩)
|
||||
// - 自动预热
|
||||
// - 异常安全
|
||||
// ============================================================================
|
||||
|
||||
// 线程本地缓存配置
|
||||
struct PoolConfig {
|
||||
static constexpr size_t DEFAULT_BLOCK_SIZE = 64;
|
||||
static constexpr size_t THREAD_CACHE_SIZE = 16;
|
||||
static constexpr size_t SHRINK_THRESHOLD_MS = 30000;
|
||||
static constexpr double SHRINK_RATIO = 0.5;
|
||||
};
|
||||
|
||||
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
|
||||
class ObjectPool {
|
||||
public:
|
||||
static_assert(std::is_default_constructible_v<T>, "T must be default constructible");
|
||||
static_assert(std::is_destructible_v<T>, "T must be destructible");
|
||||
static_assert(BlockSize > 0, "BlockSize must be greater than 0");
|
||||
static_assert(alignof(T) <= alignof(std::max_align_t),
|
||||
"Alignment requirement too high");
|
||||
|
||||
ObjectPool()
|
||||
: freeListHead_(nullptr)
|
||||
, blocks_()
|
||||
, allocatedCount_(0)
|
||||
, totalCapacity_(0)
|
||||
, isDestroyed_(false)
|
||||
, lastShrinkCheck_(0)
|
||||
, prewarmed_(false) {
|
||||
}
|
||||
|
||||
~ObjectPool() {
|
||||
clear();
|
||||
}
|
||||
|
||||
ObjectPool(const ObjectPool&) = delete;
|
||||
ObjectPool& operator=(const ObjectPool&) = delete;
|
||||
ObjectPool(ObjectPool&&) noexcept = delete;
|
||||
ObjectPool& operator=(ObjectPool&&) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief 分配一个对象(自动预热、自动扩展)
|
||||
* @return 指向分配的对象的指针,失败返回 nullptr
|
||||
*/
|
||||
T* allocate() {
|
||||
auto& cache = getThreadCache();
|
||||
|
||||
if (T* obj = cache.pop()) {
|
||||
new (obj) T();
|
||||
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
return obj;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (isDestroyed_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!prewarmed_) {
|
||||
prewarmInternal();
|
||||
}
|
||||
|
||||
if (!freeListHead_) {
|
||||
growInternal();
|
||||
}
|
||||
|
||||
T* obj = popFreeList();
|
||||
if (obj) {
|
||||
new (obj) T();
|
||||
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分配并构造一个对象(带参数)
|
||||
*/
|
||||
template <typename... Args>
|
||||
T* allocate(Args&&... args) {
|
||||
auto& cache = getThreadCache();
|
||||
|
||||
if (T* obj = cache.pop()) {
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
return obj;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (isDestroyed_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!prewarmed_) {
|
||||
prewarmInternal();
|
||||
}
|
||||
|
||||
if (!freeListHead_) {
|
||||
growInternal();
|
||||
}
|
||||
|
||||
T* obj = popFreeList();
|
||||
if (obj) {
|
||||
new (obj) T(std::forward<Args>(args)...);
|
||||
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 回收一个对象(自动异常处理)
|
||||
* @param obj 要回收的对象指针
|
||||
* @return true 如果对象成功回收
|
||||
*/
|
||||
bool deallocate(T* obj) {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
obj->~T();
|
||||
} catch (const std::exception& e) {
|
||||
Logger::log(LogLevel::Error, "ObjectPool: Exception in destructor: {}", e.what());
|
||||
} catch (...) {
|
||||
Logger::log(LogLevel::Error, "ObjectPool: Unknown exception in destructor");
|
||||
}
|
||||
|
||||
auto& cache = getThreadCache();
|
||||
if (cache.push(obj)) {
|
||||
allocatedCount_.fetch_sub(1, std::memory_order_relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (!isDestroyed_) {
|
||||
pushFreeList(obj);
|
||||
allocatedCount_.fetch_sub(1, std::memory_order_relaxed);
|
||||
tryAutoShrink();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前已分配的对象数量
|
||||
*/
|
||||
size_t allocatedCount() const {
|
||||
return allocatedCount_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取池中总的对象容量
|
||||
*/
|
||||
size_t capacity() const {
|
||||
return totalCapacity_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内存使用量(字节)
|
||||
*/
|
||||
size_t memoryUsage() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return blocks_.size() * BlockSize * sizeof(T);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空所有内存块
|
||||
*/
|
||||
void clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
isDestroyed_ = true;
|
||||
|
||||
for (auto& block : blocks_) {
|
||||
alignedFree(block);
|
||||
}
|
||||
blocks_.clear();
|
||||
freeListHead_ = nullptr;
|
||||
totalCapacity_.store(0, std::memory_order_relaxed);
|
||||
allocatedCount_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
struct FreeNode {
|
||||
FreeNode* next;
|
||||
};
|
||||
|
||||
struct ThreadCache {
|
||||
T* objects[PoolConfig::THREAD_CACHE_SIZE];
|
||||
size_t count = 0;
|
||||
|
||||
T* pop() {
|
||||
if (count > 0) {
|
||||
return objects[--count];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool push(T* obj) {
|
||||
if (count < PoolConfig::THREAD_CACHE_SIZE) {
|
||||
objects[count++] = obj;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
static ThreadCache& getThreadCache() {
|
||||
thread_local ThreadCache cache;
|
||||
return cache;
|
||||
}
|
||||
|
||||
static constexpr size_t Alignment = alignof(T);
|
||||
static constexpr size_t AlignedSize = ((sizeof(T) + Alignment - 1) / Alignment) * Alignment;
|
||||
|
||||
static void* alignedAlloc(size_t size) {
|
||||
#ifdef _WIN32
|
||||
return _aligned_malloc(size, Alignment);
|
||||
#else
|
||||
return std::aligned_alloc(Alignment, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void alignedFree(void* ptr) {
|
||||
#ifdef _WIN32
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
std::free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void prewarmInternal() {
|
||||
if (!freeListHead_) {
|
||||
growInternal();
|
||||
}
|
||||
prewarmed_ = true;
|
||||
}
|
||||
|
||||
void growInternal() {
|
||||
size_t blockSize = AlignedSize > sizeof(FreeNode) ? AlignedSize : sizeof(FreeNode);
|
||||
size_t totalSize = blockSize * BlockSize;
|
||||
|
||||
void* block = alignedAlloc(totalSize);
|
||||
if (!block) {
|
||||
Logger::log(LogLevel::Error, "ObjectPool: Failed to allocate memory block");
|
||||
return;
|
||||
}
|
||||
|
||||
blocks_.push_back(block);
|
||||
totalCapacity_.fetch_add(BlockSize, std::memory_order_relaxed);
|
||||
|
||||
char* ptr = static_cast<char*>(block);
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
pushFreeList(reinterpret_cast<T*>(ptr + i * blockSize));
|
||||
}
|
||||
}
|
||||
|
||||
void pushFreeList(T* obj) {
|
||||
FreeNode* node = reinterpret_cast<FreeNode*>(obj);
|
||||
node->next = freeListHead_;
|
||||
freeListHead_ = node;
|
||||
}
|
||||
|
||||
T* popFreeList() {
|
||||
if (!freeListHead_) {
|
||||
return nullptr;
|
||||
}
|
||||
FreeNode* node = freeListHead_;
|
||||
freeListHead_ = freeListHead_->next;
|
||||
return reinterpret_cast<T*>(node);
|
||||
}
|
||||
|
||||
void tryAutoShrink() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch()).count();
|
||||
|
||||
if (elapsed - lastShrinkCheck_ < PoolConfig::SHRINK_THRESHOLD_MS) {
|
||||
return;
|
||||
}
|
||||
lastShrinkCheck_ = elapsed;
|
||||
|
||||
size_t allocated = allocatedCount_.load(std::memory_order_relaxed);
|
||||
size_t capacity = totalCapacity_.load(std::memory_order_relaxed);
|
||||
|
||||
if (capacity > BlockSize &&
|
||||
static_cast<double>(allocated) / capacity < PoolConfig::SHRINK_RATIO) {
|
||||
shrinkInternal();
|
||||
}
|
||||
}
|
||||
|
||||
void shrinkInternal() {
|
||||
size_t toFree = 0;
|
||||
size_t freeCount = 0;
|
||||
|
||||
FreeNode* node = freeListHead_;
|
||||
while (node) {
|
||||
++freeCount;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (freeCount < BlockSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t blocksToKeep = blocks_.size();
|
||||
if (allocatedCount_.load(std::memory_order_relaxed) > 0) {
|
||||
blocksToKeep = (allocatedCount_.load() + BlockSize - 1) / BlockSize;
|
||||
blocksToKeep = std::max(blocksToKeep, size_t(1));
|
||||
}
|
||||
|
||||
if (blocksToKeep >= blocks_.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t blocksToRemove = blocks_.size() - blocksToKeep;
|
||||
for (size_t i = 0; i < blocksToRemove; ++i) {
|
||||
if (blocks_.empty()) break;
|
||||
|
||||
void* block = blocks_.back();
|
||||
blocks_.pop_back();
|
||||
alignedFree(block);
|
||||
totalCapacity_.fetch_sub(BlockSize, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
rebuildFreeList();
|
||||
}
|
||||
|
||||
void rebuildFreeList() {
|
||||
freeListHead_ = nullptr;
|
||||
size_t blockSize = AlignedSize > sizeof(FreeNode) ? AlignedSize : sizeof(FreeNode);
|
||||
|
||||
for (void* block : blocks_) {
|
||||
char* ptr = static_cast<char*>(block);
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
pushFreeList(reinterpret_cast<T*>(ptr + i * blockSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
FreeNode* freeListHead_;
|
||||
std::vector<void*> blocks_;
|
||||
std::atomic<size_t> allocatedCount_;
|
||||
std::atomic<size_t> totalCapacity_;
|
||||
bool isDestroyed_;
|
||||
uint64_t lastShrinkCheck_;
|
||||
bool prewarmed_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 智能指针支持的内存池分配器
|
||||
// ============================================================================
|
||||
|
||||
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
|
||||
class PooledAllocator {
|
||||
public:
|
||||
using PoolType = ObjectPool<T, BlockSize>;
|
||||
|
||||
PooledAllocator() : pool_(std::make_shared<PoolType>()) {}
|
||||
explicit PooledAllocator(std::shared_ptr<PoolType> pool) : pool_(pool) {}
|
||||
|
||||
/**
|
||||
* @brief 创建一个使用内存池的对象(自动管理)
|
||||
*/
|
||||
template <typename... Args>
|
||||
Ptr<T> makeShared(Args&&... args) {
|
||||
std::weak_ptr<PoolType> weakPool = pool_;
|
||||
T* obj = pool_->allocate(std::forward<Args>(args)...);
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
return Ptr<T>(obj, Deleter{weakPool});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取底层内存池
|
||||
*/
|
||||
std::shared_ptr<PoolType> getPool() const {
|
||||
return pool_;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Deleter {
|
||||
std::weak_ptr<PoolType> pool;
|
||||
|
||||
void operator()(T* obj) const {
|
||||
if (auto sharedPool = pool.lock()) {
|
||||
if (!sharedPool->deallocate(obj)) {
|
||||
Logger::log(LogLevel::Warn, "PooledAllocator: Pool destroyed, memory leaked");
|
||||
}
|
||||
} else {
|
||||
Logger::log(LogLevel::Warn, "PooledAllocator: Pool expired during deallocation");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<PoolType> pool_;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 全局内存池管理器 - 自动管理所有池的生命周期
|
||||
// ============================================================================
|
||||
|
||||
class ObjectPoolManager {
|
||||
public:
|
||||
static ObjectPoolManager& getInstance() {
|
||||
static ObjectPoolManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定类型的内存池(自动管理)
|
||||
*/
|
||||
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
|
||||
std::shared_ptr<ObjectPool<T, BlockSize>> getPool() {
|
||||
static auto pool = std::make_shared<ObjectPool<T, BlockSize>>();
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建使用内存池的对象(自动管理)
|
||||
*/
|
||||
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE, typename... Args>
|
||||
Ptr<T> makePooled(Args&&... args) {
|
||||
auto pool = getPool<T, BlockSize>();
|
||||
std::weak_ptr<ObjectPool<T, BlockSize>> weakPool = pool;
|
||||
T* obj = pool->allocate(std::forward<Args>(args)...);
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
return Ptr<T>(obj, [weakPool](T* p) {
|
||||
if (auto sharedPool = weakPool.lock()) {
|
||||
if (!sharedPool->deallocate(p)) {
|
||||
Logger::log(LogLevel::Warn, "ObjectPoolManager: Pool destroyed during deallocation");
|
||||
}
|
||||
} else {
|
||||
Logger::log(LogLevel::Warn, "ObjectPoolManager: Pool expired");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectPoolManager() = default;
|
||||
~ObjectPoolManager() = default;
|
||||
ObjectPoolManager(const ObjectPoolManager&) = delete;
|
||||
ObjectPoolManager& operator=(const ObjectPoolManager&) = delete;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 内存池宏定义(便于使用)
|
||||
// ============================================================================
|
||||
|
||||
#define E2D_DECLARE_POOL(T, BlockSize) \
|
||||
static extra2d::ObjectPool<T, BlockSize>& getPool() { \
|
||||
static extra2d::ObjectPool<T, BlockSize> pool; \
|
||||
return pool; \
|
||||
}
|
||||
|
||||
#define E2D_MAKE_POOLED(T, ...) \
|
||||
extra2d::ObjectPoolManager::getInstance().makePooled<T>(__VA_ARGS__)
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -10,7 +10,6 @@
|
|||
#include <resource/resource_manager.h>
|
||||
#include <scene/scene_manager.h>
|
||||
#include <utils/logger.h>
|
||||
#include <utils/object_pool.h>
|
||||
#include <utils/timer.h>
|
||||
|
||||
|
||||
|
|
@ -157,11 +156,6 @@ bool Application::init(const AppConfig &config) {
|
|||
// 初始化音频引擎
|
||||
AudioEngine::getInstance().initialize();
|
||||
|
||||
// ========================================
|
||||
// 6. 预热对象池(自动管理)
|
||||
// ========================================
|
||||
prewarmObjectPools();
|
||||
|
||||
initialized_ = true;
|
||||
running_ = true;
|
||||
|
||||
|
|
@ -169,17 +163,6 @@ bool Application::init(const AppConfig &config) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void Application::prewarmObjectPools() {
|
||||
E2D_LOG_INFO("Prewarming object pools...");
|
||||
|
||||
auto &poolManager = ObjectPoolManager::getInstance();
|
||||
|
||||
// 预热常用类型的对象池
|
||||
// 这些池会在首次使用时自动预热,但提前预热可以避免运行时延迟
|
||||
|
||||
E2D_LOG_INFO("Object pools prewarmed successfully");
|
||||
}
|
||||
|
||||
void Application::shutdown() {
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
|
@ -189,10 +172,6 @@ void Application::shutdown() {
|
|||
// 打印 VRAM 统计
|
||||
VRAMManager::getInstance().printStats();
|
||||
|
||||
// 打印对象池内存统计
|
||||
E2D_LOG_INFO("Object pool memory usage: {} bytes (auto-managed)",
|
||||
ObjectPoolManager::getInstance().getPool<Node>()->memoryUsage());
|
||||
|
||||
// 先结束所有场景,确保 onExit() 被正确调用
|
||||
if (sceneManager_) {
|
||||
sceneManager_->end();
|
||||
|
|
|
|||
|
|
@ -1,690 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include <app/application.h>
|
||||
#include <cmath>
|
||||
#include <core/string.h>
|
||||
#include <renderer/renderer.h>
|
||||
#include <ui/button.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// Button 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
Button::Button() {
|
||||
setAnchor(0.0f, 0.0f);
|
||||
|
||||
auto &dispatcher = getEventDispatcher();
|
||||
dispatcher.addListener(EventType::UIHoverEnter, [this](Event &) {
|
||||
hovered_ = true;
|
||||
auto &app = Application::instance();
|
||||
app.window().setCursor(hoverCursor_);
|
||||
cursorChanged_ = true;
|
||||
});
|
||||
dispatcher.addListener(EventType::UIHoverExit, [this](Event &) {
|
||||
hovered_ = false;
|
||||
pressed_ = false;
|
||||
if (cursorChanged_) {
|
||||
auto &app = Application::instance();
|
||||
app.window().resetCursor();
|
||||
cursorChanged_ = false;
|
||||
}
|
||||
});
|
||||
dispatcher.addListener(EventType::UIPressed,
|
||||
[this](Event &) { pressed_ = true; });
|
||||
dispatcher.addListener(EventType::UIReleased,
|
||||
[this](Event &) { pressed_ = false; });
|
||||
dispatcher.addListener(EventType::UIClicked, [this](Event &) {
|
||||
if (toggleMode_) {
|
||||
toggle();
|
||||
}
|
||||
if (onClick_) {
|
||||
onClick_();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 带文本的构造函数
|
||||
* @param text 按钮文本
|
||||
*/
|
||||
Button::Button(const std::string &text) : Button() { text_ = text; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 创建空按钮对象
|
||||
* @return 按钮对象指针
|
||||
*/
|
||||
Ptr<Button> Button::create() { return shared<Button>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建带文本的按钮对象
|
||||
* @param text 按钮文本
|
||||
* @return 按钮对象指针
|
||||
*/
|
||||
Ptr<Button> Button::create(const std::string &text) {
|
||||
return shared<Button>(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建带文本和字体的按钮对象
|
||||
* @param text 按钮文本
|
||||
* @param font 字体图集
|
||||
* @return 按钮对象指针
|
||||
*/
|
||||
Ptr<Button> Button::create(const std::string &text, Ptr<FontAtlas> font) {
|
||||
auto btn = shared<Button>(text);
|
||||
btn->setFont(font);
|
||||
return btn;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 普通设置方法
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief 设置按钮文本
|
||||
* @param text 文本内容
|
||||
*/
|
||||
void Button::setText(const std::string &text) {
|
||||
text_ = text;
|
||||
if (font_ && size().empty()) {
|
||||
Vec2 textSize = font_->measureText(text_);
|
||||
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置字体
|
||||
* @param font 字体图集指针
|
||||
*/
|
||||
void Button::setFont(Ptr<FontAtlas> font) {
|
||||
font_ = font;
|
||||
if (font_ && size().empty() && !text_.empty()) {
|
||||
Vec2 textSize = font_->measureText(text_);
|
||||
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置内边距(Vec2版本)
|
||||
* @param padding 内边距向量
|
||||
*/
|
||||
void Button::setPadding(const Vec2 &padding) {
|
||||
padding_ = padding;
|
||||
if (font_ && size().empty() && !text_.empty()) {
|
||||
Vec2 textSize = font_->measureText(text_);
|
||||
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置内边距(分量版本)
|
||||
* @param x X方向内边距
|
||||
* @param y Y方向内边距
|
||||
*/
|
||||
void Button::setPadding(float x, float y) { setPadding(Vec2(x, y)); }
|
||||
|
||||
/**
|
||||
* @brief 设置文本颜色
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void Button::setTextColor(const Color &color) { textColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置背景颜色
|
||||
* @param normal 普通状态颜色
|
||||
* @param hover 悬停状态颜色
|
||||
* @param pressed 按下状态颜色
|
||||
*/
|
||||
void Button::setBackgroundColor(const Color &normal, const Color &hover,
|
||||
const Color &pressed) {
|
||||
bgNormal_ = normal;
|
||||
bgHover_ = hover;
|
||||
bgPressed_ = pressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置边框
|
||||
* @param color 边框颜色
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
void Button::setBorder(const Color &color, float width) {
|
||||
borderColor_ = color;
|
||||
borderWidth_ = width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置圆角半径
|
||||
* @param radius 圆角半径
|
||||
*/
|
||||
void Button::setCornerRadius(float radius) {
|
||||
cornerRadius_ = std::max(0.0f, radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用圆角
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void Button::setRoundedCornersEnabled(bool enabled) {
|
||||
roundedCornersEnabled_ = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置是否使用Alpha遮罩进行点击检测
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void Button::setUseAlphaMaskForHitTest(bool enabled) {
|
||||
useAlphaMaskForHitTest_ = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置点击回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void Button::setOnClick(Function<void()> callback) {
|
||||
onClick_ = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置是否为切换模式
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void Button::setToggleMode(bool enabled) { toggleMode_ = enabled; }
|
||||
|
||||
/**
|
||||
* @brief 设置开关状态
|
||||
* @param on 是否开启
|
||||
*/
|
||||
void Button::setOn(bool on) {
|
||||
if (isOn_ != on) {
|
||||
isOn_ = on;
|
||||
if (onStateChange_) {
|
||||
onStateChange_(isOn_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切换状态
|
||||
*/
|
||||
void Button::toggle() { setOn(!isOn_); }
|
||||
|
||||
/**
|
||||
* @brief 设置状态改变回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void Button::setOnStateChange(Function<void(bool)> callback) {
|
||||
onStateChange_ = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置状态文字
|
||||
* @param textOff 关闭状态文字
|
||||
* @param textOn 开启状态文字
|
||||
*/
|
||||
void Button::setStateText(const std::string &textOff,
|
||||
const std::string &textOn) {
|
||||
textOff_ = textOff;
|
||||
textOn_ = textOn;
|
||||
useStateText_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置状态文字颜色
|
||||
* @param colorOff 关闭状态颜色
|
||||
* @param colorOn 开启状态颜色
|
||||
*/
|
||||
void Button::setStateTextColor(const Color &colorOff, const Color &colorOn) {
|
||||
textColorOff_ = colorOff;
|
||||
textColorOn_ = colorOn;
|
||||
useStateTextColor_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置悬停光标
|
||||
* @param cursor 光标形状
|
||||
*/
|
||||
void Button::setHoverCursor(CursorShape cursor) { hoverCursor_ = cursor; }
|
||||
|
||||
/**
|
||||
* @brief 设置背景图片
|
||||
* @param normal 普通状态图片
|
||||
* @param hover 悬停状态图片
|
||||
* @param pressed 按下状态图片
|
||||
*/
|
||||
void Button::setBackgroundImage(Ptr<Texture> normal, Ptr<Texture> hover,
|
||||
Ptr<Texture> pressed) {
|
||||
imgNormal_ = normal;
|
||||
imgHover_ = hover ? hover : normal;
|
||||
imgPressed_ = pressed ? pressed : (hover ? hover : normal);
|
||||
useImageBackground_ = (normal != nullptr);
|
||||
useTextureRect_ = false;
|
||||
|
||||
if (useImageBackground_ && scaleMode_ == ImageScaleMode::Original && normal) {
|
||||
setSize(static_cast<float>(normal->width()),
|
||||
static_cast<float>(normal->height()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置背景图片(带矩形区域)
|
||||
* @param texture 纹理
|
||||
* @param rect 矩形区域
|
||||
*/
|
||||
void Button::setBackgroundImage(Ptr<Texture> texture, const Rect &rect) {
|
||||
imgNormal_ = texture;
|
||||
imgHover_ = texture;
|
||||
imgPressed_ = texture;
|
||||
imgNormalRect_ = rect;
|
||||
imgHoverRect_ = rect;
|
||||
imgPressedRect_ = rect;
|
||||
useImageBackground_ = (texture != nullptr);
|
||||
useTextureRect_ = true;
|
||||
useStateImages_ = false;
|
||||
|
||||
if (useImageBackground_ && scaleMode_ == ImageScaleMode::Original) {
|
||||
setSize(rect.size.width, rect.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置状态背景图片
|
||||
* @param offNormal 关闭状态普通图片
|
||||
* @param onNormal 开启状态普通图片
|
||||
* @param offHover 关闭状态悬停图片
|
||||
* @param onHover 开启状态悬停图片
|
||||
* @param offPressed 关闭状态按下图片
|
||||
* @param onPressed 开启状态按下图片
|
||||
*/
|
||||
void Button::setStateBackgroundImage(
|
||||
Ptr<Texture> offNormal, Ptr<Texture> onNormal, Ptr<Texture> offHover,
|
||||
Ptr<Texture> onHover, Ptr<Texture> offPressed, Ptr<Texture> onPressed) {
|
||||
imgOffNormal_ = offNormal;
|
||||
imgOnNormal_ = onNormal;
|
||||
imgOffHover_ = offHover ? offHover : offNormal;
|
||||
imgOnHover_ = onHover ? onHover : onNormal;
|
||||
imgOffPressed_ = offPressed ? offPressed : offNormal;
|
||||
imgOnPressed_ = onPressed ? onPressed : onNormal;
|
||||
|
||||
useStateImages_ = (offNormal != nullptr && onNormal != nullptr);
|
||||
useImageBackground_ = useStateImages_;
|
||||
useTextureRect_ = false;
|
||||
|
||||
if (useStateImages_ && scaleMode_ == ImageScaleMode::Original && offNormal) {
|
||||
setSize(static_cast<float>(offNormal->width()),
|
||||
static_cast<float>(offNormal->height()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置背景图片缩放模式
|
||||
* @param mode 缩放模式
|
||||
*/
|
||||
void Button::setBackgroundImageScaleMode(ImageScaleMode mode) {
|
||||
scaleMode_ = mode;
|
||||
if (useImageBackground_ && scaleMode_ == ImageScaleMode::Original &&
|
||||
imgNormal_) {
|
||||
setSize(static_cast<float>(imgNormal_->width()),
|
||||
static_cast<float>(imgNormal_->height()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置自定义尺寸(Vec2版本)
|
||||
* @param size 尺寸向量
|
||||
*/
|
||||
void Button::setCustomSize(const Vec2 &size) { setSize(size.x, size.y); }
|
||||
|
||||
/**
|
||||
* @brief 设置自定义尺寸(分量版本)
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
void Button::setCustomSize(float width, float height) {
|
||||
setSize(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取边界框
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect Button::boundingBox() const {
|
||||
auto position = convertToWorldSpace(extra2d::Vec2::Zero());
|
||||
auto anchorPt = anchor();
|
||||
auto scaleVal = scale();
|
||||
auto widgetSize = size();
|
||||
|
||||
if (widgetSize.empty()) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
float w = widgetSize.width * scaleVal.x;
|
||||
float h = widgetSize.height * scaleVal.y;
|
||||
float x0 = position.x - widgetSize.width * anchorPt.x * scaleVal.x;
|
||||
float y0 = position.y - widgetSize.height * anchorPt.y * scaleVal.y;
|
||||
|
||||
return Rect(x0, y0, w, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算图片尺寸
|
||||
* @param buttonSize 按钮尺寸
|
||||
* @param imageSize 图片尺寸
|
||||
* @return 计算后的图片尺寸
|
||||
*/
|
||||
Vec2 Button::calculateImageSize(const Vec2 &buttonSize, const Vec2 &imageSize) {
|
||||
switch (scaleMode_) {
|
||||
case ImageScaleMode::Original:
|
||||
return imageSize;
|
||||
|
||||
case ImageScaleMode::Stretch:
|
||||
return buttonSize;
|
||||
|
||||
case ImageScaleMode::ScaleFit: {
|
||||
float scaleX = buttonSize.x / imageSize.x;
|
||||
float scaleY = buttonSize.y / imageSize.y;
|
||||
float scale = std::min(scaleX, scaleY);
|
||||
return Vec2(imageSize.x * scale, imageSize.y * scale);
|
||||
}
|
||||
|
||||
case ImageScaleMode::ScaleFill: {
|
||||
float scaleX = buttonSize.x / imageSize.x;
|
||||
float scaleY = buttonSize.y / imageSize.y;
|
||||
float scale = std::max(scaleX, scaleY);
|
||||
return Vec2(imageSize.x * scale, imageSize.y * scale);
|
||||
}
|
||||
}
|
||||
return imageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制背景图片
|
||||
* @param renderer 渲染后端
|
||||
* @param rect 绘制区域
|
||||
*/
|
||||
void Button::drawBackgroundImage(Renderer &renderer, const Rect &rect) {
|
||||
Texture *texture = nullptr;
|
||||
Rect srcRect;
|
||||
|
||||
if (useStateImages_) {
|
||||
if (isOn_) {
|
||||
if (pressed_ && imgOnPressed_) {
|
||||
texture = imgOnPressed_.get();
|
||||
} else if (hovered_ && imgOnHover_) {
|
||||
texture = imgOnHover_.get();
|
||||
} else {
|
||||
texture = imgOnNormal_.get();
|
||||
}
|
||||
} else {
|
||||
if (pressed_ && imgOffPressed_) {
|
||||
texture = imgOffPressed_.get();
|
||||
} else if (hovered_ && imgOffHover_) {
|
||||
texture = imgOffHover_.get();
|
||||
} else {
|
||||
texture = imgOffNormal_.get();
|
||||
}
|
||||
}
|
||||
if (texture) {
|
||||
srcRect = Rect(0, 0, static_cast<float>(texture->width()),
|
||||
static_cast<float>(texture->height()));
|
||||
}
|
||||
} else {
|
||||
if (pressed_ && imgPressed_) {
|
||||
texture = imgPressed_.get();
|
||||
srcRect = useTextureRect_
|
||||
? imgPressedRect_
|
||||
: Rect(0, 0, static_cast<float>(imgPressed_->width()),
|
||||
static_cast<float>(imgPressed_->height()));
|
||||
} else if (hovered_ && imgHover_) {
|
||||
texture = imgHover_.get();
|
||||
srcRect = useTextureRect_
|
||||
? imgHoverRect_
|
||||
: Rect(0, 0, static_cast<float>(imgHover_->width()),
|
||||
static_cast<float>(imgHover_->height()));
|
||||
} else if (imgNormal_) {
|
||||
texture = imgNormal_.get();
|
||||
srcRect = useTextureRect_
|
||||
? imgNormalRect_
|
||||
: Rect(0, 0, static_cast<float>(imgNormal_->width()),
|
||||
static_cast<float>(imgNormal_->height()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!texture)
|
||||
return;
|
||||
|
||||
Vec2 imageSize(srcRect.size.width, srcRect.size.height);
|
||||
Vec2 buttonSize(rect.size.width, rect.size.height);
|
||||
Vec2 drawSize = calculateImageSize(buttonSize, imageSize);
|
||||
|
||||
Vec2 drawPos(rect.origin.x + (rect.size.width - drawSize.x) * 0.5f,
|
||||
rect.origin.y + (rect.size.height - drawSize.y) * 0.5f);
|
||||
|
||||
Rect destRect(drawPos.x, drawPos.y, drawSize.x, drawSize.y);
|
||||
|
||||
renderer.drawSprite(*texture, destRect, srcRect, Colors::White, 0.0f,
|
||||
Vec2::Zero());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制圆角矩形边框
|
||||
* @param renderer 渲染后端
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
* @param radius 圆角半径
|
||||
*/
|
||||
void Button::drawRoundedRect(Renderer &renderer, const Rect &rect,
|
||||
const Color &color, float radius) {
|
||||
float maxRadius = std::min(rect.size.width, rect.size.height) * 0.5f;
|
||||
radius = std::min(radius, maxRadius);
|
||||
|
||||
if (radius <= 0.0f) {
|
||||
renderer.drawRect(rect, color, borderWidth_);
|
||||
return;
|
||||
}
|
||||
|
||||
const int segments = 8;
|
||||
float x = rect.origin.x;
|
||||
float y = rect.origin.y;
|
||||
float w = rect.size.width;
|
||||
float h = rect.size.height;
|
||||
float r = radius;
|
||||
|
||||
renderer.drawLine(Vec2(x + r, y), Vec2(x + w - r, y), color, borderWidth_);
|
||||
renderer.drawLine(Vec2(x + r, y + h), Vec2(x + w - r, y + h), color,
|
||||
borderWidth_);
|
||||
renderer.drawLine(Vec2(x, y + r), Vec2(x, y + h - r), color, borderWidth_);
|
||||
renderer.drawLine(Vec2(x + w, y + r), Vec2(x + w, y + h - r), color,
|
||||
borderWidth_);
|
||||
|
||||
for (int i = 0; i < segments; i++) {
|
||||
float angle1 = 3.14159f * 0.5f * (float)i / segments + 3.14159f;
|
||||
float angle2 = 3.14159f * 0.5f * (float)(i + 1) / segments + 3.14159f;
|
||||
Vec2 p1(x + r + r * cosf(angle1), y + r + r * sinf(angle1));
|
||||
Vec2 p2(x + r + r * cosf(angle2), y + r + r * sinf(angle2));
|
||||
renderer.drawLine(p1, p2, color, borderWidth_);
|
||||
}
|
||||
for (int i = 0; i < segments; i++) {
|
||||
float angle1 = 3.14159f * 0.5f * (float)i / segments + 3.14159f * 1.5f;
|
||||
float angle2 =
|
||||
3.14159f * 0.5f * (float)(i + 1) / segments + 3.14159f * 1.5f;
|
||||
Vec2 p1(x + w - r + r * cosf(angle1), y + r + r * sinf(angle1));
|
||||
Vec2 p2(x + w - r + r * cosf(angle2), y + r + r * sinf(angle2));
|
||||
renderer.drawLine(p1, p2, color, borderWidth_);
|
||||
}
|
||||
for (int i = 0; i < segments; i++) {
|
||||
float angle1 = 3.14159f * 0.5f * (float)i / segments;
|
||||
float angle2 = 3.14159f * 0.5f * (float)(i + 1) / segments;
|
||||
Vec2 p1(x + w - r + r * cosf(angle1), y + h - r + r * sinf(angle1));
|
||||
Vec2 p2(x + w - r + r * cosf(angle2), y + h - r + r * sinf(angle2));
|
||||
renderer.drawLine(p1, p2, color, borderWidth_);
|
||||
}
|
||||
for (int i = 0; i < segments; i++) {
|
||||
float angle1 = 3.14159f * 0.5f * (float)i / segments + 3.14159f * 0.5f;
|
||||
float angle2 =
|
||||
3.14159f * 0.5f * (float)(i + 1) / segments + 3.14159f * 0.5f;
|
||||
Vec2 p1(x + r + r * cosf(angle1), y + h - r + r * sinf(angle1));
|
||||
Vec2 p2(x + r + r * cosf(angle2), y + h - r + r * sinf(angle2));
|
||||
renderer.drawLine(p1, p2, color, borderWidth_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 填充圆角矩形
|
||||
* @param renderer 渲染后端
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
* @param radius 圆角半径
|
||||
*/
|
||||
void Button::fillRoundedRect(Renderer &renderer, const Rect &rect,
|
||||
const Color &color, float radius) {
|
||||
float maxRadius = std::min(rect.size.width, rect.size.height) * 0.5f;
|
||||
radius = std::min(radius, maxRadius);
|
||||
|
||||
if (radius <= 0.0f) {
|
||||
renderer.fillRect(rect, color);
|
||||
return;
|
||||
}
|
||||
|
||||
const int segments = 8;
|
||||
float x = rect.origin.x;
|
||||
float y = rect.origin.y;
|
||||
float w = rect.size.width;
|
||||
float h = rect.size.height;
|
||||
float r = radius;
|
||||
|
||||
std::vector<Vec2> vertices;
|
||||
|
||||
vertices.push_back(Vec2(x + r, y + r));
|
||||
vertices.push_back(Vec2(x + w - r, y + r));
|
||||
vertices.push_back(Vec2(x + w - r, y + h - r));
|
||||
vertices.push_back(Vec2(x + r, y + h - r));
|
||||
|
||||
renderer.fillPolygon(vertices, color);
|
||||
|
||||
renderer.fillRect(Rect(x + r, y, w - 2 * r, r), color);
|
||||
renderer.fillRect(Rect(x + r, y + h - r, w - 2 * r, r), color);
|
||||
renderer.fillRect(Rect(x, y + r, r, h - 2 * r), color);
|
||||
renderer.fillRect(Rect(x + w - r, y + r, r, h - 2 * r), color);
|
||||
|
||||
vertices.clear();
|
||||
vertices.push_back(Vec2(x + r, y + r));
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
float angle = 3.14159f + 3.14159f * 0.5f * (float)i / segments;
|
||||
vertices.push_back(Vec2(x + r + r * cosf(angle), y + r + r * sinf(angle)));
|
||||
}
|
||||
renderer.fillPolygon(vertices, color);
|
||||
|
||||
vertices.clear();
|
||||
vertices.push_back(Vec2(x + w - r, y + r));
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
float angle = 3.14159f * 1.5f + 3.14159f * 0.5f * (float)i / segments;
|
||||
vertices.push_back(
|
||||
Vec2(x + w - r + r * cosf(angle), y + r + r * sinf(angle)));
|
||||
}
|
||||
renderer.fillPolygon(vertices, color);
|
||||
|
||||
vertices.clear();
|
||||
vertices.push_back(Vec2(x + w - r, y + h - r));
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
float angle = 0 + 3.14159f * 0.5f * (float)i / segments;
|
||||
vertices.push_back(
|
||||
Vec2(x + w - r + r * cosf(angle), y + h - r + r * sinf(angle)));
|
||||
}
|
||||
renderer.fillPolygon(vertices, color);
|
||||
|
||||
vertices.clear();
|
||||
vertices.push_back(Vec2(x + r, y + h - r));
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
float angle = 3.14159f * 0.5f + 3.14159f * 0.5f * (float)i / segments;
|
||||
vertices.push_back(
|
||||
Vec2(x + r + r * cosf(angle), y + h - r + r * sinf(angle)));
|
||||
}
|
||||
renderer.fillPolygon(vertices, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制组件
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void Button::onDrawWidget(Renderer &renderer) {
|
||||
Rect rect = boundingBox();
|
||||
if (rect.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (useImageBackground_) {
|
||||
drawBackgroundImage(renderer, rect);
|
||||
} else {
|
||||
renderer.endSpriteBatch();
|
||||
|
||||
Color bg = bgNormal_;
|
||||
if (pressed_) {
|
||||
bg = bgPressed_;
|
||||
} else if (hovered_) {
|
||||
bg = bgHover_;
|
||||
}
|
||||
|
||||
if (roundedCornersEnabled_) {
|
||||
fillRoundedRect(renderer, rect, bg, cornerRadius_);
|
||||
} else {
|
||||
renderer.fillRect(rect, bg);
|
||||
}
|
||||
|
||||
renderer.beginSpriteBatch();
|
||||
|
||||
renderer.endSpriteBatch();
|
||||
|
||||
if (borderWidth_ > 0.0f) {
|
||||
if (roundedCornersEnabled_) {
|
||||
drawRoundedRect(renderer, rect, borderColor_, cornerRadius_);
|
||||
} else {
|
||||
renderer.drawRect(rect, borderColor_, borderWidth_);
|
||||
}
|
||||
}
|
||||
|
||||
renderer.beginSpriteBatch();
|
||||
}
|
||||
|
||||
if (font_) {
|
||||
std::string textToDraw;
|
||||
if (useStateText_) {
|
||||
textToDraw = isOn_ ? textOn_ : textOff_;
|
||||
} else {
|
||||
textToDraw = text_;
|
||||
}
|
||||
|
||||
Color colorToUse;
|
||||
if (useStateTextColor_) {
|
||||
colorToUse = isOn_ ? textColorOn_ : textColorOff_;
|
||||
} else {
|
||||
colorToUse = textColor_;
|
||||
}
|
||||
|
||||
if (!textToDraw.empty()) {
|
||||
Vec2 textSize = font_->measureText(textToDraw);
|
||||
|
||||
Vec2 textPos(rect.center().x - textSize.x * 0.5f,
|
||||
rect.center().y - textSize.y * 0.5f);
|
||||
|
||||
float minX = rect.left() + padding_.x;
|
||||
float minY = rect.top() + padding_.y;
|
||||
float maxX = rect.right() - padding_.x - textSize.x;
|
||||
float maxY = rect.bottom() - padding_.y - textSize.y;
|
||||
|
||||
textPos.x = std::max(minX, std::min(textPos.x, maxX));
|
||||
textPos.y = std::max(minY, std::min(textPos.y, maxY));
|
||||
|
||||
colorToUse.a = 1.0f;
|
||||
|
||||
renderer.drawText(*font_, textToDraw, textPos, colorToUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
#include <core/string.h>
|
||||
#include <renderer/renderer.h>
|
||||
#include <ui/check_box.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
CheckBox::CheckBox() {
|
||||
setAnchor(0.0f, 0.0f);
|
||||
setSize(boxSize_, boxSize_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建复选框对象
|
||||
* @return 复选框对象指针
|
||||
*/
|
||||
Ptr<CheckBox> CheckBox::create() { return shared<CheckBox>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建带标签的复选框对象
|
||||
* @param label 标签文本
|
||||
* @return 复选框对象指针
|
||||
*/
|
||||
Ptr<CheckBox> CheckBox::create(const std::string &label) {
|
||||
auto cb = shared<CheckBox>();
|
||||
cb->setLabel(label);
|
||||
return cb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置选中状态
|
||||
* @param checked 是否选中
|
||||
*/
|
||||
void CheckBox::setChecked(bool checked) {
|
||||
if (checked_ != checked) {
|
||||
checked_ = checked;
|
||||
if (onStateChange_) {
|
||||
onStateChange_(checked_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切换选中状态
|
||||
*/
|
||||
void CheckBox::toggle() { setChecked(!checked_); }
|
||||
|
||||
/**
|
||||
* @brief 设置标签文本
|
||||
* @param label 标签文本
|
||||
*/
|
||||
void CheckBox::setLabel(const std::string &label) { label_ = label; }
|
||||
|
||||
/**
|
||||
* @brief 设置字体
|
||||
* @param font 字体图集指针
|
||||
*/
|
||||
void CheckBox::setFont(Ptr<FontAtlas> font) { font_ = font; }
|
||||
|
||||
/**
|
||||
* @brief 设置文本颜色
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void CheckBox::setTextColor(const Color &color) { textColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置复选框尺寸
|
||||
* @param size 复选框尺寸
|
||||
*/
|
||||
void CheckBox::setBoxSize(float size) { boxSize_ = size; }
|
||||
|
||||
/**
|
||||
* @brief 设置间距
|
||||
* @param spacing 间距值
|
||||
*/
|
||||
void CheckBox::setSpacing(float spacing) { spacing_ = spacing; }
|
||||
|
||||
/**
|
||||
* @brief 设置选中颜色
|
||||
* @param color 选中颜色
|
||||
*/
|
||||
void CheckBox::setCheckedColor(const Color &color) { checkedColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置未选中颜色
|
||||
* @param color 未选中颜色
|
||||
*/
|
||||
void CheckBox::setUncheckedColor(const Color &color) {
|
||||
uncheckedColor_ = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置勾选标记颜色
|
||||
* @param color 勾选标记颜色
|
||||
*/
|
||||
void CheckBox::setCheckMarkColor(const Color &color) {
|
||||
checkMarkColor_ = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置状态改变回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void CheckBox::setOnStateChange(Function<void(bool)> callback) {
|
||||
onStateChange_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取边界框
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect CheckBox::boundingBox() const {
|
||||
Vec2 position = pos();
|
||||
float width = boxSize_;
|
||||
|
||||
if (!label_.empty() && font_) {
|
||||
Vec2 textSize = font_->measureText(label_);
|
||||
width += spacing_ + textSize.x;
|
||||
}
|
||||
|
||||
return Rect(position.x, position.y, width, boxSize_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制组件
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void CheckBox::onDrawWidget(Renderer &renderer) {
|
||||
Vec2 position = pos();
|
||||
|
||||
Rect boxRect(position.x, position.y + (size().height - boxSize_) * 0.5f,
|
||||
boxSize_, boxSize_);
|
||||
Color boxColor = checked_ ? checkedColor_ : uncheckedColor_;
|
||||
renderer.fillRect(boxRect, boxColor);
|
||||
renderer.drawRect(boxRect, Colors::White, 1.0f);
|
||||
|
||||
if (checked_) {
|
||||
float padding = boxSize_ * 0.2f;
|
||||
float x1 = boxRect.origin.x + padding;
|
||||
float y1 = boxRect.origin.y + boxSize_ * 0.5f;
|
||||
float x2 = boxRect.origin.x + boxSize_ * 0.4f;
|
||||
float y2 = boxRect.origin.y + boxSize_ - padding;
|
||||
float x3 = boxRect.origin.x + boxSize_ - padding;
|
||||
float y3 = boxRect.origin.y + padding;
|
||||
|
||||
renderer.drawLine(Vec2(x1, y1), Vec2(x2, y2), checkMarkColor_, 2.0f);
|
||||
renderer.drawLine(Vec2(x2, y2), Vec2(x3, y3), checkMarkColor_, 2.0f);
|
||||
}
|
||||
|
||||
if (!label_.empty() && font_) {
|
||||
Vec2 textPos(position.x + boxSize_ + spacing_, position.y);
|
||||
renderer.drawText(*font_, label_, textPos, textColor_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标按下事件处理
|
||||
* @param event 鼠标事件
|
||||
* @return 是否处理了事件
|
||||
*/
|
||||
bool CheckBox::onMousePress(const MouseEvent &event) {
|
||||
if (event.button == MouseButton::Left) {
|
||||
pressed_ = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标释放事件处理
|
||||
* @param event 鼠标事件
|
||||
* @return 是否处理了事件
|
||||
*/
|
||||
bool CheckBox::onMouseRelease(const MouseEvent &event) {
|
||||
if (event.button == MouseButton::Left && pressed_) {
|
||||
pressed_ = false;
|
||||
Vec2 position = pos();
|
||||
Rect boxRect(position.x, position.y, boxSize_, boxSize_);
|
||||
if (boxRect.containsPoint(Point(event.x, event.y))) {
|
||||
toggle();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
390
src/ui/label.cpp
390
src/ui/label.cpp
|
|
@ -1,390 +0,0 @@
|
|||
#include <core/string.h>
|
||||
#include <renderer/renderer.h>
|
||||
#include <ui/label.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
Label::Label() { setAnchor(0.0f, 0.0f); }
|
||||
|
||||
/**
|
||||
* @brief 带文本的构造函数
|
||||
* @param text 初始文本内容
|
||||
*/
|
||||
Label::Label(const std::string &text) : text_(text) {
|
||||
setAnchor(0.0f, 0.0f);
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建空标签对象
|
||||
* @return 标签对象指针
|
||||
*/
|
||||
Ptr<Label> Label::create() { return shared<Label>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建带文本的标签对象
|
||||
* @param text 文本内容
|
||||
* @return 标签对象指针
|
||||
*/
|
||||
Ptr<Label> Label::create(const std::string &text) {
|
||||
return shared<Label>(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建带文本和字体的标签对象
|
||||
* @param text 文本内容
|
||||
* @param font 字体图集
|
||||
* @return 标签对象指针
|
||||
*/
|
||||
Ptr<Label> Label::create(const std::string &text, Ptr<FontAtlas> font) {
|
||||
auto label = shared<Label>(text);
|
||||
label->setFont(font);
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置文本内容
|
||||
* @param text 新的文本内容
|
||||
*/
|
||||
void Label::setText(const std::string &text) {
|
||||
text_ = text;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置字体
|
||||
* @param font 字体图集指针
|
||||
*/
|
||||
void Label::setFont(Ptr<FontAtlas> font) {
|
||||
font_ = font;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置文本颜色
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void Label::setTextColor(const Color &color) { textColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置字体大小
|
||||
* @param size 字体大小
|
||||
*/
|
||||
void Label::setFontSize(int size) {
|
||||
fontSize_ = size;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置水平对齐方式
|
||||
* @param align 对齐方式
|
||||
*/
|
||||
void Label::setHorizontalAlign(HorizontalAlign align) { hAlign_ = align; }
|
||||
|
||||
/**
|
||||
* @brief 设置垂直对齐方式
|
||||
* @param align 垂直对齐方式
|
||||
*/
|
||||
void Label::setVerticalAlign(VerticalAlign align) { vAlign_ = align; }
|
||||
|
||||
/**
|
||||
* @brief 设置阴影是否启用
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void Label::setShadowEnabled(bool enabled) { shadowEnabled_ = enabled; }
|
||||
|
||||
/**
|
||||
* @brief 设置阴影颜色
|
||||
* @param color 阴影颜色
|
||||
*/
|
||||
void Label::setShadowColor(const Color &color) { shadowColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置阴影偏移
|
||||
* @param offset 偏移向量
|
||||
*/
|
||||
void Label::setShadowOffset(const Vec2 &offset) { shadowOffset_ = offset; }
|
||||
|
||||
/**
|
||||
* @brief 设置描边是否启用
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void Label::setOutlineEnabled(bool enabled) { outlineEnabled_ = enabled; }
|
||||
|
||||
/**
|
||||
* @brief 设置描边颜色
|
||||
* @param color 描边颜色
|
||||
*/
|
||||
void Label::setOutlineColor(const Color &color) { outlineColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置描边宽度
|
||||
* @param width 描边宽度
|
||||
*/
|
||||
void Label::setOutlineWidth(float width) { outlineWidth_ = width; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否多行模式
|
||||
* @param multiLine 是否多行
|
||||
*/
|
||||
void Label::setMultiLine(bool multiLine) {
|
||||
multiLine_ = multiLine;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置行间距
|
||||
* @param spacing 行间距倍数
|
||||
*/
|
||||
void Label::setLineSpacing(float spacing) {
|
||||
lineSpacing_ = spacing;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置最大宽度
|
||||
* @param maxWidth 最大宽度
|
||||
*/
|
||||
void Label::setMaxWidth(float maxWidth) {
|
||||
maxWidth_ = maxWidth;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文本尺寸
|
||||
* @return 文本的宽度和高度
|
||||
*/
|
||||
Vec2 Label::getTextSize() const {
|
||||
updateCache();
|
||||
return cachedSize_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取行高
|
||||
* @return 行高值
|
||||
*/
|
||||
float Label::getLineHeight() const {
|
||||
if (font_) {
|
||||
return font_->getLineHeight() * lineSpacing_;
|
||||
}
|
||||
return static_cast<float>(fontSize_) * lineSpacing_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新缓存
|
||||
*/
|
||||
void Label::updateCache() const {
|
||||
if (!sizeDirty_ || !font_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (multiLine_) {
|
||||
auto lines = splitLines();
|
||||
float maxWidth = 0.0f;
|
||||
float totalHeight = 0.0f;
|
||||
float lineHeight = getLineHeight();
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
Vec2 lineSize = font_->measureText(lines[i]);
|
||||
maxWidth = std::max(maxWidth, lineSize.x);
|
||||
totalHeight += lineHeight;
|
||||
}
|
||||
|
||||
cachedSize_ = Vec2(maxWidth, totalHeight);
|
||||
} else {
|
||||
cachedSize_ = font_->measureText(text_);
|
||||
}
|
||||
|
||||
sizeDirty_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分割文本为多行
|
||||
* @return 分割后的行列表
|
||||
*/
|
||||
std::vector<std::string> Label::splitLines() const {
|
||||
std::vector<std::string> lines;
|
||||
if (text_.empty()) {
|
||||
return lines;
|
||||
}
|
||||
|
||||
if (maxWidth_ <= 0.0f || !font_) {
|
||||
lines.push_back(text_);
|
||||
return lines;
|
||||
}
|
||||
|
||||
size_t start = 0;
|
||||
size_t end = text_.find('\n');
|
||||
|
||||
while (end != std::string::npos) {
|
||||
std::string line = text_.substr(start, end - start);
|
||||
|
||||
Vec2 lineSize = font_->measureText(line);
|
||||
if (lineSize.x > maxWidth_) {
|
||||
std::string currentLine;
|
||||
for (size_t i = 0; i < line.length(); ++i) {
|
||||
std::string testLine = currentLine + line[i];
|
||||
Vec2 testSize = font_->measureText(testLine);
|
||||
if (testSize.x > maxWidth_ && !currentLine.empty()) {
|
||||
lines.push_back(currentLine);
|
||||
currentLine = line[i];
|
||||
} else {
|
||||
currentLine = testLine;
|
||||
}
|
||||
}
|
||||
if (!currentLine.empty()) {
|
||||
lines.push_back(currentLine);
|
||||
}
|
||||
} else {
|
||||
lines.push_back(line);
|
||||
}
|
||||
|
||||
start = end + 1;
|
||||
end = text_.find('\n', start);
|
||||
}
|
||||
|
||||
if (start < text_.length()) {
|
||||
std::string line = text_.substr(start);
|
||||
Vec2 lineSize = font_->measureText(line);
|
||||
if (lineSize.x > maxWidth_) {
|
||||
std::string currentLine;
|
||||
for (size_t i = 0; i < line.length(); ++i) {
|
||||
std::string testLine = currentLine + line[i];
|
||||
Vec2 testSize = font_->measureText(testLine);
|
||||
if (testSize.x > maxWidth_ && !currentLine.empty()) {
|
||||
lines.push_back(currentLine);
|
||||
currentLine = line[i];
|
||||
} else {
|
||||
currentLine = testLine;
|
||||
}
|
||||
}
|
||||
if (!currentLine.empty()) {
|
||||
lines.push_back(currentLine);
|
||||
}
|
||||
} else {
|
||||
lines.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算绘制位置
|
||||
* @return 绘制位置坐标
|
||||
*/
|
||||
Vec2 Label::calculateDrawPosition() const {
|
||||
Vec2 position = pos();
|
||||
Vec2 textSize = getTextSize();
|
||||
Size widgetSize = size();
|
||||
|
||||
float refWidth = widgetSize.empty() ? textSize.x : widgetSize.width;
|
||||
float refHeight = widgetSize.empty() ? textSize.y : widgetSize.height;
|
||||
|
||||
switch (hAlign_) {
|
||||
case HorizontalAlign::Center:
|
||||
position.x += (refWidth - textSize.x) * 0.5f;
|
||||
break;
|
||||
case HorizontalAlign::Right:
|
||||
position.x += refWidth - textSize.x;
|
||||
break;
|
||||
case HorizontalAlign::Left:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (vAlign_) {
|
||||
case VerticalAlign::Middle:
|
||||
position.y += (refHeight - textSize.y) * 0.5f;
|
||||
break;
|
||||
case VerticalAlign::Bottom:
|
||||
position.y += refHeight - textSize.y;
|
||||
break;
|
||||
case VerticalAlign::Top:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制文本
|
||||
* @param renderer 渲染后端
|
||||
* @param position 绘制位置
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void Label::drawText(Renderer &renderer, const Vec2 &position,
|
||||
const Color &color) {
|
||||
if (!font_ || text_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (multiLine_) {
|
||||
auto lines = splitLines();
|
||||
float lineHeight = getLineHeight();
|
||||
Vec2 drawPos = position;
|
||||
|
||||
for (const auto &line : lines) {
|
||||
renderer.drawText(*font_, line, drawPos, color);
|
||||
drawPos.y += lineHeight;
|
||||
}
|
||||
} else {
|
||||
renderer.drawText(*font_, text_, position, color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取边界框
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect Label::boundingBox() const {
|
||||
if (!font_ || text_.empty()) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
updateCache();
|
||||
Vec2 size = cachedSize_;
|
||||
if (size.x <= 0.0f || size.y <= 0.0f) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
Vec2 drawPos = calculateDrawPosition();
|
||||
return Rect(drawPos.x, drawPos.y, size.x, size.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制组件
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void Label::onDrawWidget(Renderer &renderer) {
|
||||
if (!font_ || text_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vec2 pos = calculateDrawPosition();
|
||||
|
||||
if (shadowEnabled_) {
|
||||
Vec2 shadowPos = pos + shadowOffset_;
|
||||
drawText(renderer, shadowPos, shadowColor_);
|
||||
}
|
||||
|
||||
if (outlineEnabled_) {
|
||||
float w = outlineWidth_;
|
||||
drawText(renderer, pos + Vec2(-w, -w), outlineColor_);
|
||||
drawText(renderer, pos + Vec2(0, -w), outlineColor_);
|
||||
drawText(renderer, pos + Vec2(w, -w), outlineColor_);
|
||||
drawText(renderer, pos + Vec2(-w, 0), outlineColor_);
|
||||
drawText(renderer, pos + Vec2(w, 0), outlineColor_);
|
||||
drawText(renderer, pos + Vec2(-w, w), outlineColor_);
|
||||
drawText(renderer, pos + Vec2(0, w), outlineColor_);
|
||||
drawText(renderer, pos + Vec2(w, w), outlineColor_);
|
||||
}
|
||||
|
||||
drawText(renderer, pos, textColor_);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,502 +0,0 @@
|
|||
#include <cmath>
|
||||
#include <core/string.h>
|
||||
#include <renderer/renderer.h>
|
||||
#include <ui/progress_bar.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
ProgressBar::ProgressBar() {
|
||||
setAnchor(0.0f, 0.0f);
|
||||
setSize(200.0f, 20.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建进度条对象
|
||||
* @return 进度条对象指针
|
||||
*/
|
||||
Ptr<ProgressBar> ProgressBar::create() { return shared<ProgressBar>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建带范围的进度条对象
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @param value 当前值
|
||||
* @return 进度条对象指针
|
||||
*/
|
||||
Ptr<ProgressBar> ProgressBar::create(float min, float max, float value) {
|
||||
auto bar = shared<ProgressBar>();
|
||||
bar->setRange(min, max);
|
||||
bar->setValue(value);
|
||||
return bar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置数值范围
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
*/
|
||||
void ProgressBar::setRange(float min, float max) {
|
||||
min_ = min;
|
||||
max_ = max;
|
||||
setValue(value_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置当前值
|
||||
* @param value 新值
|
||||
*/
|
||||
void ProgressBar::setValue(float value) {
|
||||
value_ = std::clamp(value, min_, max_);
|
||||
|
||||
if (!animatedChangeEnabled_) {
|
||||
displayValue_ = value_;
|
||||
}
|
||||
|
||||
if (delayedDisplayEnabled_) {
|
||||
delayTimer_ = delayTime_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取百分比
|
||||
* @return 百分比值(0.0-1.0)
|
||||
*/
|
||||
float ProgressBar::getPercent() const {
|
||||
if (max_ <= min_)
|
||||
return 0.0f;
|
||||
return (displayValue_ - min_) / (max_ - min_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置方向
|
||||
* @param dir 方向枚举
|
||||
*/
|
||||
void ProgressBar::setDirection(Direction dir) { direction_ = dir; }
|
||||
|
||||
/**
|
||||
* @brief 设置背景颜色
|
||||
* @param color 背景颜色
|
||||
*/
|
||||
void ProgressBar::setBackgroundColor(const Color &color) { bgColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置填充颜色
|
||||
* @param color 填充颜色
|
||||
*/
|
||||
void ProgressBar::setFillColor(const Color &color) { fillColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用渐变填充
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ProgressBar::setGradientFillEnabled(bool enabled) {
|
||||
gradientEnabled_ = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置渐变结束颜色
|
||||
* @param color 结束颜色
|
||||
*/
|
||||
void ProgressBar::setFillColorEnd(const Color &color) { fillColorEnd_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用分段颜色
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ProgressBar::setSegmentedColorsEnabled(bool enabled) {
|
||||
segmentedColorsEnabled_ = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加颜色分段
|
||||
* @param percentThreshold 百分比阈值
|
||||
* @param color 颜色
|
||||
*/
|
||||
void ProgressBar::addColorSegment(float percentThreshold, const Color &color) {
|
||||
colorSegments_.push_back({percentThreshold, color});
|
||||
std::sort(colorSegments_.begin(), colorSegments_.end(),
|
||||
[](const auto &a, const auto &b) { return a.first > b.first; });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有颜色分段
|
||||
*/
|
||||
void ProgressBar::clearColorSegments() { colorSegments_.clear(); }
|
||||
|
||||
/**
|
||||
* @brief 设置圆角半径
|
||||
* @param radius 圆角半径
|
||||
*/
|
||||
void ProgressBar::setCornerRadius(float radius) { cornerRadius_ = radius; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用圆角
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ProgressBar::setRoundedCornersEnabled(bool enabled) {
|
||||
roundedCornersEnabled_ = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用边框
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ProgressBar::setBorderEnabled(bool enabled) { borderEnabled_ = enabled; }
|
||||
|
||||
/**
|
||||
* @brief 设置边框颜色
|
||||
* @param color 边框颜色
|
||||
*/
|
||||
void ProgressBar::setBorderColor(const Color &color) { borderColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置边框宽度
|
||||
* @param width 边框宽度
|
||||
*/
|
||||
void ProgressBar::setBorderWidth(float width) { borderWidth_ = width; }
|
||||
|
||||
/**
|
||||
* @brief 设置内边距
|
||||
* @param padding 内边距值
|
||||
*/
|
||||
void ProgressBar::setPadding(float padding) { padding_ = padding; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用文本显示
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ProgressBar::setTextEnabled(bool enabled) { textEnabled_ = enabled; }
|
||||
|
||||
/**
|
||||
* @brief 设置字体
|
||||
* @param font 字体图集指针
|
||||
*/
|
||||
void ProgressBar::setFont(Ptr<FontAtlas> font) { font_ = font; }
|
||||
|
||||
/**
|
||||
* @brief 设置文本颜色
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void ProgressBar::setTextColor(const Color &color) { textColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置文本格式
|
||||
* @param format 格式字符串
|
||||
*/
|
||||
void ProgressBar::setTextFormat(const std::string &format) {
|
||||
textFormat_ = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用动画变化
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ProgressBar::setAnimatedChangeEnabled(bool enabled) {
|
||||
animatedChangeEnabled_ = enabled;
|
||||
if (!enabled) {
|
||||
displayValue_ = value_;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置动画速度
|
||||
* @param speed 每秒变化量
|
||||
*/
|
||||
void ProgressBar::setAnimationSpeed(float speed) { animationSpeed_ = speed; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用延迟显示
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ProgressBar::setDelayedDisplayEnabled(bool enabled) {
|
||||
delayedDisplayEnabled_ = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置延迟时间
|
||||
* @param seconds 延迟秒数
|
||||
*/
|
||||
void ProgressBar::setDelayTime(float seconds) { delayTime_ = seconds; }
|
||||
|
||||
/**
|
||||
* @brief 设置延迟显示填充颜色
|
||||
* @param color 填充颜色
|
||||
*/
|
||||
void ProgressBar::setDelayedFillColor(const Color &color) {
|
||||
delayedFillColor_ = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用条纹效果
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void ProgressBar::setStripedEnabled(bool enabled) { stripedEnabled_ = enabled; }
|
||||
|
||||
/**
|
||||
* @brief 设置条纹颜色
|
||||
* @param color 条纹颜色
|
||||
*/
|
||||
void ProgressBar::setStripeColor(const Color &color) { stripeColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置条纹移动速度
|
||||
* @param speed 移动速度
|
||||
*/
|
||||
void ProgressBar::setStripeSpeed(float speed) { stripeSpeed_ = speed; }
|
||||
|
||||
/**
|
||||
* @brief 获取当前填充颜色
|
||||
* @return 当前填充颜色
|
||||
*/
|
||||
Color ProgressBar::getCurrentFillColor() const {
|
||||
if (segmentedColorsEnabled_ && !colorSegments_.empty()) {
|
||||
float percent = getPercent();
|
||||
for (const auto &segment : colorSegments_) {
|
||||
if (percent >= segment.first) {
|
||||
return segment.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fillColor_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 格式化文本
|
||||
* @return 格式化后的文本
|
||||
*/
|
||||
std::string ProgressBar::formatText() const {
|
||||
std::string result = textFormat_;
|
||||
|
||||
size_t pos = result.find("{value}");
|
||||
if (pos != std::string::npos) {
|
||||
result.replace(pos, 7, std::to_string(static_cast<int>(displayValue_)));
|
||||
}
|
||||
|
||||
pos = result.find("{max}");
|
||||
if (pos != std::string::npos) {
|
||||
result.replace(pos, 5, std::to_string(static_cast<int>(max_)));
|
||||
}
|
||||
|
||||
pos = result.find("{percent}");
|
||||
if (pos != std::string::npos) {
|
||||
int percent = static_cast<int>(getPercent() * 100);
|
||||
result.replace(pos, 9, std::to_string(percent));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取边界框
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect ProgressBar::boundingBox() const {
|
||||
return Rect(pos().x, pos().y, size().width, size().height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新函数
|
||||
* @param deltaTime 帧间隔时间
|
||||
*/
|
||||
void ProgressBar::onUpdate(float deltaTime) {
|
||||
if (animatedChangeEnabled_ && displayValue_ != value_) {
|
||||
float diff = value_ - displayValue_;
|
||||
float change = animationSpeed_ * deltaTime;
|
||||
|
||||
if (std::abs(diff) <= change) {
|
||||
displayValue_ = value_;
|
||||
} else {
|
||||
displayValue_ += (diff > 0 ? change : -change);
|
||||
}
|
||||
}
|
||||
|
||||
if (delayedDisplayEnabled_) {
|
||||
if (delayTimer_ > 0.0f) {
|
||||
delayTimer_ -= deltaTime;
|
||||
} else {
|
||||
float diff = displayValue_ - delayedValue_;
|
||||
float change = animationSpeed_ * deltaTime;
|
||||
|
||||
if (std::abs(diff) <= change) {
|
||||
delayedValue_ = displayValue_;
|
||||
} else {
|
||||
delayedValue_ += (diff > 0 ? change : -change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stripedEnabled_) {
|
||||
stripeOffset_ += stripeSpeed_ * deltaTime;
|
||||
if (stripeOffset_ > 20.0f) {
|
||||
stripeOffset_ -= 20.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制组件
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void ProgressBar::onDrawWidget(Renderer &renderer) {
|
||||
Vec2 position = pos();
|
||||
Size widgetSize = size();
|
||||
|
||||
float bgX = position.x + padding_;
|
||||
float bgY = position.y + padding_;
|
||||
float bgW = widgetSize.width - padding_ * 2;
|
||||
float bgH = widgetSize.height - padding_ * 2;
|
||||
Rect bgRect(bgX, bgY, bgW, bgH);
|
||||
|
||||
if (roundedCornersEnabled_) {
|
||||
fillRoundedRect(renderer, bgRect, bgColor_, cornerRadius_);
|
||||
} else {
|
||||
renderer.fillRect(bgRect, bgColor_);
|
||||
}
|
||||
|
||||
float percent = getPercent();
|
||||
float fillX = bgX, fillY = bgY, fillW = bgW, fillH = bgH;
|
||||
|
||||
switch (direction_) {
|
||||
case Direction::LeftToRight:
|
||||
fillW = bgW * percent;
|
||||
break;
|
||||
case Direction::RightToLeft:
|
||||
fillW = bgW * percent;
|
||||
fillX = bgX + bgW - fillW;
|
||||
break;
|
||||
case Direction::BottomToTop:
|
||||
fillH = bgH * percent;
|
||||
fillY = bgY + bgH - fillH;
|
||||
break;
|
||||
case Direction::TopToBottom:
|
||||
fillH = bgH * percent;
|
||||
break;
|
||||
}
|
||||
Rect fillRect(fillX, fillY, fillW, fillH);
|
||||
|
||||
if (delayedDisplayEnabled_ && delayedValue_ > displayValue_) {
|
||||
float delayedPercent = (delayedValue_ - min_) / (max_ - min_);
|
||||
float delayedX = bgX, delayedY = bgY, delayedW = bgW, delayedH = bgH;
|
||||
|
||||
switch (direction_) {
|
||||
case Direction::LeftToRight:
|
||||
delayedW = bgW * delayedPercent;
|
||||
break;
|
||||
case Direction::RightToLeft:
|
||||
delayedW = bgW * delayedPercent;
|
||||
delayedX = bgX + bgW - delayedW;
|
||||
break;
|
||||
case Direction::BottomToTop:
|
||||
delayedH = bgH * delayedPercent;
|
||||
delayedY = bgY + bgH - delayedH;
|
||||
break;
|
||||
case Direction::TopToBottom:
|
||||
delayedH = bgH * delayedPercent;
|
||||
break;
|
||||
}
|
||||
Rect delayedRect(delayedX, delayedY, delayedW, delayedH);
|
||||
|
||||
if (roundedCornersEnabled_) {
|
||||
fillRoundedRect(renderer, delayedRect, delayedFillColor_, cornerRadius_);
|
||||
} else {
|
||||
renderer.fillRect(delayedRect, delayedFillColor_);
|
||||
}
|
||||
}
|
||||
|
||||
if (fillW > 0 && fillH > 0) {
|
||||
Color fillColor = getCurrentFillColor();
|
||||
|
||||
if (roundedCornersEnabled_) {
|
||||
fillRoundedRect(renderer, fillRect, fillColor, cornerRadius_);
|
||||
} else {
|
||||
renderer.fillRect(fillRect, fillColor);
|
||||
}
|
||||
|
||||
if (stripedEnabled_) {
|
||||
drawStripes(renderer, fillRect);
|
||||
}
|
||||
}
|
||||
|
||||
if (borderEnabled_) {
|
||||
if (roundedCornersEnabled_) {
|
||||
drawRoundedRect(renderer, bgRect, borderColor_, cornerRadius_);
|
||||
} else {
|
||||
renderer.drawRect(bgRect, borderColor_, borderWidth_);
|
||||
}
|
||||
}
|
||||
|
||||
if (textEnabled_ && font_) {
|
||||
std::string text = formatText();
|
||||
Vec2 textSize = font_->measureText(text);
|
||||
|
||||
Vec2 textPos(position.x + (widgetSize.width - textSize.x) * 0.5f,
|
||||
position.y + (widgetSize.height - textSize.y) * 0.5f);
|
||||
|
||||
renderer.drawText(*font_, text, textPos, textColor_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制圆角矩形边框
|
||||
* @param renderer 渲染后端
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
* @param radius 圆角半径
|
||||
*/
|
||||
void ProgressBar::drawRoundedRect(Renderer &renderer, const Rect &rect,
|
||||
const Color &color, float radius) {
|
||||
renderer.drawRect(rect, color, borderWidth_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 填充圆角矩形
|
||||
* @param renderer 渲染后端
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
* @param radius 圆角半径
|
||||
*/
|
||||
void ProgressBar::fillRoundedRect(Renderer &renderer, const Rect &rect,
|
||||
const Color &color, float radius) {
|
||||
renderer.fillRect(rect, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制条纹效果
|
||||
* @param renderer 渲染后端
|
||||
* @param rect 矩形区域
|
||||
*/
|
||||
void ProgressBar::drawStripes(Renderer &renderer, const Rect &rect) {
|
||||
const float stripeWidth = 10.0f;
|
||||
const float spacing = 20.0f;
|
||||
float rectRight = rect.origin.x + rect.size.width;
|
||||
float rectBottom = rect.origin.y + rect.size.height;
|
||||
|
||||
for (float x = rect.origin.x - spacing + stripeOffset_; x < rectRight;
|
||||
x += spacing) {
|
||||
float x1 = x;
|
||||
float y1 = rect.origin.y;
|
||||
float x2 = x + stripeWidth;
|
||||
float y2 = rectBottom;
|
||||
|
||||
if (x1 < rect.origin.x)
|
||||
x1 = rect.origin.x;
|
||||
if (x2 > rectRight)
|
||||
x2 = rectRight;
|
||||
|
||||
if (x2 > x1) {
|
||||
for (int i = 0; i < static_cast<int>(rect.size.height); i += 4) {
|
||||
float sy = rect.origin.y + i;
|
||||
float sx = x1 + i * 0.5f;
|
||||
if (sx < x2) {
|
||||
Rect stripeRect(sx, sy, std::min(2.0f, x2 - sx), 2.0f);
|
||||
renderer.fillRect(stripeRect, stripeColor_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,258 +0,0 @@
|
|||
#include <core/string.h>
|
||||
#include <renderer/renderer.h>
|
||||
#include <ui/radio_button.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
RadioButton::RadioButton() {
|
||||
setAnchor(0.0f, 0.0f);
|
||||
setSize(circleSize_, circleSize_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建单选按钮对象
|
||||
* @return 单选按钮对象指针
|
||||
*/
|
||||
Ptr<RadioButton> RadioButton::create() { return shared<RadioButton>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建带标签的单选按钮对象
|
||||
* @param label 标签文本
|
||||
* @return 单选按钮对象指针
|
||||
*/
|
||||
Ptr<RadioButton> RadioButton::create(const std::string &label) {
|
||||
auto rb = shared<RadioButton>();
|
||||
rb->setLabel(label);
|
||||
return rb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置选中状态
|
||||
* @param selected 是否选中
|
||||
*/
|
||||
void RadioButton::setSelected(bool selected) {
|
||||
if (selected_ != selected) {
|
||||
selected_ = selected;
|
||||
if (onStateChange_) {
|
||||
onStateChange_(selected_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置标签文本
|
||||
* @param label 标签文本
|
||||
*/
|
||||
void RadioButton::setLabel(const std::string &label) { label_ = label; }
|
||||
|
||||
/**
|
||||
* @brief 设置字体
|
||||
* @param font 字体图集指针
|
||||
*/
|
||||
void RadioButton::setFont(Ptr<FontAtlas> font) { font_ = font; }
|
||||
|
||||
/**
|
||||
* @brief 设置文本颜色
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void RadioButton::setTextColor(const Color &color) { textColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置圆形尺寸
|
||||
* @param size 圆形尺寸
|
||||
*/
|
||||
void RadioButton::setCircleSize(float size) { circleSize_ = size; }
|
||||
|
||||
/**
|
||||
* @brief 设置间距
|
||||
* @param spacing 间距值
|
||||
*/
|
||||
void RadioButton::setSpacing(float spacing) { spacing_ = spacing; }
|
||||
|
||||
/**
|
||||
* @brief 设置选中颜色
|
||||
* @param color 选中颜色
|
||||
*/
|
||||
void RadioButton::setSelectedColor(const Color &color) {
|
||||
selectedColor_ = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置未选中颜色
|
||||
* @param color 未选中颜色
|
||||
*/
|
||||
void RadioButton::setUnselectedColor(const Color &color) {
|
||||
unselectedColor_ = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置圆点颜色
|
||||
* @param color 圆点颜色
|
||||
*/
|
||||
void RadioButton::setDotColor(const Color &color) { dotColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置分组ID
|
||||
* @param groupId 分组ID
|
||||
*/
|
||||
void RadioButton::setGroupId(int groupId) { groupId_ = groupId; }
|
||||
|
||||
/**
|
||||
* @brief 设置状态改变回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void RadioButton::setOnStateChange(Function<void(bool)> callback) {
|
||||
onStateChange_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取边界框
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect RadioButton::boundingBox() const {
|
||||
Vec2 position = pos();
|
||||
float width = circleSize_;
|
||||
|
||||
if (!label_.empty() && font_) {
|
||||
Vec2 textSize = font_->measureText(label_);
|
||||
width += spacing_ + textSize.x;
|
||||
}
|
||||
|
||||
return Rect(position.x, position.y, width, circleSize_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制组件
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void RadioButton::onDrawWidget(Renderer &renderer) {
|
||||
Vec2 position = pos();
|
||||
float centerX = position.x + circleSize_ * 0.5f;
|
||||
float centerY = position.y + size().height * 0.5f;
|
||||
float radius = circleSize_ * 0.5f;
|
||||
|
||||
Color circleColor = selected_ ? selectedColor_ : unselectedColor_;
|
||||
renderer.drawCircle(Vec2(centerX, centerY), radius, circleColor, true);
|
||||
renderer.drawCircle(Vec2(centerX, centerY), radius, Colors::White, false,
|
||||
1.0f);
|
||||
|
||||
if (selected_) {
|
||||
float dotRadius = radius * 0.4f;
|
||||
renderer.drawCircle(Vec2(centerX, centerY), dotRadius, dotColor_, true);
|
||||
}
|
||||
|
||||
if (!label_.empty() && font_) {
|
||||
Vec2 textPos(position.x + circleSize_ + spacing_, position.y);
|
||||
renderer.drawText(*font_, label_, textPos, textColor_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标按下事件处理
|
||||
* @param event 鼠标事件
|
||||
* @return 是否处理了事件
|
||||
*/
|
||||
bool RadioButton::onMousePress(const MouseEvent &event) {
|
||||
if (event.button == MouseButton::Left) {
|
||||
pressed_ = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标释放事件处理
|
||||
* @param event 鼠标事件
|
||||
* @return 是否处理了事件
|
||||
*/
|
||||
bool RadioButton::onMouseRelease(const MouseEvent &event) {
|
||||
if (event.button == MouseButton::Left && pressed_) {
|
||||
pressed_ = false;
|
||||
Vec2 position = pos();
|
||||
float centerX = position.x + circleSize_ * 0.5f;
|
||||
float centerY = position.y + size().height * 0.5f;
|
||||
float radius = circleSize_ * 0.5f;
|
||||
|
||||
float dx = event.x - centerX;
|
||||
float dy = event.y - centerY;
|
||||
if (dx * dx + dy * dy <= radius * radius) {
|
||||
setSelected(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RadioButtonGroup 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 添加单选按钮到组
|
||||
* @param button 单选按钮指针
|
||||
*/
|
||||
void RadioButtonGroup::addButton(RadioButton *button) {
|
||||
if (button &&
|
||||
std::find(buttons_.begin(), buttons_.end(), button) == buttons_.end()) {
|
||||
buttons_.push_back(button);
|
||||
button->setOnStateChange([this, button](bool selected) {
|
||||
if (selected) {
|
||||
selectButton(button);
|
||||
}
|
||||
});
|
||||
|
||||
if (button->isSelected() && !selectedButton_) {
|
||||
selectedButton_ = button;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从组中移除单选按钮
|
||||
* @param button 单选按钮指针
|
||||
*/
|
||||
void RadioButtonGroup::removeButton(RadioButton *button) {
|
||||
auto it = std::find(buttons_.begin(), buttons_.end(), button);
|
||||
if (it != buttons_.end()) {
|
||||
buttons_.erase(it);
|
||||
if (selectedButton_ == button) {
|
||||
selectedButton_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 选择指定的单选按钮
|
||||
* @param button 单选按钮指针
|
||||
*/
|
||||
void RadioButtonGroup::selectButton(RadioButton *button) {
|
||||
if (selectedButton_ == button)
|
||||
return;
|
||||
|
||||
if (selectedButton_) {
|
||||
selectedButton_->setSelected(false);
|
||||
}
|
||||
|
||||
selectedButton_ = button;
|
||||
if (button) {
|
||||
button->setSelected(true);
|
||||
}
|
||||
|
||||
if (onSelectionChange_) {
|
||||
onSelectionChange_(button);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置选择改变回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void RadioButtonGroup::setOnSelectionChange(
|
||||
Function<void(RadioButton *)> callback) {
|
||||
onSelectionChange_ = callback;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,435 +0,0 @@
|
|||
#include <cmath>
|
||||
#include <core/string.h>
|
||||
#include <renderer/renderer.h>
|
||||
#include <ui/slider.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
Slider::Slider() {
|
||||
setAnchor(0.0f, 0.0f);
|
||||
setSize(200.0f, 20.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建滑动条对象
|
||||
* @return 滑动条对象指针
|
||||
*/
|
||||
Ptr<Slider> Slider::create() { return shared<Slider>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建带范围的滑动条对象
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
* @param value 当前值
|
||||
* @return 滑动条对象指针
|
||||
*/
|
||||
Ptr<Slider> Slider::create(float min, float max, float value) {
|
||||
auto slider = shared<Slider>();
|
||||
slider->setRange(min, max);
|
||||
slider->setValue(value);
|
||||
return slider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置数值范围
|
||||
* @param min 最小值
|
||||
* @param max 最大值
|
||||
*/
|
||||
void Slider::setRange(float min, float max) {
|
||||
min_ = min;
|
||||
max_ = max;
|
||||
setValue(value_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置当前值
|
||||
* @param value 新值
|
||||
*/
|
||||
void Slider::setValue(float value) {
|
||||
float newValue = std::clamp(value, min_, max_);
|
||||
if (step_ > 0.0f) {
|
||||
newValue = snapToStep(newValue);
|
||||
}
|
||||
|
||||
if (value_ != newValue) {
|
||||
value_ = newValue;
|
||||
if (onValueChange_) {
|
||||
onValueChange_(value_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置步进值
|
||||
* @param step 步进值
|
||||
*/
|
||||
void Slider::setStep(float step) {
|
||||
step_ = step;
|
||||
if (step_ > 0.0f) {
|
||||
setValue(value_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置是否为垂直方向
|
||||
* @param vertical 是否垂直
|
||||
*/
|
||||
void Slider::setVertical(bool vertical) { vertical_ = vertical; }
|
||||
|
||||
/**
|
||||
* @brief 设置轨道尺寸
|
||||
* @param size 轨道尺寸
|
||||
*/
|
||||
void Slider::setTrackSize(float size) { trackSize_ = size; }
|
||||
|
||||
/**
|
||||
* @brief 设置滑块尺寸
|
||||
* @param size 滑块尺寸
|
||||
*/
|
||||
void Slider::setThumbSize(float size) { thumbSize_ = size; }
|
||||
|
||||
/**
|
||||
* @brief 设置轨道颜色
|
||||
* @param color 轨道颜色
|
||||
*/
|
||||
void Slider::setTrackColor(const Color &color) { trackColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置填充颜色
|
||||
* @param color 填充颜色
|
||||
*/
|
||||
void Slider::setFillColor(const Color &color) { fillColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置滑块颜色
|
||||
* @param color 滑块颜色
|
||||
*/
|
||||
void Slider::setThumbColor(const Color &color) { thumbColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置滑块悬停颜色
|
||||
* @param color 悬停颜色
|
||||
*/
|
||||
void Slider::setThumbHoverColor(const Color &color) {
|
||||
thumbHoverColor_ = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置滑块按下颜色
|
||||
* @param color 按下颜色
|
||||
*/
|
||||
void Slider::setThumbPressedColor(const Color &color) {
|
||||
thumbPressedColor_ = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置是否显示滑块
|
||||
* @param show 是否显示
|
||||
*/
|
||||
void Slider::setShowThumb(bool show) { showThumb_ = show; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否显示填充
|
||||
* @param show 是否显示
|
||||
*/
|
||||
void Slider::setShowFill(bool show) { showFill_ = show; }
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用文本显示
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void Slider::setTextEnabled(bool enabled) { textEnabled_ = enabled; }
|
||||
|
||||
/**
|
||||
* @brief 设置字体
|
||||
* @param font 字体图集指针
|
||||
*/
|
||||
void Slider::setFont(Ptr<FontAtlas> font) { font_ = font; }
|
||||
|
||||
/**
|
||||
* @brief 设置文本颜色
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void Slider::setTextColor(const Color &color) { textColor_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置文本格式
|
||||
* @param format 格式字符串
|
||||
*/
|
||||
void Slider::setTextFormat(const std::string &format) { textFormat_ = format; }
|
||||
|
||||
/**
|
||||
* @brief 设置值改变回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void Slider::setOnValueChange(Function<void(float)> callback) {
|
||||
onValueChange_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置拖拽开始回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void Slider::setOnDragStart(Function<void()> callback) {
|
||||
onDragStart_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置拖拽结束回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void Slider::setOnDragEnd(Function<void()> callback) { onDragEnd_ = callback; }
|
||||
|
||||
/**
|
||||
* @brief 获取边界框
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect Slider::boundingBox() const {
|
||||
return Rect(pos().x, pos().y, size().width, size().height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将值转换为位置
|
||||
* @param value 数值
|
||||
* @return 位置坐标
|
||||
*/
|
||||
float Slider::valueToPosition(float value) const {
|
||||
Vec2 position = pos();
|
||||
Size sz = size();
|
||||
|
||||
float percent = (value - min_) / (max_ - min_);
|
||||
|
||||
if (vertical_) {
|
||||
return position.y + sz.height - percent * sz.height;
|
||||
} else {
|
||||
return position.x + percent * sz.width;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将位置转换为值
|
||||
* @param position 位置坐标
|
||||
* @return 数值
|
||||
*/
|
||||
float Slider::positionToValue(float position) const {
|
||||
Vec2 widgetPos = pos();
|
||||
Size widgetSize = size();
|
||||
|
||||
float percent;
|
||||
if (vertical_) {
|
||||
percent = (widgetPos.y + widgetSize.height - position) / widgetSize.height;
|
||||
} else {
|
||||
percent = (position - widgetPos.x) / widgetSize.width;
|
||||
}
|
||||
|
||||
percent = std::clamp(percent, 0.0f, 1.0f);
|
||||
return min_ + percent * (max_ - min_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取滑块矩形
|
||||
* @return 滑块矩形
|
||||
*/
|
||||
Rect Slider::getThumbRect() const {
|
||||
Vec2 widgetPos = pos();
|
||||
Size widgetSize = size();
|
||||
|
||||
float thumbPos = valueToPosition(value_);
|
||||
|
||||
if (vertical_) {
|
||||
return Rect(widgetPos.x + (widgetSize.width - thumbSize_) * 0.5f,
|
||||
thumbPos - thumbSize_ * 0.5f, thumbSize_, thumbSize_);
|
||||
} else {
|
||||
return Rect(thumbPos - thumbSize_ * 0.5f,
|
||||
widgetPos.y + (widgetSize.height - thumbSize_) * 0.5f,
|
||||
thumbSize_, thumbSize_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取轨道矩形
|
||||
* @return 轨道矩形
|
||||
*/
|
||||
Rect Slider::getTrackRect() const {
|
||||
Vec2 widgetPos = pos();
|
||||
Size widgetSize = size();
|
||||
|
||||
if (vertical_) {
|
||||
return Rect(widgetPos.x + (widgetSize.width - trackSize_) * 0.5f,
|
||||
widgetPos.y, trackSize_, widgetSize.height);
|
||||
} else {
|
||||
return Rect(widgetPos.x,
|
||||
widgetPos.y + (widgetSize.height - trackSize_) * 0.5f,
|
||||
widgetSize.width, trackSize_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 格式化文本
|
||||
* @return 格式化后的文本
|
||||
*/
|
||||
std::string Slider::formatText() const {
|
||||
std::string result = textFormat_;
|
||||
|
||||
size_t pos = result.find("{value}");
|
||||
if (pos != std::string::npos) {
|
||||
result.replace(pos, 7, std::to_string(static_cast<int>(value_)));
|
||||
}
|
||||
|
||||
pos = result.find("{value:");
|
||||
if (pos != std::string::npos) {
|
||||
size_t endPos = result.find("}", pos);
|
||||
if (endPos != std::string::npos) {
|
||||
std::string format = result.substr(pos + 7, endPos - pos - 8);
|
||||
result.replace(pos, endPos - pos + 1,
|
||||
std::to_string(static_cast<int>(value_)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将值对齐到步进
|
||||
* @param value 原始值
|
||||
* @return 对齐后的值
|
||||
*/
|
||||
float Slider::snapToStep(float value) const {
|
||||
float steps = std::round((value - min_) / step_);
|
||||
return min_ + steps * step_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制组件
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void Slider::onDrawWidget(Renderer &renderer) {
|
||||
Rect trackRect = getTrackRect();
|
||||
|
||||
renderer.fillRect(trackRect, trackColor_);
|
||||
|
||||
if (showFill_) {
|
||||
float percent = (value_ - min_) / (max_ - min_);
|
||||
float fillX = trackRect.origin.x;
|
||||
float fillY = trackRect.origin.y;
|
||||
float fillW = trackRect.size.width;
|
||||
float fillH = trackRect.size.height;
|
||||
|
||||
if (vertical_) {
|
||||
fillH = trackRect.size.height * percent;
|
||||
fillY = trackRect.origin.y + trackRect.size.height - fillH;
|
||||
} else {
|
||||
fillW = trackRect.size.width * percent;
|
||||
}
|
||||
|
||||
Rect fillRect(fillX, fillY, fillW, fillH);
|
||||
renderer.fillRect(fillRect, fillColor_);
|
||||
}
|
||||
|
||||
if (showThumb_) {
|
||||
Rect thumbRect = getThumbRect();
|
||||
Color thumbColor = thumbColor_;
|
||||
|
||||
if (dragging_) {
|
||||
thumbColor = thumbPressedColor_;
|
||||
} else if (hovered_) {
|
||||
thumbColor = thumbHoverColor_;
|
||||
}
|
||||
|
||||
renderer.fillRect(thumbRect, thumbColor);
|
||||
renderer.drawRect(thumbRect, Colors::White, 1.0f);
|
||||
}
|
||||
|
||||
if (textEnabled_ && font_) {
|
||||
std::string text = formatText();
|
||||
Vec2 textSize = font_->measureText(text);
|
||||
Vec2 widgetPos = pos();
|
||||
Size widgetSize = size();
|
||||
|
||||
Vec2 textPos(widgetPos.x + widgetSize.width + 10.0f,
|
||||
widgetPos.y + (widgetSize.height - textSize.y) * 0.5f);
|
||||
|
||||
renderer.drawText(*font_, text, textPos, textColor_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标按下事件处理
|
||||
* @param event 鼠标事件
|
||||
* @return 是否处理了事件
|
||||
*/
|
||||
bool Slider::onMousePress(const MouseEvent &event) {
|
||||
if (event.button == MouseButton::Left) {
|
||||
Rect thumbRect = getThumbRect();
|
||||
|
||||
if (thumbRect.containsPoint(Point(event.x, event.y))) {
|
||||
dragging_ = true;
|
||||
if (onDragStart_) {
|
||||
onDragStart_();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Rect trackRect = getTrackRect();
|
||||
if (trackRect.containsPoint(Point(event.x, event.y))) {
|
||||
float newValue = positionToValue(vertical_ ? event.y : event.x);
|
||||
setValue(newValue);
|
||||
dragging_ = true;
|
||||
if (onDragStart_) {
|
||||
onDragStart_();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标释放事件处理
|
||||
* @param event 鼠标事件
|
||||
* @return 是否处理了事件
|
||||
*/
|
||||
bool Slider::onMouseRelease(const MouseEvent &event) {
|
||||
if (event.button == MouseButton::Left && dragging_) {
|
||||
dragging_ = false;
|
||||
if (onDragEnd_) {
|
||||
onDragEnd_();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标移动事件处理
|
||||
* @param event 鼠标事件
|
||||
* @return 是否处理了事件
|
||||
*/
|
||||
bool Slider::onMouseMove(const MouseEvent &event) {
|
||||
if (dragging_) {
|
||||
float newValue = positionToValue(vertical_ ? event.y : event.x);
|
||||
setValue(newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
Rect thumbRect = getThumbRect();
|
||||
bool wasHovered = hovered_;
|
||||
hovered_ = thumbRect.containsPoint(Point(event.x, event.y));
|
||||
|
||||
return hovered_ != wasHovered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标进入事件处理
|
||||
*/
|
||||
void Slider::onMouseEnter() { hovered_ = true; }
|
||||
|
||||
/**
|
||||
* @brief 鼠标离开事件处理
|
||||
*/
|
||||
void Slider::onMouseLeave() { hovered_ = false; }
|
||||
|
||||
} // namespace extra2d
|
||||
239
src/ui/text.cpp
239
src/ui/text.cpp
|
|
@ -1,239 +0,0 @@
|
|||
#include <core/string.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <renderer/renderer.h>
|
||||
#include <ui/text.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
Text::Text() { setAnchor(0.0f, 0.0f); }
|
||||
|
||||
/**
|
||||
* @brief 带文本的构造函数
|
||||
* @param text 初始文本内容
|
||||
*/
|
||||
Text::Text(const std::string &text) : text_(text) {
|
||||
sizeDirty_ = true;
|
||||
setAnchor(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置文本内容
|
||||
* @param text 新的文本内容
|
||||
*/
|
||||
void Text::setText(const std::string &text) {
|
||||
text_ = text;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置字体
|
||||
* @param font 字体图集指针
|
||||
*/
|
||||
void Text::setFont(Ptr<FontAtlas> font) {
|
||||
font_ = font;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置文本颜色
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
void Text::setTextColor(const Color &color) { color_ = color; }
|
||||
|
||||
/**
|
||||
* @brief 设置字体大小
|
||||
* @param size 字体大小
|
||||
*/
|
||||
void Text::setFontSize(int size) {
|
||||
fontSize_ = size;
|
||||
sizeDirty_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置水平对齐方式
|
||||
* @param align 对齐方式
|
||||
*/
|
||||
void Text::setAlignment(Alignment align) { alignment_ = align; }
|
||||
|
||||
/**
|
||||
* @brief 设置垂直对齐方式
|
||||
* @param align 垂直对齐方式
|
||||
*/
|
||||
void Text::setVerticalAlignment(VerticalAlignment align) {
|
||||
verticalAlignment_ = align;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文本尺寸
|
||||
* @return 文本的宽度和高度
|
||||
*/
|
||||
Vec2 Text::getTextSize() const {
|
||||
updateCache();
|
||||
return cachedSize_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取行高
|
||||
* @return 行高值
|
||||
*/
|
||||
float Text::getLineHeight() const {
|
||||
if (font_) {
|
||||
return font_->getLineHeight();
|
||||
}
|
||||
return static_cast<float>(fontSize_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新缓存
|
||||
*/
|
||||
void Text::updateCache() const {
|
||||
if (!sizeDirty_ || !font_) {
|
||||
return;
|
||||
}
|
||||
|
||||
cachedSize_ = font_->measureText(text_);
|
||||
sizeDirty_ = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算绘制位置
|
||||
* @return 绘制位置坐标
|
||||
*/
|
||||
Vec2 Text::calculateDrawPosition() const {
|
||||
Vec2 position = pos();
|
||||
Vec2 textSize = getTextSize();
|
||||
Size widgetSize = size();
|
||||
Vec2 anchorPt = anchor();
|
||||
|
||||
float refWidth = widgetSize.empty() ? textSize.x : widgetSize.width;
|
||||
float refHeight = widgetSize.empty() ? textSize.y : widgetSize.height;
|
||||
|
||||
position.x -= textSize.x * anchorPt.x;
|
||||
position.y -= textSize.y * anchorPt.y;
|
||||
|
||||
if (!widgetSize.empty()) {
|
||||
switch (alignment_) {
|
||||
case Alignment::Center:
|
||||
position.x += (refWidth - textSize.x) * 0.5f;
|
||||
break;
|
||||
case Alignment::Right:
|
||||
position.x += refWidth - textSize.x;
|
||||
break;
|
||||
case Alignment::Left:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!widgetSize.empty()) {
|
||||
switch (verticalAlignment_) {
|
||||
case VerticalAlignment::Middle:
|
||||
position.y += (refHeight - textSize.y) * 0.5f;
|
||||
break;
|
||||
case VerticalAlignment::Bottom:
|
||||
position.y += refHeight - textSize.y;
|
||||
break;
|
||||
case VerticalAlignment::Top:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建空文本对象
|
||||
* @return 文本对象指针
|
||||
*/
|
||||
Ptr<Text> Text::create() { return shared<Text>(); }
|
||||
|
||||
/**
|
||||
* @brief 创建带文本的对象
|
||||
* @param text 文本内容
|
||||
* @return 文本对象指针
|
||||
*/
|
||||
Ptr<Text> Text::create(const std::string &text) { return shared<Text>(text); }
|
||||
|
||||
/**
|
||||
* @brief 创建带文本和字体的对象
|
||||
* @param text 文本内容
|
||||
* @param font 字体图集
|
||||
* @return 文本对象指针
|
||||
*/
|
||||
Ptr<Text> Text::create(const std::string &text, Ptr<FontAtlas> font) {
|
||||
auto t = shared<Text>(text);
|
||||
t->setFont(font);
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 格式化创建文本
|
||||
* @param fmt 格式字符串
|
||||
* @param ... 可变参数
|
||||
* @return 文本对象指针
|
||||
*/
|
||||
Ptr<Text> Text::createFormat(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char buffer[256];
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
return shared<Text>(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 格式化创建文本(带字体)
|
||||
* @param font 字体图集
|
||||
* @param fmt 格式字符串
|
||||
* @param ... 可变参数
|
||||
* @return 文本对象指针
|
||||
*/
|
||||
Ptr<Text> Text::createFormat(Ptr<FontAtlas> font, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char buffer[256];
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
auto t = shared<Text>(buffer);
|
||||
t->setFont(font);
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取边界框
|
||||
* @return 边界矩形
|
||||
*/
|
||||
Rect Text::boundingBox() const {
|
||||
if (!font_ || text_.empty()) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
updateCache();
|
||||
Vec2 size = cachedSize_;
|
||||
if (size.x <= 0.0f || size.y <= 0.0f) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
Vec2 pos = calculateDrawPosition();
|
||||
return Rect(pos.x, pos.y, size.x, size.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制组件
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void Text::onDrawWidget(Renderer &renderer) {
|
||||
if (!font_ || text_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vec2 pos = calculateDrawPosition();
|
||||
renderer.drawText(*font_, text_, pos, color_);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#include <cmath>
|
||||
#include <ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Widget::Widget() {}
|
||||
|
||||
void Widget::setSize(const Size &size) {
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
void Widget::setSize(float width, float height) {
|
||||
setSize(Size(width, height));
|
||||
}
|
||||
|
||||
Rect Widget::boundingBox() const {
|
||||
if (size_.empty()) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
auto position = convertToWorldSpace(extra2d::Vec2::Zero());
|
||||
auto anchorPt = anchor();
|
||||
auto scaleVal = scale();
|
||||
|
||||
float w = size_.width * scaleVal.x;
|
||||
float h = size_.height * scaleVal.y;
|
||||
float x0 = position.x - size_.width * anchorPt.x * scaleVal.x;
|
||||
float y0 = position.y - size_.height * anchorPt.y * scaleVal.y;
|
||||
float x1 = x0 + w;
|
||||
float y1 = y0 + h;
|
||||
|
||||
float l = std::min(x0, x1);
|
||||
float t = std::min(y0, y1);
|
||||
return Rect(l, t, std::abs(w), std::abs(h));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 重写 onDraw
|
||||
// ------------------------------------------------------------------------
|
||||
void Widget::onDraw(Renderer &renderer) { onDrawWidget(renderer); }
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#include <utils/object_pool.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ObjectPoolManager 单例实现
|
||||
// 所有对象池通过静态局部变量自动管理生命周期
|
||||
// 程序退出时自动清理,无需手动调用 cleanup
|
||||
|
||||
} // namespace extra2d
|
||||
Loading…
Reference in New Issue