Work on ScrollWidget (Work In Progress/Incomplete)

This commit is contained in:
Philippe Symons 2024-08-01 21:27:49 +02:00
parent 9313571610
commit 65a59d050e
5 changed files with 164 additions and 63 deletions

View File

@ -2,8 +2,16 @@
#define _TESTSCENE_H #define _TESTSCENE_H
#include "scenes/AbstractUIScene.h" #include "scenes/AbstractUIScene.h"
#include "core/RDPQGraphics.h"
#include "core/Sprite.h" #include "core/Sprite.h"
#include "widget/ScrollWidget.h"
#include <vector>
class ImageWidget;
class TextWidget;
typedef std::vector<IWidget*> WidgetList;
class TestScene : public AbstractUIScene class TestScene : public AbstractUIScene
{ {
@ -17,20 +25,11 @@ public:
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override; void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
protected: protected:
private: private:
rdpq_font_t* arialFont_; ScrollWidget scrollWidget_;
uint8_t arialFontId_; WidgetFocusChainSegment scrollWidgetFocusSegment_;
uint8_t fontStyleWhite_; WidgetList widgets_;
sprite_t* pokeballSprite_; sprite_t* pokeballSprite_;
sprite_t* oakSprite_; sprite_t* oakSprite_;
sprite_t* menu9SliceSprite_;
const Rectangle rectBounds_;
const Rectangle textRect_;
const Rectangle spriteBounds_;
Rectangle oakBounds_;
Rectangle oakSrcBounds_;
const Rectangle menuBounds_;
TextRenderSettings textRenderSettings_;
const SpriteRenderSettings menuRenderSettings_;
}; };
#endif #endif

View File

@ -12,6 +12,7 @@ class RDPQGraphics;
class IWidget class IWidget
{ {
public: public:
virtual ~IWidget(){}
/** /**
* @brief Returns whether the widget is currently focused * @brief Returns whether the widget is currently focused
*/ */

View File

@ -33,9 +33,12 @@ public:
uint32_t getDurationInMs() const override; uint32_t getDurationInMs() const override;
void start(const Point& startPoint, const Point& endPoint); void start(const Point& startPoint, const Point& endPoint);
void extend(const Point& endPoint);
protected: protected:
void apply(float pos) override; void apply(float pos) override;
private: private:
Point calculatePoint(float pos);
ScrollWidget* list_; ScrollWidget* list_;
Point windowStartPoint_; Point windowStartPoint_;
Point windowEndPoint_; Point windowEndPoint_;
@ -69,6 +72,7 @@ public:
void addWidget(IWidget* widget); void addWidget(IWidget* widget);
void removeWidget(IWidget* widget); void removeWidget(IWidget* widget);
void clearWidgets();
bool handleUserInput(const joypad_inputs_t& userInput) override; bool handleUserInput(const joypad_inputs_t& userInput) override;
void render(RDPQGraphics& gfx, const Rectangle& parentBounds) override; void render(RDPQGraphics& gfx, const Rectangle& parentBounds) override;

View File

@ -1,7 +1,9 @@
#include "scenes/TestScene.h" #include "scenes/TestScene.h"
#include "core/RDPQGraphics.h" #include "core/RDPQGraphics.h"
#include "core/FontManager.h"
#include "widget/ImageWidget.h"
#include "widget/TextWidget.h"
#include <cstdio>
#include <n64sys.h> #include <n64sys.h>
static const char* tvtypeToString(tv_type_t type) static const char* tvtypeToString(tv_type_t type)
@ -21,23 +23,13 @@ static const char* tvtypeToString(tv_type_t type)
TestScene::TestScene(SceneDependencies& deps, void*) TestScene::TestScene(SceneDependencies& deps, void*)
: AbstractUIScene(deps) : AbstractUIScene(deps)
, arialFont_(nullptr) , scrollWidget_(deps.animationManager)
, arialFontId_(1) , scrollWidgetFocusSegment_({
, fontStyleWhite_(0) .current = &scrollWidget_
})
, widgets_()
, pokeballSprite_(nullptr) , pokeballSprite_(nullptr)
, oakSprite_(nullptr) , oakSprite_(nullptr)
, menu9SliceSprite_(nullptr)
, rectBounds_({0, 0, 320, 240})
, textRect_({70, 70, 180, 60})
, spriteBounds_({320 - 128, 0, 128, 128})
, oakBounds_({0})
, oakSrcBounds_({0})
, menuBounds_({ 100, 20, 100, 100})
, textRenderSettings_({})
, menuRenderSettings_({
.renderMode = SpriteRenderMode::NINESLICE,
.srcRect = { 6, 6, 6, 6 }
})
{ {
} }
@ -47,47 +39,129 @@ TestScene::~TestScene()
void TestScene::init() void TestScene::init()
{ {
arialFont_ = rdpq_font_load("rom://Arial.font64"); uint8_t widgetType;
rdpq_fontstyle_t arialWhiteFontStyle = { const uint8_t fontId = deps_.fontManager.getFont("rom://Arial.font64");
.color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF)
};
rdpq_font_style(arialFont_, fontStyleWhite_, &arialWhiteFontStyle);
// TODO: this is a problem: there's no way to unregister a font.
// Therefore if we'd load the same font > 1 times, we'll get a crash (due to assert)
// We'll need to create a FontManager class to handle this
rdpq_text_register_font(arialFontId_, arialFont_);
textRenderSettings_.fontId = arialFontId_;
pokeballSprite_ = sprite_load("rom:/pokeball.sprite"); pokeballSprite_ = sprite_load("rom:/pokeball.sprite");
oakSprite_ = sprite_load("rom://oak.sprite"); oakSprite_ = sprite_load("rom://oak.sprite");
menu9SliceSprite_ = sprite_load("rom://menu-bg-9slice.sprite");
oakBounds_ ={10, 80, oakSprite_->width, oakSprite_->height}; debugf("Hello Phil! Your tv type is: %s\r\n", tvtypeToString(get_tv_type()));
oakSrcBounds_ = {oakSprite_->width / 3, oakSprite_->height / 3, oakSprite_->width * 2 / 3, oakSprite_->height * 2 / 3};
printf("Hello Phil! Your tv type is: %s\n", tvtypeToString(get_tv_type())); const ScrollWidgetStyle scrollStyle = {
.scrollStep = 10
};
scrollWidget_.setBounds(Rectangle{0, 0, 320, 240});
scrollWidget_.setStyle(scrollStyle);
scrollWidget_.setFocused(true);
setFocusChain(&scrollWidgetFocusSegment_);
const Dimensions textDimensions = {.width = 50, .height = 16};
const Dimensions oakDimensions = {.width = oakSprite_->width, .height = oakSprite_->height };
const Dimensions pokeballDimensions = {.width = pokeballSprite_->width, .height = pokeballSprite_->height};
TextWidgetStyle type1Style = {
.size = {textDimensions.width, textDimensions.height},
.renderSettingsNotFocused = {
.fontId = fontId
}
};
ImageWidgetStyle type2Style = {
.size = oakDimensions,
.image = {
.sprite = oakSprite_,
.spriteBounds = {0, 0, oakDimensions.width, oakDimensions.height}
}
};
ImageWidgetStyle type3Style = {
.size = pokeballDimensions,
.image = {
.sprite = pokeballSprite_,
.spriteBounds = {0, 0, pokeballDimensions.width, pokeballDimensions.height}
}
};
int curXPos = 0;
int curYPos = 0;
int nextYPos = 0;
for(uint8_t i=0; i < 6; ++i)
{
for(uint8_t j=0; j < 6; ++j)
{
widgetType = (j % 3);
switch(widgetType)
{
case 0:
{
TextWidget* textWidget = new TextWidget();
textWidget->setStyle(type1Style);
textWidget->setBounds(Rectangle{curXPos, curYPos, textDimensions.width, textDimensions.height});
textWidget->setData("Hello!");
scrollWidget_.addWidget(textWidget);
widgets_.push_back(textWidget);
curXPos += textDimensions.width;
if(curYPos + textDimensions.height > nextYPos)
{
nextYPos = curYPos + textDimensions.height;
}
break;
}
case 1:
{
ImageWidget* imageWidget = new ImageWidget();
imageWidget->setStyle(type2Style);
imageWidget->setBounds(Rectangle{curXPos, curYPos, oakDimensions.width, oakDimensions.height});
scrollWidget_.addWidget(imageWidget);
widgets_.push_back(imageWidget);
curXPos += oakDimensions.width;
if(curYPos + oakDimensions.height > nextYPos)
{
nextYPos = curYPos + oakDimensions.height;
}
break;
}
case 2:
{
ImageWidget* imageWidget = new ImageWidget();
imageWidget->setStyle(type3Style);
imageWidget->setBounds(Rectangle{curXPos, curYPos, pokeballDimensions.width, pokeballDimensions.height});
scrollWidget_.addWidget(imageWidget);
widgets_.push_back(imageWidget);
curXPos += pokeballDimensions.width;
if(curYPos + pokeballDimensions.height > nextYPos)
{
nextYPos = curYPos + pokeballDimensions.height;
}
break;
}
default:
break;
}
}
curYPos = nextYPos;
curXPos = 0;
}
} }
void TestScene::destroy() void TestScene::destroy()
{ {
sprite_free(menu9SliceSprite_); scrollWidget_.clearWidgets();
menu9SliceSprite_ = nullptr; for(IWidget* widget : widgets_)
{
delete widget;
}
widgets_.clear();
sprite_free(oakSprite_); sprite_free(oakSprite_);
oakSprite_ = nullptr; oakSprite_ = nullptr;
sprite_free(pokeballSprite_); sprite_free(pokeballSprite_);
pokeballSprite_ = nullptr; pokeballSprite_ = nullptr;
rdpq_font_free(arialFont_);
} }
void TestScene::render(RDPQGraphics& gfx, const Rectangle& /*sceneBounds*/) void TestScene::render(RDPQGraphics& gfx, const Rectangle& sceneBounds)
{ {
// gfx.fillRectangle(rectBounds_, RGBA32(200, 0, 0, 0)); scrollWidget_.render(gfx, sceneBounds);
// gfx.drawText(textRect_, "Hello Phil!", textRenderSettings_);
// gfx.drawSprite(spriteBounds_, pokeballSprite_, SpriteRenderSettings());
// gfx.drawSprite(oakBounds_, oakSprite_, {.srcRect = oakSrcBounds_});
gfx.drawSprite(menuBounds_, menu9SliceSprite_, menuRenderSettings_);
} }

View File

@ -110,15 +110,28 @@ uint32_t MoveScrollWidgetWindowAnimation::getDurationInMs() const
void MoveScrollWidgetWindowAnimation::start(const Point& startPoint, const Point& endPoint) void MoveScrollWidgetWindowAnimation::start(const Point& startPoint, const Point& endPoint)
{ {
currentTimePos_ = 0.f;
windowStartPoint_ = startPoint; windowStartPoint_ = startPoint;
windowEndPoint_ = endPoint; windowEndPoint_ = endPoint;
} }
void MoveScrollWidgetWindowAnimation::extend(const Point& endPoint)
{
const Point currentPoint = calculatePoint(currentTimePos_);
start(currentPoint, endPoint);
}
void MoveScrollWidgetWindowAnimation::apply(float pos) void MoveScrollWidgetWindowAnimation::apply(float pos)
{
const Point newWindowStart = calculatePoint(pos);
list_->setWindowStart(newWindowStart);
}
Point MoveScrollWidgetWindowAnimation::calculatePoint(float pos)
{ {
const int px = static_cast<int>(ceil(windowStartPoint_.x + (pos * (windowEndPoint_.x - windowStartPoint_.x)))); const int px = static_cast<int>(ceil(windowStartPoint_.x + (pos * (windowEndPoint_.x - windowStartPoint_.x))));
const int py = static_cast<int>(ceil(windowStartPoint_.y + (pos * (windowEndPoint_.y - windowStartPoint_.y)))); const int py = static_cast<int>(ceil(windowStartPoint_.y + (pos * (windowEndPoint_.y - windowStartPoint_.y))));
list_->setWindowStart(Point{px, py}); return Point{px, py};
} }
ScrollWidget::ScrollWidget(AnimationManager& animManager) ScrollWidget::ScrollWidget(AnimationManager& animManager)
@ -195,6 +208,12 @@ void ScrollWidget::removeWidget(IWidget* widget)
recalculateWindowSize(); recalculateWindowSize();
} }
void ScrollWidget::clearWidgets()
{
widgets_.clear();
windowBounds_ = {0};
}
bool ScrollWidget::handleUserInput(const joypad_inputs_t& userInput) bool ScrollWidget::handleUserInput(const joypad_inputs_t& userInput)
{ {
if(!focused_ || style_.scrollStep == 0) if(!focused_ || style_.scrollStep == 0)
@ -203,21 +222,25 @@ bool ScrollWidget::handleUserInput(const joypad_inputs_t& userInput)
} }
const FloatVector scrollDirection = determineScrollDirection(userInput); const FloatVector scrollDirection = determineScrollDirection(userInput);
// debugf("[ScrollWidget]: %s: scrollDirection [%f, %f]\r\n", __FUNCTION__, scrollDirection.x, scrollDirection.y);
if(scrollDirection.x == 0.f && scrollDirection.y == 0.f) if(scrollDirection.x == 0.f && scrollDirection.y == 0.f)
{ {
return false; return false;
} }
if(!windowAnimation_.isFinished())
{
windowAnimation_.skipToEnd();
}
const Point windowStartPoint = {.x = windowBounds_.x, .y = windowBounds_.y};
Point windowEndPoint = {.x = windowBounds_.x, .y = windowBounds_.y}; Point windowEndPoint = {.x = windowBounds_.x, .y = windowBounds_.y};
windowEndPoint.x += static_cast<int>(ceil(scrollDirection.x * style_.scrollStep)); windowEndPoint.x += static_cast<int>(ceil(scrollDirection.x * style_.scrollStep));
windowEndPoint.y += static_cast<int>(ceil(scrollDirection.y * style_.scrollStep)); windowEndPoint.y += static_cast<int>(ceil(scrollDirection.y * style_.scrollStep));
windowAnimation_.start(windowStartPoint, windowEndPoint);
if(!windowAnimation_.isFinished())
{
windowAnimation_.extend(windowEndPoint);
}
else
{
const Point windowStartPoint = {.x = windowBounds_.x, .y = windowBounds_.y};
windowAnimation_.start(windowStartPoint, windowEndPoint);
}
return true; return true;
} }