refactor: 统一边界框方法名称为 boundingBox
将 getBoundingBox 方法重命名为 boundingBox,以遵循更简洁的命名规范 同时更新相关文档和示例代码中的调用
This commit is contained in:
parent
377ec373b0
commit
ea5ecd383f
|
|
@ -316,8 +316,8 @@ if (buttonFrame) {
|
||||||
**按钮使用世界坐标空间(World Space)**:
|
**按钮使用世界坐标空间(World Space)**:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// Button::getBoundingBox() 返回世界坐标系的包围盒
|
// Button::boundingBox() 返回世界坐标系的包围盒
|
||||||
Rect Button::getBoundingBox() const {
|
Rect Button::boundingBox() const {
|
||||||
auto pos = getRenderPosition(); // 获取世界坐标位置
|
auto pos = getRenderPosition(); // 获取世界坐标位置
|
||||||
// ... 计算包围盒
|
// ... 计算包围盒
|
||||||
return Rect(x0, y0, w, h); // 世界坐标
|
return Rect(x0, y0, w, h); // 世界坐标
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// 必须实现 getBoundingBox 方法
|
// 必须实现 getBoundingBox 方法
|
||||||
Rect getBoundingBox() const override {
|
Rect boundingBox() const override {
|
||||||
Vec2 pos = getPosition();
|
Vec2 pos = getPosition();
|
||||||
return Rect(pos.x - width_ / 2, pos.y - height_ / 2, width_, height_);
|
return Rect(pos.x - width_ / 2, pos.y - height_ / 2, width_, height_);
|
||||||
}
|
}
|
||||||
|
|
@ -212,7 +212,7 @@ private:
|
||||||
## 关键要点
|
## 关键要点
|
||||||
|
|
||||||
1. **必须调用 `setSpatialIndexed(true)`** - 启用节点的空间索引
|
1. **必须调用 `setSpatialIndexed(true)`** - 启用节点的空间索引
|
||||||
2. **必须实现 `getBoundingBox()`** - 返回准确的边界框
|
2. **必须实现 `boundingBox()`** - 返回准确的边界框
|
||||||
3. **在 `onEnter()` 中调用 `Scene::onEnter()`** - 确保节点正确注册到空间索引
|
3. **在 `onEnter()` 中调用 `Scene::onEnter()`** - 确保节点正确注册到空间索引
|
||||||
4. **使用 `queryCollisions()`** - 自动利用空间索引优化检测
|
4. **使用 `queryCollisions()`** - 自动利用空间索引优化检测
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,11 @@ public:
|
||||||
|
|
||||||
void setColliding(bool colliding) { isColliding_ = colliding; }
|
void setColliding(bool colliding) { isColliding_ = colliding; }
|
||||||
|
|
||||||
Rect getBoundingBox() const override {
|
Rect boundingBox() const override {
|
||||||
// 返回实际的矩形边界
|
// 返回实际的矩形边界
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
return Rect(position.x - width_ / 2, position.y - height_ / 2, width_, height_);
|
return Rect(position.x - width_ / 2, position.y - height_ / 2, width_,
|
||||||
|
height_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRender(RenderBackend &renderer) override {
|
void onRender(RenderBackend &renderer) override {
|
||||||
|
|
@ -27,17 +28,17 @@ public:
|
||||||
|
|
||||||
// 绘制填充矩形
|
// 绘制填充矩形
|
||||||
Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.8f) : color_;
|
Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.8f) : color_;
|
||||||
renderer.fillRect(
|
renderer.fillRect(Rect(position.x - width_ / 2, position.y - height_ / 2,
|
||||||
Rect(position.x - width_ / 2, position.y - height_ / 2, width_, height_),
|
width_, height_),
|
||||||
fillColor);
|
fillColor);
|
||||||
|
|
||||||
// 绘制边框
|
// 绘制边框
|
||||||
Color borderColor = isColliding_ ? Color(1.0f, 0.0f, 0.0f, 1.0f)
|
Color borderColor = isColliding_ ? Color(1.0f, 0.0f, 0.0f, 1.0f)
|
||||||
: Color(1.0f, 1.0f, 1.0f, 0.5f);
|
: Color(1.0f, 1.0f, 1.0f, 0.5f);
|
||||||
float borderWidth = isColliding_ ? 3.0f : 2.0f;
|
float borderWidth = isColliding_ ? 3.0f : 2.0f;
|
||||||
renderer.drawRect(
|
renderer.drawRect(Rect(position.x - width_ / 2, position.y - height_ / 2,
|
||||||
Rect(position.x - width_ / 2, position.y - height_ / 2, width_, height_),
|
width_, height_),
|
||||||
borderColor, borderWidth);
|
borderColor, borderWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -149,7 +150,8 @@ private:
|
||||||
addChild(fpsText_);
|
addChild(fpsText_);
|
||||||
|
|
||||||
// 创建退出提示文本
|
// 创建退出提示文本
|
||||||
float screenHeight = static_cast<float>(Application::instance().getConfig().height);
|
float screenHeight =
|
||||||
|
static_cast<float>(Application::instance().getConfig().height);
|
||||||
exitHintText_ = Text::create("按 + 键退出", infoFont_);
|
exitHintText_ = Text::create("按 + 键退出", infoFont_);
|
||||||
exitHintText_->setPosition(50.0f, screenHeight - 50.0f);
|
exitHintText_->setPosition(50.0f, screenHeight - 50.0f);
|
||||||
exitHintText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f));
|
exitHintText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f));
|
||||||
|
|
@ -238,8 +240,7 @@ private:
|
||||||
// 程序入口
|
// 程序入口
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv) {
|
||||||
{
|
|
||||||
// 初始化日志系统
|
// 初始化日志系统
|
||||||
Logger::init();
|
Logger::init();
|
||||||
Logger::setLevel(LogLevel::Debug);
|
Logger::setLevel(LogLevel::Debug);
|
||||||
|
|
|
||||||
|
|
@ -123,8 +123,7 @@ void GameScene::onUpdate(float dt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bird_->isLiving() && GAME_HEIGHT - bird_->pos().y <= 123.0f) {
|
if (bird_->isLiving() && GAME_HEIGHT - bird_->pos().y <= 123.0f) {
|
||||||
bird_->setPosition(
|
bird_->setPosition(extra2d::Vec2(bird_->pos().x, GAME_HEIGHT - 123.0f));
|
||||||
extra2d::Vec2(bird_->pos().x, GAME_HEIGHT - 123.0f));
|
|
||||||
bird_->setStatus(Bird::Status::Still);
|
bird_->setStatus(Bird::Status::Still);
|
||||||
onHit();
|
onHit();
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +158,7 @@ bool GameScene::checkCollision() {
|
||||||
if (!bird_ || !pipes_)
|
if (!bird_ || !pipes_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
extra2d::Rect birdBox = bird_->getBoundingBox();
|
extra2d::Rect birdBox = bird_->boundingBox();
|
||||||
|
|
||||||
// 检查与每个水管的碰撞
|
// 检查与每个水管的碰撞
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ void StartScene::onEnter() {
|
||||||
playBtn_->setAnchor(0.5f, 0.5f);
|
playBtn_->setAnchor(0.5f, 0.5f);
|
||||||
// PLAY 按钮在小鸟下方
|
// PLAY 按钮在小鸟下方
|
||||||
playBtn_->setPosition(screenWidth / 2.0f,
|
playBtn_->setPosition(screenWidth / 2.0f,
|
||||||
screenHeight - playBtn_->getSize().height - 100.0f);
|
screenHeight - playBtn_->size().height - 100.0f);
|
||||||
playBtn_->setOnClick([this]() {
|
playBtn_->setOnClick([this]() {
|
||||||
ResLoader::playMusic(MusicType::Click);
|
ResLoader::playMusic(MusicType::Click);
|
||||||
startGame();
|
startGame();
|
||||||
|
|
@ -95,7 +95,7 @@ void StartScene::onEnter() {
|
||||||
shareBtn_->setAnchor(0.5f, 0.5f);
|
shareBtn_->setAnchor(0.5f, 0.5f);
|
||||||
// SHARE 按钮在 PLAY 按钮下方,靠近地面
|
// SHARE 按钮在 PLAY 按钮下方,靠近地面
|
||||||
shareBtn_->setPosition(screenWidth / 2.0f,
|
shareBtn_->setPosition(screenWidth / 2.0f,
|
||||||
screenHeight - shareBtn_->getSize().height - 80.0f);
|
screenHeight - shareBtn_->size().height - 80.0f);
|
||||||
shareBtn_->setOnClick([this]() {
|
shareBtn_->setOnClick([this]() {
|
||||||
ResLoader::playMusic(MusicType::Click);
|
ResLoader::playMusic(MusicType::Click);
|
||||||
// 分享功能暂不实现
|
// 分享功能暂不实现
|
||||||
|
|
|
||||||
|
|
@ -7,189 +7,189 @@
|
||||||
|
|
||||||
namespace flappybird {
|
namespace flappybird {
|
||||||
|
|
||||||
Bird::Bird() {
|
Bird::Bird() { setStatus(Status::Idle); }
|
||||||
setStatus(Status::Idle);
|
|
||||||
}
|
|
||||||
|
|
||||||
Bird::~Bird() = default;
|
Bird::~Bird() = default;
|
||||||
|
|
||||||
void Bird::onEnter() {
|
void Bird::onEnter() {
|
||||||
Node::onEnter();
|
Node::onEnter();
|
||||||
if (!sprite_) {
|
if (!sprite_) {
|
||||||
initAnimations();
|
initAnimations();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::initAnimations() {
|
void Bird::initAnimations() {
|
||||||
// 随机选择小鸟颜色(0-2)
|
// 随机选择小鸟颜色(0-2)
|
||||||
int colorMode = extra2d::randomInt(0, 2);
|
int colorMode = extra2d::randomInt(0, 2);
|
||||||
std::string prefix = "bird" + std::to_string(colorMode) + "_";
|
std::string prefix = "bird" + std::to_string(colorMode) + "_";
|
||||||
|
|
||||||
// 加载动画帧序列: 0 -> 1 -> 2 -> 1
|
// 加载动画帧序列: 0 -> 1 -> 2 -> 1
|
||||||
int frameSequence[] = {0, 1, 2, 1};
|
int frameSequence[] = {0, 1, 2, 1};
|
||||||
for (int frameIndex : frameSequence) {
|
for (int frameIndex : frameSequence) {
|
||||||
auto frameSprite = ResLoader::getKeyFrame(prefix + std::to_string(frameIndex));
|
auto frameSprite =
|
||||||
if (frameSprite) {
|
ResLoader::getKeyFrame(prefix + std::to_string(frameIndex));
|
||||||
frames_.push_back(frameSprite);
|
if (frameSprite) {
|
||||||
} else {
|
frames_.push_back(frameSprite);
|
||||||
E2D_LOG_WARN("无法加载动画帧: {}{}", prefix, frameIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建精灵
|
|
||||||
if (!frames_.empty()) {
|
|
||||||
sprite_ = extra2d::Sprite::create();
|
|
||||||
setCurrentFrame(0);
|
|
||||||
addChild(sprite_);
|
|
||||||
E2D_LOG_INFO("小鸟动画创建成功: 颜色={}, 帧数={}", colorMode, frames_.size());
|
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_ERROR("小鸟动画创建失败: 没有找到任何动画帧");
|
E2D_LOG_WARN("无法加载动画帧: {}{}", prefix, frameIndex);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建精灵
|
||||||
|
if (!frames_.empty()) {
|
||||||
|
sprite_ = extra2d::Sprite::create();
|
||||||
|
setCurrentFrame(0);
|
||||||
|
addChild(sprite_);
|
||||||
|
E2D_LOG_INFO("小鸟动画创建成功: 颜色={}, 帧数={}", colorMode,
|
||||||
|
frames_.size());
|
||||||
|
} else {
|
||||||
|
E2D_LOG_ERROR("小鸟动画创建失败: 没有找到任何动画帧");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::setCurrentFrame(int frameIndex) {
|
void Bird::setCurrentFrame(int frameIndex) {
|
||||||
if (frames_.empty() || !sprite_) return;
|
if (frames_.empty() || !sprite_)
|
||||||
|
return;
|
||||||
frameIndex = frameIndex % static_cast<int>(frames_.size());
|
|
||||||
currentFrame_ = frameIndex;
|
frameIndex = frameIndex % static_cast<int>(frames_.size());
|
||||||
|
currentFrame_ = frameIndex;
|
||||||
auto& frame = frames_[frameIndex];
|
|
||||||
sprite_->setTexture(frame->getTexture());
|
auto &frame = frames_[frameIndex];
|
||||||
sprite_->setTextureRect(frame->getRect());
|
sprite_->setTexture(frame->getTexture());
|
||||||
|
sprite_->setTextureRect(frame->getRect());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::updateFrameAnimation(float dt) {
|
void Bird::updateFrameAnimation(float dt) {
|
||||||
if (frames_.empty() || status_ == Status::Still) return;
|
if (frames_.empty() || status_ == Status::Still)
|
||||||
|
return;
|
||||||
|
|
||||||
frameTimer_ += dt;
|
frameTimer_ += dt;
|
||||||
|
|
||||||
float interval = frameInterval_;
|
float interval = frameInterval_;
|
||||||
if (status_ == Status::StartToFly) {
|
if (status_ == Status::StartToFly) {
|
||||||
interval = 0.05f; // 2倍速度
|
interval = 0.05f; // 2倍速度
|
||||||
}
|
}
|
||||||
|
|
||||||
while (frameTimer_ >= interval) {
|
while (frameTimer_ >= interval) {
|
||||||
frameTimer_ -= interval;
|
frameTimer_ -= interval;
|
||||||
setCurrentFrame((currentFrame_ + 1) % static_cast<int>(frames_.size()));
|
setCurrentFrame((currentFrame_ + 1) % static_cast<int>(frames_.size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::onUpdate(float dt) {
|
void Bird::onUpdate(float dt) {
|
||||||
extra2d::Node::onUpdate(dt);
|
extra2d::Node::onUpdate(dt);
|
||||||
|
|
||||||
// 更新帧动画
|
// 更新帧动画
|
||||||
updateFrameAnimation(dt);
|
updateFrameAnimation(dt);
|
||||||
|
|
||||||
// 处理闲置动画(上下浮动)
|
// 处理闲置动画(上下浮动)
|
||||||
if (status_ == Status::Idle) {
|
if (status_ == Status::Idle) {
|
||||||
idleTimer_ += dt;
|
idleTimer_ += dt;
|
||||||
idleOffset_ = std::sin(idleTimer_ * 5.0f) * 4.0f;
|
idleOffset_ = std::sin(idleTimer_ * 5.0f) * 4.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::onRender(extra2d::RenderBackend& renderer) {
|
void Bird::onRender(extra2d::RenderBackend &renderer) {
|
||||||
// 精灵会自动渲染,这里只需要处理旋转和偏移
|
// 精灵会自动渲染,这里只需要处理旋转和偏移
|
||||||
if (sprite_) {
|
if (sprite_) {
|
||||||
sprite_->setRotation(rotation_);
|
sprite_->setRotation(rotation_);
|
||||||
|
|
||||||
// 应用闲置偏移
|
// 应用闲置偏移
|
||||||
if (status_ == Status::Idle) {
|
if (status_ == Status::Idle) {
|
||||||
sprite_->setPosition(extra2d::Vec2(0.0f, idleOffset_));
|
sprite_->setPosition(extra2d::Vec2(0.0f, idleOffset_));
|
||||||
} else {
|
} else {
|
||||||
sprite_->setPosition(extra2d::Vec2(0.0f, 0.0f));
|
sprite_->setPosition(extra2d::Vec2(0.0f, 0.0f));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 调用父类的 onRender 来渲染子节点
|
// 调用父类的 onRender 来渲染子节点
|
||||||
Node::onRender(renderer);
|
Node::onRender(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::fall(float dt) {
|
void Bird::fall(float dt) {
|
||||||
if (!living_) return;
|
if (!living_)
|
||||||
|
return;
|
||||||
|
|
||||||
// 更新垂直位置
|
// 更新垂直位置
|
||||||
extra2d::Vec2 position = pos();
|
extra2d::Vec2 position = pos();
|
||||||
position.y += speed_ * dt;
|
position.y += speed_ * dt;
|
||||||
|
setPosition(position);
|
||||||
|
|
||||||
|
// 应用重力
|
||||||
|
speed_ += gravity * dt;
|
||||||
|
|
||||||
|
// 限制顶部边界
|
||||||
|
if (position.y < 0) {
|
||||||
|
position.y = 0;
|
||||||
setPosition(position);
|
setPosition(position);
|
||||||
|
speed_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// 应用重力
|
// 根据速度计算旋转角度
|
||||||
speed_ += gravity * dt;
|
// 上升时抬头(-15度),下降时低头(最大90度)
|
||||||
|
if (speed_ < 0) {
|
||||||
// 限制顶部边界
|
rotation_ = -15.0f;
|
||||||
if (position.y < 0) {
|
} else {
|
||||||
position.y = 0;
|
rotation_ = std::min(90.0f, speed_ * 0.15f);
|
||||||
setPosition(position);
|
}
|
||||||
speed_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据速度计算旋转角度
|
|
||||||
// 上升时抬头(-15度),下降时低头(最大90度)
|
|
||||||
if (speed_ < 0) {
|
|
||||||
rotation_ = -15.0f;
|
|
||||||
} else {
|
|
||||||
rotation_ = std::min(90.0f, speed_ * 0.15f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::jump() {
|
void Bird::jump() {
|
||||||
if (!living_) return;
|
if (!living_)
|
||||||
|
return;
|
||||||
|
|
||||||
// 给小鸟向上的速度
|
// 给小鸟向上的速度
|
||||||
speed_ = -jumpSpeed;
|
speed_ = -jumpSpeed;
|
||||||
|
|
||||||
// 设置状态为飞行
|
// 设置状态为飞行
|
||||||
setStatus(Status::Fly);
|
setStatus(Status::Fly);
|
||||||
|
|
||||||
// 播放音效
|
// 播放音效
|
||||||
ResLoader::playMusic(MusicType::Fly);
|
ResLoader::playMusic(MusicType::Fly);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::die() {
|
void Bird::die() {
|
||||||
living_ = false;
|
living_ = false;
|
||||||
|
|
||||||
// 播放死亡音效
|
// 播放死亡音效
|
||||||
ResLoader::playMusic(MusicType::Hit);
|
ResLoader::playMusic(MusicType::Hit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bird::setStatus(Status status) {
|
void Bird::setStatus(Status status) {
|
||||||
status_ = status;
|
status_ = status;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case Status::Still:
|
case Status::Still:
|
||||||
// 停止所有动画
|
// 停止所有动画
|
||||||
frameTimer_ = 0.0f;
|
frameTimer_ = 0.0f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Status::Idle:
|
case Status::Idle:
|
||||||
// 开始闲置动画
|
// 开始闲置动画
|
||||||
frameInterval_ = 0.1f; // 正常速度
|
frameInterval_ = 0.1f; // 正常速度
|
||||||
idleTimer_ = 0.0f;
|
idleTimer_ = 0.0f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Status::StartToFly:
|
case Status::StartToFly:
|
||||||
// 停止闲置动画,加速翅膀扇动
|
// 停止闲置动画,加速翅膀扇动
|
||||||
idleOffset_ = 0.0f;
|
idleOffset_ = 0.0f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Status::Fly:
|
case Status::Fly:
|
||||||
// 飞行状态
|
// 飞行状态
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extra2d::Rect Bird::getBoundingBox() const {
|
extra2d::Rect Bird::boundingBox() const {
|
||||||
extra2d::Vec2 position = pos();
|
extra2d::Vec2 position = pos();
|
||||||
// 小鸟碰撞框大小约为 24x24
|
// 小鸟碰撞框大小约为 24x24
|
||||||
float halfSize = 12.0f;
|
float halfSize = 12.0f;
|
||||||
return extra2d::Rect(
|
return extra2d::Rect(position.x - halfSize, position.y - halfSize,
|
||||||
position.x - halfSize,
|
halfSize * 2.0f, halfSize * 2.0f);
|
||||||
position.y - halfSize,
|
|
||||||
halfSize * 2.0f,
|
|
||||||
halfSize * 2.0f
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace flappybird
|
} // namespace flappybird
|
||||||
|
|
|
||||||
|
|
@ -15,116 +15,116 @@ namespace flappybird {
|
||||||
*/
|
*/
|
||||||
class Bird : public extra2d::Node {
|
class Bird : public extra2d::Node {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief 小鸟状态枚举
|
* @brief 小鸟状态枚举
|
||||||
*/
|
*/
|
||||||
enum class Status {
|
enum class Status {
|
||||||
Still, // 静止不动
|
Still, // 静止不动
|
||||||
Idle, // 上下浮动(菜单展示)
|
Idle, // 上下浮动(菜单展示)
|
||||||
StartToFly, // 开始飞行
|
StartToFly, // 开始飞行
|
||||||
Fly // 飞行中
|
Fly // 飞行中
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 构造函数
|
* @brief 构造函数
|
||||||
*/
|
*/
|
||||||
Bird();
|
Bird();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 析构函数
|
* @brief 析构函数
|
||||||
*/
|
*/
|
||||||
~Bird();
|
~Bird();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 每帧更新
|
* @brief 每帧更新
|
||||||
* @param dt 时间间隔(秒)
|
* @param dt 时间间隔(秒)
|
||||||
*/
|
*/
|
||||||
void onUpdate(float dt) override;
|
void onUpdate(float dt) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 渲染小鸟
|
* @brief 渲染小鸟
|
||||||
* @param renderer 渲染后端
|
* @param renderer 渲染后端
|
||||||
*/
|
*/
|
||||||
void onRender(extra2d::RenderBackend& renderer) override;
|
void onRender(extra2d::RenderBackend &renderer) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 进入场景时调用
|
* @brief 进入场景时调用
|
||||||
*/
|
*/
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 模拟下落
|
* @brief 模拟下落
|
||||||
* @param dt 时间间隔(秒)
|
* @param dt 时间间隔(秒)
|
||||||
*/
|
*/
|
||||||
void fall(float dt);
|
void fall(float dt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 跳跃
|
* @brief 跳跃
|
||||||
*/
|
*/
|
||||||
void jump();
|
void jump();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 死亡
|
* @brief 死亡
|
||||||
*/
|
*/
|
||||||
void die();
|
void die();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置小鸟状态
|
* @brief 设置小鸟状态
|
||||||
* @param status 新状态
|
* @param status 新状态
|
||||||
*/
|
*/
|
||||||
void setStatus(Status status);
|
void setStatus(Status status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前状态
|
* @brief 获取当前状态
|
||||||
* @return 当前状态
|
* @return 当前状态
|
||||||
*/
|
*/
|
||||||
Status getStatus() const { return status_; }
|
Status getStatus() const { return status_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 检查是否存活
|
* @brief 检查是否存活
|
||||||
* @return 是否存活
|
* @return 是否存活
|
||||||
*/
|
*/
|
||||||
bool isLiving() const { return living_; }
|
bool isLiving() const { return living_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取边界框(用于碰撞检测)
|
* @brief 获取边界框(用于碰撞检测)
|
||||||
* @return 边界框
|
* @return 边界框
|
||||||
*/
|
*/
|
||||||
extra2d::Rect getBoundingBox() const override;
|
extra2d::Rect boundingBox() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief 初始化动画
|
* @brief 初始化动画
|
||||||
*/
|
*/
|
||||||
void initAnimations();
|
void initAnimations();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 更新帧动画
|
* @brief 更新帧动画
|
||||||
*/
|
*/
|
||||||
void updateFrameAnimation(float dt);
|
void updateFrameAnimation(float dt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置当前帧
|
* @brief 设置当前帧
|
||||||
*/
|
*/
|
||||||
void setCurrentFrame(int frameIndex);
|
void setCurrentFrame(int frameIndex);
|
||||||
|
|
||||||
bool living_ = true; // 是否存活
|
bool living_ = true; // 是否存活
|
||||||
float speed_ = 0.0f; // 垂直速度
|
float speed_ = 0.0f; // 垂直速度
|
||||||
float rotation_ = 0.0f; // 旋转角度
|
float rotation_ = 0.0f; // 旋转角度
|
||||||
Status status_ = Status::Idle; // 当前状态
|
Status status_ = Status::Idle; // 当前状态
|
||||||
|
|
||||||
// 动画相关
|
// 动画相关
|
||||||
extra2d::Ptr<extra2d::Sprite> sprite_; // 精灵
|
extra2d::Ptr<extra2d::Sprite> sprite_; // 精灵
|
||||||
std::vector<extra2d::Ptr<extra2d::SpriteFrame>> frames_; // 动画帧
|
std::vector<extra2d::Ptr<extra2d::SpriteFrame>> frames_; // 动画帧
|
||||||
int currentFrame_ = 0; // 当前帧索引
|
int currentFrame_ = 0; // 当前帧索引
|
||||||
float frameTimer_ = 0.0f; // 帧计时器
|
float frameTimer_ = 0.0f; // 帧计时器
|
||||||
float frameInterval_ = 0.1f; // 帧间隔(秒)
|
float frameInterval_ = 0.1f; // 帧间隔(秒)
|
||||||
float idleTimer_ = 0.0f; // 闲置动画计时器
|
float idleTimer_ = 0.0f; // 闲置动画计时器
|
||||||
float idleOffset_ = 0.0f; // 闲置偏移量
|
float idleOffset_ = 0.0f; // 闲置偏移量
|
||||||
|
|
||||||
// 物理常量
|
// 物理常量
|
||||||
static constexpr float gravity = 1440.0f; // 重力加速度
|
static constexpr float gravity = 1440.0f; // 重力加速度
|
||||||
static constexpr float jumpSpeed = 432.0f; // 跳跃初速度
|
static constexpr float jumpSpeed = 432.0f; // 跳跃初速度
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace flappybird
|
} // namespace flappybird
|
||||||
|
|
|
||||||
|
|
@ -3,111 +3,105 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#include "Pipe.h"
|
#include "Pipe.h"
|
||||||
#include "ResLoader.h"
|
|
||||||
#include "BaseScene.h"
|
#include "BaseScene.h"
|
||||||
|
#include "ResLoader.h"
|
||||||
|
|
||||||
|
|
||||||
namespace flappybird {
|
namespace flappybird {
|
||||||
|
|
||||||
Pipe::Pipe() {
|
Pipe::Pipe() {
|
||||||
scored = false;
|
scored = false;
|
||||||
// 注意:不要在构造函数中创建子节点
|
// 注意:不要在构造函数中创建子节点
|
||||||
// 因为此时 weak_from_this() 还不能使用
|
// 因为此时 weak_from_this() 还不能使用
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pipe::onEnter() {
|
void Pipe::onEnter() {
|
||||||
Node::onEnter();
|
Node::onEnter();
|
||||||
|
|
||||||
// 在 onEnter 中创建子节点,此时 weak_from_this() 可用
|
|
||||||
if (!topPipe_ && !bottomPipe_) {
|
|
||||||
// 使用游戏逻辑高度
|
|
||||||
float screenHeight = GAME_HEIGHT;
|
|
||||||
|
|
||||||
// 获取地面高度
|
// 在 onEnter 中创建子节点,此时 weak_from_this() 可用
|
||||||
auto landFrame = ResLoader::getKeyFrame("land");
|
if (!topPipe_ && !bottomPipe_) {
|
||||||
float landHeight = landFrame ? landFrame->getRect().size.height : 112.0f;
|
// 使用游戏逻辑高度
|
||||||
|
float screenHeight = GAME_HEIGHT;
|
||||||
|
|
||||||
// 随机生成水管高度
|
// 获取地面高度
|
||||||
// 范围:与屏幕顶部最小距离不小于 100 像素
|
auto landFrame = ResLoader::getKeyFrame("land");
|
||||||
// 与屏幕底部最小距离不小于地面上方 100 像素
|
float landHeight = landFrame ? landFrame->getRect().size.height : 112.0f;
|
||||||
float minHeight = 100.0f;
|
|
||||||
float maxHeight = screenHeight - landHeight - 100.0f - gapHeight_;
|
|
||||||
float height = static_cast<float>(extra2d::randomInt(static_cast<int>(minHeight), static_cast<int>(maxHeight)));
|
|
||||||
|
|
||||||
// 创建上水管
|
// 随机生成水管高度
|
||||||
auto topFrame = ResLoader::getKeyFrame("pipe_above");
|
// 范围:与屏幕顶部最小距离不小于 100 像素
|
||||||
if (topFrame) {
|
// 与屏幕底部最小距离不小于地面上方 100 像素
|
||||||
topPipe_ = extra2d::Sprite::create(topFrame->getTexture(), topFrame->getRect());
|
float minHeight = 100.0f;
|
||||||
topPipe_->setAnchor(extra2d::Vec2(0.5f, 1.0f)); // 锚点设在底部中心
|
float maxHeight = screenHeight - landHeight - 100.0f - gapHeight_;
|
||||||
topPipe_->setPosition(extra2d::Vec2(0.0f, height - gapHeight_ / 2.0f));
|
float height = static_cast<float>(extra2d::randomInt(
|
||||||
addChild(topPipe_);
|
static_cast<int>(minHeight), static_cast<int>(maxHeight)));
|
||||||
}
|
|
||||||
|
|
||||||
// 创建下水管
|
// 创建上水管
|
||||||
auto bottomFrame = ResLoader::getKeyFrame("pipe_below");
|
auto topFrame = ResLoader::getKeyFrame("pipe_above");
|
||||||
if (bottomFrame) {
|
if (topFrame) {
|
||||||
bottomPipe_ = extra2d::Sprite::create(bottomFrame->getTexture(), bottomFrame->getRect());
|
topPipe_ =
|
||||||
bottomPipe_->setAnchor(extra2d::Vec2(0.5f, 0.0f)); // 锚点设在顶部中心
|
extra2d::Sprite::create(topFrame->getTexture(), topFrame->getRect());
|
||||||
bottomPipe_->setPosition(extra2d::Vec2(0.0f, height + gapHeight_ / 2.0f));
|
topPipe_->setAnchor(extra2d::Vec2(0.5f, 1.0f)); // 锚点设在底部中心
|
||||||
addChild(bottomPipe_);
|
topPipe_->setPosition(extra2d::Vec2(0.0f, height - gapHeight_ / 2.0f));
|
||||||
}
|
addChild(topPipe_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建下水管
|
||||||
|
auto bottomFrame = ResLoader::getKeyFrame("pipe_below");
|
||||||
|
if (bottomFrame) {
|
||||||
|
bottomPipe_ = extra2d::Sprite::create(bottomFrame->getTexture(),
|
||||||
|
bottomFrame->getRect());
|
||||||
|
bottomPipe_->setAnchor(extra2d::Vec2(0.5f, 0.0f)); // 锚点设在顶部中心
|
||||||
|
bottomPipe_->setPosition(extra2d::Vec2(0.0f, height + gapHeight_ / 2.0f));
|
||||||
|
addChild(bottomPipe_);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pipe::~Pipe() = default;
|
Pipe::~Pipe() = default;
|
||||||
|
|
||||||
extra2d::Rect Pipe::getBoundingBox() const {
|
extra2d::Rect Pipe::boundingBox() const {
|
||||||
// 返回整个水管的边界框(包含上下两根)
|
// 返回整个水管的边界框(包含上下两根)
|
||||||
extra2d::Vec2 position = pos();
|
extra2d::Vec2 position = pos();
|
||||||
|
|
||||||
// 水管宽度约为 52
|
// 水管宽度约为 52
|
||||||
float pipeWidth = 52.0f;
|
float pipeWidth = 52.0f;
|
||||||
float halfWidth = pipeWidth / 2.0f;
|
float halfWidth = pipeWidth / 2.0f;
|
||||||
|
|
||||||
// 使用游戏逻辑高度
|
// 使用游戏逻辑高度
|
||||||
float screenHeight = GAME_HEIGHT;
|
float screenHeight = GAME_HEIGHT;
|
||||||
|
|
||||||
return extra2d::Rect(
|
return extra2d::Rect(position.x - halfWidth, 0.0f, pipeWidth, screenHeight);
|
||||||
position.x - halfWidth,
|
|
||||||
0.0f,
|
|
||||||
pipeWidth,
|
|
||||||
screenHeight
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extra2d::Rect Pipe::getTopPipeBox() const {
|
extra2d::Rect Pipe::getTopPipeBox() const {
|
||||||
if (!topPipe_) return extra2d::Rect();
|
if (!topPipe_)
|
||||||
|
return extra2d::Rect();
|
||||||
extra2d::Vec2 position = pos();
|
|
||||||
extra2d::Vec2 topPos = topPipe_->pos();
|
extra2d::Vec2 position = pos();
|
||||||
|
extra2d::Vec2 topPos = topPipe_->pos();
|
||||||
// 上水管尺寸
|
|
||||||
float pipeWidth = 52.0f;
|
// 上水管尺寸
|
||||||
float pipeHeight = 320.0f;
|
float pipeWidth = 52.0f;
|
||||||
|
float pipeHeight = 320.0f;
|
||||||
return extra2d::Rect(
|
|
||||||
position.x - pipeWidth / 2.0f,
|
return extra2d::Rect(position.x - pipeWidth / 2.0f,
|
||||||
position.y + topPos.y - pipeHeight,
|
position.y + topPos.y - pipeHeight, pipeWidth,
|
||||||
pipeWidth,
|
pipeHeight);
|
||||||
pipeHeight
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extra2d::Rect Pipe::getBottomPipeBox() const {
|
extra2d::Rect Pipe::getBottomPipeBox() const {
|
||||||
if (!bottomPipe_) return extra2d::Rect();
|
if (!bottomPipe_)
|
||||||
|
return extra2d::Rect();
|
||||||
extra2d::Vec2 position = pos();
|
|
||||||
extra2d::Vec2 bottomPos = bottomPipe_->pos();
|
extra2d::Vec2 position = pos();
|
||||||
|
extra2d::Vec2 bottomPos = bottomPipe_->pos();
|
||||||
// 下水管尺寸
|
|
||||||
float pipeWidth = 52.0f;
|
// 下水管尺寸
|
||||||
float pipeHeight = 320.0f;
|
float pipeWidth = 52.0f;
|
||||||
|
float pipeHeight = 320.0f;
|
||||||
return extra2d::Rect(
|
|
||||||
position.x - pipeWidth / 2.0f,
|
return extra2d::Rect(position.x - pipeWidth / 2.0f, position.y + bottomPos.y,
|
||||||
position.y + bottomPos.y,
|
pipeWidth, pipeHeight);
|
||||||
pipeWidth,
|
|
||||||
pipeHeight
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace flappybird
|
} // namespace flappybird
|
||||||
|
|
|
||||||
|
|
@ -15,45 +15,45 @@ namespace flappybird {
|
||||||
*/
|
*/
|
||||||
class Pipe : public extra2d::Node {
|
class Pipe : public extra2d::Node {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief 构造函数
|
* @brief 构造函数
|
||||||
*/
|
*/
|
||||||
Pipe();
|
Pipe();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 析构函数
|
* @brief 析构函数
|
||||||
*/
|
*/
|
||||||
~Pipe();
|
~Pipe();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 进入场景时调用
|
* @brief 进入场景时调用
|
||||||
*/
|
*/
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取边界框(用于碰撞检测)
|
* @brief 获取边界框(用于碰撞检测)
|
||||||
* @return 边界框
|
* @return 边界框
|
||||||
*/
|
*/
|
||||||
extra2d::Rect getBoundingBox() const override;
|
extra2d::Rect boundingBox() const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取上水管边界框
|
* @brief 获取上水管边界框
|
||||||
* @return 边界框
|
* @return 边界框
|
||||||
*/
|
*/
|
||||||
extra2d::Rect getTopPipeBox() const;
|
extra2d::Rect getTopPipeBox() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取下水管边界框
|
* @brief 获取下水管边界框
|
||||||
* @return 边界框
|
* @return 边界框
|
||||||
*/
|
*/
|
||||||
extra2d::Rect getBottomPipeBox() const;
|
extra2d::Rect getBottomPipeBox() const;
|
||||||
|
|
||||||
bool scored = false; // 是否已计分
|
bool scored = false; // 是否已计分
|
||||||
|
|
||||||
private:
|
private:
|
||||||
extra2d::Ptr<extra2d::Sprite> topPipe_; // 上水管
|
extra2d::Ptr<extra2d::Sprite> topPipe_; // 上水管
|
||||||
extra2d::Ptr<extra2d::Sprite> bottomPipe_; // 下水管
|
extra2d::Ptr<extra2d::Sprite> bottomPipe_; // 下水管
|
||||||
float gapHeight_ = 120.0f; // 间隙高度
|
float gapHeight_ = 120.0f; // 间隙高度
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace flappybird
|
} // namespace flappybird
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ public:
|
||||||
bool isColliding() const { return isColliding_; }
|
bool isColliding() const { return isColliding_; }
|
||||||
int getId() const { return id_; }
|
int getId() const { return id_; }
|
||||||
|
|
||||||
// 必须实现 getBoundingBox() 才能参与空间索引碰撞检测
|
// 必须实现 boundingBox() 才能参与空间索引碰撞检测
|
||||||
Rect getBoundingBox() const override {
|
Rect boundingBox() const override {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
return Rect(position.x - size_ / 2, position.y - size_ / 2, size_, size_);
|
return Rect(position.x - size_ / 2, position.y - size_ / 2, size_, size_);
|
||||||
}
|
}
|
||||||
|
|
@ -65,14 +65,16 @@ public:
|
||||||
|
|
||||||
// 碰撞时变红色
|
// 碰撞时变红色
|
||||||
Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.9f) : color_;
|
Color fillColor = isColliding_ ? Color(1.0f, 0.2f, 0.2f, 0.9f) : color_;
|
||||||
renderer.fillRect(Rect(position.x - size_ / 2, position.y - size_ / 2, size_, size_),
|
renderer.fillRect(
|
||||||
fillColor);
|
Rect(position.x - size_ / 2, position.y - size_ / 2, size_, size_),
|
||||||
|
fillColor);
|
||||||
|
|
||||||
// 绘制边框
|
// 绘制边框
|
||||||
Color borderColor = isColliding_ ? Color(1.0f, 0.0f, 0.0f, 1.0f)
|
Color borderColor = isColliding_ ? Color(1.0f, 0.0f, 0.0f, 1.0f)
|
||||||
: Color(0.3f, 0.3f, 0.3f, 0.5f);
|
: Color(0.3f, 0.3f, 0.3f, 0.5f);
|
||||||
renderer.drawRect(Rect(position.x - size_ / 2, position.y - size_ / 2, size_, size_),
|
renderer.drawRect(
|
||||||
borderColor, 1.0f);
|
Rect(position.x - size_ / 2, position.y - size_ / 2, size_, size_),
|
||||||
|
borderColor, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public:
|
||||||
/// 获取遮罩尺寸
|
/// 获取遮罩尺寸
|
||||||
int getWidth() const { return width_; }
|
int getWidth() const { return width_; }
|
||||||
int getHeight() const { return height_; }
|
int getHeight() const { return height_; }
|
||||||
Size getSize() const {
|
Size size() const {
|
||||||
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <graphics/texture.h>
|
|
||||||
#include <graphics/alpha_mask.h>
|
#include <graphics/alpha_mask.h>
|
||||||
|
#include <graphics/texture.h>
|
||||||
|
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
|
@ -15,58 +16,64 @@ namespace extra2d {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class GLTexture : public Texture {
|
class GLTexture : public Texture {
|
||||||
public:
|
public:
|
||||||
GLTexture(int width, int height, const uint8_t* pixels, int channels);
|
GLTexture(int width, int height, const uint8_t *pixels, int channels);
|
||||||
GLTexture(const std::string& filepath);
|
GLTexture(const std::string &filepath);
|
||||||
~GLTexture();
|
~GLTexture();
|
||||||
|
|
||||||
// Texture 接口实现
|
// Texture 接口实现
|
||||||
int getWidth() const override { return width_; }
|
int getWidth() const override { return width_; }
|
||||||
int getHeight() const override { return height_; }
|
int getHeight() const override { return height_; }
|
||||||
Size getSize() const override { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
Size size() const override {
|
||||||
int getChannels() const override { return channels_; }
|
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||||
PixelFormat getFormat() const override;
|
}
|
||||||
void* getNativeHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(textureID_)); }
|
int getChannels() const override { return channels_; }
|
||||||
bool isValid() const override { return textureID_ != 0; }
|
PixelFormat getFormat() const override;
|
||||||
void setFilter(bool linear) override;
|
void *getNativeHandle() const override {
|
||||||
void setWrap(bool repeat) override;
|
return reinterpret_cast<void *>(static_cast<uintptr_t>(textureID_));
|
||||||
|
}
|
||||||
|
bool isValid() const override { return textureID_ != 0; }
|
||||||
|
void setFilter(bool linear) override;
|
||||||
|
void setWrap(bool repeat) override;
|
||||||
|
|
||||||
// 从参数创建纹理的工厂方法
|
// 从参数创建纹理的工厂方法
|
||||||
static Ptr<Texture> create(int width, int height, PixelFormat format);
|
static Ptr<Texture> create(int width, int height, PixelFormat format);
|
||||||
|
|
||||||
// 加载压缩纹理(KTX/DDS 格式)
|
// 加载压缩纹理(KTX/DDS 格式)
|
||||||
bool loadCompressed(const std::string& filepath);
|
bool loadCompressed(const std::string &filepath);
|
||||||
|
|
||||||
// OpenGL 特定
|
// OpenGL 特定
|
||||||
GLuint getTextureID() const { return textureID_; }
|
GLuint getTextureID() const { return textureID_; }
|
||||||
void bind(unsigned int slot = 0) const;
|
void bind(unsigned int slot = 0) const;
|
||||||
void unbind() const;
|
void unbind() const;
|
||||||
|
|
||||||
// 获取纹理数据大小(字节),用于 VRAM 跟踪
|
// 获取纹理数据大小(字节),用于 VRAM 跟踪
|
||||||
size_t getDataSize() const { return dataSize_; }
|
size_t getDataSize() const { return dataSize_; }
|
||||||
|
|
||||||
// Alpha 遮罩
|
// Alpha 遮罩
|
||||||
bool hasAlphaMask() const { return alphaMask_ != nullptr && alphaMask_->isValid(); }
|
bool hasAlphaMask() const {
|
||||||
const AlphaMask* getAlphaMask() const { return alphaMask_.get(); }
|
return alphaMask_ != nullptr && alphaMask_->isValid();
|
||||||
void generateAlphaMask(); // 从当前纹理数据生成遮罩
|
}
|
||||||
|
const AlphaMask *getAlphaMask() const { return alphaMask_.get(); }
|
||||||
|
void generateAlphaMask(); // 从当前纹理数据生成遮罩
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLuint textureID_;
|
GLuint textureID_;
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
int channels_;
|
int channels_;
|
||||||
PixelFormat format_;
|
PixelFormat format_;
|
||||||
size_t dataSize_;
|
size_t dataSize_;
|
||||||
|
|
||||||
// 原始像素数据(用于生成遮罩)
|
// 原始像素数据(用于生成遮罩)
|
||||||
std::vector<uint8_t> pixelData_;
|
std::vector<uint8_t> pixelData_;
|
||||||
std::unique_ptr<AlphaMask> alphaMask_;
|
std::unique_ptr<AlphaMask> alphaMask_;
|
||||||
|
|
||||||
void createTexture(const uint8_t* pixels);
|
void createTexture(const uint8_t *pixels);
|
||||||
|
|
||||||
// KTX 文件加载
|
// KTX 文件加载
|
||||||
bool loadKTX(const std::string& filepath);
|
bool loadKTX(const std::string &filepath);
|
||||||
// DDS 文件加载
|
// DDS 文件加载
|
||||||
bool loadDDS(const std::string& filepath);
|
bool loadDDS(const std::string &filepath);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ public:
|
||||||
|
|
||||||
int getWidth() const { return width_; }
|
int getWidth() const { return width_; }
|
||||||
int getHeight() const { return height_; }
|
int getHeight() const { return height_; }
|
||||||
Vec2 getSize() const {
|
Vec2 size() const {
|
||||||
return Vec2(static_cast<float>(width_), static_cast<float>(height_));
|
return Vec2(static_cast<float>(width_), static_cast<float>(height_));
|
||||||
}
|
}
|
||||||
PixelFormat getColorFormat() const { return colorFormat_; }
|
PixelFormat getColorFormat() const { return colorFormat_; }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <core/types.h>
|
|
||||||
#include <core/math_types.h>
|
#include <core/math_types.h>
|
||||||
|
#include <core/types.h>
|
||||||
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -9,25 +10,25 @@ namespace extra2d {
|
||||||
// 像素格式枚举
|
// 像素格式枚举
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
enum class PixelFormat {
|
enum class PixelFormat {
|
||||||
R8, // 单通道灰度
|
R8, // 单通道灰度
|
||||||
RG8, // 双通道
|
RG8, // 双通道
|
||||||
RGB8, // RGB 24位
|
RGB8, // RGB 24位
|
||||||
RGBA8, // RGBA 32位(默认)
|
RGBA8, // RGBA 32位(默认)
|
||||||
RGB16F, // RGB 半精度浮点
|
RGB16F, // RGB 半精度浮点
|
||||||
RGBA16F, // RGBA 半精度浮点
|
RGBA16F, // RGBA 半精度浮点
|
||||||
RGB32F, // RGB 全精度浮点
|
RGB32F, // RGB 全精度浮点
|
||||||
RGBA32F, // RGBA 全精度浮点
|
RGBA32F, // RGBA 全精度浮点
|
||||||
Depth16, // 16位深度
|
Depth16, // 16位深度
|
||||||
Depth24, // 24位深度
|
Depth24, // 24位深度
|
||||||
Depth32F, // 32位浮点深度
|
Depth32F, // 32位浮点深度
|
||||||
Depth24Stencil8, // 24位深度 + 8位模板
|
Depth24Stencil8, // 24位深度 + 8位模板
|
||||||
|
|
||||||
// 压缩纹理格式
|
// 压缩纹理格式
|
||||||
ETC2_RGB8, // ETC2 RGB 压缩
|
ETC2_RGB8, // ETC2 RGB 压缩
|
||||||
ETC2_RGBA8, // ETC2 RGBA 压缩
|
ETC2_RGBA8, // ETC2 RGBA 压缩
|
||||||
ASTC_4x4, // ASTC 4x4 压缩
|
ASTC_4x4, // ASTC 4x4 压缩
|
||||||
ASTC_6x6, // ASTC 6x6 压缩
|
ASTC_6x6, // ASTC 6x6 压缩
|
||||||
ASTC_8x8 // ASTC 8x8 压缩
|
ASTC_8x8 // ASTC 8x8 压缩
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -35,30 +36,30 @@ enum class PixelFormat {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class Texture {
|
class Texture {
|
||||||
public:
|
public:
|
||||||
virtual ~Texture() = default;
|
virtual ~Texture() = default;
|
||||||
|
|
||||||
// 获取尺寸
|
// 获取尺寸
|
||||||
virtual int getWidth() const = 0;
|
virtual int getWidth() const = 0;
|
||||||
virtual int getHeight() const = 0;
|
virtual int getHeight() const = 0;
|
||||||
virtual Size getSize() const = 0;
|
virtual Size size() const = 0;
|
||||||
|
|
||||||
// 获取通道数
|
// 获取通道数
|
||||||
virtual int getChannels() const = 0;
|
virtual int getChannels() const = 0;
|
||||||
|
|
||||||
// 获取像素格式
|
// 获取像素格式
|
||||||
virtual PixelFormat getFormat() const = 0;
|
virtual PixelFormat getFormat() const = 0;
|
||||||
|
|
||||||
// 获取原始句柄(用于底层渲染)
|
// 获取原始句柄(用于底层渲染)
|
||||||
virtual void* getNativeHandle() const = 0;
|
virtual void *getNativeHandle() const = 0;
|
||||||
|
|
||||||
// 是否有效
|
// 是否有效
|
||||||
virtual bool isValid() const = 0;
|
virtual bool isValid() const = 0;
|
||||||
|
|
||||||
// 设置过滤模式
|
// 设置过滤模式
|
||||||
virtual void setFilter(bool linear) = 0;
|
virtual void setFilter(bool linear) = 0;
|
||||||
|
|
||||||
// 设置环绕模式
|
// 设置环绕模式
|
||||||
virtual void setWrap(bool repeat) = 0;
|
virtual void setWrap(bool repeat) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <core/types.h>
|
|
||||||
#include <core/string.h>
|
|
||||||
#include <core/math_types.h>
|
#include <core/math_types.h>
|
||||||
|
#include <core/string.h>
|
||||||
|
#include <core/types.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -17,32 +18,33 @@ class Input;
|
||||||
// 窗口配置
|
// 窗口配置
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
struct WindowConfig {
|
struct WindowConfig {
|
||||||
std::string title = "Extra2D Application";
|
std::string title = "Extra2D Application";
|
||||||
int width = 1280;
|
int width = 1280;
|
||||||
int height = 720;
|
int height = 720;
|
||||||
bool fullscreen = true;
|
bool fullscreen = true;
|
||||||
bool resizable = false;
|
bool resizable = false;
|
||||||
bool vsync = true;
|
bool vsync = true;
|
||||||
int msaaSamples = 0;
|
int msaaSamples = 0;
|
||||||
bool centerWindow = true;
|
bool centerWindow = true;
|
||||||
bool enableCursors = true;
|
bool enableCursors = true;
|
||||||
bool enableDpiScale = true;
|
bool enableDpiScale = true;
|
||||||
bool fullscreenDesktop = true; // true: SDL_WINDOW_FULLSCREEN_DESKTOP, false: SDL_WINDOW_FULLSCREEN
|
bool fullscreenDesktop =
|
||||||
|
true; // true: SDL_WINDOW_FULLSCREEN_DESKTOP, false: SDL_WINDOW_FULLSCREEN
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 鼠标光标形状枚举
|
// 鼠标光标形状枚举
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
enum class CursorShape {
|
enum class CursorShape {
|
||||||
Arrow,
|
Arrow,
|
||||||
IBeam,
|
IBeam,
|
||||||
Crosshair,
|
Crosshair,
|
||||||
Hand,
|
Hand,
|
||||||
HResize,
|
HResize,
|
||||||
VResize,
|
VResize,
|
||||||
ResizeAll,
|
ResizeAll,
|
||||||
ResizeNWSE,
|
ResizeNWSE,
|
||||||
ResizeNESW
|
ResizeNESW
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -51,103 +53,107 @@ enum class CursorShape {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class Window {
|
class Window {
|
||||||
public:
|
public:
|
||||||
Window();
|
Window();
|
||||||
~Window();
|
~Window();
|
||||||
|
|
||||||
// 创建窗口
|
// 创建窗口
|
||||||
bool create(const WindowConfig& config);
|
bool create(const WindowConfig &config);
|
||||||
void destroy();
|
void destroy();
|
||||||
|
|
||||||
// 窗口操作
|
// 窗口操作
|
||||||
void pollEvents();
|
void pollEvents();
|
||||||
void swapBuffers();
|
void swapBuffers();
|
||||||
bool shouldClose() const;
|
bool shouldClose() const;
|
||||||
void setShouldClose(bool close);
|
void setShouldClose(bool close);
|
||||||
|
|
||||||
// 窗口属性
|
// 窗口属性
|
||||||
void setTitle(const std::string& title);
|
void setTitle(const std::string &title);
|
||||||
void setSize(int width, int height);
|
void setSize(int width, int height);
|
||||||
void setPosition(int x, int y);
|
void setPosition(int x, int y);
|
||||||
void setFullscreen(bool fullscreen);
|
void setFullscreen(bool fullscreen);
|
||||||
void setVSync(bool enabled);
|
void setVSync(bool enabled);
|
||||||
void setResizable(bool resizable);
|
void setResizable(bool resizable);
|
||||||
|
|
||||||
// 获取窗口属性
|
// 获取窗口属性
|
||||||
int getWidth() const { return width_; }
|
int getWidth() const { return width_; }
|
||||||
int getHeight() const { return height_; }
|
int getHeight() const { return height_; }
|
||||||
Size getSize() const { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
|
Size size() const {
|
||||||
Vec2 pos() const;
|
return Size(static_cast<float>(width_), static_cast<float>(height_));
|
||||||
bool isFullscreen() const { return fullscreen_; }
|
}
|
||||||
bool isVSync() const { return vsync_; }
|
Vec2 pos() const;
|
||||||
|
bool isFullscreen() const { return fullscreen_; }
|
||||||
|
bool isVSync() const { return vsync_; }
|
||||||
|
|
||||||
// DPI 缩放 (PC 端自动检测,Switch 固定 1.0)
|
// DPI 缩放 (PC 端自动检测,Switch 固定 1.0)
|
||||||
float getContentScaleX() const;
|
float getContentScaleX() const;
|
||||||
float getContentScaleY() const;
|
float getContentScaleY() const;
|
||||||
Vec2 getContentScale() const;
|
Vec2 getContentScale() const;
|
||||||
|
|
||||||
// 窗口状态
|
// 窗口状态
|
||||||
bool isFocused() const { return focused_; }
|
bool isFocused() const { return focused_; }
|
||||||
bool isMinimized() const;
|
bool isMinimized() const;
|
||||||
bool isMaximized() const;
|
bool isMaximized() const;
|
||||||
|
|
||||||
// 获取 SDL2 窗口和 GL 上下文
|
// 获取 SDL2 窗口和 GL 上下文
|
||||||
SDL_Window* getSDLWindow() const { return sdlWindow_; }
|
SDL_Window *getSDLWindow() const { return sdlWindow_; }
|
||||||
SDL_GLContext getGLContext() const { return glContext_; }
|
SDL_GLContext getGLContext() const { return glContext_; }
|
||||||
|
|
||||||
// 设置/获取用户数据
|
// 设置/获取用户数据
|
||||||
void setUserData(void* data) { userData_ = data; }
|
void setUserData(void *data) { userData_ = data; }
|
||||||
void* getUserData() const { return userData_; }
|
void *getUserData() const { return userData_; }
|
||||||
|
|
||||||
// 事件队列
|
// 事件队列
|
||||||
void setEventQueue(EventQueue* queue) { eventQueue_ = queue; }
|
void setEventQueue(EventQueue *queue) { eventQueue_ = queue; }
|
||||||
EventQueue* getEventQueue() const { return eventQueue_; }
|
EventQueue *getEventQueue() const { return eventQueue_; }
|
||||||
|
|
||||||
// 获取输入管理器
|
// 获取输入管理器
|
||||||
Input* getInput() const { return input_.get(); }
|
Input *getInput() const { return input_.get(); }
|
||||||
|
|
||||||
// 光标操作 (PC 端有效,Switch 上为空操作)
|
// 光标操作 (PC 端有效,Switch 上为空操作)
|
||||||
void setCursor(CursorShape shape);
|
void setCursor(CursorShape shape);
|
||||||
void resetCursor();
|
void resetCursor();
|
||||||
void setMouseVisible(bool visible);
|
void setMouseVisible(bool visible);
|
||||||
|
|
||||||
// 窗口回调
|
// 窗口回调
|
||||||
using ResizeCallback = std::function<void(int width, int height)>;
|
using ResizeCallback = std::function<void(int width, int height)>;
|
||||||
using FocusCallback = std::function<void(bool focused)>;
|
using FocusCallback = std::function<void(bool focused)>;
|
||||||
using CloseCallback = std::function<void()>;
|
using CloseCallback = std::function<void()>;
|
||||||
|
|
||||||
void setResizeCallback(ResizeCallback callback) { resizeCallback_ = callback; }
|
void setResizeCallback(ResizeCallback callback) {
|
||||||
void setFocusCallback(FocusCallback callback) { focusCallback_ = callback; }
|
resizeCallback_ = callback;
|
||||||
void setCloseCallback(CloseCallback callback) { closeCallback_ = callback; }
|
}
|
||||||
|
void setFocusCallback(FocusCallback callback) { focusCallback_ = callback; }
|
||||||
|
void setCloseCallback(CloseCallback callback) { closeCallback_ = callback; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// SDL2 状态
|
// SDL2 状态
|
||||||
SDL_Window* sdlWindow_;
|
SDL_Window *sdlWindow_;
|
||||||
SDL_GLContext glContext_;
|
SDL_GLContext glContext_;
|
||||||
SDL_Cursor* sdlCursors_[9]; // 光标缓存
|
SDL_Cursor *sdlCursors_[9]; // 光标缓存
|
||||||
SDL_Cursor* currentCursor_;
|
SDL_Cursor *currentCursor_;
|
||||||
|
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
bool vsync_;
|
bool vsync_;
|
||||||
bool shouldClose_;
|
bool shouldClose_;
|
||||||
bool fullscreen_;
|
bool fullscreen_;
|
||||||
bool focused_;
|
bool focused_;
|
||||||
float contentScaleX_;
|
float contentScaleX_;
|
||||||
float contentScaleY_;
|
float contentScaleY_;
|
||||||
bool enableDpiScale_;
|
bool enableDpiScale_;
|
||||||
void* userData_;
|
void *userData_;
|
||||||
EventQueue* eventQueue_;
|
EventQueue *eventQueue_;
|
||||||
UniquePtr<Input> input_;
|
UniquePtr<Input> input_;
|
||||||
|
|
||||||
ResizeCallback resizeCallback_;
|
ResizeCallback resizeCallback_;
|
||||||
FocusCallback focusCallback_;
|
FocusCallback focusCallback_;
|
||||||
CloseCallback closeCallback_;
|
CloseCallback closeCallback_;
|
||||||
|
|
||||||
bool initSDL(const WindowConfig& config);
|
bool initSDL(const WindowConfig &config);
|
||||||
void deinitSDL();
|
void deinitSDL();
|
||||||
void initCursors();
|
void initCursors();
|
||||||
void deinitCursors();
|
void deinitCursors();
|
||||||
void updateContentScale();
|
void updateContentScale();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,13 @@
|
||||||
#include <core/math_types.h>
|
#include <core/math_types.h>
|
||||||
#include <core/types.h>
|
#include <core/types.h>
|
||||||
#include <event/event_dispatcher.h>
|
#include <event/event_dispatcher.h>
|
||||||
#include <graphics/render_backend.h>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <graphics/render_backend.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
// 前向声明
|
// 前向声明
|
||||||
|
|
@ -82,7 +83,7 @@ public:
|
||||||
* @brief 设置颜色
|
* @brief 设置颜色
|
||||||
* @param color RGB颜色
|
* @param color RGB颜色
|
||||||
*/
|
*/
|
||||||
void setColor(const Color3B& color);
|
void setColor(const Color3B &color);
|
||||||
Color3B getColor() const { return color_; }
|
Color3B getColor() const { return color_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -148,7 +149,7 @@ public:
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 边界框(用于空间索引)
|
// 边界框(用于空间索引)
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
virtual Rect getBoundingBox() const;
|
virtual Rect boundingBox() const;
|
||||||
|
|
||||||
// 是否需要参与空间索引(默认 true)
|
// 是否需要参与空间索引(默认 true)
|
||||||
void setSpatialIndexed(bool indexed) { spatialIndexed_ = indexed; }
|
void setSpatialIndexed(bool indexed) { spatialIndexed_ = indexed; }
|
||||||
|
|
@ -165,17 +166,17 @@ public:
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Tween 动画系统
|
// Tween 动画系统
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建 Tween 动画
|
* @brief 创建 Tween 动画
|
||||||
* @return Tween 对象引用,支持链式调用
|
* @return Tween 对象引用,支持链式调用
|
||||||
*/
|
*/
|
||||||
Tween& tween();
|
Tween &tween();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建具名 Tween 动画
|
* @brief 创建具名 Tween 动画
|
||||||
*/
|
*/
|
||||||
Tween& tween(const std::string &name);
|
Tween &tween(const std::string &name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 移动到目标位置
|
* @brief 移动到目标位置
|
||||||
|
|
@ -216,12 +217,14 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief 淡入(透明度从当前到1)
|
* @brief 淡入(透明度从当前到1)
|
||||||
*/
|
*/
|
||||||
Tween &fadeIn(float duration, TweenEasing easing = static_cast<TweenEasing>(0));
|
Tween &fadeIn(float duration,
|
||||||
|
TweenEasing easing = static_cast<TweenEasing>(0));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 淡出(透明度从当前到0)
|
* @brief 淡出(透明度从当前到0)
|
||||||
*/
|
*/
|
||||||
Tween &fadeOut(float duration, TweenEasing easing = static_cast<TweenEasing>(0));
|
Tween &fadeOut(float duration,
|
||||||
|
TweenEasing easing = static_cast<TweenEasing>(0));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 透明度变化到目标值
|
* @brief 透明度变化到目标值
|
||||||
|
|
@ -270,62 +273,62 @@ protected:
|
||||||
private:
|
private:
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// 成员变量按类型大小降序排列,减少内存对齐填充
|
// 成员变量按类型大小降序排列,减少内存对齐填充
|
||||||
// 64位系统对齐:std::string(32) > glm::mat4(64) > std::vector(24) >
|
// 64位系统对齐:std::string(32) > glm::mat4(64) > std::vector(24) >
|
||||||
// double(8) > float(4) > int(4) > bool(1)
|
// double(8) > float(4) > int(4) > bool(1)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
// 1. 大块内存(64字节)
|
// 1. 大块内存(64字节)
|
||||||
mutable glm::mat4 localTransform_; // 64 bytes
|
mutable glm::mat4 localTransform_; // 64 bytes
|
||||||
mutable glm::mat4 worldTransform_; // 64 bytes
|
mutable glm::mat4 worldTransform_; // 64 bytes
|
||||||
|
|
||||||
// 2. 字符串和容器(24-32字节)
|
// 2. 字符串和容器(24-32字节)
|
||||||
std::string name_; // 32 bytes
|
std::string name_; // 32 bytes
|
||||||
std::vector<Ptr<Node>> children_; // 24 bytes
|
std::vector<Ptr<Node>> children_; // 24 bytes
|
||||||
|
|
||||||
// 3. 子节点索引(加速查找)
|
// 3. 子节点索引(加速查找)
|
||||||
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
|
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
|
||||||
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
|
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
|
||||||
|
|
||||||
// 4. 事件分发器
|
// 4. 事件分发器
|
||||||
EventDispatcher eventDispatcher_; // 大小取决于实现
|
EventDispatcher eventDispatcher_; // 大小取决于实现
|
||||||
|
|
||||||
// 5. 父节点引用
|
// 5. 父节点引用
|
||||||
WeakPtr<Node> parent_; // 16 bytes
|
WeakPtr<Node> parent_; // 16 bytes
|
||||||
|
|
||||||
// 7. 变换属性(按访问频率分组)
|
// 7. 变换属性(按访问频率分组)
|
||||||
Vec2 position_ = Vec2::Zero(); // 8 bytes
|
Vec2 position_ = Vec2::Zero(); // 8 bytes
|
||||||
Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes
|
Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes
|
||||||
Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes
|
Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes
|
||||||
Vec2 skew_ = Vec2::Zero(); // 8 bytes
|
Vec2 skew_ = Vec2::Zero(); // 8 bytes
|
||||||
|
|
||||||
// 8. 边界框(用于空间索引)
|
// 8. 边界框(用于空间索引)
|
||||||
Rect lastSpatialBounds_; // 16 bytes
|
Rect lastSpatialBounds_; // 16 bytes
|
||||||
|
|
||||||
// 9. 浮点属性
|
// 9. 浮点属性
|
||||||
float rotation_ = 0.0f; // 4 bytes
|
float rotation_ = 0.0f; // 4 bytes
|
||||||
float opacity_ = 1.0f; // 4 bytes
|
float opacity_ = 1.0f; // 4 bytes
|
||||||
|
|
||||||
// 10. 颜色属性
|
// 10. 颜色属性
|
||||||
Color3B color_ = Color3B(255, 255, 255); // 3 bytes
|
Color3B color_ = Color3B(255, 255, 255); // 3 bytes
|
||||||
|
|
||||||
// 11. 整数属性
|
// 11. 整数属性
|
||||||
int zOrder_ = 0; // 4 bytes
|
int zOrder_ = 0; // 4 bytes
|
||||||
int tag_ = -1; // 4 bytes
|
int tag_ = -1; // 4 bytes
|
||||||
|
|
||||||
// 12. 布尔属性
|
// 12. 布尔属性
|
||||||
bool flipX_ = false; // 1 byte
|
bool flipX_ = false; // 1 byte
|
||||||
bool flipY_ = false; // 1 byte
|
bool flipY_ = false; // 1 byte
|
||||||
|
|
||||||
// 11. 场景指针
|
// 11. 场景指针
|
||||||
Scene *scene_ = nullptr; // 8 bytes
|
Scene *scene_ = nullptr; // 8 bytes
|
||||||
|
|
||||||
// 12. 布尔标志(打包在一起)
|
// 12. 布尔标志(打包在一起)
|
||||||
mutable bool transformDirty_ = true; // 1 byte
|
mutable bool transformDirty_ = true; // 1 byte
|
||||||
mutable bool worldTransformDirty_ = true; // 1 byte
|
mutable bool worldTransformDirty_ = true; // 1 byte
|
||||||
bool childrenOrderDirty_ = false; // 1 byte
|
bool childrenOrderDirty_ = false; // 1 byte
|
||||||
bool visible_ = true; // 1 byte
|
bool visible_ = true; // 1 byte
|
||||||
bool running_ = false; // 1 byte
|
bool running_ = false; // 1 byte
|
||||||
bool spatialIndexed_ = true; // 1 byte
|
bool spatialIndexed_ = true; // 1 byte
|
||||||
|
|
||||||
// 13. Tween 动画列表
|
// 13. Tween 动画列表
|
||||||
std::vector<std::shared_ptr<Tween>> tweens_;
|
std::vector<std::shared_ptr<Tween>> tweens_;
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ public:
|
||||||
void addPoint(const Vec2 &point);
|
void addPoint(const Vec2 &point);
|
||||||
void clearPoints();
|
void clearPoints();
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
Rect boundingBox() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onDraw(RenderBackend &renderer) override;
|
void onDraw(RenderBackend &renderer) override;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ public:
|
||||||
static Ptr<Sprite> create(Ptr<Texture> texture);
|
static Ptr<Sprite> create(Ptr<Texture> texture);
|
||||||
static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect);
|
static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect);
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
Rect boundingBox() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onDraw(RenderBackend &renderer) override;
|
void onDraw(RenderBackend &renderer) override;
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void setStateTextColor(const Color &colorOff, const Color &colorOn);
|
void setStateTextColor(const Color &colorOff, const Color &colorOn);
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
Rect boundingBox() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onDrawWidget(RenderBackend &renderer) override;
|
void onDrawWidget(RenderBackend &renderer) override;
|
||||||
|
|
|
||||||
|
|
@ -11,88 +11,88 @@ namespace extra2d {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class CheckBox : public Widget {
|
class CheckBox : public Widget {
|
||||||
public:
|
public:
|
||||||
CheckBox();
|
CheckBox();
|
||||||
~CheckBox() override = default;
|
~CheckBox() override = default;
|
||||||
|
|
||||||
static Ptr<CheckBox> create();
|
static Ptr<CheckBox> create();
|
||||||
static Ptr<CheckBox> create(const std::string &label);
|
static Ptr<CheckBox> create(const std::string &label);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 选中状态
|
// 选中状态
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setChecked(bool checked);
|
void setChecked(bool checked);
|
||||||
bool isChecked() const { return checked_; }
|
bool isChecked() const { return checked_; }
|
||||||
void toggle();
|
void toggle();
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 标签设置
|
// 标签设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setLabel(const std::string &label);
|
void setLabel(const std::string &label);
|
||||||
const std::string &getLabel() const { return label_; }
|
const std::string &getLabel() const { return label_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 字体设置
|
// 字体设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setFont(Ptr<FontAtlas> font);
|
void setFont(Ptr<FontAtlas> font);
|
||||||
Ptr<FontAtlas> getFont() const { return font_; }
|
Ptr<FontAtlas> getFont() const { return font_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 文字颜色
|
// 文字颜色
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setTextColor(const Color &color);
|
void setTextColor(const Color &color);
|
||||||
Color getTextColor() const { return textColor_; }
|
Color getTextColor() const { return textColor_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 复选框尺寸
|
// 复选框尺寸
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setBoxSize(float size);
|
void setBoxSize(float size);
|
||||||
float getBoxSize() const { return boxSize_; }
|
float getBoxSize() const { return boxSize_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 间距
|
// 间距
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setSpacing(float spacing);
|
void setSpacing(float spacing);
|
||||||
float getSpacing() const { return spacing_; }
|
float getSpacing() const { return spacing_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 颜色设置
|
// 颜色设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setCheckedColor(const Color &color);
|
void setCheckedColor(const Color &color);
|
||||||
Color getCheckedColor() const { return checkedColor_; }
|
Color getCheckedColor() const { return checkedColor_; }
|
||||||
|
|
||||||
void setUncheckedColor(const Color &color);
|
void setUncheckedColor(const Color &color);
|
||||||
Color getUncheckedColor() const { return uncheckedColor_; }
|
Color getUncheckedColor() const { return uncheckedColor_; }
|
||||||
|
|
||||||
void setCheckMarkColor(const Color &color);
|
void setCheckMarkColor(const Color &color);
|
||||||
Color getCheckMarkColor() const { return checkMarkColor_; }
|
Color getCheckMarkColor() const { return checkMarkColor_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 回调设置
|
// 回调设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setOnStateChange(Function<void(bool)> callback);
|
void setOnStateChange(Function<void(bool)> callback);
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
Rect boundingBox() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onDrawWidget(RenderBackend &renderer) override;
|
void onDrawWidget(RenderBackend &renderer) override;
|
||||||
bool onMousePress(const MouseEvent &event) override;
|
bool onMousePress(const MouseEvent &event) override;
|
||||||
bool onMouseRelease(const MouseEvent &event) override;
|
bool onMouseRelease(const MouseEvent &event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool checked_ = false;
|
bool checked_ = false;
|
||||||
std::string label_;
|
std::string label_;
|
||||||
Ptr<FontAtlas> font_;
|
Ptr<FontAtlas> font_;
|
||||||
Color textColor_ = Colors::White;
|
Color textColor_ = Colors::White;
|
||||||
|
|
||||||
float boxSize_ = 20.0f;
|
float boxSize_ = 20.0f;
|
||||||
float spacing_ = 8.0f;
|
float spacing_ = 8.0f;
|
||||||
|
|
||||||
Color checkedColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
Color checkedColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
||||||
Color uncheckedColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
Color uncheckedColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||||
Color checkMarkColor_ = Colors::White;
|
Color checkMarkColor_ = Colors::White;
|
||||||
|
|
||||||
bool pressed_ = false;
|
bool pressed_ = false;
|
||||||
Function<void(bool)> onStateChange_;
|
Function<void(bool)> onStateChange_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -12,135 +12,136 @@ namespace extra2d {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class Label : public Widget {
|
class Label : public Widget {
|
||||||
public:
|
public:
|
||||||
Label();
|
Label();
|
||||||
explicit Label(const std::string &text);
|
explicit Label(const std::string &text);
|
||||||
~Label() override = default;
|
~Label() override = default;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 静态创建方法
|
// 静态创建方法
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
static Ptr<Label> create();
|
static Ptr<Label> create();
|
||||||
static Ptr<Label> create(const std::string &text);
|
static Ptr<Label> create(const std::string &text);
|
||||||
static Ptr<Label> create(const std::string &text, Ptr<FontAtlas> font);
|
static Ptr<Label> create(const std::string &text, Ptr<FontAtlas> font);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 文本内容
|
// 文本内容
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setText(const std::string &text);
|
void setText(const std::string &text);
|
||||||
const std::string &getText() const { return text_; }
|
const std::string &getText() const { return text_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 字体设置
|
// 字体设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setFont(Ptr<FontAtlas> font);
|
void setFont(Ptr<FontAtlas> font);
|
||||||
Ptr<FontAtlas> getFont() const { return font_; }
|
Ptr<FontAtlas> getFont() const { return font_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 文字颜色
|
// 文字颜色
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setTextColor(const Color &color);
|
void setTextColor(const Color &color);
|
||||||
Color getTextColor() const { return textColor_; }
|
Color getTextColor() const { return textColor_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 字体大小
|
// 字体大小
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setFontSize(int size);
|
void setFontSize(int size);
|
||||||
int getFontSize() const { return fontSize_; }
|
int getFontSize() const { return fontSize_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 水平对齐方式
|
// 水平对齐方式
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
enum class HorizontalAlign { Left, Center, Right };
|
enum class HorizontalAlign { Left, Center, Right };
|
||||||
|
|
||||||
void setHorizontalAlign(HorizontalAlign align);
|
|
||||||
HorizontalAlign getHorizontalAlign() const { return hAlign_; }
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
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);
|
enum class VerticalAlign { Top, Middle, Bottom };
|
||||||
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 setVerticalAlign(VerticalAlign align);
|
||||||
// 描边效果
|
VerticalAlign getVerticalAlign() const { return vAlign_; }
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
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);
|
void setShadowEnabled(bool enabled);
|
||||||
bool isMultiLine() const { return multiLine_; }
|
bool isShadowEnabled() const { return shadowEnabled_; }
|
||||||
|
|
||||||
void setLineSpacing(float spacing);
|
|
||||||
float getLineSpacing() const { return lineSpacing_; }
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
void setShadowColor(const Color &color);
|
||||||
// 最大宽度(用于自动换行)
|
Color getShadowColor() const { return shadowColor_; }
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
void setMaxWidth(float maxWidth);
|
|
||||||
float getMaxWidth() const { return maxWidth_; }
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
void setShadowOffset(const Vec2 &offset);
|
||||||
// 尺寸计算
|
Vec2 getShadowOffset() const { return shadowOffset_; }
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
Vec2 getTextSize() const;
|
|
||||||
float getLineHeight() const;
|
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
// ------------------------------------------------------------------------
|
||||||
|
// 描边效果
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
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:
|
protected:
|
||||||
void onDrawWidget(RenderBackend &renderer) override;
|
void onDrawWidget(RenderBackend &renderer) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string text_;
|
std::string text_;
|
||||||
Ptr<FontAtlas> font_;
|
Ptr<FontAtlas> font_;
|
||||||
Color textColor_ = Colors::White;
|
Color textColor_ = Colors::White;
|
||||||
int fontSize_ = 16;
|
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;
|
HorizontalAlign hAlign_ = HorizontalAlign::Left;
|
||||||
void drawText(RenderBackend &renderer, const Vec2 &position, const Color &color);
|
VerticalAlign vAlign_ = VerticalAlign::Top;
|
||||||
Vec2 calculateDrawPosition() const;
|
|
||||||
std::vector<std::string> splitLines() const;
|
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(RenderBackend &renderer, const Vec2 &position,
|
||||||
|
const Color &color);
|
||||||
|
Vec2 calculateDrawPosition() const;
|
||||||
|
std::vector<std::string> splitLines() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -12,207 +12,209 @@ namespace extra2d {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class ProgressBar : public Widget {
|
class ProgressBar : public Widget {
|
||||||
public:
|
public:
|
||||||
ProgressBar();
|
ProgressBar();
|
||||||
~ProgressBar() override = default;
|
~ProgressBar() override = default;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 静态创建方法
|
// 静态创建方法
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
static Ptr<ProgressBar> create();
|
static Ptr<ProgressBar> create();
|
||||||
static Ptr<ProgressBar> create(float min, float max, float value);
|
static Ptr<ProgressBar> create(float min, float max, float value);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 数值范围
|
// 数值范围
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setRange(float min, float max);
|
void setRange(float min, float max);
|
||||||
float getMin() const { return min_; }
|
float getMin() const { return min_; }
|
||||||
float getMax() const { return max_; }
|
float getMax() const { return max_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 当前值
|
// 当前值
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setValue(float value);
|
void setValue(float value);
|
||||||
float getValue() const { return value_; }
|
float getValue() const { return value_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 获取百分比 (0.0 - 1.0)
|
// 获取百分比 (0.0 - 1.0)
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
float getPercent() const;
|
float getPercent() const;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 进度条方向
|
// 进度条方向
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
enum class Direction { LeftToRight, RightToLeft, BottomToTop, TopToBottom };
|
enum class Direction { LeftToRight, RightToLeft, BottomToTop, TopToBottom };
|
||||||
|
|
||||||
void setDirection(Direction dir);
|
|
||||||
Direction getDirection() const { return direction_; }
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
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);
|
void setBackgroundColor(const Color &color);
|
||||||
bool isSegmentedColorsEnabled() const { return segmentedColorsEnabled_; }
|
Color getBackgroundColor() const { return bgColor_; }
|
||||||
|
|
||||||
// 设置分段阈值和颜色,例如:>70%绿色, >30%黄色, 其他红色
|
|
||||||
void addColorSegment(float percentThreshold, const Color &color);
|
|
||||||
void clearColorSegments();
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
void setFillColor(const Color &color);
|
||||||
// 圆角设置
|
Color getFillColor() const { return fillColor_; }
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
void setCornerRadius(float radius);
|
|
||||||
float getCornerRadius() const { return cornerRadius_; }
|
|
||||||
|
|
||||||
void setRoundedCornersEnabled(bool enabled);
|
|
||||||
bool isRoundedCornersEnabled() const { return roundedCornersEnabled_; }
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// 渐变填充色(从 fillColor_ 到 fillColorEnd_)
|
||||||
// 边框设置
|
void setGradientFillEnabled(bool enabled);
|
||||||
// ------------------------------------------------------------------------
|
bool isGradientFillEnabled() const { return gradientEnabled_; }
|
||||||
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 setFillColorEnd(const Color &color);
|
||||||
// 内边距(填充与边框的距离)
|
Color getFillColorEnd() const { return fillColorEnd_; }
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
void setPadding(float padding);
|
|
||||||
float getPadding() const { return padding_; }
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 文本显示
|
// 分段颜色(根据百分比自动切换颜色)
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setTextEnabled(bool enabled);
|
void setSegmentedColorsEnabled(bool enabled);
|
||||||
bool isTextEnabled() const { return textEnabled_; }
|
bool isSegmentedColorsEnabled() const { return segmentedColorsEnabled_; }
|
||||||
|
|
||||||
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_; }
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// 设置分段阈值和颜色,例如:>70%绿色, >30%黄色, 其他红色
|
||||||
// 动画效果
|
void addColorSegment(float percentThreshold, const Color &color);
|
||||||
// ------------------------------------------------------------------------
|
void clearColorSegments();
|
||||||
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);
|
void setCornerRadius(float radius);
|
||||||
bool isStripedEnabled() const { return stripedEnabled_; }
|
float getCornerRadius() const { return cornerRadius_; }
|
||||||
|
|
||||||
void setStripeColor(const Color &color);
|
|
||||||
Color getStripeColor() const { return stripeColor_; }
|
|
||||||
|
|
||||||
void setStripeSpeed(float speed); // 条纹移动速度
|
|
||||||
float getStripeSpeed() const { return stripeSpeed_; }
|
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
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:
|
protected:
|
||||||
void onUpdate(float deltaTime) override;
|
void onUpdate(float deltaTime) override;
|
||||||
void onDrawWidget(RenderBackend &renderer) override;
|
void onDrawWidget(RenderBackend &renderer) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 数值
|
// 数值
|
||||||
float min_ = 0.0f;
|
float min_ = 0.0f;
|
||||||
float max_ = 100.0f;
|
float max_ = 100.0f;
|
||||||
float value_ = 50.0f;
|
float value_ = 50.0f;
|
||||||
|
|
||||||
// 方向
|
// 方向
|
||||||
Direction direction_ = Direction::LeftToRight;
|
Direction direction_ = Direction::LeftToRight;
|
||||||
|
|
||||||
// 颜色
|
// 颜色
|
||||||
Color bgColor_ = Color(0.2f, 0.2f, 0.2f, 1.0f);
|
Color bgColor_ = Color(0.2f, 0.2f, 0.2f, 1.0f);
|
||||||
Color fillColor_ = Color(0.0f, 0.8f, 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);
|
Color fillColorEnd_ = Color(0.0f, 0.6f, 0.1f, 1.0f);
|
||||||
bool gradientEnabled_ = false;
|
bool gradientEnabled_ = false;
|
||||||
|
|
||||||
// 分段颜色
|
// 分段颜色
|
||||||
bool segmentedColorsEnabled_ = false;
|
bool segmentedColorsEnabled_ = false;
|
||||||
std::vector<std::pair<float, Color>> colorSegments_;
|
std::vector<std::pair<float, Color>> colorSegments_;
|
||||||
|
|
||||||
// 圆角
|
// 圆角
|
||||||
float cornerRadius_ = 4.0f;
|
float cornerRadius_ = 4.0f;
|
||||||
bool roundedCornersEnabled_ = true;
|
bool roundedCornersEnabled_ = true;
|
||||||
|
|
||||||
// 边框
|
// 边框
|
||||||
bool borderEnabled_ = false;
|
bool borderEnabled_ = false;
|
||||||
Color borderColor_ = Colors::White;
|
Color borderColor_ = Colors::White;
|
||||||
float borderWidth_ = 1.0f;
|
float borderWidth_ = 1.0f;
|
||||||
|
|
||||||
// 内边距
|
// 内边距
|
||||||
float padding_ = 2.0f;
|
float padding_ = 2.0f;
|
||||||
|
|
||||||
// 文本
|
// 文本
|
||||||
bool textEnabled_ = false;
|
bool textEnabled_ = false;
|
||||||
Ptr<FontAtlas> font_;
|
Ptr<FontAtlas> font_;
|
||||||
Color textColor_ = Colors::White;
|
Color textColor_ = Colors::White;
|
||||||
std::string textFormat_ = "{percent:.0f}%";
|
std::string textFormat_ = "{percent:.0f}%";
|
||||||
|
|
||||||
// 动画
|
// 动画
|
||||||
bool animatedChangeEnabled_ = false;
|
bool animatedChangeEnabled_ = false;
|
||||||
float animationSpeed_ = 100.0f;
|
float animationSpeed_ = 100.0f;
|
||||||
float displayValue_ = 50.0f; // 用于动画的显示值
|
float displayValue_ = 50.0f; // 用于动画的显示值
|
||||||
|
|
||||||
// 延迟显示
|
// 延迟显示
|
||||||
bool delayedDisplayEnabled_ = false;
|
bool delayedDisplayEnabled_ = false;
|
||||||
float delayTime_ = 0.3f;
|
float delayTime_ = 0.3f;
|
||||||
float delayTimer_ = 0.0f;
|
float delayTimer_ = 0.0f;
|
||||||
float delayedValue_ = 50.0f;
|
float delayedValue_ = 50.0f;
|
||||||
Color delayedFillColor_ = Color(1.0f, 0.0f, 0.0f, 0.5f);
|
Color delayedFillColor_ = Color(1.0f, 0.0f, 0.0f, 0.5f);
|
||||||
|
|
||||||
// 条纹
|
// 条纹
|
||||||
bool stripedEnabled_ = false;
|
bool stripedEnabled_ = false;
|
||||||
Color stripeColor_ = Color(1.0f, 1.0f, 1.0f, 0.2f);
|
Color stripeColor_ = Color(1.0f, 1.0f, 1.0f, 0.2f);
|
||||||
float stripeSpeed_ = 50.0f;
|
float stripeSpeed_ = 50.0f;
|
||||||
float stripeOffset_ = 0.0f;
|
float stripeOffset_ = 0.0f;
|
||||||
|
|
||||||
Color getCurrentFillColor() const;
|
Color getCurrentFillColor() const;
|
||||||
std::string formatText() const;
|
std::string formatText() const;
|
||||||
void drawRoundedRect(RenderBackend &renderer, const Rect &rect, const Color &color, float radius);
|
void drawRoundedRect(RenderBackend &renderer, const Rect &rect,
|
||||||
void fillRoundedRect(RenderBackend &renderer, const Rect &rect, const Color &color, float radius);
|
const Color &color, float radius);
|
||||||
void drawStripes(RenderBackend &renderer, const Rect &rect);
|
void fillRoundedRect(RenderBackend &renderer, const Rect &rect,
|
||||||
|
const Color &color, float radius);
|
||||||
|
void drawStripes(RenderBackend &renderer, const Rect &rect);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -11,95 +11,95 @@ namespace extra2d {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class RadioButton : public Widget {
|
class RadioButton : public Widget {
|
||||||
public:
|
public:
|
||||||
RadioButton();
|
RadioButton();
|
||||||
~RadioButton() override = default;
|
~RadioButton() override = default;
|
||||||
|
|
||||||
static Ptr<RadioButton> create();
|
static Ptr<RadioButton> create();
|
||||||
static Ptr<RadioButton> create(const std::string &label);
|
static Ptr<RadioButton> create(const std::string &label);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 选择状态
|
// 选择状态
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setSelected(bool selected);
|
void setSelected(bool selected);
|
||||||
bool isSelected() const { return selected_; }
|
bool isSelected() const { return selected_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 标签设置
|
// 标签设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setLabel(const std::string &label);
|
void setLabel(const std::string &label);
|
||||||
const std::string &getLabel() const { return label_; }
|
const std::string &getLabel() const { return label_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 字体设置
|
// 字体设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setFont(Ptr<FontAtlas> font);
|
void setFont(Ptr<FontAtlas> font);
|
||||||
Ptr<FontAtlas> getFont() const { return font_; }
|
Ptr<FontAtlas> getFont() const { return font_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 文字颜色
|
// 文字颜色
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setTextColor(const Color &color);
|
void setTextColor(const Color &color);
|
||||||
Color getTextColor() const { return textColor_; }
|
Color getTextColor() const { return textColor_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 圆形尺寸
|
// 圆形尺寸
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setCircleSize(float size);
|
void setCircleSize(float size);
|
||||||
float getCircleSize() const { return circleSize_; }
|
float getCircleSize() const { return circleSize_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 间距
|
// 间距
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setSpacing(float spacing);
|
void setSpacing(float spacing);
|
||||||
float getSpacing() const { return spacing_; }
|
float getSpacing() const { return spacing_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 颜色设置
|
// 颜色设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setSelectedColor(const Color &color);
|
void setSelectedColor(const Color &color);
|
||||||
Color getSelectedColor() const { return selectedColor_; }
|
Color getSelectedColor() const { return selectedColor_; }
|
||||||
|
|
||||||
void setUnselectedColor(const Color &color);
|
void setUnselectedColor(const Color &color);
|
||||||
Color getUnselectedColor() const { return unselectedColor_; }
|
Color getUnselectedColor() const { return unselectedColor_; }
|
||||||
|
|
||||||
void setDotColor(const Color &color);
|
void setDotColor(const Color &color);
|
||||||
Color getDotColor() const { return dotColor_; }
|
Color getDotColor() const { return dotColor_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 分组
|
// 分组
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setGroupId(int groupId);
|
void setGroupId(int groupId);
|
||||||
int getGroupId() const { return groupId_; }
|
int getGroupId() const { return groupId_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 回调设置
|
// 回调设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setOnStateChange(Function<void(bool)> callback);
|
void setOnStateChange(Function<void(bool)> callback);
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
Rect boundingBox() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onDrawWidget(RenderBackend &renderer) override;
|
void onDrawWidget(RenderBackend &renderer) override;
|
||||||
bool onMousePress(const MouseEvent &event) override;
|
bool onMousePress(const MouseEvent &event) override;
|
||||||
bool onMouseRelease(const MouseEvent &event) override;
|
bool onMouseRelease(const MouseEvent &event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool selected_ = false;
|
bool selected_ = false;
|
||||||
std::string label_;
|
std::string label_;
|
||||||
Ptr<FontAtlas> font_;
|
Ptr<FontAtlas> font_;
|
||||||
Color textColor_ = Colors::White;
|
Color textColor_ = Colors::White;
|
||||||
|
|
||||||
float circleSize_ = 20.0f;
|
float circleSize_ = 20.0f;
|
||||||
float spacing_ = 8.0f;
|
float spacing_ = 8.0f;
|
||||||
|
|
||||||
Color selectedColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
Color selectedColor_ = Color(0.2f, 0.6f, 1.0f, 1.0f);
|
||||||
Color unselectedColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
Color unselectedColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||||
Color dotColor_ = Colors::White;
|
Color dotColor_ = Colors::White;
|
||||||
|
|
||||||
int groupId_ = 0;
|
int groupId_ = 0;
|
||||||
|
|
||||||
bool pressed_ = false;
|
bool pressed_ = false;
|
||||||
Function<void(bool)> onStateChange_;
|
Function<void(bool)> onStateChange_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -107,17 +107,17 @@ private:
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class RadioButtonGroup {
|
class RadioButtonGroup {
|
||||||
public:
|
public:
|
||||||
void addButton(RadioButton *button);
|
void addButton(RadioButton *button);
|
||||||
void removeButton(RadioButton *button);
|
void removeButton(RadioButton *button);
|
||||||
void selectButton(RadioButton *button);
|
void selectButton(RadioButton *button);
|
||||||
RadioButton *getSelectedButton() const { return selectedButton_; }
|
RadioButton *getSelectedButton() const { return selectedButton_; }
|
||||||
|
|
||||||
void setOnSelectionChange(Function<void(RadioButton*)> callback);
|
void setOnSelectionChange(Function<void(RadioButton *)> callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<RadioButton*> buttons_;
|
std::vector<RadioButton *> buttons_;
|
||||||
RadioButton *selectedButton_ = nullptr;
|
RadioButton *selectedButton_ = nullptr;
|
||||||
Function<void(RadioButton*)> onSelectionChange_;
|
Function<void(RadioButton *)> onSelectionChange_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -11,145 +11,145 @@ namespace extra2d {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
class Slider : public Widget {
|
class Slider : public Widget {
|
||||||
public:
|
public:
|
||||||
Slider();
|
Slider();
|
||||||
~Slider() override = default;
|
~Slider() override = default;
|
||||||
|
|
||||||
static Ptr<Slider> create();
|
static Ptr<Slider> create();
|
||||||
static Ptr<Slider> create(float min, float max, float value);
|
static Ptr<Slider> create(float min, float max, float value);
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 数值范围
|
// 数值范围
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setRange(float min, float max);
|
void setRange(float min, float max);
|
||||||
float getMin() const { return min_; }
|
float getMin() const { return min_; }
|
||||||
float getMax() const { return max_; }
|
float getMax() const { return max_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 当前值
|
// 当前值
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setValue(float value);
|
void setValue(float value);
|
||||||
float getValue() const { return value_; }
|
float getValue() const { return value_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 步进值
|
// 步进值
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setStep(float step);
|
void setStep(float step);
|
||||||
float getStep() const { return step_; }
|
float getStep() const { return step_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 方向
|
// 方向
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setVertical(bool vertical);
|
void setVertical(bool vertical);
|
||||||
bool isVertical() const { return vertical_; }
|
bool isVertical() const { return vertical_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 轨道尺寸
|
// 轨道尺寸
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setTrackSize(float size);
|
void setTrackSize(float size);
|
||||||
float getTrackSize() const { return trackSize_; }
|
float getTrackSize() const { return trackSize_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 滑块尺寸
|
// 滑块尺寸
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setThumbSize(float size);
|
void setThumbSize(float size);
|
||||||
float getThumbSize() const { return thumbSize_; }
|
float getThumbSize() const { return thumbSize_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 颜色设置
|
// 颜色设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setTrackColor(const Color &color);
|
void setTrackColor(const Color &color);
|
||||||
Color getTrackColor() const { return trackColor_; }
|
Color getTrackColor() const { return trackColor_; }
|
||||||
|
|
||||||
void setFillColor(const Color &color);
|
void setFillColor(const Color &color);
|
||||||
Color getFillColor() const { return fillColor_; }
|
Color getFillColor() const { return fillColor_; }
|
||||||
|
|
||||||
void setThumbColor(const Color &color);
|
void setThumbColor(const Color &color);
|
||||||
Color getThumbColor() const { return thumbColor_; }
|
Color getThumbColor() const { return thumbColor_; }
|
||||||
|
|
||||||
void setThumbHoverColor(const Color &color);
|
void setThumbHoverColor(const Color &color);
|
||||||
Color getThumbHoverColor() const { return thumbHoverColor_; }
|
Color getThumbHoverColor() const { return thumbHoverColor_; }
|
||||||
|
|
||||||
void setThumbPressedColor(const Color &color);
|
void setThumbPressedColor(const Color &color);
|
||||||
Color getThumbPressedColor() const { return thumbPressedColor_; }
|
Color getThumbPressedColor() const { return thumbPressedColor_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 显示设置
|
// 显示设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setShowThumb(bool show);
|
void setShowThumb(bool show);
|
||||||
bool isShowThumb() const { return showThumb_; }
|
bool isShowThumb() const { return showThumb_; }
|
||||||
|
|
||||||
void setShowFill(bool show);
|
void setShowFill(bool show);
|
||||||
bool isShowFill() const { return showFill_; }
|
bool isShowFill() const { return showFill_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 文本显示
|
// 文本显示
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setTextEnabled(bool enabled);
|
void setTextEnabled(bool enabled);
|
||||||
bool isTextEnabled() const { return textEnabled_; }
|
bool isTextEnabled() const { return textEnabled_; }
|
||||||
|
|
||||||
void setFont(Ptr<FontAtlas> font);
|
void setFont(Ptr<FontAtlas> font);
|
||||||
Ptr<FontAtlas> getFont() const { return font_; }
|
Ptr<FontAtlas> getFont() const { return font_; }
|
||||||
|
|
||||||
void setTextColor(const Color &color);
|
void setTextColor(const Color &color);
|
||||||
Color getTextColor() const { return textColor_; }
|
Color getTextColor() const { return textColor_; }
|
||||||
|
|
||||||
void setTextFormat(const std::string &format);
|
void setTextFormat(const std::string &format);
|
||||||
const std::string &getTextFormat() const { return textFormat_; }
|
const std::string &getTextFormat() const { return textFormat_; }
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 回调设置
|
// 回调设置
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void setOnValueChange(Function<void(float)> callback);
|
void setOnValueChange(Function<void(float)> callback);
|
||||||
void setOnDragStart(Function<void()> callback);
|
void setOnDragStart(Function<void()> callback);
|
||||||
void setOnDragEnd(Function<void()> callback);
|
void setOnDragEnd(Function<void()> callback);
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
Rect boundingBox() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onDrawWidget(RenderBackend &renderer) override;
|
void onDrawWidget(RenderBackend &renderer) override;
|
||||||
bool onMousePress(const MouseEvent &event) override;
|
bool onMousePress(const MouseEvent &event) override;
|
||||||
bool onMouseRelease(const MouseEvent &event) override;
|
bool onMouseRelease(const MouseEvent &event) override;
|
||||||
bool onMouseMove(const MouseEvent &event) override;
|
bool onMouseMove(const MouseEvent &event) override;
|
||||||
void onMouseEnter() override;
|
void onMouseEnter() override;
|
||||||
void onMouseLeave() override;
|
void onMouseLeave() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float min_ = 0.0f;
|
float min_ = 0.0f;
|
||||||
float max_ = 100.0f;
|
float max_ = 100.0f;
|
||||||
float value_ = 50.0f;
|
float value_ = 50.0f;
|
||||||
float step_ = 0.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;
|
bool vertical_ = false;
|
||||||
float positionToValue(float pos) const;
|
float trackSize_ = 6.0f;
|
||||||
Rect getThumbRect() const;
|
float thumbSize_ = 16.0f;
|
||||||
Rect getTrackRect() const;
|
|
||||||
std::string formatText() const;
|
Color trackColor_ = Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||||
float snapToStep(float value) const;
|
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
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@
|
||||||
|
|
||||||
#include <core/color.h>
|
#include <core/color.h>
|
||||||
#include <core/types.h>
|
#include <core/types.h>
|
||||||
#include <graphics/font.h>
|
|
||||||
#include <ui/widget.h>
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <graphics/font.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <ui/widget.h>
|
||||||
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -98,7 +99,7 @@ public:
|
||||||
Vec2 getTextSize() const;
|
Vec2 getTextSize() const;
|
||||||
float getLineHeight() const;
|
float getLineHeight() const;
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
Rect boundingBox() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onDrawWidget(RenderBackend &renderer) override;
|
void onDrawWidget(RenderBackend &renderer) override;
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ public:
|
||||||
|
|
||||||
void setSize(const Size &size);
|
void setSize(const Size &size);
|
||||||
void setSize(float width, float height);
|
void setSize(float width, float height);
|
||||||
Size getSize() const { return size_; }
|
Size size() const { return size_; }
|
||||||
|
|
||||||
Rect getBoundingBox() const override;
|
Rect boundingBox() const override;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 鼠标事件处理(子类可重写)
|
// 鼠标事件处理(子类可重写)
|
||||||
|
|
@ -55,7 +55,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
// 供子类使用的辅助方法
|
// 供子类使用的辅助方法
|
||||||
bool isPointInside(float x, float y) const {
|
bool isPointInside(float x, float y) const {
|
||||||
return getBoundingBox().containsPoint(Point(x, y));
|
return boundingBox().containsPoint(Point(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 子类重写此方法以支持自定义渲染
|
// 子类重写此方法以支持自定义渲染
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
|
||||||
#include <animation/tween.h>
|
#include <animation/tween.h>
|
||||||
|
#include <cmath>
|
||||||
#include <graphics/render_command.h>
|
#include <graphics/render_command.h>
|
||||||
#include <scene/node.h>
|
#include <scene/node.h>
|
||||||
#include <scene/scene.h>
|
#include <scene/scene.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
Node::Node() = default;
|
Node::Node() = default;
|
||||||
|
|
||||||
Node::~Node() {
|
Node::~Node() { removeAllChildren(); }
|
||||||
removeAllChildren();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Node::addChild(Ptr<Node> child) {
|
void Node::addChild(Ptr<Node> child) {
|
||||||
if (!child || child.get() == this) {
|
if (!child || child.get() == this) {
|
||||||
|
|
@ -200,7 +199,7 @@ void Node::setOpacity(float opacity) {
|
||||||
|
|
||||||
void Node::setVisible(bool visible) { visible_ = visible; }
|
void Node::setVisible(bool visible) { visible_ = visible; }
|
||||||
|
|
||||||
void Node::setColor(const Color3B& color) { color_ = color; }
|
void Node::setColor(const Color3B &color) { color_ = color; }
|
||||||
|
|
||||||
void Node::setFlipX(bool flipX) { flipX_ = flipX; }
|
void Node::setFlipX(bool flipX) { flipX_ = flipX; }
|
||||||
|
|
||||||
|
|
@ -260,9 +259,9 @@ glm::mat4 Node::getWorldTransform() const {
|
||||||
if (worldTransformDirty_) {
|
if (worldTransformDirty_) {
|
||||||
// 使用线程局部存储的固定数组,避免每帧内存分配
|
// 使用线程局部存储的固定数组,避免每帧内存分配
|
||||||
// 限制最大深度为 256 层,足以覆盖绝大多数场景
|
// 限制最大深度为 256 层,足以覆盖绝大多数场景
|
||||||
thread_local std::array<const Node*, 256> nodeChainCache;
|
thread_local std::array<const Node *, 256> nodeChainCache;
|
||||||
thread_local size_t chainCount = 0;
|
thread_local size_t chainCount = 0;
|
||||||
|
|
||||||
chainCount = 0;
|
chainCount = 0;
|
||||||
const Node *current = this;
|
const Node *current = this;
|
||||||
while (current && chainCount < nodeChainCache.size()) {
|
while (current && chainCount < nodeChainCache.size()) {
|
||||||
|
|
@ -300,7 +299,7 @@ void Node::batchUpdateTransforms() {
|
||||||
if (transformDirty_) {
|
if (transformDirty_) {
|
||||||
(void)getLocalTransform(); // 这会计算并缓存本地变换
|
(void)getLocalTransform(); // 这会计算并缓存本地变换
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果世界变换脏了,需要重新计算
|
// 如果世界变换脏了,需要重新计算
|
||||||
if (worldTransformDirty_) {
|
if (worldTransformDirty_) {
|
||||||
auto parent = parent_.lock();
|
auto parent = parent_.lock();
|
||||||
|
|
@ -313,7 +312,7 @@ void Node::batchUpdateTransforms() {
|
||||||
}
|
}
|
||||||
worldTransformDirty_ = false;
|
worldTransformDirty_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归更新子节点
|
// 递归更新子节点
|
||||||
for (auto &child : children_) {
|
for (auto &child : children_) {
|
||||||
child->batchUpdateTransforms();
|
child->batchUpdateTransforms();
|
||||||
|
|
@ -384,7 +383,7 @@ void Node::onDetachFromScene() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect Node::getBoundingBox() const {
|
Rect Node::boundingBox() const {
|
||||||
// 默认返回一个以位置为中心的点矩形
|
// 默认返回一个以位置为中心的点矩形
|
||||||
return Rect(position_.x, position_.y, 0, 0);
|
return Rect(position_.x, position_.y, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -394,7 +393,7 @@ void Node::updateSpatialIndex() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect newBounds = getBoundingBox();
|
Rect newBounds = boundingBox();
|
||||||
if (newBounds != lastSpatialBounds_) {
|
if (newBounds != lastSpatialBounds_) {
|
||||||
scene_->updateNodeInSpatialIndex(this, lastSpatialBounds_, newBounds);
|
scene_->updateNodeInSpatialIndex(this, lastSpatialBounds_, newBounds);
|
||||||
lastSpatialBounds_ = newBounds;
|
lastSpatialBounds_ = newBounds;
|
||||||
|
|
@ -462,13 +461,13 @@ void Node::collectRenderCommands(std::vector<RenderCommand> &commands,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tween& Node::tween() {
|
Tween &Node::tween() {
|
||||||
auto tw = std::make_shared<Tween>(this);
|
auto tw = std::make_shared<Tween>(this);
|
||||||
tweens_.push_back(tw);
|
tweens_.push_back(tw);
|
||||||
return *tweens_.back();
|
return *tweens_.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
Tween& Node::tween(const std::string &name) {
|
Tween &Node::tween(const std::string &name) {
|
||||||
auto tw = std::make_shared<Tween>(this, name);
|
auto tw = std::make_shared<Tween>(this, name);
|
||||||
tweens_.push_back(tw);
|
tweens_.push_back(tw);
|
||||||
return *tweens_.back();
|
return *tweens_.back();
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ Node *hitTestTopmost(const Ptr<Node> &node, const Vec2 &worldPos) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect bounds = node->getBoundingBox();
|
Rect bounds = node->boundingBox();
|
||||||
if (!bounds.empty() && bounds.containsPoint(worldPos)) {
|
if (!bounds.empty() && bounds.containsPoint(worldPos)) {
|
||||||
return node.get();
|
return node.get();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <graphics/render_backend.h>
|
#include <graphics/render_backend.h>
|
||||||
#include <graphics/render_command.h>
|
#include <graphics/render_command.h>
|
||||||
#include <scene/shape_node.h>
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <scene/shape_node.h>
|
||||||
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -124,7 +125,7 @@ void ShapeNode::clearPoints() {
|
||||||
updateSpatialIndex();
|
updateSpatialIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect ShapeNode::getBoundingBox() const {
|
Rect ShapeNode::boundingBox() const {
|
||||||
if (points_.empty()) {
|
if (points_.empty()) {
|
||||||
return Rect();
|
return Rect();
|
||||||
}
|
}
|
||||||
|
|
@ -262,16 +263,16 @@ void ShapeNode::generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||||
case ShapeType::Point:
|
case ShapeType::Point:
|
||||||
if (!points_.empty()) {
|
if (!points_.empty()) {
|
||||||
cmd.type = RenderCommandType::FilledCircle;
|
cmd.type = RenderCommandType::FilledCircle;
|
||||||
cmd.data =
|
cmd.data = CircleCommandData{
|
||||||
CircleCommandData{points_[0] + offset, lineWidth_ * 0.5f, color_, 8, 0.0f, true};
|
points_[0] + offset, lineWidth_ * 0.5f, color_, 8, 0.0f, true};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShapeType::Line:
|
case ShapeType::Line:
|
||||||
if (points_.size() >= 2) {
|
if (points_.size() >= 2) {
|
||||||
cmd.type = RenderCommandType::Line;
|
cmd.type = RenderCommandType::Line;
|
||||||
cmd.data = LineCommandData{points_[0] + offset, points_[1] + offset, color_,
|
cmd.data = LineCommandData{points_[0] + offset, points_[1] + offset,
|
||||||
lineWidth_};
|
color_, lineWidth_};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -281,14 +282,14 @@ void ShapeNode::generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||||
cmd.type = RenderCommandType::FilledRect;
|
cmd.type = RenderCommandType::FilledRect;
|
||||||
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
|
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
|
||||||
points_[2].y - points_[0].y);
|
points_[2].y - points_[0].y);
|
||||||
cmd.data =
|
cmd.data = RectCommandData{Rect(rect.origin + offset, rect.size),
|
||||||
RectCommandData{Rect(rect.origin + offset, rect.size), color_, 0.0f, true};
|
color_, 0.0f, true};
|
||||||
} else {
|
} else {
|
||||||
cmd.type = RenderCommandType::Rect;
|
cmd.type = RenderCommandType::Rect;
|
||||||
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
|
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
|
||||||
points_[2].y - points_[0].y);
|
points_[2].y - points_[0].y);
|
||||||
cmd.data =
|
cmd.data = RectCommandData{Rect(rect.origin + offset, rect.size),
|
||||||
RectCommandData{Rect(rect.origin + offset, rect.size), color_, lineWidth_, false};
|
color_, lineWidth_, false};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -298,12 +299,12 @@ void ShapeNode::generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||||
float radius = points_[1].x;
|
float radius = points_[1].x;
|
||||||
if (filled_) {
|
if (filled_) {
|
||||||
cmd.type = RenderCommandType::FilledCircle;
|
cmd.type = RenderCommandType::FilledCircle;
|
||||||
cmd.data =
|
cmd.data = CircleCommandData{points_[0] + offset, radius, color_,
|
||||||
CircleCommandData{points_[0] + offset, radius, color_, segments_, 0.0f, true};
|
segments_, 0.0f, true};
|
||||||
} else {
|
} else {
|
||||||
cmd.type = RenderCommandType::Circle;
|
cmd.type = RenderCommandType::Circle;
|
||||||
cmd.data = CircleCommandData{points_[0] + offset, radius, color_, segments_,
|
cmd.data = CircleCommandData{points_[0] + offset, radius, color_,
|
||||||
lineWidth_, false};
|
segments_, lineWidth_, false};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -336,7 +337,8 @@ void ShapeNode::generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||||
cmd.data = PolygonCommandData{transformedPoints, color_, 0.0f, true};
|
cmd.data = PolygonCommandData{transformedPoints, color_, 0.0f, true};
|
||||||
} else {
|
} else {
|
||||||
cmd.type = RenderCommandType::Polygon;
|
cmd.type = RenderCommandType::Polygon;
|
||||||
cmd.data = PolygonCommandData{transformedPoints, color_, lineWidth_, false};
|
cmd.data =
|
||||||
|
PolygonCommandData{transformedPoints, color_, lineWidth_, false};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ Ptr<Sprite> Sprite::create(Ptr<Texture> texture, const Rect &rect) {
|
||||||
return sprite;
|
return sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect Sprite::getBoundingBox() const {
|
Rect Sprite::boundingBox() const {
|
||||||
if (!texture_ || !texture_->isValid()) {
|
if (!texture_ || !texture_->isValid()) {
|
||||||
return Rect();
|
return Rect();
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +159,7 @@ void Sprite::generateRenderCommand(std::vector<RenderCommand> &commands,
|
||||||
cmd.type = RenderCommandType::Sprite;
|
cmd.type = RenderCommandType::Sprite;
|
||||||
cmd.layer = zOrder;
|
cmd.layer = zOrder;
|
||||||
cmd.data = SpriteCommandData{texture_.get(), destRect, srcRect, color_,
|
cmd.data = SpriteCommandData{texture_.get(), destRect, srcRect, color_,
|
||||||
worldRotation, anchorPt, 0};
|
worldRotation, anchorPt, 0};
|
||||||
|
|
||||||
commands.push_back(std::move(cmd));
|
commands.push_back(std::move(cmd));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ void SpatialManager::rebuild() {
|
||||||
auto objects = oldIndex->query(bounds);
|
auto objects = oldIndex->query(bounds);
|
||||||
for (Node *node : objects) {
|
for (Node *node : objects) {
|
||||||
if (node) {
|
if (node) {
|
||||||
auto nodeBounds = node->getBoundingBox();
|
auto nodeBounds = node->boundingBox();
|
||||||
index_->insert(node, nodeBounds);
|
index_->insert(node, nodeBounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
|
||||||
#include <app/application.h>
|
#include <app/application.h>
|
||||||
|
#include <cmath>
|
||||||
#include <core/string.h>
|
#include <core/string.h>
|
||||||
#include <graphics/render_backend.h>
|
#include <graphics/render_backend.h>
|
||||||
#include <ui/button.h>
|
#include <ui/button.h>
|
||||||
|
|
@ -93,7 +93,7 @@ Ptr<Button> Button::create(const std::string &text, Ptr<FontAtlas> font) {
|
||||||
*/
|
*/
|
||||||
void Button::setText(const std::string &text) {
|
void Button::setText(const std::string &text) {
|
||||||
text_ = text;
|
text_ = text;
|
||||||
if (font_ && getSize().empty()) {
|
if (font_ && size().empty()) {
|
||||||
Vec2 textSize = font_->measureText(text_);
|
Vec2 textSize = font_->measureText(text_);
|
||||||
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +105,7 @@ void Button::setText(const std::string &text) {
|
||||||
*/
|
*/
|
||||||
void Button::setFont(Ptr<FontAtlas> font) {
|
void Button::setFont(Ptr<FontAtlas> font) {
|
||||||
font_ = font;
|
font_ = font;
|
||||||
if (font_ && getSize().empty() && !text_.empty()) {
|
if (font_ && size().empty() && !text_.empty()) {
|
||||||
Vec2 textSize = font_->measureText(text_);
|
Vec2 textSize = font_->measureText(text_);
|
||||||
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +117,7 @@ void Button::setFont(Ptr<FontAtlas> font) {
|
||||||
*/
|
*/
|
||||||
void Button::setPadding(const Vec2 &padding) {
|
void Button::setPadding(const Vec2 &padding) {
|
||||||
padding_ = padding;
|
padding_ = padding;
|
||||||
if (font_ && getSize().empty() && !text_.empty()) {
|
if (font_ && size().empty() && !text_.empty()) {
|
||||||
Vec2 textSize = font_->measureText(text_);
|
Vec2 textSize = font_->measureText(text_);
|
||||||
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
setSize(textSize.x + padding_.x * 2.0f, textSize.y + padding_.y * 2.0f);
|
||||||
}
|
}
|
||||||
|
|
@ -128,9 +128,7 @@ void Button::setPadding(const Vec2 &padding) {
|
||||||
* @param x X方向内边距
|
* @param x X方向内边距
|
||||||
* @param y Y方向内边距
|
* @param y Y方向内边距
|
||||||
*/
|
*/
|
||||||
void Button::setPadding(float x, float y) {
|
void Button::setPadding(float x, float y) { setPadding(Vec2(x, y)); }
|
||||||
setPadding(Vec2(x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置文本颜色
|
* @brief 设置文本颜色
|
||||||
|
|
@ -356,20 +354,20 @@ void Button::setCustomSize(float width, float height) {
|
||||||
* @brief 获取边界框
|
* @brief 获取边界框
|
||||||
* @return 边界矩形
|
* @return 边界矩形
|
||||||
*/
|
*/
|
||||||
Rect Button::getBoundingBox() const {
|
Rect Button::boundingBox() const {
|
||||||
auto position = convertToWorldSpace(extra2d::Vec2::Zero());
|
auto position = convertToWorldSpace(extra2d::Vec2::Zero());
|
||||||
auto anchorPt = anchor();
|
auto anchorPt = anchor();
|
||||||
auto scaleVal = scale();
|
auto scaleVal = scale();
|
||||||
auto size = getSize();
|
auto widgetSize = size();
|
||||||
|
|
||||||
if (size.empty()) {
|
if (widgetSize.empty()) {
|
||||||
return Rect();
|
return Rect();
|
||||||
}
|
}
|
||||||
|
|
||||||
float w = size.width * scaleVal.x;
|
float w = widgetSize.width * scaleVal.x;
|
||||||
float h = size.height * scaleVal.y;
|
float h = widgetSize.height * scaleVal.y;
|
||||||
float x0 = position.x - size.width * anchorPt.x * scaleVal.x;
|
float x0 = position.x - widgetSize.width * anchorPt.x * scaleVal.x;
|
||||||
float y0 = position.y - size.height * anchorPt.y * scaleVal.y;
|
float y0 = position.y - widgetSize.height * anchorPt.y * scaleVal.y;
|
||||||
|
|
||||||
return Rect(x0, y0, w, h);
|
return Rect(x0, y0, w, h);
|
||||||
}
|
}
|
||||||
|
|
@ -616,7 +614,7 @@ void Button::fillRoundedRect(RenderBackend &renderer, const Rect &rect,
|
||||||
* @param renderer 渲染后端
|
* @param renderer 渲染后端
|
||||||
*/
|
*/
|
||||||
void Button::onDrawWidget(RenderBackend &renderer) {
|
void Button::onDrawWidget(RenderBackend &renderer) {
|
||||||
Rect rect = getBoundingBox();
|
Rect rect = boundingBox();
|
||||||
if (rect.empty()) {
|
if (rect.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include <ui/check_box.h>
|
|
||||||
#include <graphics/render_backend.h>
|
|
||||||
#include <core/string.h>
|
#include <core/string.h>
|
||||||
|
#include <graphics/render_backend.h>
|
||||||
|
#include <ui/check_box.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -8,17 +8,15 @@ namespace extra2d {
|
||||||
* @brief 默认构造函数
|
* @brief 默认构造函数
|
||||||
*/
|
*/
|
||||||
CheckBox::CheckBox() {
|
CheckBox::CheckBox() {
|
||||||
setAnchor(0.0f, 0.0f);
|
setAnchor(0.0f, 0.0f);
|
||||||
setSize(boxSize_, boxSize_);
|
setSize(boxSize_, boxSize_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建复选框对象
|
* @brief 创建复选框对象
|
||||||
* @return 复选框对象指针
|
* @return 复选框对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<CheckBox> CheckBox::create() {
|
Ptr<CheckBox> CheckBox::create() { return makePtr<CheckBox>(); }
|
||||||
return makePtr<CheckBox>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建带标签的复选框对象
|
* @brief 创建带标签的复选框对象
|
||||||
|
|
@ -26,9 +24,9 @@ Ptr<CheckBox> CheckBox::create() {
|
||||||
* @return 复选框对象指针
|
* @return 复选框对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<CheckBox> CheckBox::create(const std::string &label) {
|
Ptr<CheckBox> CheckBox::create(const std::string &label) {
|
||||||
auto cb = makePtr<CheckBox>();
|
auto cb = makePtr<CheckBox>();
|
||||||
cb->setLabel(label);
|
cb->setLabel(label);
|
||||||
return cb;
|
return cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,75 +34,61 @@ Ptr<CheckBox> CheckBox::create(const std::string &label) {
|
||||||
* @param checked 是否选中
|
* @param checked 是否选中
|
||||||
*/
|
*/
|
||||||
void CheckBox::setChecked(bool checked) {
|
void CheckBox::setChecked(bool checked) {
|
||||||
if (checked_ != checked) {
|
if (checked_ != checked) {
|
||||||
checked_ = checked;
|
checked_ = checked;
|
||||||
if (onStateChange_) {
|
if (onStateChange_) {
|
||||||
onStateChange_(checked_);
|
onStateChange_(checked_);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 切换选中状态
|
* @brief 切换选中状态
|
||||||
*/
|
*/
|
||||||
void CheckBox::toggle() {
|
void CheckBox::toggle() { setChecked(!checked_); }
|
||||||
setChecked(!checked_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置标签文本
|
* @brief 设置标签文本
|
||||||
* @param label 标签文本
|
* @param label 标签文本
|
||||||
*/
|
*/
|
||||||
void CheckBox::setLabel(const std::string &label) {
|
void CheckBox::setLabel(const std::string &label) { label_ = label; }
|
||||||
label_ = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置字体
|
* @brief 设置字体
|
||||||
* @param font 字体图集指针
|
* @param font 字体图集指针
|
||||||
*/
|
*/
|
||||||
void CheckBox::setFont(Ptr<FontAtlas> font) {
|
void CheckBox::setFont(Ptr<FontAtlas> font) { font_ = font; }
|
||||||
font_ = font;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置文本颜色
|
* @brief 设置文本颜色
|
||||||
* @param color 文本颜色
|
* @param color 文本颜色
|
||||||
*/
|
*/
|
||||||
void CheckBox::setTextColor(const Color &color) {
|
void CheckBox::setTextColor(const Color &color) { textColor_ = color; }
|
||||||
textColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置复选框尺寸
|
* @brief 设置复选框尺寸
|
||||||
* @param size 复选框尺寸
|
* @param size 复选框尺寸
|
||||||
*/
|
*/
|
||||||
void CheckBox::setBoxSize(float size) {
|
void CheckBox::setBoxSize(float size) { boxSize_ = size; }
|
||||||
boxSize_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置间距
|
* @brief 设置间距
|
||||||
* @param spacing 间距值
|
* @param spacing 间距值
|
||||||
*/
|
*/
|
||||||
void CheckBox::setSpacing(float spacing) {
|
void CheckBox::setSpacing(float spacing) { spacing_ = spacing; }
|
||||||
spacing_ = spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置选中颜色
|
* @brief 设置选中颜色
|
||||||
* @param color 选中颜色
|
* @param color 选中颜色
|
||||||
*/
|
*/
|
||||||
void CheckBox::setCheckedColor(const Color &color) {
|
void CheckBox::setCheckedColor(const Color &color) { checkedColor_ = color; }
|
||||||
checkedColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置未选中颜色
|
* @brief 设置未选中颜色
|
||||||
* @param color 未选中颜色
|
* @param color 未选中颜色
|
||||||
*/
|
*/
|
||||||
void CheckBox::setUncheckedColor(const Color &color) {
|
void CheckBox::setUncheckedColor(const Color &color) {
|
||||||
uncheckedColor_ = color;
|
uncheckedColor_ = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -112,7 +96,7 @@ void CheckBox::setUncheckedColor(const Color &color) {
|
||||||
* @param color 勾选标记颜色
|
* @param color 勾选标记颜色
|
||||||
*/
|
*/
|
||||||
void CheckBox::setCheckMarkColor(const Color &color) {
|
void CheckBox::setCheckMarkColor(const Color &color) {
|
||||||
checkMarkColor_ = color;
|
checkMarkColor_ = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -120,23 +104,23 @@ void CheckBox::setCheckMarkColor(const Color &color) {
|
||||||
* @param callback 回调函数
|
* @param callback 回调函数
|
||||||
*/
|
*/
|
||||||
void CheckBox::setOnStateChange(Function<void(bool)> callback) {
|
void CheckBox::setOnStateChange(Function<void(bool)> callback) {
|
||||||
onStateChange_ = callback;
|
onStateChange_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取边界框
|
* @brief 获取边界框
|
||||||
* @return 边界矩形
|
* @return 边界矩形
|
||||||
*/
|
*/
|
||||||
Rect CheckBox::getBoundingBox() const {
|
Rect CheckBox::boundingBox() const {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
float width = boxSize_;
|
float width = boxSize_;
|
||||||
|
|
||||||
if (!label_.empty() && font_) {
|
if (!label_.empty() && font_) {
|
||||||
Vec2 textSize = font_->measureText(label_);
|
Vec2 textSize = font_->measureText(label_);
|
||||||
width += spacing_ + textSize.x;
|
width += spacing_ + textSize.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Rect(position.x, position.y, width, boxSize_);
|
return Rect(position.x, position.y, width, boxSize_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -144,30 +128,31 @@ Rect CheckBox::getBoundingBox() const {
|
||||||
* @param renderer 渲染后端
|
* @param renderer 渲染后端
|
||||||
*/
|
*/
|
||||||
void CheckBox::onDrawWidget(RenderBackend &renderer) {
|
void CheckBox::onDrawWidget(RenderBackend &renderer) {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
|
|
||||||
Rect boxRect(position.x, position.y + (getSize().height - boxSize_) * 0.5f, boxSize_, boxSize_);
|
Rect boxRect(position.x, position.y + (size().height - boxSize_) * 0.5f,
|
||||||
Color boxColor = checked_ ? checkedColor_ : uncheckedColor_;
|
boxSize_, boxSize_);
|
||||||
renderer.fillRect(boxRect, boxColor);
|
Color boxColor = checked_ ? checkedColor_ : uncheckedColor_;
|
||||||
renderer.drawRect(boxRect, Colors::White, 1.0f);
|
renderer.fillRect(boxRect, boxColor);
|
||||||
|
renderer.drawRect(boxRect, Colors::White, 1.0f);
|
||||||
if (checked_) {
|
|
||||||
float padding = boxSize_ * 0.2f;
|
if (checked_) {
|
||||||
float x1 = boxRect.origin.x + padding;
|
float padding = boxSize_ * 0.2f;
|
||||||
float y1 = boxRect.origin.y + boxSize_ * 0.5f;
|
float x1 = boxRect.origin.x + padding;
|
||||||
float x2 = boxRect.origin.x + boxSize_ * 0.4f;
|
float y1 = boxRect.origin.y + boxSize_ * 0.5f;
|
||||||
float y2 = boxRect.origin.y + boxSize_ - padding;
|
float x2 = boxRect.origin.x + boxSize_ * 0.4f;
|
||||||
float x3 = boxRect.origin.x + boxSize_ - padding;
|
float y2 = boxRect.origin.y + boxSize_ - padding;
|
||||||
float y3 = boxRect.origin.y + 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);
|
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);
|
if (!label_.empty() && font_) {
|
||||||
renderer.drawText(*font_, label_, textPos, textColor_);
|
Vec2 textPos(position.x + boxSize_ + spacing_, position.y);
|
||||||
}
|
renderer.drawText(*font_, label_, textPos, textColor_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -176,11 +161,11 @@ void CheckBox::onDrawWidget(RenderBackend &renderer) {
|
||||||
* @return 是否处理了事件
|
* @return 是否处理了事件
|
||||||
*/
|
*/
|
||||||
bool CheckBox::onMousePress(const MouseEvent &event) {
|
bool CheckBox::onMousePress(const MouseEvent &event) {
|
||||||
if (event.button == MouseButton::Left) {
|
if (event.button == MouseButton::Left) {
|
||||||
pressed_ = true;
|
pressed_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -189,16 +174,16 @@ bool CheckBox::onMousePress(const MouseEvent &event) {
|
||||||
* @return 是否处理了事件
|
* @return 是否处理了事件
|
||||||
*/
|
*/
|
||||||
bool CheckBox::onMouseRelease(const MouseEvent &event) {
|
bool CheckBox::onMouseRelease(const MouseEvent &event) {
|
||||||
if (event.button == MouseButton::Left && pressed_) {
|
if (event.button == MouseButton::Left && pressed_) {
|
||||||
pressed_ = false;
|
pressed_ = false;
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
Rect boxRect(position.x, position.y, boxSize_, boxSize_);
|
Rect boxRect(position.x, position.y, boxSize_, boxSize_);
|
||||||
if (boxRect.containsPoint(Point(event.x, event.y))) {
|
if (boxRect.containsPoint(Point(event.x, event.y))) {
|
||||||
toggle();
|
toggle();
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
429
src/ui/label.cpp
429
src/ui/label.cpp
|
|
@ -1,32 +1,28 @@
|
||||||
#include <ui/label.h>
|
|
||||||
#include <graphics/render_backend.h>
|
|
||||||
#include <core/string.h>
|
#include <core/string.h>
|
||||||
|
#include <graphics/render_backend.h>
|
||||||
|
#include <ui/label.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 默认构造函数
|
* @brief 默认构造函数
|
||||||
*/
|
*/
|
||||||
Label::Label() {
|
Label::Label() { setAnchor(0.0f, 0.0f); }
|
||||||
setAnchor(0.0f, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 带文本的构造函数
|
* @brief 带文本的构造函数
|
||||||
* @param text 初始文本内容
|
* @param text 初始文本内容
|
||||||
*/
|
*/
|
||||||
Label::Label(const std::string &text) : text_(text) {
|
Label::Label(const std::string &text) : text_(text) {
|
||||||
setAnchor(0.0f, 0.0f);
|
setAnchor(0.0f, 0.0f);
|
||||||
sizeDirty_ = true;
|
sizeDirty_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建空标签对象
|
* @brief 创建空标签对象
|
||||||
* @return 标签对象指针
|
* @return 标签对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<Label> Label::create() {
|
Ptr<Label> Label::create() { return makePtr<Label>(); }
|
||||||
return makePtr<Label>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建带文本的标签对象
|
* @brief 创建带文本的标签对象
|
||||||
|
|
@ -34,7 +30,7 @@ Ptr<Label> Label::create() {
|
||||||
* @return 标签对象指针
|
* @return 标签对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<Label> Label::create(const std::string &text) {
|
Ptr<Label> Label::create(const std::string &text) {
|
||||||
return makePtr<Label>(text);
|
return makePtr<Label>(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -44,9 +40,9 @@ Ptr<Label> Label::create(const std::string &text) {
|
||||||
* @return 标签对象指针
|
* @return 标签对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<Label> Label::create(const std::string &text, Ptr<FontAtlas> font) {
|
Ptr<Label> Label::create(const std::string &text, Ptr<FontAtlas> font) {
|
||||||
auto label = makePtr<Label>(text);
|
auto label = makePtr<Label>(text);
|
||||||
label->setFont(font);
|
label->setFont(font);
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,9 +50,9 @@ Ptr<Label> Label::create(const std::string &text, Ptr<FontAtlas> font) {
|
||||||
* @param text 新的文本内容
|
* @param text 新的文本内容
|
||||||
*/
|
*/
|
||||||
void Label::setText(const std::string &text) {
|
void Label::setText(const std::string &text) {
|
||||||
text_ = text;
|
text_ = text;
|
||||||
sizeDirty_ = true;
|
sizeDirty_ = true;
|
||||||
updateSpatialIndex();
|
updateSpatialIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -64,101 +60,83 @@ void Label::setText(const std::string &text) {
|
||||||
* @param font 字体图集指针
|
* @param font 字体图集指针
|
||||||
*/
|
*/
|
||||||
void Label::setFont(Ptr<FontAtlas> font) {
|
void Label::setFont(Ptr<FontAtlas> font) {
|
||||||
font_ = font;
|
font_ = font;
|
||||||
sizeDirty_ = true;
|
sizeDirty_ = true;
|
||||||
updateSpatialIndex();
|
updateSpatialIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置文本颜色
|
* @brief 设置文本颜色
|
||||||
* @param color 文本颜色
|
* @param color 文本颜色
|
||||||
*/
|
*/
|
||||||
void Label::setTextColor(const Color &color) {
|
void Label::setTextColor(const Color &color) { textColor_ = color; }
|
||||||
textColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置字体大小
|
* @brief 设置字体大小
|
||||||
* @param size 字体大小
|
* @param size 字体大小
|
||||||
*/
|
*/
|
||||||
void Label::setFontSize(int size) {
|
void Label::setFontSize(int size) {
|
||||||
fontSize_ = size;
|
fontSize_ = size;
|
||||||
sizeDirty_ = true;
|
sizeDirty_ = true;
|
||||||
updateSpatialIndex();
|
updateSpatialIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置水平对齐方式
|
* @brief 设置水平对齐方式
|
||||||
* @param align 对齐方式
|
* @param align 对齐方式
|
||||||
*/
|
*/
|
||||||
void Label::setHorizontalAlign(HorizontalAlign align) {
|
void Label::setHorizontalAlign(HorizontalAlign align) { hAlign_ = align; }
|
||||||
hAlign_ = align;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置垂直对齐方式
|
* @brief 设置垂直对齐方式
|
||||||
* @param align 垂直对齐方式
|
* @param align 垂直对齐方式
|
||||||
*/
|
*/
|
||||||
void Label::setVerticalAlign(VerticalAlign align) {
|
void Label::setVerticalAlign(VerticalAlign align) { vAlign_ = align; }
|
||||||
vAlign_ = align;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置阴影是否启用
|
* @brief 设置阴影是否启用
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void Label::setShadowEnabled(bool enabled) {
|
void Label::setShadowEnabled(bool enabled) { shadowEnabled_ = enabled; }
|
||||||
shadowEnabled_ = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置阴影颜色
|
* @brief 设置阴影颜色
|
||||||
* @param color 阴影颜色
|
* @param color 阴影颜色
|
||||||
*/
|
*/
|
||||||
void Label::setShadowColor(const Color &color) {
|
void Label::setShadowColor(const Color &color) { shadowColor_ = color; }
|
||||||
shadowColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置阴影偏移
|
* @brief 设置阴影偏移
|
||||||
* @param offset 偏移向量
|
* @param offset 偏移向量
|
||||||
*/
|
*/
|
||||||
void Label::setShadowOffset(const Vec2 &offset) {
|
void Label::setShadowOffset(const Vec2 &offset) { shadowOffset_ = offset; }
|
||||||
shadowOffset_ = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置描边是否启用
|
* @brief 设置描边是否启用
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void Label::setOutlineEnabled(bool enabled) {
|
void Label::setOutlineEnabled(bool enabled) { outlineEnabled_ = enabled; }
|
||||||
outlineEnabled_ = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置描边颜色
|
* @brief 设置描边颜色
|
||||||
* @param color 描边颜色
|
* @param color 描边颜色
|
||||||
*/
|
*/
|
||||||
void Label::setOutlineColor(const Color &color) {
|
void Label::setOutlineColor(const Color &color) { outlineColor_ = color; }
|
||||||
outlineColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置描边宽度
|
* @brief 设置描边宽度
|
||||||
* @param width 描边宽度
|
* @param width 描边宽度
|
||||||
*/
|
*/
|
||||||
void Label::setOutlineWidth(float width) {
|
void Label::setOutlineWidth(float width) { outlineWidth_ = width; }
|
||||||
outlineWidth_ = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否多行模式
|
* @brief 设置是否多行模式
|
||||||
* @param multiLine 是否多行
|
* @param multiLine 是否多行
|
||||||
*/
|
*/
|
||||||
void Label::setMultiLine(bool multiLine) {
|
void Label::setMultiLine(bool multiLine) {
|
||||||
multiLine_ = multiLine;
|
multiLine_ = multiLine;
|
||||||
sizeDirty_ = true;
|
sizeDirty_ = true;
|
||||||
updateSpatialIndex();
|
updateSpatialIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -166,9 +144,9 @@ void Label::setMultiLine(bool multiLine) {
|
||||||
* @param spacing 行间距倍数
|
* @param spacing 行间距倍数
|
||||||
*/
|
*/
|
||||||
void Label::setLineSpacing(float spacing) {
|
void Label::setLineSpacing(float spacing) {
|
||||||
lineSpacing_ = spacing;
|
lineSpacing_ = spacing;
|
||||||
sizeDirty_ = true;
|
sizeDirty_ = true;
|
||||||
updateSpatialIndex();
|
updateSpatialIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -176,9 +154,9 @@ void Label::setLineSpacing(float spacing) {
|
||||||
* @param maxWidth 最大宽度
|
* @param maxWidth 最大宽度
|
||||||
*/
|
*/
|
||||||
void Label::setMaxWidth(float maxWidth) {
|
void Label::setMaxWidth(float maxWidth) {
|
||||||
maxWidth_ = maxWidth;
|
maxWidth_ = maxWidth;
|
||||||
sizeDirty_ = true;
|
sizeDirty_ = true;
|
||||||
updateSpatialIndex();
|
updateSpatialIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -186,8 +164,8 @@ void Label::setMaxWidth(float maxWidth) {
|
||||||
* @return 文本的宽度和高度
|
* @return 文本的宽度和高度
|
||||||
*/
|
*/
|
||||||
Vec2 Label::getTextSize() const {
|
Vec2 Label::getTextSize() const {
|
||||||
updateCache();
|
updateCache();
|
||||||
return cachedSize_;
|
return cachedSize_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -195,38 +173,38 @@ Vec2 Label::getTextSize() const {
|
||||||
* @return 行高值
|
* @return 行高值
|
||||||
*/
|
*/
|
||||||
float Label::getLineHeight() const {
|
float Label::getLineHeight() const {
|
||||||
if (font_) {
|
if (font_) {
|
||||||
return font_->getLineHeight() * lineSpacing_;
|
return font_->getLineHeight() * lineSpacing_;
|
||||||
}
|
}
|
||||||
return static_cast<float>(fontSize_) * lineSpacing_;
|
return static_cast<float>(fontSize_) * lineSpacing_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 更新缓存
|
* @brief 更新缓存
|
||||||
*/
|
*/
|
||||||
void Label::updateCache() const {
|
void Label::updateCache() const {
|
||||||
if (!sizeDirty_ || !font_) {
|
if (!sizeDirty_ || !font_) {
|
||||||
return;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multiLine_) {
|
cachedSize_ = Vec2(maxWidth, totalHeight);
|
||||||
auto lines = splitLines();
|
} else {
|
||||||
float maxWidth = 0.0f;
|
cachedSize_ = font_->measureText(text_);
|
||||||
float totalHeight = 0.0f;
|
}
|
||||||
float lineHeight = getLineHeight();
|
|
||||||
|
sizeDirty_ = false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -234,70 +212,70 @@ void Label::updateCache() const {
|
||||||
* @return 分割后的行列表
|
* @return 分割后的行列表
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> Label::splitLines() const {
|
std::vector<std::string> Label::splitLines() const {
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
if (text_.empty()) {
|
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;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -305,38 +283,38 @@ std::vector<std::string> Label::splitLines() const {
|
||||||
* @return 绘制位置坐标
|
* @return 绘制位置坐标
|
||||||
*/
|
*/
|
||||||
Vec2 Label::calculateDrawPosition() const {
|
Vec2 Label::calculateDrawPosition() const {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
Vec2 size = getTextSize();
|
Vec2 textSize = getTextSize();
|
||||||
Size widgetSize = getSize();
|
Size widgetSize = size();
|
||||||
|
|
||||||
float refWidth = widgetSize.empty() ? size.x : widgetSize.width;
|
float refWidth = widgetSize.empty() ? textSize.x : widgetSize.width;
|
||||||
float refHeight = widgetSize.empty() ? size.y : widgetSize.height;
|
float refHeight = widgetSize.empty() ? textSize.y : widgetSize.height;
|
||||||
|
|
||||||
switch (hAlign_) {
|
switch (hAlign_) {
|
||||||
case HorizontalAlign::Center:
|
case HorizontalAlign::Center:
|
||||||
position.x += (refWidth - size.x) * 0.5f;
|
position.x += (refWidth - textSize.x) * 0.5f;
|
||||||
break;
|
break;
|
||||||
case HorizontalAlign::Right:
|
case HorizontalAlign::Right:
|
||||||
position.x += refWidth - size.x;
|
position.x += refWidth - textSize.x;
|
||||||
break;
|
break;
|
||||||
case HorizontalAlign::Left:
|
case HorizontalAlign::Left:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (vAlign_) {
|
switch (vAlign_) {
|
||||||
case VerticalAlign::Middle:
|
case VerticalAlign::Middle:
|
||||||
position.y += (refHeight - size.y) * 0.5f;
|
position.y += (refHeight - textSize.y) * 0.5f;
|
||||||
break;
|
break;
|
||||||
case VerticalAlign::Bottom:
|
case VerticalAlign::Bottom:
|
||||||
position.y += refHeight - size.y;
|
position.y += refHeight - textSize.y;
|
||||||
break;
|
break;
|
||||||
case VerticalAlign::Top:
|
case VerticalAlign::Top:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -345,42 +323,43 @@ Vec2 Label::calculateDrawPosition() const {
|
||||||
* @param position 绘制位置
|
* @param position 绘制位置
|
||||||
* @param color 文本颜色
|
* @param color 文本颜色
|
||||||
*/
|
*/
|
||||||
void Label::drawText(RenderBackend &renderer, const Vec2 &position, const Color &color) {
|
void Label::drawText(RenderBackend &renderer, const Vec2 &position,
|
||||||
if (!font_ || text_.empty()) {
|
const Color &color) {
|
||||||
return;
|
if (!font_ || text_.empty()) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (multiLine_) {
|
if (multiLine_) {
|
||||||
auto lines = splitLines();
|
auto lines = splitLines();
|
||||||
float lineHeight = getLineHeight();
|
float lineHeight = getLineHeight();
|
||||||
Vec2 drawPos = position;
|
Vec2 drawPos = position;
|
||||||
|
|
||||||
for (const auto &line : lines) {
|
for (const auto &line : lines) {
|
||||||
renderer.drawText(*font_, line, drawPos, color);
|
renderer.drawText(*font_, line, drawPos, color);
|
||||||
drawPos.y += lineHeight;
|
drawPos.y += lineHeight;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
renderer.drawText(*font_, text_, position, color);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
renderer.drawText(*font_, text_, position, color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取边界框
|
* @brief 获取边界框
|
||||||
* @return 边界矩形
|
* @return 边界矩形
|
||||||
*/
|
*/
|
||||||
Rect Label::getBoundingBox() const {
|
Rect Label::boundingBox() const {
|
||||||
if (!font_ || text_.empty()) {
|
if (!font_ || text_.empty()) {
|
||||||
return Rect();
|
return Rect();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCache();
|
updateCache();
|
||||||
Vec2 size = cachedSize_;
|
Vec2 size = cachedSize_;
|
||||||
if (size.x <= 0.0f || size.y <= 0.0f) {
|
if (size.x <= 0.0f || size.y <= 0.0f) {
|
||||||
return Rect();
|
return Rect();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 drawPos = calculateDrawPosition();
|
Vec2 drawPos = calculateDrawPosition();
|
||||||
return Rect(drawPos.x, drawPos.y, size.x, size.y);
|
return Rect(drawPos.x, drawPos.y, size.x, size.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -388,30 +367,30 @@ Rect Label::getBoundingBox() const {
|
||||||
* @param renderer 渲染后端
|
* @param renderer 渲染后端
|
||||||
*/
|
*/
|
||||||
void Label::onDrawWidget(RenderBackend &renderer) {
|
void Label::onDrawWidget(RenderBackend &renderer) {
|
||||||
if (!font_ || text_.empty()) {
|
if (!font_ || text_.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 pos = calculateDrawPosition();
|
Vec2 pos = calculateDrawPosition();
|
||||||
|
|
||||||
if (shadowEnabled_) {
|
if (shadowEnabled_) {
|
||||||
Vec2 shadowPos = pos + shadowOffset_;
|
Vec2 shadowPos = pos + shadowOffset_;
|
||||||
drawText(renderer, shadowPos, shadowColor_);
|
drawText(renderer, shadowPos, shadowColor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outlineEnabled_) {
|
if (outlineEnabled_) {
|
||||||
float w = outlineWidth_;
|
float w = outlineWidth_;
|
||||||
drawText(renderer, pos + Vec2(-w, -w), outlineColor_);
|
drawText(renderer, pos + Vec2(-w, -w), outlineColor_);
|
||||||
drawText(renderer, pos + Vec2(0, -w), outlineColor_);
|
drawText(renderer, pos + Vec2(0, -w), outlineColor_);
|
||||||
drawText(renderer, pos + Vec2(w, -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, 0), outlineColor_);
|
drawText(renderer, pos + Vec2(w, 0), outlineColor_);
|
||||||
drawText(renderer, pos + Vec2(-w, w), outlineColor_);
|
drawText(renderer, pos + Vec2(-w, w), outlineColor_);
|
||||||
drawText(renderer, pos + Vec2(0, w), outlineColor_);
|
drawText(renderer, pos + Vec2(0, w), outlineColor_);
|
||||||
drawText(renderer, pos + Vec2(w, w), outlineColor_);
|
drawText(renderer, pos + Vec2(w, w), outlineColor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawText(renderer, pos, textColor_);
|
drawText(renderer, pos, textColor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include <ui/progress_bar.h>
|
|
||||||
#include <graphics/render_backend.h>
|
|
||||||
#include <core/string.h>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <core/string.h>
|
||||||
|
#include <graphics/render_backend.h>
|
||||||
|
#include <ui/progress_bar.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -9,17 +9,15 @@ namespace extra2d {
|
||||||
* @brief 默认构造函数
|
* @brief 默认构造函数
|
||||||
*/
|
*/
|
||||||
ProgressBar::ProgressBar() {
|
ProgressBar::ProgressBar() {
|
||||||
setAnchor(0.0f, 0.0f);
|
setAnchor(0.0f, 0.0f);
|
||||||
setSize(200.0f, 20.0f);
|
setSize(200.0f, 20.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建进度条对象
|
* @brief 创建进度条对象
|
||||||
* @return 进度条对象指针
|
* @return 进度条对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<ProgressBar> ProgressBar::create() {
|
Ptr<ProgressBar> ProgressBar::create() { return makePtr<ProgressBar>(); }
|
||||||
return makePtr<ProgressBar>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建带范围的进度条对象
|
* @brief 创建带范围的进度条对象
|
||||||
|
|
@ -29,10 +27,10 @@ Ptr<ProgressBar> ProgressBar::create() {
|
||||||
* @return 进度条对象指针
|
* @return 进度条对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<ProgressBar> ProgressBar::create(float min, float max, float value) {
|
Ptr<ProgressBar> ProgressBar::create(float min, float max, float value) {
|
||||||
auto bar = makePtr<ProgressBar>();
|
auto bar = makePtr<ProgressBar>();
|
||||||
bar->setRange(min, max);
|
bar->setRange(min, max);
|
||||||
bar->setValue(value);
|
bar->setValue(value);
|
||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -41,9 +39,9 @@ Ptr<ProgressBar> ProgressBar::create(float min, float max, float value) {
|
||||||
* @param max 最大值
|
* @param max 最大值
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setRange(float min, float max) {
|
void ProgressBar::setRange(float min, float max) {
|
||||||
min_ = min;
|
min_ = min;
|
||||||
max_ = max;
|
max_ = max;
|
||||||
setValue(value_);
|
setValue(value_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,15 +49,15 @@ void ProgressBar::setRange(float min, float max) {
|
||||||
* @param value 新值
|
* @param value 新值
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setValue(float value) {
|
void ProgressBar::setValue(float value) {
|
||||||
value_ = std::clamp(value, min_, max_);
|
value_ = std::clamp(value, min_, max_);
|
||||||
|
|
||||||
if (!animatedChangeEnabled_) {
|
if (!animatedChangeEnabled_) {
|
||||||
displayValue_ = value_;
|
displayValue_ = value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delayedDisplayEnabled_) {
|
if (delayedDisplayEnabled_) {
|
||||||
delayTimer_ = delayTime_;
|
delayTimer_ = delayTime_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -67,56 +65,49 @@ void ProgressBar::setValue(float value) {
|
||||||
* @return 百分比值(0.0-1.0)
|
* @return 百分比值(0.0-1.0)
|
||||||
*/
|
*/
|
||||||
float ProgressBar::getPercent() const {
|
float ProgressBar::getPercent() const {
|
||||||
if (max_ <= min_) return 0.0f;
|
if (max_ <= min_)
|
||||||
return (displayValue_ - min_) / (max_ - min_);
|
return 0.0f;
|
||||||
|
return (displayValue_ - min_) / (max_ - min_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置方向
|
* @brief 设置方向
|
||||||
* @param dir 方向枚举
|
* @param dir 方向枚举
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setDirection(Direction dir) {
|
void ProgressBar::setDirection(Direction dir) { direction_ = dir; }
|
||||||
direction_ = dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置背景颜色
|
* @brief 设置背景颜色
|
||||||
* @param color 背景颜色
|
* @param color 背景颜色
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setBackgroundColor(const Color &color) {
|
void ProgressBar::setBackgroundColor(const Color &color) { bgColor_ = color; }
|
||||||
bgColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置填充颜色
|
* @brief 设置填充颜色
|
||||||
* @param color 填充颜色
|
* @param color 填充颜色
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setFillColor(const Color &color) {
|
void ProgressBar::setFillColor(const Color &color) { fillColor_ = color; }
|
||||||
fillColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否启用渐变填充
|
* @brief 设置是否启用渐变填充
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setGradientFillEnabled(bool enabled) {
|
void ProgressBar::setGradientFillEnabled(bool enabled) {
|
||||||
gradientEnabled_ = enabled;
|
gradientEnabled_ = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置渐变结束颜色
|
* @brief 设置渐变结束颜色
|
||||||
* @param color 结束颜色
|
* @param color 结束颜色
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setFillColorEnd(const Color &color) {
|
void ProgressBar::setFillColorEnd(const Color &color) { fillColorEnd_ = color; }
|
||||||
fillColorEnd_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否启用分段颜色
|
* @brief 设置是否启用分段颜色
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setSegmentedColorsEnabled(bool enabled) {
|
void ProgressBar::setSegmentedColorsEnabled(bool enabled) {
|
||||||
segmentedColorsEnabled_ = enabled;
|
segmentedColorsEnabled_ = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -125,96 +116,78 @@ void ProgressBar::setSegmentedColorsEnabled(bool enabled) {
|
||||||
* @param color 颜色
|
* @param color 颜色
|
||||||
*/
|
*/
|
||||||
void ProgressBar::addColorSegment(float percentThreshold, const Color &color) {
|
void ProgressBar::addColorSegment(float percentThreshold, const Color &color) {
|
||||||
colorSegments_.push_back({percentThreshold, color});
|
colorSegments_.push_back({percentThreshold, color});
|
||||||
std::sort(colorSegments_.begin(), colorSegments_.end(),
|
std::sort(colorSegments_.begin(), colorSegments_.end(),
|
||||||
[](const auto &a, const auto &b) { return a.first > b.first; });
|
[](const auto &a, const auto &b) { return a.first > b.first; });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 清除所有颜色分段
|
* @brief 清除所有颜色分段
|
||||||
*/
|
*/
|
||||||
void ProgressBar::clearColorSegments() {
|
void ProgressBar::clearColorSegments() { colorSegments_.clear(); }
|
||||||
colorSegments_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置圆角半径
|
* @brief 设置圆角半径
|
||||||
* @param radius 圆角半径
|
* @param radius 圆角半径
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setCornerRadius(float radius) {
|
void ProgressBar::setCornerRadius(float radius) { cornerRadius_ = radius; }
|
||||||
cornerRadius_ = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否启用圆角
|
* @brief 设置是否启用圆角
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setRoundedCornersEnabled(bool enabled) {
|
void ProgressBar::setRoundedCornersEnabled(bool enabled) {
|
||||||
roundedCornersEnabled_ = enabled;
|
roundedCornersEnabled_ = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否启用边框
|
* @brief 设置是否启用边框
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setBorderEnabled(bool enabled) {
|
void ProgressBar::setBorderEnabled(bool enabled) { borderEnabled_ = enabled; }
|
||||||
borderEnabled_ = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置边框颜色
|
* @brief 设置边框颜色
|
||||||
* @param color 边框颜色
|
* @param color 边框颜色
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setBorderColor(const Color &color) {
|
void ProgressBar::setBorderColor(const Color &color) { borderColor_ = color; }
|
||||||
borderColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置边框宽度
|
* @brief 设置边框宽度
|
||||||
* @param width 边框宽度
|
* @param width 边框宽度
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setBorderWidth(float width) {
|
void ProgressBar::setBorderWidth(float width) { borderWidth_ = width; }
|
||||||
borderWidth_ = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置内边距
|
* @brief 设置内边距
|
||||||
* @param padding 内边距值
|
* @param padding 内边距值
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setPadding(float padding) {
|
void ProgressBar::setPadding(float padding) { padding_ = padding; }
|
||||||
padding_ = padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否启用文本显示
|
* @brief 设置是否启用文本显示
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setTextEnabled(bool enabled) {
|
void ProgressBar::setTextEnabled(bool enabled) { textEnabled_ = enabled; }
|
||||||
textEnabled_ = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置字体
|
* @brief 设置字体
|
||||||
* @param font 字体图集指针
|
* @param font 字体图集指针
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setFont(Ptr<FontAtlas> font) {
|
void ProgressBar::setFont(Ptr<FontAtlas> font) { font_ = font; }
|
||||||
font_ = font;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置文本颜色
|
* @brief 设置文本颜色
|
||||||
* @param color 文本颜色
|
* @param color 文本颜色
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setTextColor(const Color &color) {
|
void ProgressBar::setTextColor(const Color &color) { textColor_ = color; }
|
||||||
textColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置文本格式
|
* @brief 设置文本格式
|
||||||
* @param format 格式字符串
|
* @param format 格式字符串
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setTextFormat(const std::string &format) {
|
void ProgressBar::setTextFormat(const std::string &format) {
|
||||||
textFormat_ = format;
|
textFormat_ = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -222,82 +195,72 @@ void ProgressBar::setTextFormat(const std::string &format) {
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setAnimatedChangeEnabled(bool enabled) {
|
void ProgressBar::setAnimatedChangeEnabled(bool enabled) {
|
||||||
animatedChangeEnabled_ = enabled;
|
animatedChangeEnabled_ = enabled;
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
displayValue_ = value_;
|
displayValue_ = value_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置动画速度
|
* @brief 设置动画速度
|
||||||
* @param speed 每秒变化量
|
* @param speed 每秒变化量
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setAnimationSpeed(float speed) {
|
void ProgressBar::setAnimationSpeed(float speed) { animationSpeed_ = speed; }
|
||||||
animationSpeed_ = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否启用延迟显示
|
* @brief 设置是否启用延迟显示
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setDelayedDisplayEnabled(bool enabled) {
|
void ProgressBar::setDelayedDisplayEnabled(bool enabled) {
|
||||||
delayedDisplayEnabled_ = enabled;
|
delayedDisplayEnabled_ = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置延迟时间
|
* @brief 设置延迟时间
|
||||||
* @param seconds 延迟秒数
|
* @param seconds 延迟秒数
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setDelayTime(float seconds) {
|
void ProgressBar::setDelayTime(float seconds) { delayTime_ = seconds; }
|
||||||
delayTime_ = seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置延迟显示填充颜色
|
* @brief 设置延迟显示填充颜色
|
||||||
* @param color 填充颜色
|
* @param color 填充颜色
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setDelayedFillColor(const Color &color) {
|
void ProgressBar::setDelayedFillColor(const Color &color) {
|
||||||
delayedFillColor_ = color;
|
delayedFillColor_ = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否启用条纹效果
|
* @brief 设置是否启用条纹效果
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setStripedEnabled(bool enabled) {
|
void ProgressBar::setStripedEnabled(bool enabled) { stripedEnabled_ = enabled; }
|
||||||
stripedEnabled_ = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置条纹颜色
|
* @brief 设置条纹颜色
|
||||||
* @param color 条纹颜色
|
* @param color 条纹颜色
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setStripeColor(const Color &color) {
|
void ProgressBar::setStripeColor(const Color &color) { stripeColor_ = color; }
|
||||||
stripeColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置条纹移动速度
|
* @brief 设置条纹移动速度
|
||||||
* @param speed 移动速度
|
* @param speed 移动速度
|
||||||
*/
|
*/
|
||||||
void ProgressBar::setStripeSpeed(float speed) {
|
void ProgressBar::setStripeSpeed(float speed) { stripeSpeed_ = speed; }
|
||||||
stripeSpeed_ = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前填充颜色
|
* @brief 获取当前填充颜色
|
||||||
* @return 当前填充颜色
|
* @return 当前填充颜色
|
||||||
*/
|
*/
|
||||||
Color ProgressBar::getCurrentFillColor() const {
|
Color ProgressBar::getCurrentFillColor() const {
|
||||||
if (segmentedColorsEnabled_ && !colorSegments_.empty()) {
|
if (segmentedColorsEnabled_ && !colorSegments_.empty()) {
|
||||||
float percent = getPercent();
|
float percent = getPercent();
|
||||||
for (const auto &segment : colorSegments_) {
|
for (const auto &segment : colorSegments_) {
|
||||||
if (percent >= segment.first) {
|
if (percent >= segment.first) {
|
||||||
return segment.second;
|
return segment.second;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return fillColor_;
|
}
|
||||||
|
return fillColor_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -305,33 +268,33 @@ Color ProgressBar::getCurrentFillColor() const {
|
||||||
* @return 格式化后的文本
|
* @return 格式化后的文本
|
||||||
*/
|
*/
|
||||||
std::string ProgressBar::formatText() const {
|
std::string ProgressBar::formatText() const {
|
||||||
std::string result = textFormat_;
|
std::string result = textFormat_;
|
||||||
|
|
||||||
size_t pos = result.find("{value}");
|
size_t pos = result.find("{value}");
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
result.replace(pos, 7, std::to_string(static_cast<int>(displayValue_)));
|
result.replace(pos, 7, std::to_string(static_cast<int>(displayValue_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = result.find("{max}");
|
pos = result.find("{max}");
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
result.replace(pos, 5, std::to_string(static_cast<int>(max_)));
|
result.replace(pos, 5, std::to_string(static_cast<int>(max_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = result.find("{percent}");
|
pos = result.find("{percent}");
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
int percent = static_cast<int>(getPercent() * 100);
|
int percent = static_cast<int>(getPercent() * 100);
|
||||||
result.replace(pos, 9, std::to_string(percent));
|
result.replace(pos, 9, std::to_string(percent));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取边界框
|
* @brief 获取边界框
|
||||||
* @return 边界矩形
|
* @return 边界矩形
|
||||||
*/
|
*/
|
||||||
Rect ProgressBar::getBoundingBox() const {
|
Rect ProgressBar::boundingBox() const {
|
||||||
return Rect(pos().x, pos().y, getSize().width, getSize().height);
|
return Rect(pos().x, pos().y, size().width, size().height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -339,38 +302,38 @@ Rect ProgressBar::getBoundingBox() const {
|
||||||
* @param deltaTime 帧间隔时间
|
* @param deltaTime 帧间隔时间
|
||||||
*/
|
*/
|
||||||
void ProgressBar::onUpdate(float deltaTime) {
|
void ProgressBar::onUpdate(float deltaTime) {
|
||||||
if (animatedChangeEnabled_ && displayValue_ != value_) {
|
if (animatedChangeEnabled_ && displayValue_ != value_) {
|
||||||
float diff = value_ - displayValue_;
|
float diff = value_ - displayValue_;
|
||||||
float change = animationSpeed_ * deltaTime;
|
float change = animationSpeed_ * deltaTime;
|
||||||
|
|
||||||
if (std::abs(diff) <= change) {
|
if (std::abs(diff) <= change) {
|
||||||
displayValue_ = value_;
|
displayValue_ = value_;
|
||||||
} else {
|
} else {
|
||||||
displayValue_ += (diff > 0 ? change : -change);
|
displayValue_ += (diff > 0 ? change : -change);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (delayedDisplayEnabled_) {
|
|
||||||
if (delayTimer_ > 0.0f) {
|
if (delayedDisplayEnabled_) {
|
||||||
delayTimer_ -= deltaTime;
|
if (delayTimer_ > 0.0f) {
|
||||||
} else {
|
delayTimer_ -= deltaTime;
|
||||||
float diff = displayValue_ - delayedValue_;
|
} else {
|
||||||
float change = animationSpeed_ * deltaTime;
|
float diff = displayValue_ - delayedValue_;
|
||||||
|
float change = animationSpeed_ * deltaTime;
|
||||||
if (std::abs(diff) <= change) {
|
|
||||||
delayedValue_ = displayValue_;
|
if (std::abs(diff) <= change) {
|
||||||
} else {
|
delayedValue_ = displayValue_;
|
||||||
delayedValue_ += (diff > 0 ? change : -change);
|
} else {
|
||||||
}
|
delayedValue_ += (diff > 0 ? change : -change);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (stripedEnabled_) {
|
|
||||||
stripeOffset_ += stripeSpeed_ * deltaTime;
|
if (stripedEnabled_) {
|
||||||
if (stripeOffset_ > 20.0f) {
|
stripeOffset_ += stripeSpeed_ * deltaTime;
|
||||||
stripeOffset_ -= 20.0f;
|
if (stripeOffset_ > 20.0f) {
|
||||||
}
|
stripeOffset_ -= 20.0f;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -378,104 +341,102 @@ void ProgressBar::onUpdate(float deltaTime) {
|
||||||
* @param renderer 渲染后端
|
* @param renderer 渲染后端
|
||||||
*/
|
*/
|
||||||
void ProgressBar::onDrawWidget(RenderBackend &renderer) {
|
void ProgressBar::onDrawWidget(RenderBackend &renderer) {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
Size size = getSize();
|
Size widgetSize = size();
|
||||||
|
|
||||||
float bgX = position.x + padding_;
|
float bgX = position.x + padding_;
|
||||||
float bgY = position.y + padding_;
|
float bgY = position.y + padding_;
|
||||||
float bgW = size.width - padding_ * 2;
|
float bgW = widgetSize.width - padding_ * 2;
|
||||||
float bgH = size.height - padding_ * 2;
|
float bgH = widgetSize.height - padding_ * 2;
|
||||||
Rect bgRect(bgX, bgY, bgW, bgH);
|
Rect bgRect(bgX, bgY, bgW, bgH);
|
||||||
|
|
||||||
if (roundedCornersEnabled_) {
|
if (roundedCornersEnabled_) {
|
||||||
fillRoundedRect(renderer, bgRect, bgColor_, cornerRadius_);
|
fillRoundedRect(renderer, bgRect, bgColor_, cornerRadius_);
|
||||||
} else {
|
} else {
|
||||||
renderer.fillRect(bgRect, bgColor_);
|
renderer.fillRect(bgRect, bgColor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
float percent = getPercent();
|
float percent = getPercent();
|
||||||
float fillX = bgX, fillY = bgY, fillW = bgW, fillH = bgH;
|
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_) {
|
switch (direction_) {
|
||||||
case Direction::LeftToRight:
|
case Direction::LeftToRight:
|
||||||
fillW = bgW * percent;
|
delayedW = bgW * delayedPercent;
|
||||||
break;
|
break;
|
||||||
case Direction::RightToLeft:
|
case Direction::RightToLeft:
|
||||||
fillW = bgW * percent;
|
delayedW = bgW * delayedPercent;
|
||||||
fillX = bgX + bgW - fillW;
|
delayedX = bgX + bgW - delayedW;
|
||||||
break;
|
break;
|
||||||
case Direction::BottomToTop:
|
case Direction::BottomToTop:
|
||||||
fillH = bgH * percent;
|
delayedH = bgH * delayedPercent;
|
||||||
fillY = bgY + bgH - fillH;
|
delayedY = bgY + bgH - delayedH;
|
||||||
break;
|
break;
|
||||||
case Direction::TopToBottom:
|
case Direction::TopToBottom:
|
||||||
fillH = bgH * percent;
|
delayedH = bgH * delayedPercent;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Rect fillRect(fillX, fillY, fillW, fillH);
|
Rect delayedRect(delayedX, delayedY, delayedW, delayedH);
|
||||||
|
|
||||||
if (delayedDisplayEnabled_ && delayedValue_ > displayValue_) {
|
if (roundedCornersEnabled_) {
|
||||||
float delayedPercent = (delayedValue_ - min_) / (max_ - min_);
|
fillRoundedRect(renderer, delayedRect, delayedFillColor_, cornerRadius_);
|
||||||
float delayedX = bgX, delayedY = bgY, delayedW = bgW, delayedH = bgH;
|
} else {
|
||||||
|
renderer.fillRect(delayedRect, delayedFillColor_);
|
||||||
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 (fillW > 0 && fillH > 0) {
|
||||||
|
Color fillColor = getCurrentFillColor();
|
||||||
if (roundedCornersEnabled_) {
|
|
||||||
fillRoundedRect(renderer, fillRect, fillColor, cornerRadius_);
|
if (roundedCornersEnabled_) {
|
||||||
} else {
|
fillRoundedRect(renderer, fillRect, fillColor, cornerRadius_);
|
||||||
renderer.fillRect(fillRect, fillColor);
|
} else {
|
||||||
}
|
renderer.fillRect(fillRect, fillColor);
|
||||||
|
|
||||||
if (stripedEnabled_) {
|
|
||||||
drawStripes(renderer, fillRect);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (borderEnabled_) {
|
if (stripedEnabled_) {
|
||||||
if (roundedCornersEnabled_) {
|
drawStripes(renderer, fillRect);
|
||||||
drawRoundedRect(renderer, bgRect, borderColor_, cornerRadius_);
|
|
||||||
} else {
|
|
||||||
renderer.drawRect(bgRect, borderColor_, borderWidth_);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (textEnabled_ && font_) {
|
|
||||||
std::string text = formatText();
|
if (borderEnabled_) {
|
||||||
Vec2 textSize = font_->measureText(text);
|
if (roundedCornersEnabled_) {
|
||||||
|
drawRoundedRect(renderer, bgRect, borderColor_, cornerRadius_);
|
||||||
Vec2 textPos(
|
} else {
|
||||||
position.x + (size.width - textSize.x) * 0.5f,
|
renderer.drawRect(bgRect, borderColor_, borderWidth_);
|
||||||
position.y + (size.height - textSize.y) * 0.5f
|
|
||||||
);
|
|
||||||
|
|
||||||
renderer.drawText(*font_, text, textPos, textColor_);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -485,8 +446,9 @@ void ProgressBar::onDrawWidget(RenderBackend &renderer) {
|
||||||
* @param color 颜色
|
* @param color 颜色
|
||||||
* @param radius 圆角半径
|
* @param radius 圆角半径
|
||||||
*/
|
*/
|
||||||
void ProgressBar::drawRoundedRect(RenderBackend &renderer, const Rect &rect, const Color &color, float radius) {
|
void ProgressBar::drawRoundedRect(RenderBackend &renderer, const Rect &rect,
|
||||||
renderer.drawRect(rect, color, borderWidth_);
|
const Color &color, float radius) {
|
||||||
|
renderer.drawRect(rect, color, borderWidth_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -496,8 +458,9 @@ void ProgressBar::drawRoundedRect(RenderBackend &renderer, const Rect &rect, con
|
||||||
* @param color 颜色
|
* @param color 颜色
|
||||||
* @param radius 圆角半径
|
* @param radius 圆角半径
|
||||||
*/
|
*/
|
||||||
void ProgressBar::fillRoundedRect(RenderBackend &renderer, const Rect &rect, const Color &color, float radius) {
|
void ProgressBar::fillRoundedRect(RenderBackend &renderer, const Rect &rect,
|
||||||
renderer.fillRect(rect, color);
|
const Color &color, float radius) {
|
||||||
|
renderer.fillRect(rect, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -506,31 +469,34 @@ void ProgressBar::fillRoundedRect(RenderBackend &renderer, const Rect &rect, con
|
||||||
* @param rect 矩形区域
|
* @param rect 矩形区域
|
||||||
*/
|
*/
|
||||||
void ProgressBar::drawStripes(RenderBackend &renderer, const Rect &rect) {
|
void ProgressBar::drawStripes(RenderBackend &renderer, const Rect &rect) {
|
||||||
const float stripeWidth = 10.0f;
|
const float stripeWidth = 10.0f;
|
||||||
const float spacing = 20.0f;
|
const float spacing = 20.0f;
|
||||||
float rectRight = rect.origin.x + rect.size.width;
|
float rectRight = rect.origin.x + rect.size.width;
|
||||||
float rectBottom = rect.origin.y + rect.size.height;
|
float rectBottom = rect.origin.y + rect.size.height;
|
||||||
|
|
||||||
for (float x = rect.origin.x - spacing + stripeOffset_; x < rectRight; x += spacing) {
|
for (float x = rect.origin.x - spacing + stripeOffset_; x < rectRight;
|
||||||
float x1 = x;
|
x += spacing) {
|
||||||
float y1 = rect.origin.y;
|
float x1 = x;
|
||||||
float x2 = x + stripeWidth;
|
float y1 = rect.origin.y;
|
||||||
float y2 = rectBottom;
|
float x2 = x + stripeWidth;
|
||||||
|
float y2 = rectBottom;
|
||||||
if (x1 < rect.origin.x) x1 = rect.origin.x;
|
|
||||||
if (x2 > rectRight) x2 = rectRight;
|
if (x1 < rect.origin.x)
|
||||||
|
x1 = rect.origin.x;
|
||||||
if (x2 > x1) {
|
if (x2 > rectRight)
|
||||||
for (int i = 0; i < static_cast<int>(rect.size.height); i += 4) {
|
x2 = rectRight;
|
||||||
float sy = rect.origin.y + i;
|
|
||||||
float sx = x1 + i * 0.5f;
|
if (x2 > x1) {
|
||||||
if (sx < x2) {
|
for (int i = 0; i < static_cast<int>(rect.size.height); i += 4) {
|
||||||
Rect stripeRect(sx, sy, std::min(2.0f, x2 - sx), 2.0f);
|
float sy = rect.origin.y + i;
|
||||||
renderer.fillRect(stripeRect, stripeColor_);
|
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
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include <ui/radio_button.h>
|
|
||||||
#include <graphics/render_backend.h>
|
|
||||||
#include <core/string.h>
|
#include <core/string.h>
|
||||||
|
#include <graphics/render_backend.h>
|
||||||
|
#include <ui/radio_button.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -8,17 +8,15 @@ namespace extra2d {
|
||||||
* @brief 默认构造函数
|
* @brief 默认构造函数
|
||||||
*/
|
*/
|
||||||
RadioButton::RadioButton() {
|
RadioButton::RadioButton() {
|
||||||
setAnchor(0.0f, 0.0f);
|
setAnchor(0.0f, 0.0f);
|
||||||
setSize(circleSize_, circleSize_);
|
setSize(circleSize_, circleSize_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建单选按钮对象
|
* @brief 创建单选按钮对象
|
||||||
* @return 单选按钮对象指针
|
* @return 单选按钮对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<RadioButton> RadioButton::create() {
|
Ptr<RadioButton> RadioButton::create() { return makePtr<RadioButton>(); }
|
||||||
return makePtr<RadioButton>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建带标签的单选按钮对象
|
* @brief 创建带标签的单选按钮对象
|
||||||
|
|
@ -26,9 +24,9 @@ Ptr<RadioButton> RadioButton::create() {
|
||||||
* @return 单选按钮对象指针
|
* @return 单选按钮对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<RadioButton> RadioButton::create(const std::string &label) {
|
Ptr<RadioButton> RadioButton::create(const std::string &label) {
|
||||||
auto rb = makePtr<RadioButton>();
|
auto rb = makePtr<RadioButton>();
|
||||||
rb->setLabel(label);
|
rb->setLabel(label);
|
||||||
return rb;
|
return rb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,60 +34,50 @@ Ptr<RadioButton> RadioButton::create(const std::string &label) {
|
||||||
* @param selected 是否选中
|
* @param selected 是否选中
|
||||||
*/
|
*/
|
||||||
void RadioButton::setSelected(bool selected) {
|
void RadioButton::setSelected(bool selected) {
|
||||||
if (selected_ != selected) {
|
if (selected_ != selected) {
|
||||||
selected_ = selected;
|
selected_ = selected;
|
||||||
if (onStateChange_) {
|
if (onStateChange_) {
|
||||||
onStateChange_(selected_);
|
onStateChange_(selected_);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置标签文本
|
* @brief 设置标签文本
|
||||||
* @param label 标签文本
|
* @param label 标签文本
|
||||||
*/
|
*/
|
||||||
void RadioButton::setLabel(const std::string &label) {
|
void RadioButton::setLabel(const std::string &label) { label_ = label; }
|
||||||
label_ = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置字体
|
* @brief 设置字体
|
||||||
* @param font 字体图集指针
|
* @param font 字体图集指针
|
||||||
*/
|
*/
|
||||||
void RadioButton::setFont(Ptr<FontAtlas> font) {
|
void RadioButton::setFont(Ptr<FontAtlas> font) { font_ = font; }
|
||||||
font_ = font;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置文本颜色
|
* @brief 设置文本颜色
|
||||||
* @param color 文本颜色
|
* @param color 文本颜色
|
||||||
*/
|
*/
|
||||||
void RadioButton::setTextColor(const Color &color) {
|
void RadioButton::setTextColor(const Color &color) { textColor_ = color; }
|
||||||
textColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置圆形尺寸
|
* @brief 设置圆形尺寸
|
||||||
* @param size 圆形尺寸
|
* @param size 圆形尺寸
|
||||||
*/
|
*/
|
||||||
void RadioButton::setCircleSize(float size) {
|
void RadioButton::setCircleSize(float size) { circleSize_ = size; }
|
||||||
circleSize_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置间距
|
* @brief 设置间距
|
||||||
* @param spacing 间距值
|
* @param spacing 间距值
|
||||||
*/
|
*/
|
||||||
void RadioButton::setSpacing(float spacing) {
|
void RadioButton::setSpacing(float spacing) { spacing_ = spacing; }
|
||||||
spacing_ = spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置选中颜色
|
* @brief 设置选中颜色
|
||||||
* @param color 选中颜色
|
* @param color 选中颜色
|
||||||
*/
|
*/
|
||||||
void RadioButton::setSelectedColor(const Color &color) {
|
void RadioButton::setSelectedColor(const Color &color) {
|
||||||
selectedColor_ = color;
|
selectedColor_ = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -97,47 +85,43 @@ void RadioButton::setSelectedColor(const Color &color) {
|
||||||
* @param color 未选中颜色
|
* @param color 未选中颜色
|
||||||
*/
|
*/
|
||||||
void RadioButton::setUnselectedColor(const Color &color) {
|
void RadioButton::setUnselectedColor(const Color &color) {
|
||||||
unselectedColor_ = color;
|
unselectedColor_ = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置圆点颜色
|
* @brief 设置圆点颜色
|
||||||
* @param color 圆点颜色
|
* @param color 圆点颜色
|
||||||
*/
|
*/
|
||||||
void RadioButton::setDotColor(const Color &color) {
|
void RadioButton::setDotColor(const Color &color) { dotColor_ = color; }
|
||||||
dotColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置分组ID
|
* @brief 设置分组ID
|
||||||
* @param groupId 分组ID
|
* @param groupId 分组ID
|
||||||
*/
|
*/
|
||||||
void RadioButton::setGroupId(int groupId) {
|
void RadioButton::setGroupId(int groupId) { groupId_ = groupId; }
|
||||||
groupId_ = groupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置状态改变回调
|
* @brief 设置状态改变回调
|
||||||
* @param callback 回调函数
|
* @param callback 回调函数
|
||||||
*/
|
*/
|
||||||
void RadioButton::setOnStateChange(Function<void(bool)> callback) {
|
void RadioButton::setOnStateChange(Function<void(bool)> callback) {
|
||||||
onStateChange_ = callback;
|
onStateChange_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取边界框
|
* @brief 获取边界框
|
||||||
* @return 边界矩形
|
* @return 边界矩形
|
||||||
*/
|
*/
|
||||||
Rect RadioButton::getBoundingBox() const {
|
Rect RadioButton::boundingBox() const {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
float width = circleSize_;
|
float width = circleSize_;
|
||||||
|
|
||||||
if (!label_.empty() && font_) {
|
if (!label_.empty() && font_) {
|
||||||
Vec2 textSize = font_->measureText(label_);
|
Vec2 textSize = font_->measureText(label_);
|
||||||
width += spacing_ + textSize.x;
|
width += spacing_ + textSize.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Rect(position.x, position.y, width, circleSize_);
|
return Rect(position.x, position.y, width, circleSize_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -145,24 +129,25 @@ Rect RadioButton::getBoundingBox() const {
|
||||||
* @param renderer 渲染后端
|
* @param renderer 渲染后端
|
||||||
*/
|
*/
|
||||||
void RadioButton::onDrawWidget(RenderBackend &renderer) {
|
void RadioButton::onDrawWidget(RenderBackend &renderer) {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
float centerX = position.x + circleSize_ * 0.5f;
|
float centerX = position.x + circleSize_ * 0.5f;
|
||||||
float centerY = position.y + getSize().height * 0.5f;
|
float centerY = position.y + size().height * 0.5f;
|
||||||
float radius = circleSize_ * 0.5f;
|
float radius = circleSize_ * 0.5f;
|
||||||
|
|
||||||
Color circleColor = selected_ ? selectedColor_ : unselectedColor_;
|
Color circleColor = selected_ ? selectedColor_ : unselectedColor_;
|
||||||
renderer.drawCircle(Vec2(centerX, centerY), radius, circleColor, true);
|
renderer.drawCircle(Vec2(centerX, centerY), radius, circleColor, true);
|
||||||
renderer.drawCircle(Vec2(centerX, centerY), radius, Colors::White, false, 1.0f);
|
renderer.drawCircle(Vec2(centerX, centerY), radius, Colors::White, false,
|
||||||
|
1.0f);
|
||||||
if (selected_) {
|
|
||||||
float dotRadius = radius * 0.4f;
|
if (selected_) {
|
||||||
renderer.drawCircle(Vec2(centerX, centerY), dotRadius, dotColor_, true);
|
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);
|
if (!label_.empty() && font_) {
|
||||||
renderer.drawText(*font_, label_, textPos, textColor_);
|
Vec2 textPos(position.x + circleSize_ + spacing_, position.y);
|
||||||
}
|
renderer.drawText(*font_, label_, textPos, textColor_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -171,11 +156,11 @@ void RadioButton::onDrawWidget(RenderBackend &renderer) {
|
||||||
* @return 是否处理了事件
|
* @return 是否处理了事件
|
||||||
*/
|
*/
|
||||||
bool RadioButton::onMousePress(const MouseEvent &event) {
|
bool RadioButton::onMousePress(const MouseEvent &event) {
|
||||||
if (event.button == MouseButton::Left) {
|
if (event.button == MouseButton::Left) {
|
||||||
pressed_ = true;
|
pressed_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -184,21 +169,21 @@ bool RadioButton::onMousePress(const MouseEvent &event) {
|
||||||
* @return 是否处理了事件
|
* @return 是否处理了事件
|
||||||
*/
|
*/
|
||||||
bool RadioButton::onMouseRelease(const MouseEvent &event) {
|
bool RadioButton::onMouseRelease(const MouseEvent &event) {
|
||||||
if (event.button == MouseButton::Left && pressed_) {
|
if (event.button == MouseButton::Left && pressed_) {
|
||||||
pressed_ = false;
|
pressed_ = false;
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
float centerX = position.x + circleSize_ * 0.5f;
|
float centerX = position.x + circleSize_ * 0.5f;
|
||||||
float centerY = position.y + getSize().height * 0.5f;
|
float centerY = position.y + size().height * 0.5f;
|
||||||
float radius = circleSize_ * 0.5f;
|
float radius = circleSize_ * 0.5f;
|
||||||
|
|
||||||
float dx = event.x - centerX;
|
float dx = event.x - centerX;
|
||||||
float dy = event.y - centerY;
|
float dy = event.y - centerY;
|
||||||
if (dx * dx + dy * dy <= radius * radius) {
|
if (dx * dx + dy * dy <= radius * radius) {
|
||||||
setSelected(true);
|
setSelected(true);
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -210,18 +195,19 @@ bool RadioButton::onMouseRelease(const MouseEvent &event) {
|
||||||
* @param button 单选按钮指针
|
* @param button 单选按钮指针
|
||||||
*/
|
*/
|
||||||
void RadioButtonGroup::addButton(RadioButton *button) {
|
void RadioButtonGroup::addButton(RadioButton *button) {
|
||||||
if (button && std::find(buttons_.begin(), buttons_.end(), button) == buttons_.end()) {
|
if (button &&
|
||||||
buttons_.push_back(button);
|
std::find(buttons_.begin(), buttons_.end(), button) == buttons_.end()) {
|
||||||
button->setOnStateChange([this, button](bool selected) {
|
buttons_.push_back(button);
|
||||||
if (selected) {
|
button->setOnStateChange([this, button](bool selected) {
|
||||||
selectButton(button);
|
if (selected) {
|
||||||
}
|
selectButton(button);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
if (button->isSelected() && !selectedButton_) {
|
|
||||||
selectedButton_ = button;
|
if (button->isSelected() && !selectedButton_) {
|
||||||
}
|
selectedButton_ = button;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -229,13 +215,13 @@ void RadioButtonGroup::addButton(RadioButton *button) {
|
||||||
* @param button 单选按钮指针
|
* @param button 单选按钮指针
|
||||||
*/
|
*/
|
||||||
void RadioButtonGroup::removeButton(RadioButton *button) {
|
void RadioButtonGroup::removeButton(RadioButton *button) {
|
||||||
auto it = std::find(buttons_.begin(), buttons_.end(), button);
|
auto it = std::find(buttons_.begin(), buttons_.end(), button);
|
||||||
if (it != buttons_.end()) {
|
if (it != buttons_.end()) {
|
||||||
buttons_.erase(it);
|
buttons_.erase(it);
|
||||||
if (selectedButton_ == button) {
|
if (selectedButton_ == button) {
|
||||||
selectedButton_ = nullptr;
|
selectedButton_ = nullptr;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -243,28 +229,30 @@ void RadioButtonGroup::removeButton(RadioButton *button) {
|
||||||
* @param button 单选按钮指针
|
* @param button 单选按钮指针
|
||||||
*/
|
*/
|
||||||
void RadioButtonGroup::selectButton(RadioButton *button) {
|
void RadioButtonGroup::selectButton(RadioButton *button) {
|
||||||
if (selectedButton_ == button) return;
|
if (selectedButton_ == button)
|
||||||
|
return;
|
||||||
if (selectedButton_) {
|
|
||||||
selectedButton_->setSelected(false);
|
if (selectedButton_) {
|
||||||
}
|
selectedButton_->setSelected(false);
|
||||||
|
}
|
||||||
selectedButton_ = button;
|
|
||||||
if (button) {
|
selectedButton_ = button;
|
||||||
button->setSelected(true);
|
if (button) {
|
||||||
}
|
button->setSelected(true);
|
||||||
|
}
|
||||||
if (onSelectionChange_) {
|
|
||||||
onSelectionChange_(button);
|
if (onSelectionChange_) {
|
||||||
}
|
onSelectionChange_(button);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置选择改变回调
|
* @brief 设置选择改变回调
|
||||||
* @param callback 回调函数
|
* @param callback 回调函数
|
||||||
*/
|
*/
|
||||||
void RadioButtonGroup::setOnSelectionChange(Function<void(RadioButton*)> callback) {
|
void RadioButtonGroup::setOnSelectionChange(
|
||||||
onSelectionChange_ = callback;
|
Function<void(RadioButton *)> callback) {
|
||||||
|
onSelectionChange_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include <ui/slider.h>
|
|
||||||
#include <graphics/render_backend.h>
|
|
||||||
#include <core/string.h>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <core/string.h>
|
||||||
|
#include <graphics/render_backend.h>
|
||||||
|
#include <ui/slider.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -9,17 +9,15 @@ namespace extra2d {
|
||||||
* @brief 默认构造函数
|
* @brief 默认构造函数
|
||||||
*/
|
*/
|
||||||
Slider::Slider() {
|
Slider::Slider() {
|
||||||
setAnchor(0.0f, 0.0f);
|
setAnchor(0.0f, 0.0f);
|
||||||
setSize(200.0f, 20.0f);
|
setSize(200.0f, 20.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建滑动条对象
|
* @brief 创建滑动条对象
|
||||||
* @return 滑动条对象指针
|
* @return 滑动条对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<Slider> Slider::create() {
|
Ptr<Slider> Slider::create() { return makePtr<Slider>(); }
|
||||||
return makePtr<Slider>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 创建带范围的滑动条对象
|
* @brief 创建带范围的滑动条对象
|
||||||
|
|
@ -29,10 +27,10 @@ Ptr<Slider> Slider::create() {
|
||||||
* @return 滑动条对象指针
|
* @return 滑动条对象指针
|
||||||
*/
|
*/
|
||||||
Ptr<Slider> Slider::create(float min, float max, float value) {
|
Ptr<Slider> Slider::create(float min, float max, float value) {
|
||||||
auto slider = makePtr<Slider>();
|
auto slider = makePtr<Slider>();
|
||||||
slider->setRange(min, max);
|
slider->setRange(min, max);
|
||||||
slider->setValue(value);
|
slider->setValue(value);
|
||||||
return slider;
|
return slider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -41,9 +39,9 @@ Ptr<Slider> Slider::create(float min, float max, float value) {
|
||||||
* @param max 最大值
|
* @param max 最大值
|
||||||
*/
|
*/
|
||||||
void Slider::setRange(float min, float max) {
|
void Slider::setRange(float min, float max) {
|
||||||
min_ = min;
|
min_ = min;
|
||||||
max_ = max;
|
max_ = max;
|
||||||
setValue(value_);
|
setValue(value_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,17 +49,17 @@ void Slider::setRange(float min, float max) {
|
||||||
* @param value 新值
|
* @param value 新值
|
||||||
*/
|
*/
|
||||||
void Slider::setValue(float value) {
|
void Slider::setValue(float value) {
|
||||||
float newValue = std::clamp(value, min_, max_);
|
float newValue = std::clamp(value, min_, max_);
|
||||||
if (step_ > 0.0f) {
|
if (step_ > 0.0f) {
|
||||||
newValue = snapToStep(newValue);
|
newValue = snapToStep(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value_ != newValue) {
|
if (value_ != newValue) {
|
||||||
value_ = newValue;
|
value_ = newValue;
|
||||||
if (onValueChange_) {
|
if (onValueChange_) {
|
||||||
onValueChange_(value_);
|
onValueChange_(value_);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,66 +67,54 @@ void Slider::setValue(float value) {
|
||||||
* @param step 步进值
|
* @param step 步进值
|
||||||
*/
|
*/
|
||||||
void Slider::setStep(float step) {
|
void Slider::setStep(float step) {
|
||||||
step_ = step;
|
step_ = step;
|
||||||
if (step_ > 0.0f) {
|
if (step_ > 0.0f) {
|
||||||
setValue(value_);
|
setValue(value_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否为垂直方向
|
* @brief 设置是否为垂直方向
|
||||||
* @param vertical 是否垂直
|
* @param vertical 是否垂直
|
||||||
*/
|
*/
|
||||||
void Slider::setVertical(bool vertical) {
|
void Slider::setVertical(bool vertical) { vertical_ = vertical; }
|
||||||
vertical_ = vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置轨道尺寸
|
* @brief 设置轨道尺寸
|
||||||
* @param size 轨道尺寸
|
* @param size 轨道尺寸
|
||||||
*/
|
*/
|
||||||
void Slider::setTrackSize(float size) {
|
void Slider::setTrackSize(float size) { trackSize_ = size; }
|
||||||
trackSize_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置滑块尺寸
|
* @brief 设置滑块尺寸
|
||||||
* @param size 滑块尺寸
|
* @param size 滑块尺寸
|
||||||
*/
|
*/
|
||||||
void Slider::setThumbSize(float size) {
|
void Slider::setThumbSize(float size) { thumbSize_ = size; }
|
||||||
thumbSize_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置轨道颜色
|
* @brief 设置轨道颜色
|
||||||
* @param color 轨道颜色
|
* @param color 轨道颜色
|
||||||
*/
|
*/
|
||||||
void Slider::setTrackColor(const Color &color) {
|
void Slider::setTrackColor(const Color &color) { trackColor_ = color; }
|
||||||
trackColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置填充颜色
|
* @brief 设置填充颜色
|
||||||
* @param color 填充颜色
|
* @param color 填充颜色
|
||||||
*/
|
*/
|
||||||
void Slider::setFillColor(const Color &color) {
|
void Slider::setFillColor(const Color &color) { fillColor_ = color; }
|
||||||
fillColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置滑块颜色
|
* @brief 设置滑块颜色
|
||||||
* @param color 滑块颜色
|
* @param color 滑块颜色
|
||||||
*/
|
*/
|
||||||
void Slider::setThumbColor(const Color &color) {
|
void Slider::setThumbColor(const Color &color) { thumbColor_ = color; }
|
||||||
thumbColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置滑块悬停颜色
|
* @brief 设置滑块悬停颜色
|
||||||
* @param color 悬停颜色
|
* @param color 悬停颜色
|
||||||
*/
|
*/
|
||||||
void Slider::setThumbHoverColor(const Color &color) {
|
void Slider::setThumbHoverColor(const Color &color) {
|
||||||
thumbHoverColor_ = color;
|
thumbHoverColor_ = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -136,63 +122,51 @@ void Slider::setThumbHoverColor(const Color &color) {
|
||||||
* @param color 按下颜色
|
* @param color 按下颜色
|
||||||
*/
|
*/
|
||||||
void Slider::setThumbPressedColor(const Color &color) {
|
void Slider::setThumbPressedColor(const Color &color) {
|
||||||
thumbPressedColor_ = color;
|
thumbPressedColor_ = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否显示滑块
|
* @brief 设置是否显示滑块
|
||||||
* @param show 是否显示
|
* @param show 是否显示
|
||||||
*/
|
*/
|
||||||
void Slider::setShowThumb(bool show) {
|
void Slider::setShowThumb(bool show) { showThumb_ = show; }
|
||||||
showThumb_ = show;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否显示填充
|
* @brief 设置是否显示填充
|
||||||
* @param show 是否显示
|
* @param show 是否显示
|
||||||
*/
|
*/
|
||||||
void Slider::setShowFill(bool show) {
|
void Slider::setShowFill(bool show) { showFill_ = show; }
|
||||||
showFill_ = show;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置是否启用文本显示
|
* @brief 设置是否启用文本显示
|
||||||
* @param enabled 是否启用
|
* @param enabled 是否启用
|
||||||
*/
|
*/
|
||||||
void Slider::setTextEnabled(bool enabled) {
|
void Slider::setTextEnabled(bool enabled) { textEnabled_ = enabled; }
|
||||||
textEnabled_ = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置字体
|
* @brief 设置字体
|
||||||
* @param font 字体图集指针
|
* @param font 字体图集指针
|
||||||
*/
|
*/
|
||||||
void Slider::setFont(Ptr<FontAtlas> font) {
|
void Slider::setFont(Ptr<FontAtlas> font) { font_ = font; }
|
||||||
font_ = font;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置文本颜色
|
* @brief 设置文本颜色
|
||||||
* @param color 文本颜色
|
* @param color 文本颜色
|
||||||
*/
|
*/
|
||||||
void Slider::setTextColor(const Color &color) {
|
void Slider::setTextColor(const Color &color) { textColor_ = color; }
|
||||||
textColor_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置文本格式
|
* @brief 设置文本格式
|
||||||
* @param format 格式字符串
|
* @param format 格式字符串
|
||||||
*/
|
*/
|
||||||
void Slider::setTextFormat(const std::string &format) {
|
void Slider::setTextFormat(const std::string &format) { textFormat_ = format; }
|
||||||
textFormat_ = format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置值改变回调
|
* @brief 设置值改变回调
|
||||||
* @param callback 回调函数
|
* @param callback 回调函数
|
||||||
*/
|
*/
|
||||||
void Slider::setOnValueChange(Function<void(float)> callback) {
|
void Slider::setOnValueChange(Function<void(float)> callback) {
|
||||||
onValueChange_ = callback;
|
onValueChange_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,23 +174,21 @@ void Slider::setOnValueChange(Function<void(float)> callback) {
|
||||||
* @param callback 回调函数
|
* @param callback 回调函数
|
||||||
*/
|
*/
|
||||||
void Slider::setOnDragStart(Function<void()> callback) {
|
void Slider::setOnDragStart(Function<void()> callback) {
|
||||||
onDragStart_ = callback;
|
onDragStart_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 设置拖拽结束回调
|
* @brief 设置拖拽结束回调
|
||||||
* @param callback 回调函数
|
* @param callback 回调函数
|
||||||
*/
|
*/
|
||||||
void Slider::setOnDragEnd(Function<void()> callback) {
|
void Slider::setOnDragEnd(Function<void()> callback) { onDragEnd_ = callback; }
|
||||||
onDragEnd_ = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取边界框
|
* @brief 获取边界框
|
||||||
* @return 边界矩形
|
* @return 边界矩形
|
||||||
*/
|
*/
|
||||||
Rect Slider::getBoundingBox() const {
|
Rect Slider::boundingBox() const {
|
||||||
return Rect(pos().x, pos().y, getSize().width, getSize().height);
|
return Rect(pos().x, pos().y, size().width, size().height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -225,16 +197,16 @@ Rect Slider::getBoundingBox() const {
|
||||||
* @return 位置坐标
|
* @return 位置坐标
|
||||||
*/
|
*/
|
||||||
float Slider::valueToPosition(float value) const {
|
float Slider::valueToPosition(float value) const {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
Size size = getSize();
|
Size sz = size();
|
||||||
|
|
||||||
float percent = (value - min_) / (max_ - min_);
|
float percent = (value - min_) / (max_ - min_);
|
||||||
|
|
||||||
if (vertical_) {
|
if (vertical_) {
|
||||||
return position.y + size.height - percent * size.height;
|
return position.y + sz.height - percent * sz.height;
|
||||||
} else {
|
} else {
|
||||||
return position.x + percent * size.width;
|
return position.x + percent * sz.width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -243,18 +215,18 @@ float Slider::valueToPosition(float value) const {
|
||||||
* @return 数值
|
* @return 数值
|
||||||
*/
|
*/
|
||||||
float Slider::positionToValue(float position) const {
|
float Slider::positionToValue(float position) const {
|
||||||
Vec2 widgetPos = pos();
|
Vec2 widgetPos = pos();
|
||||||
Size size = getSize();
|
Size widgetSize = size();
|
||||||
|
|
||||||
float percent;
|
float percent;
|
||||||
if (vertical_) {
|
if (vertical_) {
|
||||||
percent = (widgetPos.y + size.height - position) / size.height;
|
percent = (widgetPos.y + widgetSize.height - position) / widgetSize.height;
|
||||||
} else {
|
} else {
|
||||||
percent = (position - widgetPos.x) / size.width;
|
percent = (position - widgetPos.x) / widgetSize.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
percent = std::clamp(percent, 0.0f, 1.0f);
|
percent = std::clamp(percent, 0.0f, 1.0f);
|
||||||
return min_ + percent * (max_ - min_);
|
return min_ + percent * (max_ - min_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -262,26 +234,19 @@ float Slider::positionToValue(float position) const {
|
||||||
* @return 滑块矩形
|
* @return 滑块矩形
|
||||||
*/
|
*/
|
||||||
Rect Slider::getThumbRect() const {
|
Rect Slider::getThumbRect() const {
|
||||||
Vec2 position = pos();
|
Vec2 widgetPos = pos();
|
||||||
Size size = getSize();
|
Size widgetSize = size();
|
||||||
|
|
||||||
float thumbPos = valueToPosition(value_);
|
float thumbPos = valueToPosition(value_);
|
||||||
|
|
||||||
if (vertical_) {
|
if (vertical_) {
|
||||||
return Rect(
|
return Rect(widgetPos.x + (widgetSize.width - thumbSize_) * 0.5f,
|
||||||
position.x + (size.width - thumbSize_) * 0.5f,
|
thumbPos - thumbSize_ * 0.5f, thumbSize_, thumbSize_);
|
||||||
thumbPos - thumbSize_ * 0.5f,
|
} else {
|
||||||
thumbSize_,
|
return Rect(thumbPos - thumbSize_ * 0.5f,
|
||||||
thumbSize_
|
widgetPos.y + (widgetSize.height - thumbSize_) * 0.5f,
|
||||||
);
|
thumbSize_, thumbSize_);
|
||||||
} else {
|
}
|
||||||
return Rect(
|
|
||||||
thumbPos - thumbSize_ * 0.5f,
|
|
||||||
position.y + (size.height - thumbSize_) * 0.5f,
|
|
||||||
thumbSize_,
|
|
||||||
thumbSize_
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -289,24 +254,17 @@ Rect Slider::getThumbRect() const {
|
||||||
* @return 轨道矩形
|
* @return 轨道矩形
|
||||||
*/
|
*/
|
||||||
Rect Slider::getTrackRect() const {
|
Rect Slider::getTrackRect() const {
|
||||||
Vec2 position = pos();
|
Vec2 widgetPos = pos();
|
||||||
Size size = getSize();
|
Size widgetSize = size();
|
||||||
|
|
||||||
if (vertical_) {
|
if (vertical_) {
|
||||||
return Rect(
|
return Rect(widgetPos.x + (widgetSize.width - trackSize_) * 0.5f,
|
||||||
position.x + (size.width - trackSize_) * 0.5f,
|
widgetPos.y, trackSize_, widgetSize.height);
|
||||||
position.y,
|
} else {
|
||||||
trackSize_,
|
return Rect(widgetPos.x,
|
||||||
size.height
|
widgetPos.y + (widgetSize.height - trackSize_) * 0.5f,
|
||||||
);
|
widgetSize.width, trackSize_);
|
||||||
} else {
|
}
|
||||||
return Rect(
|
|
||||||
position.x,
|
|
||||||
position.y + (size.height - trackSize_) * 0.5f,
|
|
||||||
size.width,
|
|
||||||
trackSize_
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -314,23 +272,24 @@ Rect Slider::getTrackRect() const {
|
||||||
* @return 格式化后的文本
|
* @return 格式化后的文本
|
||||||
*/
|
*/
|
||||||
std::string Slider::formatText() const {
|
std::string Slider::formatText() const {
|
||||||
std::string result = textFormat_;
|
std::string result = textFormat_;
|
||||||
|
|
||||||
size_t pos = result.find("{value}");
|
size_t pos = result.find("{value}");
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
result.replace(pos, 7, std::to_string(static_cast<int>(value_)));
|
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_)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pos = result.find("{value:");
|
|
||||||
if (pos != std::string::npos) {
|
return result;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -339,8 +298,8 @@ std::string Slider::formatText() const {
|
||||||
* @return 对齐后的值
|
* @return 对齐后的值
|
||||||
*/
|
*/
|
||||||
float Slider::snapToStep(float value) const {
|
float Slider::snapToStep(float value) const {
|
||||||
float steps = std::round((value - min_) / step_);
|
float steps = std::round((value - min_) / step_);
|
||||||
return min_ + steps * step_;
|
return min_ + steps * step_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -348,55 +307,53 @@ float Slider::snapToStep(float value) const {
|
||||||
* @param renderer 渲染后端
|
* @param renderer 渲染后端
|
||||||
*/
|
*/
|
||||||
void Slider::onDrawWidget(RenderBackend &renderer) {
|
void Slider::onDrawWidget(RenderBackend &renderer) {
|
||||||
Rect trackRect = getTrackRect();
|
Rect trackRect = getTrackRect();
|
||||||
|
|
||||||
renderer.fillRect(trackRect, trackColor_);
|
renderer.fillRect(trackRect, trackColor_);
|
||||||
|
|
||||||
if (showFill_) {
|
if (showFill_) {
|
||||||
float percent = (value_ - min_) / (max_ - min_);
|
float percent = (value_ - min_) / (max_ - min_);
|
||||||
float fillX = trackRect.origin.x;
|
float fillX = trackRect.origin.x;
|
||||||
float fillY = trackRect.origin.y;
|
float fillY = trackRect.origin.y;
|
||||||
float fillW = trackRect.size.width;
|
float fillW = trackRect.size.width;
|
||||||
float fillH = trackRect.size.height;
|
float fillH = trackRect.size.height;
|
||||||
|
|
||||||
if (vertical_) {
|
if (vertical_) {
|
||||||
fillH = trackRect.size.height * percent;
|
fillH = trackRect.size.height * percent;
|
||||||
fillY = trackRect.origin.y + trackRect.size.height - fillH;
|
fillY = trackRect.origin.y + trackRect.size.height - fillH;
|
||||||
} else {
|
} else {
|
||||||
fillW = trackRect.size.width * percent;
|
fillW = trackRect.size.width * percent;
|
||||||
}
|
|
||||||
|
|
||||||
Rect fillRect(fillX, fillY, fillW, fillH);
|
|
||||||
renderer.fillRect(fillRect, fillColor_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showThumb_) {
|
Rect fillRect(fillX, fillY, fillW, fillH);
|
||||||
Rect thumbRect = getThumbRect();
|
renderer.fillRect(fillRect, fillColor_);
|
||||||
Color thumbColor = thumbColor_;
|
}
|
||||||
|
|
||||||
if (dragging_) {
|
if (showThumb_) {
|
||||||
thumbColor = thumbPressedColor_;
|
Rect thumbRect = getThumbRect();
|
||||||
} else if (hovered_) {
|
Color thumbColor = thumbColor_;
|
||||||
thumbColor = thumbHoverColor_;
|
|
||||||
}
|
if (dragging_) {
|
||||||
|
thumbColor = thumbPressedColor_;
|
||||||
renderer.fillRect(thumbRect, thumbColor);
|
} else if (hovered_) {
|
||||||
renderer.drawRect(thumbRect, Colors::White, 1.0f);
|
thumbColor = thumbHoverColor_;
|
||||||
}
|
|
||||||
|
|
||||||
if (textEnabled_ && font_) {
|
|
||||||
std::string text = formatText();
|
|
||||||
Vec2 textSize = font_->measureText(text);
|
|
||||||
Vec2 position = pos();
|
|
||||||
Size size = getSize();
|
|
||||||
|
|
||||||
Vec2 textPos(
|
|
||||||
position.x + size.width + 10.0f,
|
|
||||||
position.y + (size.height - textSize.y) * 0.5f
|
|
||||||
);
|
|
||||||
|
|
||||||
renderer.drawText(*font_, text, textPos, textColor_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -405,29 +362,29 @@ void Slider::onDrawWidget(RenderBackend &renderer) {
|
||||||
* @return 是否处理了事件
|
* @return 是否处理了事件
|
||||||
*/
|
*/
|
||||||
bool Slider::onMousePress(const MouseEvent &event) {
|
bool Slider::onMousePress(const MouseEvent &event) {
|
||||||
if (event.button == MouseButton::Left) {
|
if (event.button == MouseButton::Left) {
|
||||||
Rect thumbRect = getThumbRect();
|
Rect thumbRect = getThumbRect();
|
||||||
|
|
||||||
if (thumbRect.containsPoint(Point(event.x, event.y))) {
|
if (thumbRect.containsPoint(Point(event.x, event.y))) {
|
||||||
dragging_ = true;
|
dragging_ = true;
|
||||||
if (onDragStart_) {
|
if (onDragStart_) {
|
||||||
onDragStart_();
|
onDragStart_();
|
||||||
}
|
}
|
||||||
return true;
|
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;
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -436,14 +393,14 @@ bool Slider::onMousePress(const MouseEvent &event) {
|
||||||
* @return 是否处理了事件
|
* @return 是否处理了事件
|
||||||
*/
|
*/
|
||||||
bool Slider::onMouseRelease(const MouseEvent &event) {
|
bool Slider::onMouseRelease(const MouseEvent &event) {
|
||||||
if (event.button == MouseButton::Left && dragging_) {
|
if (event.button == MouseButton::Left && dragging_) {
|
||||||
dragging_ = false;
|
dragging_ = false;
|
||||||
if (onDragEnd_) {
|
if (onDragEnd_) {
|
||||||
onDragEnd_();
|
onDragEnd_();
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -452,31 +409,27 @@ bool Slider::onMouseRelease(const MouseEvent &event) {
|
||||||
* @return 是否处理了事件
|
* @return 是否处理了事件
|
||||||
*/
|
*/
|
||||||
bool Slider::onMouseMove(const MouseEvent &event) {
|
bool Slider::onMouseMove(const MouseEvent &event) {
|
||||||
if (dragging_) {
|
if (dragging_) {
|
||||||
float newValue = positionToValue(vertical_ ? event.y : event.x);
|
float newValue = positionToValue(vertical_ ? event.y : event.x);
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect thumbRect = getThumbRect();
|
Rect thumbRect = getThumbRect();
|
||||||
bool wasHovered = hovered_;
|
bool wasHovered = hovered_;
|
||||||
hovered_ = thumbRect.containsPoint(Point(event.x, event.y));
|
hovered_ = thumbRect.containsPoint(Point(event.x, event.y));
|
||||||
|
|
||||||
return hovered_ != wasHovered;
|
return hovered_ != wasHovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 鼠标进入事件处理
|
* @brief 鼠标进入事件处理
|
||||||
*/
|
*/
|
||||||
void Slider::onMouseEnter() {
|
void Slider::onMouseEnter() { hovered_ = true; }
|
||||||
hovered_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 鼠标离开事件处理
|
* @brief 鼠标离开事件处理
|
||||||
*/
|
*/
|
||||||
void Slider::onMouseLeave() {
|
void Slider::onMouseLeave() { hovered_ = false; }
|
||||||
hovered_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
|
#include <core/string.h>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <core/string.h>
|
|
||||||
#include <graphics/render_backend.h>
|
#include <graphics/render_backend.h>
|
||||||
#include <ui/text.h>
|
#include <ui/text.h>
|
||||||
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -114,7 +113,7 @@ void Text::updateCache() const {
|
||||||
Vec2 Text::calculateDrawPosition() const {
|
Vec2 Text::calculateDrawPosition() const {
|
||||||
Vec2 position = pos();
|
Vec2 position = pos();
|
||||||
Vec2 textSize = getTextSize();
|
Vec2 textSize = getTextSize();
|
||||||
Size widgetSize = getSize();
|
Size widgetSize = size();
|
||||||
Vec2 anchorPt = anchor();
|
Vec2 anchorPt = anchor();
|
||||||
|
|
||||||
float refWidth = widgetSize.empty() ? textSize.x : widgetSize.width;
|
float refWidth = widgetSize.empty() ? textSize.x : widgetSize.width;
|
||||||
|
|
@ -216,7 +215,7 @@ Ptr<Text> Text::createFormat(Ptr<FontAtlas> font, const char *fmt, ...) {
|
||||||
* @brief 获取边界框
|
* @brief 获取边界框
|
||||||
* @return 边界矩形
|
* @return 边界矩形
|
||||||
*/
|
*/
|
||||||
Rect Text::getBoundingBox() const {
|
Rect Text::boundingBox() const {
|
||||||
if (!font_ || text_.empty()) {
|
if (!font_ || text_.empty()) {
|
||||||
return Rect();
|
return Rect();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
Widget::Widget() {
|
Widget::Widget() { setSpatialIndexed(false); }
|
||||||
setSpatialIndexed(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Widget::setSize(const Size &size) {
|
void Widget::setSize(const Size &size) {
|
||||||
size_ = size;
|
size_ = size;
|
||||||
|
|
@ -16,7 +14,7 @@ void Widget::setSize(float width, float height) {
|
||||||
setSize(Size(width, height));
|
setSize(Size(width, height));
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect Widget::getBoundingBox() const {
|
Rect Widget::boundingBox() const {
|
||||||
if (size_.empty()) {
|
if (size_.empty()) {
|
||||||
return Rect();
|
return Rect();
|
||||||
}
|
}
|
||||||
|
|
@ -40,8 +38,6 @@ Rect Widget::getBoundingBox() const {
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// 重写 onDraw
|
// 重写 onDraw
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void Widget::onDraw(RenderBackend &renderer) {
|
void Widget::onDraw(RenderBackend &renderer) { onDrawWidget(renderer); }
|
||||||
onDrawWidget(renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue