feat(ui): 为UI组件添加链式调用构建器方法和坐标空间支持
为ProgressBar、Slider、RadioButton、Label、CheckBox、Button等UI组件添加链式调用构建器方法,简化组件创建和配置流程。 新增CoordinateSpace枚举支持三种坐标空间:Screen(屏幕空间)、World(世界空间)和Camera(相机空间)。 重构onDraw方法为onDrawWidget,统一处理不同坐标空间的渲染逻辑。 更新示例代码展示如何使用链式调用和坐标空间功能。
This commit is contained in:
parent
d0314447ee
commit
2751b27d90
|
|
@ -22,52 +22,118 @@ enum class ImageScaleMode {
|
|||
class Button : public Widget {
|
||||
public:
|
||||
Button();
|
||||
explicit Button(const String &text);
|
||||
~Button() override = default;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
static Ptr<Button> create();
|
||||
static Ptr<Button> create(const String &text);
|
||||
static Ptr<Button> create(const String &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
Button *withPosition(float x, float y);
|
||||
Button *withPosition(const Vec2 &pos);
|
||||
Button *withAnchor(float x, float y);
|
||||
Button *withAnchor(const Vec2 &anchor);
|
||||
Button *withText(const String &text);
|
||||
Button *withFont(Ptr<FontAtlas> font);
|
||||
Button *withTextColor(const Color &color);
|
||||
Button *withBackgroundColor(const Color &normal, const Color &hover,
|
||||
const Color &pressed);
|
||||
Button *withSize(float width, float height);
|
||||
Button *withPadding(const Vec2 &padding);
|
||||
Button *withPadding(float x, float y);
|
||||
Button *withBorder(const Color &color, float width);
|
||||
Button *withCornerRadius(float radius);
|
||||
Button *withRoundedCornersEnabled(bool enabled);
|
||||
Button *withHoverCursor(CursorShape cursor);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用 - 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
Button *withCoordinateSpace(CoordinateSpace space);
|
||||
Button *withScreenPosition(float x, float y);
|
||||
Button *withScreenPosition(const Vec2 &pos);
|
||||
Button *withCameraOffset(float x, float y);
|
||||
Button *withCameraOffset(const Vec2 &offset);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字内容
|
||||
// ------------------------------------------------------------------------
|
||||
void setText(const String &text);
|
||||
const String &getText() const { return text_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 字体
|
||||
// ------------------------------------------------------------------------
|
||||
void setFont(Ptr<FontAtlas> font);
|
||||
Ptr<FontAtlas> getFont() const { return font_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 内边距
|
||||
// ------------------------------------------------------------------------
|
||||
void setPadding(const Vec2 &padding);
|
||||
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);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 图片背景设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setBackgroundImage(Ptr<Texture> normal, Ptr<Texture> hover = nullptr,
|
||||
Ptr<Texture> pressed = 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遮罩点击检测(用于不规则形状按钮)
|
||||
// ------------------------------------------------------------------------
|
||||
// Alpha遮罩点击检测
|
||||
// ------------------------------------------------------------------------
|
||||
void setUseAlphaMaskForHitTest(bool enabled);
|
||||
bool isUseAlphaMaskForHitTest() const { return useAlphaMaskForHitTest_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 点击回调
|
||||
// ------------------------------------------------------------------------
|
||||
void setOnClick(Function<void()> callback);
|
||||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
void drawBackgroundImage(RenderBackend &renderer, const Rect &rect);
|
||||
void drawRoundedRect(RenderBackend &renderer, const Rect &rect,
|
||||
const Color &color, float radius);
|
||||
|
|
@ -104,15 +170,15 @@ private:
|
|||
float borderWidth_ = 1.0f;
|
||||
|
||||
// 圆角矩形
|
||||
float cornerRadius_ = 8.0f; // 圆角半径
|
||||
bool roundedCornersEnabled_ = false; // 默认关闭
|
||||
float cornerRadius_ = 8.0f;
|
||||
bool roundedCornersEnabled_ = false;
|
||||
|
||||
// 鼠标光标
|
||||
CursorShape hoverCursor_ = CursorShape::Hand; // 悬停时默认显示手型光标
|
||||
bool cursorChanged_ = false; // 标记是否已改变光标
|
||||
CursorShape hoverCursor_ = CursorShape::Hand;
|
||||
bool cursorChanged_ = false;
|
||||
|
||||
// Alpha遮罩点击检测(用于不规则形状按钮)
|
||||
bool useAlphaMaskForHitTest_ = false; // 默认关闭
|
||||
// Alpha遮罩点击检测
|
||||
bool useAlphaMaskForHitTest_ = false;
|
||||
|
||||
bool hovered_ = false;
|
||||
bool pressed_ = false;
|
||||
|
|
@ -152,7 +218,7 @@ public:
|
|||
void setOnStateChange(Function<void(bool)> callback);
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
// 状态图片
|
||||
|
|
|
|||
|
|
@ -17,6 +17,27 @@ public:
|
|||
static Ptr<CheckBox> create();
|
||||
static Ptr<CheckBox> create(const String &label);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
CheckBox *withPosition(float x, float y);
|
||||
CheckBox *withPosition(const Vec2 &pos);
|
||||
CheckBox *withAnchor(float x, float y);
|
||||
CheckBox *withAnchor(const Vec2 &anchor);
|
||||
CheckBox *withText(const String &text);
|
||||
CheckBox *withFont(Ptr<FontAtlas> font);
|
||||
CheckBox *withTextColor(const Color &color);
|
||||
CheckBox *withSize(float width, float height);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用 - 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
CheckBox *withCoordinateSpace(CoordinateSpace space);
|
||||
CheckBox *withScreenPosition(float x, float y);
|
||||
CheckBox *withScreenPosition(const Vec2 &pos);
|
||||
CheckBox *withCameraOffset(float x, float y);
|
||||
CheckBox *withCameraOffset(const Vec2 &offset);
|
||||
|
||||
void setChecked(bool checked);
|
||||
bool isChecked() const { return checked_; }
|
||||
void toggle();
|
||||
|
|
@ -50,7 +71,7 @@ public:
|
|||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,27 @@ public:
|
|||
static Ptr<Label> create(const String &text);
|
||||
static Ptr<Label> create(const String &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
Label *withPosition(float x, float y);
|
||||
Label *withPosition(const Vec2 &pos);
|
||||
Label *withAnchor(float x, float y);
|
||||
Label *withAnchor(const Vec2 &anchor);
|
||||
Label *withText(const String &text);
|
||||
Label *withFont(Ptr<FontAtlas> font);
|
||||
Label *withTextColor(const Color &color);
|
||||
Label *withFontSize(int size);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标空间设置(链式调用)
|
||||
// ------------------------------------------------------------------------
|
||||
Label *withCoordinateSpace(CoordinateSpace space);
|
||||
Label *withScreenPosition(float x, float y);
|
||||
Label *withScreenPosition(const Vec2 &pos);
|
||||
Label *withCameraOffset(float x, float y);
|
||||
Label *withCameraOffset(const Vec2 &offset);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本内容
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -111,7 +132,7 @@ public:
|
|||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
String text_;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,21 @@ public:
|
|||
static Ptr<ProgressBar> create();
|
||||
static Ptr<ProgressBar> create(float min, float max, float value);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法 - 坐标空间支持
|
||||
// ------------------------------------------------------------------------
|
||||
ProgressBar *withPosition(float x, float y);
|
||||
ProgressBar *withPosition(const Vec2 &pos);
|
||||
ProgressBar *withAnchor(float x, float y);
|
||||
ProgressBar *withAnchor(const Vec2 &anchor);
|
||||
ProgressBar *withSize(float width, float height);
|
||||
ProgressBar *withProgress(float progress);
|
||||
ProgressBar *withCoordinateSpace(CoordinateSpace space);
|
||||
ProgressBar *withScreenPosition(float x, float y);
|
||||
ProgressBar *withScreenPosition(const Vec2 &pos);
|
||||
ProgressBar *withCameraOffset(float x, float y);
|
||||
ProgressBar *withCameraOffset(const Vec2 &offset);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 数值范围
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -151,7 +166,7 @@ public:
|
|||
|
||||
protected:
|
||||
void onUpdate(float deltaTime) override;
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
// 数值
|
||||
|
|
|
|||
|
|
@ -17,6 +17,23 @@ public:
|
|||
static Ptr<RadioButton> create();
|
||||
static Ptr<RadioButton> create(const String &label);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
RadioButton *withPosition(float x, float y);
|
||||
RadioButton *withPosition(const Vec2 &pos);
|
||||
RadioButton *withAnchor(float x, float y);
|
||||
RadioButton *withAnchor(const Vec2 &anchor);
|
||||
RadioButton *withText(const String &text);
|
||||
RadioButton *withFont(Ptr<FontAtlas> font);
|
||||
RadioButton *withTextColor(const Color &color);
|
||||
RadioButton *withSize(float width, float height);
|
||||
RadioButton *withCoordinateSpace(CoordinateSpace space);
|
||||
RadioButton *withScreenPosition(float x, float y);
|
||||
RadioButton *withScreenPosition(const Vec2 &pos);
|
||||
RadioButton *withCameraOffset(float x, float y);
|
||||
RadioButton *withCameraOffset(const Vec2 &offset);
|
||||
|
||||
void setSelected(bool selected);
|
||||
bool isSelected() const { return selected_; }
|
||||
|
||||
|
|
@ -52,7 +69,7 @@ public:
|
|||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,23 @@ public:
|
|||
static Ptr<Slider> create();
|
||||
static Ptr<Slider> create(float min, float max, float value);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
Slider *withPosition(float x, float y);
|
||||
Slider *withPosition(const Vec2 &pos);
|
||||
Slider *withAnchor(float x, float y);
|
||||
Slider *withAnchor(const Vec2 &anchor);
|
||||
Slider *withSize(float width, float height);
|
||||
Slider *withMinValue(float min);
|
||||
Slider *withMaxValue(float max);
|
||||
Slider *withValue(float value);
|
||||
Slider *withCoordinateSpace(CoordinateSpace space);
|
||||
Slider *withScreenPosition(float x, float y);
|
||||
Slider *withScreenPosition(const Vec2 &pos);
|
||||
Slider *withCameraOffset(float x, float y);
|
||||
Slider *withCameraOffset(const Vec2 &offset);
|
||||
|
||||
void setRange(float min, float max);
|
||||
float getMin() const { return min_; }
|
||||
float getMax() const { return max_; }
|
||||
|
|
@ -76,7 +93,7 @@ public:
|
|||
Rect getBoundingBox() const override;
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
bool onMousePress(const MouseEvent &event) override;
|
||||
bool onMouseRelease(const MouseEvent &event) override;
|
||||
bool onMouseMove(const MouseEvent &event) override;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ namespace extra2d {
|
|||
// ============================================================================
|
||||
class Text : public Widget {
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
// 对齐方式枚举
|
||||
// ------------------------------------------------------------------------
|
||||
enum class Alignment { Left, Center, Right };
|
||||
enum class VerticalAlignment { Top, Middle, Bottom };
|
||||
|
||||
Text();
|
||||
explicit Text(const String &text);
|
||||
~Text() override = default;
|
||||
|
|
@ -23,6 +29,19 @@ public:
|
|||
static Ptr<Text> create(const String &text);
|
||||
static Ptr<Text> create(const String &text, Ptr<FontAtlas> font);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
Text *withPosition(float x, float y);
|
||||
Text *withPosition(const Vec2 &pos);
|
||||
Text *withAnchor(float x, float y);
|
||||
Text *withAnchor(const Vec2 &anchor);
|
||||
Text *withTextColor(const Color &color);
|
||||
Text *withFont(Ptr<FontAtlas> font);
|
||||
Text *withFontSize(int size);
|
||||
Text *withAlignment(Alignment align);
|
||||
Text *withVerticalAlignment(VerticalAlignment align);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文字内容
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -47,16 +66,12 @@ public:
|
|||
// ------------------------------------------------------------------------
|
||||
// 对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
enum class Alignment { Left, Center, Right };
|
||||
|
||||
void setAlignment(Alignment align);
|
||||
Alignment getAlignment() const { return alignment_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 垂直对齐方式
|
||||
// ------------------------------------------------------------------------
|
||||
enum class VerticalAlignment { Top, Middle, Bottom };
|
||||
|
||||
void setVerticalAlignment(VerticalAlignment align);
|
||||
VerticalAlignment getVerticalAlignment() const { return verticalAlignment_; }
|
||||
|
||||
|
|
@ -68,8 +83,17 @@ public:
|
|||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用 - 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
Text *withCoordinateSpace(CoordinateSpace space);
|
||||
Text *withScreenPosition(float x, float y);
|
||||
Text *withScreenPosition(const Vec2 &pos);
|
||||
Text *withCameraOffset(float x, float y);
|
||||
Text *withCameraOffset(const Vec2 &offset);
|
||||
|
||||
protected:
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
void onDrawWidget(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
String text_;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,15 @@ struct MouseEvent {
|
|||
int mods; // 修饰键状态
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 坐标空间枚举 - 定义 UI 组件的渲染坐标空间
|
||||
// ============================================================================
|
||||
enum class CoordinateSpace {
|
||||
Screen, // 屏幕空间 - 固定位置,不随相机移动
|
||||
World, // 世界空间 - 随相机移动(默认行为)
|
||||
Camera, // 相机空间 - 相对于相机位置的偏移
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Widget 基类 - UI 组件的基础
|
||||
// ============================================================================
|
||||
|
|
@ -31,6 +40,26 @@ public:
|
|||
|
||||
Rect getBoundingBox() const override;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
void setCoordinateSpace(CoordinateSpace space);
|
||||
CoordinateSpace getCoordinateSpace() const { return coordinateSpace_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 屏幕空间位置设置(仅在 Screen 空间下有效)
|
||||
// ------------------------------------------------------------------------
|
||||
void setScreenPosition(const Vec2 &pos);
|
||||
void setScreenPosition(float x, float y);
|
||||
Vec2 getScreenPosition() const { return screenPosition_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 相机空间偏移设置(仅在 Camera 空间下有效)
|
||||
// ------------------------------------------------------------------------
|
||||
void setCameraOffset(const Vec2 &offset);
|
||||
void setCameraOffset(float x, float y);
|
||||
Vec2 getCameraOffset() const { return cameraOffset_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 鼠标事件处理(子类可重写)
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -58,11 +87,25 @@ protected:
|
|||
return getBoundingBox().containsPoint(Point(x, y));
|
||||
}
|
||||
|
||||
// 获取实际渲染位置(根据坐标空间计算)
|
||||
Vec2 getRenderPosition() const;
|
||||
|
||||
// 子类重写此方法以支持自定义渲染
|
||||
virtual void onDrawWidget(RenderBackend &renderer) {}
|
||||
|
||||
// 重写 Node 的 onDraw 以处理坐标空间
|
||||
void onDraw(RenderBackend &renderer) override;
|
||||
|
||||
private:
|
||||
Size size_ = Size::Zero();
|
||||
bool enabled_ = true;
|
||||
bool focused_ = false;
|
||||
bool hovered_ = false;
|
||||
|
||||
// 坐标空间相关
|
||||
CoordinateSpace coordinateSpace_ = CoordinateSpace::World;
|
||||
Vec2 screenPosition_ = Vec2::Zero(); // 屏幕空间位置
|
||||
Vec2 cameraOffset_ = Vec2::Zero(); // 相机空间偏移
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -7,15 +7,18 @@
|
|||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 构造函数,初始化按钮状态并设置事件监听器
|
||||
*/
|
||||
// ============================================================================
|
||||
// Button 实现
|
||||
// ============================================================================
|
||||
|
||||
Button::Button() {
|
||||
// 按钮默认锚点为左上角,这样setPosition(0, 0)会在左上角显示
|
||||
setAnchor(0.0f, 0.0f);
|
||||
setSpatialIndexed(false);
|
||||
|
||||
auto &dispatcher = getEventDispatcher();
|
||||
dispatcher.addListener(EventType::UIHoverEnter, [this](Event &) {
|
||||
hovered_ = true;
|
||||
// 鼠标进入按钮区域,改变光标为手型
|
||||
auto &app = Application::instance();
|
||||
app.window().setCursor(hoverCursor_);
|
||||
cursorChanged_ = true;
|
||||
|
|
@ -23,7 +26,6 @@ Button::Button() {
|
|||
dispatcher.addListener(EventType::UIHoverExit, [this](Event &) {
|
||||
hovered_ = false;
|
||||
pressed_ = false;
|
||||
// 鼠标离开按钮区域,恢复默认光标
|
||||
if (cursorChanged_) {
|
||||
auto &app = Application::instance();
|
||||
app.window().resetCursor();
|
||||
|
|
@ -40,16 +42,137 @@ Button::Button() {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建按钮实例的工厂方法
|
||||
* @return 按钮智能指针
|
||||
*/
|
||||
Ptr<Button> Button::create() { return makePtr<Button>(); }
|
||||
Button::Button(const String &text) : Button() {
|
||||
text_ = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置按钮文字,并根据文字大小自动调整按钮尺寸
|
||||
* @param text 要显示的文字
|
||||
*/
|
||||
// ------------------------------------------------------------------------
|
||||
// 静态创建方法
|
||||
// ------------------------------------------------------------------------
|
||||
Ptr<Button> Button::create() {
|
||||
return makePtr<Button>();
|
||||
}
|
||||
|
||||
Ptr<Button> Button::create(const String &text) {
|
||||
return makePtr<Button>(text);
|
||||
}
|
||||
|
||||
Ptr<Button> Button::create(const String &text, Ptr<FontAtlas> font) {
|
||||
auto btn = makePtr<Button>(text);
|
||||
btn->setFont(font);
|
||||
return btn;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
Button *Button::withPosition(float x, float y) {
|
||||
setPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withPosition(const Vec2 &pos) {
|
||||
setPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withAnchor(float x, float y) {
|
||||
setAnchor(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withAnchor(const Vec2 &anchor) {
|
||||
setAnchor(anchor);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withText(const String &text) {
|
||||
setText(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withFont(Ptr<FontAtlas> font) {
|
||||
setFont(font);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withTextColor(const Color &color) {
|
||||
setTextColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withBackgroundColor(const Color &normal, const Color &hover,
|
||||
const Color &pressed) {
|
||||
setBackgroundColor(normal, hover, pressed);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withSize(float width, float height) {
|
||||
setSize(width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withPadding(const Vec2 &padding) {
|
||||
setPadding(padding);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withPadding(float x, float y) {
|
||||
setPadding(Vec2(x, y));
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withBorder(const Color &color, float width) {
|
||||
setBorder(color, width);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withCornerRadius(float radius) {
|
||||
setCornerRadius(radius);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withRoundedCornersEnabled(bool enabled) {
|
||||
setRoundedCornersEnabled(enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withHoverCursor(CursorShape cursor) {
|
||||
setHoverCursor(cursor);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用 - 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
Button *Button::withCoordinateSpace(CoordinateSpace space) {
|
||||
setCoordinateSpace(space);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withScreenPosition(float x, float y) {
|
||||
setScreenPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withScreenPosition(const Vec2 &pos) {
|
||||
setScreenPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withCameraOffset(float x, float y) {
|
||||
setCameraOffset(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Button *Button::withCameraOffset(const Vec2 &offset) {
|
||||
setCameraOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 普通设置方法
|
||||
// ------------------------------------------------------------------------
|
||||
void Button::setText(const String &text) {
|
||||
text_ = text;
|
||||
if (font_ && getSize().empty()) {
|
||||
|
|
@ -58,10 +181,6 @@ void Button::setText(const String &text) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置字体,并根据当前文字自动调整按钮尺寸
|
||||
* @param font 字体图集
|
||||
*/
|
||||
void Button::setFont(Ptr<FontAtlas> font) {
|
||||
font_ = font;
|
||||
if (font_ && getSize().empty() && !text_.empty()) {
|
||||
|
|
@ -70,10 +189,6 @@ void Button::setFont(Ptr<FontAtlas> font) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置内边距,并根据当前文字自动调整按钮尺寸
|
||||
* @param padding 内边距向量
|
||||
*/
|
||||
void Button::setPadding(const Vec2 &padding) {
|
||||
padding_ = padding;
|
||||
if (font_ && getSize().empty() && !text_.empty()) {
|
||||
|
|
@ -82,18 +197,10 @@ void Button::setPadding(const Vec2 &padding) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置文字颜色
|
||||
* @param color 颜色值
|
||||
*/
|
||||
void Button::setTextColor(const Color &color) { textColor_ = 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;
|
||||
|
|
@ -101,60 +208,31 @@ void Button::setBackgroundColor(const Color &normal, const Color &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 cursor 光标形状枚举
|
||||
*/
|
||||
void Button::setHoverCursor(CursorShape cursor) { hoverCursor_ = 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;
|
||||
|
|
@ -162,20 +240,14 @@ void Button::setBackgroundImage(Ptr<Texture> normal, Ptr<Texture> hover,
|
|||
imgPressed_ = pressed ? pressed : (hover ? hover : normal);
|
||||
useImageBackground_ = (normal != nullptr);
|
||||
|
||||
// 如果使用原图大小模式,设置按钮大小为图片大小
|
||||
if (useImageBackground_ && scaleMode_ == ImageScaleMode::Original && normal) {
|
||||
setSize(static_cast<float>(normal->getWidth()),
|
||||
static_cast<float>(normal->getHeight()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置图片缩放模式
|
||||
* @param mode 缩放模式枚举值
|
||||
*/
|
||||
void Button::setBackgroundImageScaleMode(ImageScaleMode mode) {
|
||||
scaleMode_ = mode;
|
||||
// 如果切换到原图大小模式,更新按钮大小
|
||||
if (useImageBackground_ && scaleMode_ == ImageScaleMode::Original &&
|
||||
imgNormal_) {
|
||||
setSize(static_cast<float>(imgNormal_->getWidth()),
|
||||
|
|
@ -183,27 +255,32 @@ void Button::setBackgroundImageScaleMode(ImageScaleMode mode) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置自定义尺寸
|
||||
* @param size 尺寸向量
|
||||
*/
|
||||
void Button::setCustomSize(const Vec2 &size) { setSize(size.x, size.y); }
|
||||
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 根据缩放模式计算图片绘制尺寸
|
||||
* @param buttonSize 按钮尺寸
|
||||
* @param imageSize 图片原始尺寸
|
||||
* @return 计算后的绘制尺寸
|
||||
*/
|
||||
Rect Button::getBoundingBox() const {
|
||||
auto pos = getRenderPosition();
|
||||
auto anchor = getAnchor();
|
||||
auto scale = getScale();
|
||||
auto size = getSize();
|
||||
|
||||
if (size.empty()) {
|
||||
return Rect();
|
||||
}
|
||||
|
||||
float w = size.width * scale.x;
|
||||
float h = size.height * scale.y;
|
||||
float x0 = pos.x - size.width * anchor.x * scale.x;
|
||||
float y0 = pos.y - size.height * anchor.y * scale.y;
|
||||
|
||||
return Rect(x0, y0, w, h);
|
||||
}
|
||||
|
||||
Vec2 Button::calculateImageSize(const Vec2 &buttonSize, const Vec2 &imageSize) {
|
||||
switch (scaleMode_) {
|
||||
case ImageScaleMode::Original:
|
||||
|
|
@ -229,11 +306,6 @@ Vec2 Button::calculateImageSize(const Vec2 &buttonSize, const Vec2 &imageSize) {
|
|||
return imageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制背景图片,根据当前状态选择对应的图片
|
||||
* @param renderer 渲染后端
|
||||
* @param rect 按钮矩形区域
|
||||
*/
|
||||
void Button::drawBackgroundImage(RenderBackend &renderer, const Rect &rect) {
|
||||
Texture *texture = nullptr;
|
||||
if (pressed_ && imgPressed_) {
|
||||
|
|
@ -252,57 +324,37 @@ void Button::drawBackgroundImage(RenderBackend &renderer, const Rect &rect) {
|
|||
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);
|
||||
|
||||
// 绘制图片(使用Alpha混合)
|
||||
renderer.drawSprite(*texture, destRect, Rect(0, 0, imageSize.x, imageSize.y),
|
||||
Colors::White, 0.0f, Vec2::Zero());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绘制圆角矩形边框
|
||||
* @param renderer 渲染后端
|
||||
* @param rect 矩形区域
|
||||
* @param color 颜色
|
||||
* @param radius 圆角半径
|
||||
*/
|
||||
void Button::drawRoundedRect(RenderBackend &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) {
|
||||
// 圆角为0,使用普通矩形
|
||||
renderer.drawRect(rect, color, borderWidth_);
|
||||
return;
|
||||
}
|
||||
|
||||
const int segments = 8; // 每个圆角的线段数
|
||||
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 + 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_);
|
||||
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;
|
||||
|
|
@ -310,16 +362,13 @@ void Button::drawRoundedRect(RenderBackend &renderer, const Rect &rect,
|
|||
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;
|
||||
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;
|
||||
|
|
@ -327,66 +376,46 @@ void Button::drawRoundedRect(RenderBackend &renderer, const Rect &rect,
|
|||
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;
|
||||
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(RenderBackend &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) {
|
||||
// 圆角为0,使用普通矩形填充
|
||||
renderer.fillRect(rect, color);
|
||||
return;
|
||||
}
|
||||
|
||||
const int segments = 8; // 每个圆角的线段数
|
||||
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)); // 左下内角
|
||||
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++) {
|
||||
|
|
@ -395,7 +424,6 @@ void Button::fillRoundedRect(RenderBackend &renderer, const Rect &rect,
|
|||
}
|
||||
renderer.fillPolygon(vertices, color);
|
||||
|
||||
// 右上角
|
||||
vertices.clear();
|
||||
vertices.push_back(Vec2(x + w - r, y + r));
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
|
|
@ -405,7 +433,6 @@ void Button::fillRoundedRect(RenderBackend &renderer, const Rect &rect,
|
|||
}
|
||||
renderer.fillPolygon(vertices, color);
|
||||
|
||||
// 右下角
|
||||
vertices.clear();
|
||||
vertices.push_back(Vec2(x + w - r, y + h - r));
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
|
|
@ -415,7 +442,6 @@ void Button::fillRoundedRect(RenderBackend &renderer, const Rect &rect,
|
|||
}
|
||||
renderer.fillPolygon(vertices, color);
|
||||
|
||||
// 左下角
|
||||
vertices.clear();
|
||||
vertices.push_back(Vec2(x + r, y + h - r));
|
||||
for (int i = 0; i <= segments; i++) {
|
||||
|
|
@ -426,30 +452,15 @@ void Button::fillRoundedRect(RenderBackend &renderer, const Rect &rect,
|
|||
renderer.fillPolygon(vertices, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按钮绘制主函数,按正确顺序渲染所有视觉元素
|
||||
*
|
||||
* 渲染顺序:
|
||||
* 1. 背景(图片或纯色)- 最底层
|
||||
* 2. 边框 - 中间层
|
||||
* 3. 文字 - 最顶层
|
||||
*
|
||||
* 注意:此方法在场景渲染的精灵批次中被调用。
|
||||
* 由于 fillRect 和 drawRect 使用形状渲染管线(与精灵批次不同),
|
||||
* 需要在调用它们之前结束精灵批次,然后在 drawText 之前重新开始。
|
||||
*/
|
||||
void Button::onDraw(RenderBackend &renderer) {
|
||||
void Button::onDrawWidget(RenderBackend &renderer) {
|
||||
Rect rect = getBoundingBox();
|
||||
if (rect.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ========== 第1层:绘制背景(图片或纯色)==========
|
||||
if (useImageBackground_) {
|
||||
// 图片背景使用精灵批次绘制
|
||||
drawBackgroundImage(renderer, rect);
|
||||
} else {
|
||||
// 纯色背景使用 fillRect 或 fillRoundedRect 绘制
|
||||
renderer.endSpriteBatch();
|
||||
|
||||
Color bg = bgNormal_;
|
||||
|
|
@ -468,7 +479,6 @@ void Button::onDraw(RenderBackend &renderer) {
|
|||
renderer.beginSpriteBatch();
|
||||
}
|
||||
|
||||
// ========== 第2层:绘制边框 ==========
|
||||
renderer.endSpriteBatch();
|
||||
|
||||
if (borderWidth_ > 0.0f) {
|
||||
|
|
@ -481,7 +491,6 @@ void Button::onDraw(RenderBackend &renderer) {
|
|||
|
||||
renderer.beginSpriteBatch();
|
||||
|
||||
// ========== 第3层:绘制文字 ==========
|
||||
if (font_ && !text_.empty()) {
|
||||
Vec2 textSize = font_->measureText(text_);
|
||||
|
||||
|
|
@ -507,30 +516,14 @@ void Button::onDraw(RenderBackend &renderer) {
|
|||
// ToggleImageButton 实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 切换按钮构造函数
|
||||
*/
|
||||
ToggleImageButton::ToggleImageButton() {
|
||||
setOnClick([this]() { toggle(); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建切换按钮实例的工厂方法
|
||||
* @return 切换按钮智能指针
|
||||
*/
|
||||
Ptr<ToggleImageButton> ToggleImageButton::create() {
|
||||
return makePtr<ToggleImageButton>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置两种状态的图片
|
||||
* @param stateOffNormal 关闭状态-正常
|
||||
* @param stateOnNormal 开启状态-正常
|
||||
* @param stateOffHover 关闭状态-悬停(可选)
|
||||
* @param stateOnHover 开启状态-悬停(可选)
|
||||
* @param stateOffPressed 关闭状态-按下(可选)
|
||||
* @param stateOnPressed 开启状态-按下(可选)
|
||||
*/
|
||||
void ToggleImageButton::setStateImages(Ptr<Texture> stateOffNormal,
|
||||
Ptr<Texture> stateOnNormal,
|
||||
Ptr<Texture> stateOffHover,
|
||||
|
|
@ -550,10 +543,6 @@ void ToggleImageButton::setStateImages(Ptr<Texture> stateOffNormal,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置开关状态
|
||||
* @param on 是否开启
|
||||
*/
|
||||
void ToggleImageButton::setOn(bool on) {
|
||||
if (isOn_ != on) {
|
||||
isOn_ = on;
|
||||
|
|
@ -563,24 +552,14 @@ void ToggleImageButton::setOn(bool on) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切换当前状态
|
||||
*/
|
||||
void ToggleImageButton::toggle() { setOn(!isOn_); }
|
||||
void ToggleImageButton::toggle() {
|
||||
setOn(!isOn_);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置状态改变回调
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
void ToggleImageButton::setOnStateChange(Function<void(bool)> callback) {
|
||||
onStateChange_ = std::move(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置两种状态的文字
|
||||
* @param textOff 关闭状态文字
|
||||
* @param textOn 开启状态文字
|
||||
*/
|
||||
void ToggleImageButton::setStateText(const String &textOff,
|
||||
const String &textOn) {
|
||||
textOff_ = textOff;
|
||||
|
|
@ -588,11 +567,6 @@ void ToggleImageButton::setStateText(const String &textOff,
|
|||
useStateText_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置两种状态的文字颜色
|
||||
* @param colorOff 关闭状态颜色
|
||||
* @param colorOn 开启状态颜色
|
||||
*/
|
||||
void ToggleImageButton::setStateTextColor(const Color &colorOff,
|
||||
const Color &colorOn) {
|
||||
textColorOff_ = colorOff;
|
||||
|
|
@ -600,21 +574,12 @@ void ToggleImageButton::setStateTextColor(const Color &colorOff,
|
|||
useStateTextColor_ = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 切换按钮绘制主函数
|
||||
*
|
||||
* 渲染顺序:
|
||||
* 1. 状态图片背景 - 最底层
|
||||
* 2. 边框 - 中间层
|
||||
* 3. 状态文字 - 最顶层
|
||||
*/
|
||||
void ToggleImageButton::onDraw(RenderBackend &renderer) {
|
||||
void ToggleImageButton::onDrawWidget(RenderBackend &renderer) {
|
||||
Rect rect = getBoundingBox();
|
||||
if (rect.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ========== 第1层:根据当前状态和交互状态选择并绘制图片 ==========
|
||||
Ptr<Texture> texture = nullptr;
|
||||
|
||||
if (isOn_) {
|
||||
|
|
@ -650,7 +615,6 @@ void ToggleImageButton::onDraw(RenderBackend &renderer) {
|
|||
0.0f, Vec2::Zero());
|
||||
}
|
||||
|
||||
// ========== 第2层:绘制边框 ==========
|
||||
renderer.endSpriteBatch();
|
||||
|
||||
float borderWidth = 1.0f;
|
||||
|
|
@ -666,7 +630,6 @@ void ToggleImageButton::onDraw(RenderBackend &renderer) {
|
|||
|
||||
renderer.beginSpriteBatch();
|
||||
|
||||
// ========== 第3层:绘制状态文字 ==========
|
||||
auto font = getFont();
|
||||
if (font) {
|
||||
String textToDraw;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,77 @@ Ptr<CheckBox> CheckBox::create(const String &label) {
|
|||
return cb;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
CheckBox *CheckBox::withPosition(float x, float y) {
|
||||
setPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withPosition(const Vec2 &pos) {
|
||||
setPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withAnchor(float x, float y) {
|
||||
setAnchor(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withAnchor(const Vec2 &anchor) {
|
||||
setAnchor(anchor);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withText(const String &text) {
|
||||
setLabel(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withFont(Ptr<FontAtlas> font) {
|
||||
setFont(font);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withTextColor(const Color &color) {
|
||||
setTextColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withSize(float width, float height) {
|
||||
setSize(width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用 - 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
CheckBox *CheckBox::withCoordinateSpace(CoordinateSpace space) {
|
||||
setCoordinateSpace(space);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withScreenPosition(float x, float y) {
|
||||
setScreenPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withScreenPosition(const Vec2 &pos) {
|
||||
setScreenPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withCameraOffset(float x, float y) {
|
||||
setCameraOffset(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckBox *CheckBox::withCameraOffset(const Vec2 &offset) {
|
||||
setCameraOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
void CheckBox::setChecked(bool checked) {
|
||||
if (checked_ != checked) {
|
||||
checked_ = checked;
|
||||
|
|
@ -80,7 +151,7 @@ Rect CheckBox::getBoundingBox() const {
|
|||
return Rect(pos.x, pos.y, width, boxSize_);
|
||||
}
|
||||
|
||||
void CheckBox::onDraw(RenderBackend &renderer) {
|
||||
void CheckBox::onDrawWidget(RenderBackend &renderer) {
|
||||
Vec2 pos = getPosition();
|
||||
|
||||
// 绘制复选框
|
||||
|
|
|
|||
|
|
@ -28,6 +28,77 @@ Ptr<Label> Label::create(const String &text, Ptr<FontAtlas> font) {
|
|||
return label;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
Label *Label::withPosition(float x, float y) {
|
||||
setPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withPosition(const Vec2 &pos) {
|
||||
setPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withAnchor(float x, float y) {
|
||||
setAnchor(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withAnchor(const Vec2 &anchor) {
|
||||
setAnchor(anchor);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withText(const String &text) {
|
||||
setText(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withFont(Ptr<FontAtlas> font) {
|
||||
setFont(font);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withTextColor(const Color &color) {
|
||||
setTextColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withFontSize(int size) {
|
||||
setFontSize(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标空间设置(链式调用)
|
||||
// ------------------------------------------------------------------------
|
||||
Label *Label::withCoordinateSpace(CoordinateSpace space) {
|
||||
setCoordinateSpace(space);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withScreenPosition(float x, float y) {
|
||||
setScreenPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withScreenPosition(const Vec2 &pos) {
|
||||
setScreenPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withCameraOffset(float x, float y) {
|
||||
setCameraOffset(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Label *Label::withCameraOffset(const Vec2 &offset) {
|
||||
setCameraOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
void Label::setText(const String &text) {
|
||||
text_ = text;
|
||||
sizeDirty_ = true;
|
||||
|
|
@ -280,7 +351,7 @@ Rect Label::getBoundingBox() const {
|
|||
return Rect(pos.x, pos.y, size.x, size.y);
|
||||
}
|
||||
|
||||
void Label::onDraw(RenderBackend &renderer) {
|
||||
void Label::onDrawWidget(RenderBackend &renderer) {
|
||||
if (!font_ || text_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,65 @@ Ptr<ProgressBar> ProgressBar::create(float min, float max, float value) {
|
|||
return bar;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 链式调用构建器方法实现
|
||||
// ============================================================================
|
||||
|
||||
ProgressBar *ProgressBar::withPosition(float x, float y) {
|
||||
setPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withPosition(const Vec2 &pos) {
|
||||
setPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withAnchor(float x, float y) {
|
||||
setAnchor(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withAnchor(const Vec2 &anchor) {
|
||||
setAnchor(anchor);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withSize(float width, float height) {
|
||||
setSize(width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withProgress(float progress) {
|
||||
setValue(min_ + progress * (max_ - min_));
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withCoordinateSpace(CoordinateSpace space) {
|
||||
setCoordinateSpace(space);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withScreenPosition(float x, float y) {
|
||||
setScreenPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withScreenPosition(const Vec2 &pos) {
|
||||
setScreenPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withCameraOffset(float x, float y) {
|
||||
setCameraOffset(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
ProgressBar *ProgressBar::withCameraOffset(const Vec2 &offset) {
|
||||
setCameraOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
void ProgressBar::setRange(float min, float max) {
|
||||
min_ = min;
|
||||
max_ = max;
|
||||
|
|
@ -226,7 +285,7 @@ void ProgressBar::onUpdate(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
void ProgressBar::onDraw(RenderBackend &renderer) {
|
||||
void ProgressBar::onDrawWidget(RenderBackend &renderer) {
|
||||
Vec2 pos = getPosition();
|
||||
Size size = getSize();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,80 @@ Ptr<RadioButton> RadioButton::create(const String &label) {
|
|||
return rb;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
RadioButton *RadioButton::withPosition(float x, float y) {
|
||||
setPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withPosition(const Vec2 &pos) {
|
||||
setPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withAnchor(float x, float y) {
|
||||
setAnchor(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withAnchor(const Vec2 &anchor) {
|
||||
setAnchor(anchor);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withText(const String &text) {
|
||||
setLabel(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withFont(Ptr<FontAtlas> font) {
|
||||
setFont(font);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withTextColor(const Color &color) {
|
||||
setTextColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withSize(float width, float height) {
|
||||
setSize(width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用 - 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
RadioButton *RadioButton::withCoordinateSpace(CoordinateSpace space) {
|
||||
setCoordinateSpace(space);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withScreenPosition(float x, float y) {
|
||||
setScreenPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withScreenPosition(const Vec2 &pos) {
|
||||
setScreenPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withCameraOffset(float x, float y) {
|
||||
setCameraOffset(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
RadioButton *RadioButton::withCameraOffset(const Vec2 &offset) {
|
||||
setCameraOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 普通设置方法
|
||||
// ------------------------------------------------------------------------
|
||||
void RadioButton::setSelected(bool selected) {
|
||||
if (selected_ != selected) {
|
||||
selected_ = selected;
|
||||
|
|
@ -80,7 +154,7 @@ Rect RadioButton::getBoundingBox() const {
|
|||
return Rect(pos.x, pos.y, width, circleSize_);
|
||||
}
|
||||
|
||||
void RadioButton::onDraw(RenderBackend &renderer) {
|
||||
void RadioButton::onDrawWidget(RenderBackend &renderer) {
|
||||
Vec2 pos = getPosition();
|
||||
float centerX = pos.x + circleSize_ * 0.5f;
|
||||
float centerY = pos.y + getSize().height * 0.5f;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,147 @@ Ptr<Slider> Slider::create(float min, float max, float value) {
|
|||
return slider;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 链式调用构建器方法实现
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief 设置位置(浮点坐标)
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withPosition(float x, float y) {
|
||||
setPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置位置(Vec2坐标)
|
||||
* @param pos 位置向量
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withPosition(const Vec2 &pos) {
|
||||
setPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置锚点(浮点坐标)
|
||||
* @param x X锚点(0-1)
|
||||
* @param y Y锚点(0-1)
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withAnchor(float x, float y) {
|
||||
setAnchor(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置锚点(Vec2坐标)
|
||||
* @param anchor 锚点向量
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withAnchor(const Vec2 &anchor) {
|
||||
setAnchor(anchor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置尺寸
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withSize(float width, float height) {
|
||||
setSize(width, height);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置最小值
|
||||
* @param min 最小值
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withMinValue(float min) {
|
||||
min_ = min;
|
||||
setValue(value_);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置最大值
|
||||
* @param max 最大值
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withMaxValue(float max) {
|
||||
max_ = max;
|
||||
setValue(value_);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置当前值
|
||||
* @param value 当前值
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withValue(float value) {
|
||||
setValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置坐标空间
|
||||
* @param space 坐标空间类型
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withCoordinateSpace(CoordinateSpace space) {
|
||||
setCoordinateSpace(space);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置屏幕位置(浮点坐标)
|
||||
* @param x X屏幕坐标
|
||||
* @param y Y屏幕坐标
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withScreenPosition(float x, float y) {
|
||||
setScreenPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置屏幕位置(Vec2坐标)
|
||||
* @param pos 屏幕位置向量
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withScreenPosition(const Vec2 &pos) {
|
||||
setScreenPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机偏移(浮点坐标)
|
||||
* @param x X偏移量
|
||||
* @param y Y偏移量
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withCameraOffset(float x, float y) {
|
||||
setCameraOffset(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置相机偏移(Vec2坐标)
|
||||
* @param offset 偏移向量
|
||||
* @return 返回this指针,支持链式调用
|
||||
*/
|
||||
Slider *Slider::withCameraOffset(const Vec2 &offset) {
|
||||
setCameraOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
void Slider::setRange(float min, float max) {
|
||||
min_ = min;
|
||||
max_ = max;
|
||||
|
|
@ -217,7 +358,7 @@ float Slider::snapToStep(float value) const {
|
|||
return min_ + steps * step_;
|
||||
}
|
||||
|
||||
void Slider::onDraw(RenderBackend &renderer) {
|
||||
void Slider::onDrawWidget(RenderBackend &renderer) {
|
||||
Rect trackRect = getTrackRect();
|
||||
|
||||
// 绘制轨道背景
|
||||
|
|
|
|||
|
|
@ -15,6 +15,85 @@ Text::Text(const String &text) : text_(text) {
|
|||
setAnchor(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用构建器方法
|
||||
// ------------------------------------------------------------------------
|
||||
Text *Text::withPosition(float x, float y) {
|
||||
setPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withPosition(const Vec2 &pos) {
|
||||
setPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withAnchor(float x, float y) {
|
||||
setAnchor(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withAnchor(const Vec2 &anchor) {
|
||||
setAnchor(anchor);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withTextColor(const Color &color) {
|
||||
setTextColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withFont(Ptr<FontAtlas> font) {
|
||||
setFont(font);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withFontSize(int size) {
|
||||
setFontSize(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withAlignment(Alignment align) {
|
||||
setAlignment(align);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withVerticalAlignment(VerticalAlignment align) {
|
||||
setVerticalAlignment(align);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 链式调用 - 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
Text *Text::withCoordinateSpace(CoordinateSpace space) {
|
||||
setCoordinateSpace(space);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withScreenPosition(float x, float y) {
|
||||
setScreenPosition(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withScreenPosition(const Vec2 &pos) {
|
||||
setScreenPosition(pos);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withCameraOffset(float x, float y) {
|
||||
setCameraOffset(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
Text *Text::withCameraOffset(const Vec2 &offset) {
|
||||
setCameraOffset(offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 普通设置方法
|
||||
// ------------------------------------------------------------------------
|
||||
void Text::setText(const String &text) {
|
||||
text_ = text;
|
||||
sizeDirty_ = true;
|
||||
|
|
@ -70,12 +149,19 @@ Vec2 Text::calculateDrawPosition() const {
|
|||
Vec2 pos = getPosition();
|
||||
Vec2 textSize = getTextSize();
|
||||
Size widgetSize = getSize();
|
||||
Vec2 anchor = getAnchor();
|
||||
|
||||
// 如果设置了控件大小,使用控件大小作为对齐参考
|
||||
float refWidth = widgetSize.empty() ? textSize.x : widgetSize.width;
|
||||
float refHeight = widgetSize.empty() ? textSize.y : widgetSize.height;
|
||||
|
||||
// 水平对齐
|
||||
// 锚点调整:锚点(0.5, 0.5)表示文本中心在pos位置
|
||||
// 需要将文本向左上方偏移锚点比例 * 文本大小
|
||||
pos.x -= textSize.x * anchor.x;
|
||||
pos.y -= textSize.y * anchor.y;
|
||||
|
||||
// 水平对齐(仅在设置了控件大小时生效)
|
||||
if (!widgetSize.empty()) {
|
||||
switch (alignment_) {
|
||||
case Alignment::Center:
|
||||
pos.x += (refWidth - textSize.x) * 0.5f;
|
||||
|
|
@ -87,8 +173,10 @@ Vec2 Text::calculateDrawPosition() const {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 垂直对齐
|
||||
// 垂直对齐(仅在设置了控件大小时生效)
|
||||
if (!widgetSize.empty()) {
|
||||
switch (verticalAlignment_) {
|
||||
case VerticalAlignment::Middle:
|
||||
pos.y += (refHeight - textSize.y) * 0.5f;
|
||||
|
|
@ -100,6 +188,7 @@ Vec2 Text::calculateDrawPosition() const {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
|
@ -129,7 +218,7 @@ Rect Text::getBoundingBox() const {
|
|||
return Rect(pos.x, pos.y, size.x, size.y);
|
||||
}
|
||||
|
||||
void Text::onDraw(RenderBackend &renderer) {
|
||||
void Text::onDrawWidget(RenderBackend &renderer) {
|
||||
if (!font_ || text_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include <cmath>
|
||||
#include <extra2d/graphics/camera.h>
|
||||
#include <extra2d/scene/scene.h>
|
||||
#include <extra2d/ui/widget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -21,7 +23,7 @@ Rect Widget::getBoundingBox() const {
|
|||
return Rect();
|
||||
}
|
||||
|
||||
auto pos = getPosition();
|
||||
auto pos = getRenderPosition();
|
||||
auto anchor = getAnchor();
|
||||
auto scale = getScale();
|
||||
|
||||
|
|
@ -37,4 +39,103 @@ Rect Widget::getBoundingBox() const {
|
|||
return Rect(l, t, std::abs(w), std::abs(h));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 坐标空间设置
|
||||
// ------------------------------------------------------------------------
|
||||
void Widget::setCoordinateSpace(CoordinateSpace space) {
|
||||
coordinateSpace_ = space;
|
||||
}
|
||||
|
||||
void Widget::setScreenPosition(const Vec2 &pos) {
|
||||
screenPosition_ = pos;
|
||||
if (coordinateSpace_ == CoordinateSpace::Screen) {
|
||||
updateSpatialIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setScreenPosition(float x, float y) {
|
||||
setScreenPosition(Vec2(x, y));
|
||||
}
|
||||
|
||||
void Widget::setCameraOffset(const Vec2 &offset) {
|
||||
cameraOffset_ = offset;
|
||||
if (coordinateSpace_ == CoordinateSpace::Camera) {
|
||||
updateSpatialIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setCameraOffset(float x, float y) {
|
||||
setCameraOffset(Vec2(x, y));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 获取实际渲染位置(根据坐标空间计算)
|
||||
// ------------------------------------------------------------------------
|
||||
Vec2 Widget::getRenderPosition() const {
|
||||
switch (coordinateSpace_) {
|
||||
case CoordinateSpace::Screen:
|
||||
// 屏幕空间:直接使用屏幕位置
|
||||
return screenPosition_;
|
||||
|
||||
case CoordinateSpace::Camera: {
|
||||
// 相机空间:相机位置 + 偏移
|
||||
Scene *scene = getScene();
|
||||
if (scene) {
|
||||
Camera *camera = scene->getActiveCamera();
|
||||
if (camera) {
|
||||
return camera->getPosition() + cameraOffset_;
|
||||
}
|
||||
}
|
||||
// 如果没有场景或相机,使用偏移作为绝对位置
|
||||
return cameraOffset_;
|
||||
}
|
||||
|
||||
case CoordinateSpace::World:
|
||||
default:
|
||||
// 世界空间:使用节点的世界位置
|
||||
return getPosition();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 重写 onDraw 以处理坐标空间
|
||||
// ------------------------------------------------------------------------
|
||||
void Widget::onDraw(RenderBackend &renderer) {
|
||||
// 根据坐标空间调整渲染
|
||||
if (coordinateSpace_ == CoordinateSpace::Screen) {
|
||||
// 屏幕空间:临时修改位置为屏幕位置,保持锚点不变
|
||||
Vec2 worldPos = getPosition();
|
||||
const_cast<Widget*>(this)->setPosition(screenPosition_);
|
||||
|
||||
// 调用子类的绘制
|
||||
onDrawWidget(renderer);
|
||||
|
||||
// 恢复原始位置
|
||||
const_cast<Widget*>(this)->setPosition(worldPos);
|
||||
} else if (coordinateSpace_ == CoordinateSpace::Camera) {
|
||||
// 相机空间:计算相对于相机的位置
|
||||
Scene *scene = getScene();
|
||||
if (scene) {
|
||||
Camera *camera = scene->getActiveCamera();
|
||||
if (camera) {
|
||||
Vec2 worldPos = getPosition();
|
||||
Vec2 cameraRelativePos = camera->getPosition() + cameraOffset_;
|
||||
const_cast<Widget*>(this)->setPosition(cameraRelativePos);
|
||||
|
||||
// 调用子类的绘制
|
||||
onDrawWidget(renderer);
|
||||
|
||||
// 恢复原始位置
|
||||
const_cast<Widget*>(this)->setPosition(worldPos);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 如果没有场景或相机,按世界空间处理
|
||||
onDrawWidget(renderer);
|
||||
} else {
|
||||
// 世界空间:正常渲染
|
||||
onDrawWidget(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -28,7 +28,40 @@ public:
|
|||
|
||||
if (!font_) {
|
||||
E2D_LOG_ERROR("字体加载失败,文字渲染将不可用!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建 "你好世界" 文本组件 - 使用屏幕空间(固定位置,不随相机移动)
|
||||
auto text1 = Text::create("你好世界", font_);
|
||||
text1->withCoordinateSpace(CoordinateSpace::Screen)
|
||||
->withScreenPosition(640.0f, 360.0f) // 屏幕中心
|
||||
->withAnchor(0.5f, 0.5f) // 中心锚点,让文字中心对准位置
|
||||
->withTextColor(Color(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
addChild(text1);
|
||||
|
||||
// 创建提示文本组件 - 使用屏幕空间,固定在屏幕底部
|
||||
auto text2 = Text::create("退出按键(START 按钮)", font_);
|
||||
text2->withCoordinateSpace(CoordinateSpace::Screen)
|
||||
->withScreenPosition(640.0f, 650.0f) // 屏幕底部
|
||||
->withAnchor(0.5f, 0.5f)
|
||||
->withTextColor(Color(1.0f, 1.0f, 0.0f, 1.0f));
|
||||
addChild(text2);
|
||||
|
||||
// 创建相机空间文本 - 跟随相机但保持相对偏移
|
||||
auto text3 = Text::create("相机空间文本", font_);
|
||||
text3->withCoordinateSpace(CoordinateSpace::Camera)
|
||||
->withCameraOffset(50.0f, 50.0f) // 相机左上角偏移(屏幕坐标系Y向下)
|
||||
->withAnchor(0.0f, 0.0f) // 左上角锚点,文字从指定位置开始显示
|
||||
->withTextColor(Color(0.0f, 1.0f, 1.0f, 1.0f));
|
||||
addChild(text3);
|
||||
|
||||
// 创建世界空间文本 - 随相机移动(默认行为)
|
||||
auto text4 = Text::create("世界空间文本", font_);
|
||||
text4->withCoordinateSpace(CoordinateSpace::World)
|
||||
->withPosition(100.0f, 100.0f) // 世界坐标
|
||||
->withAnchor(0.0f, 0.0f) // 左上角锚点,文字从指定位置开始显示
|
||||
->withTextColor(Color(1.0f, 0.5f, 0.5f, 1.0f));
|
||||
addChild(text4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -48,27 +81,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染时调用
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void onRender(RenderBackend &renderer) override {
|
||||
Scene::onRender(renderer);
|
||||
|
||||
if (!font_)
|
||||
return;
|
||||
|
||||
// 屏幕中心位置
|
||||
float centerX = 640.0f; // 1280 / 2
|
||||
float centerY = 360.0f; // 720 / 2
|
||||
|
||||
// 绘制 "你好世界" 文字(白色,居中)
|
||||
renderer.drawText(*font_, "你好世界", Vec2(centerX - 100.0f, centerY),Color(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
// 绘制提示文字(黄色)
|
||||
renderer.drawText(*font_, "退出按键(START 按钮)",Vec2(centerX - 80.0f, centerY + 50.0f), Color(1.0f, 1.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
private:
|
||||
Ptr<FontAtlas> font_; // 字体图集
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue