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
#include "scenes/AbstractUIScene.h"
#include "core/RDPQGraphics.h"
#include "core/Sprite.h"
#include "widget/ScrollWidget.h"
#include <vector>
class ImageWidget;
class TextWidget;
typedef std::vector<IWidget*> WidgetList;
class TestScene : public AbstractUIScene
{
@ -17,20 +25,11 @@ public:
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
protected:
private:
rdpq_font_t* arialFont_;
uint8_t arialFontId_;
uint8_t fontStyleWhite_;
ScrollWidget scrollWidget_;
WidgetFocusChainSegment scrollWidgetFocusSegment_;
WidgetList widgets_;
sprite_t* pokeballSprite_;
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

View File

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

View File

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

View File

@ -1,7 +1,9 @@
#include "scenes/TestScene.h"
#include "core/RDPQGraphics.h"
#include "core/FontManager.h"
#include "widget/ImageWidget.h"
#include "widget/TextWidget.h"
#include <cstdio>
#include <n64sys.h>
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*)
: AbstractUIScene(deps)
, arialFont_(nullptr)
, arialFontId_(1)
, fontStyleWhite_(0)
, scrollWidget_(deps.animationManager)
, scrollWidgetFocusSegment_({
.current = &scrollWidget_
})
, widgets_()
, pokeballSprite_(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()
{
arialFont_ = rdpq_font_load("rom://Arial.font64");
rdpq_fontstyle_t arialWhiteFontStyle = {
.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_;
uint8_t widgetType;
const uint8_t fontId = deps_.fontManager.getFont("rom://Arial.font64");
pokeballSprite_ = sprite_load("rom:/pokeball.sprite");
oakSprite_ = sprite_load("rom://oak.sprite");
menu9SliceSprite_ = sprite_load("rom://menu-bg-9slice.sprite");
oakBounds_ ={10, 80, oakSprite_->width, oakSprite_->height};
oakSrcBounds_ = {oakSprite_->width / 3, oakSprite_->height / 3, oakSprite_->width * 2 / 3, oakSprite_->height * 2 / 3};
debugf("Hello Phil! Your tv type is: %s\r\n", tvtypeToString(get_tv_type()));
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()
{
sprite_free(menu9SliceSprite_);
menu9SliceSprite_ = nullptr;
scrollWidget_.clearWidgets();
for(IWidget* widget : widgets_)
{
delete widget;
}
widgets_.clear();
sprite_free(oakSprite_);
oakSprite_ = nullptr;
sprite_free(pokeballSprite_);
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));
// gfx.drawText(textRect_, "Hello Phil!", textRenderSettings_);
// gfx.drawSprite(spriteBounds_, pokeballSprite_, SpriteRenderSettings());
// gfx.drawSprite(oakBounds_, oakSprite_, {.srcRect = oakSrcBounds_});
gfx.drawSprite(menuBounds_, menu9SliceSprite_, menuRenderSettings_);
scrollWidget_.render(gfx, sceneBounds);
}

View File

@ -110,15 +110,28 @@ uint32_t MoveScrollWidgetWindowAnimation::getDurationInMs() const
void MoveScrollWidgetWindowAnimation::start(const Point& startPoint, const Point& endPoint)
{
currentTimePos_ = 0.f;
windowStartPoint_ = startPoint;
windowEndPoint_ = endPoint;
}
void MoveScrollWidgetWindowAnimation::extend(const Point& endPoint)
{
const Point currentPoint = calculatePoint(currentTimePos_);
start(currentPoint, endPoint);
}
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 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)
@ -195,6 +208,12 @@ void ScrollWidget::removeWidget(IWidget* widget)
recalculateWindowSize();
}
void ScrollWidget::clearWidgets()
{
widgets_.clear();
windowBounds_ = {0};
}
bool ScrollWidget::handleUserInput(const joypad_inputs_t& userInput)
{
if(!focused_ || style_.scrollStep == 0)
@ -203,21 +222,25 @@ bool ScrollWidget::handleUserInput(const joypad_inputs_t& 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)
{
return false;
}
if(!windowAnimation_.isFinished())
{
windowAnimation_.skipToEnd();
}
const Point windowStartPoint = {.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.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;
}