mirror of
https://github.com/risingPhil/PokeMe64.git
synced 2026-03-21 18:04:15 -05:00
Various changes
- Add "Pikachu Bed" and "Tentacool Doll" decoration unlock options for gen 2 - bugfix for unsafe behaviour - attempt to make the RESET button work for cartridge switching. It failed though. (not reliable). Therefore I just added a warning message instead. - Work on new ScrollWidget for Credits/About screen (Work In Progress)
This commit is contained in:
parent
6ce9c1ec23
commit
f7a687b147
|
|
@ -16,6 +16,8 @@ public:
|
|||
void init();
|
||||
|
||||
void run();
|
||||
|
||||
void onResetInterrupt();
|
||||
protected:
|
||||
private:
|
||||
RDPQGraphics graphics_;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,21 @@
|
|||
#ifndef _CORE_COMMON_H
|
||||
#define _CORE_COMMON_H
|
||||
|
||||
typedef struct Point
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
} Point;
|
||||
|
||||
typedef struct FloatPoint
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
} FloatPoint;
|
||||
|
||||
typedef Point Vector;
|
||||
typedef FloatPoint FloatVector;
|
||||
|
||||
typedef struct Dimensions
|
||||
{
|
||||
int width;
|
||||
|
|
@ -27,6 +42,10 @@ bool isZeroSizeRectangle(const Rectangle& rect);
|
|||
*/
|
||||
Rectangle addOffset(const Rectangle& a, const Rectangle& b);
|
||||
|
||||
bool isPointInsideRectangle(const Rectangle& a, const Point& p);
|
||||
|
||||
bool doRectanglesOverlap(const Rectangle& a, const Rectangle& b);
|
||||
|
||||
/**
|
||||
* @brief Extract a Dimensions struct from a Rectangle that only contains the width and height
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,12 +3,17 @@
|
|||
|
||||
#include "Moves.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// these are used to pass as a pointer to the gen1PrepareToTeachPikachu
|
||||
extern const Move MOVE_SURF;
|
||||
extern const Move MOVE_FLY;
|
||||
|
||||
void printMessage(void* context, const void* param);
|
||||
extern const uint16_t GEN2_EVENTFLAG_DECORATION_PIKACHU_BED;
|
||||
extern const uint16_t GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL;
|
||||
|
||||
void activateFrameLog(void* context, const void* param);
|
||||
void advanceDialog(void* context, const void* param);
|
||||
|
||||
void goToTestScene(void* context, const void* param);
|
||||
|
||||
|
|
@ -19,5 +24,7 @@ void goToGen2PCNYDistributionPokemonMenu(void* context, const void* param);
|
|||
void gen1PrepareToTeachPikachu(void* context, const void* param);
|
||||
void gen1TeachPikachu(void* context, const void* param);
|
||||
void gen2ReceiveGSBall(void* context, const void* param);
|
||||
void gen2SetEventFlag(void* context, const void* param);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -49,6 +49,8 @@ typedef struct WidgetFocusChainSegment
|
|||
class AbstractUIScene : public IScene
|
||||
{
|
||||
public:
|
||||
static const uint16_t MINIMUM_TIME_BETWEEN_INPUT_EVENTS;
|
||||
|
||||
AbstractUIScene(SceneDependencies& deps);
|
||||
virtual ~AbstractUIScene();
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ typedef struct MenuSceneContext
|
|||
{
|
||||
MenuItemData* menuEntries;
|
||||
uint32_t numMenuEntries;
|
||||
bool bButtonMeansUserWantsToSwitchCartridge;
|
||||
} MenuSceneContext;
|
||||
|
||||
/**
|
||||
|
|
@ -38,7 +39,7 @@ public:
|
|||
|
||||
SceneDependencies& getDependencies();
|
||||
|
||||
virtual void showDialog(DialogData* diagData);
|
||||
void showDialog(DialogData* diagData) override;
|
||||
protected:
|
||||
virtual void setupMenu();
|
||||
void setupDialog(DialogWidgetStyle& style) override;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public:
|
|||
* WARNING: If you specify a sceneContext, you MUST also specify a deleteContextFunc callback function pointer.
|
||||
* This is needed because you can't call delete() on a void*. And we need to keep the context around in the sceneHistory for as long as it needs to
|
||||
*/
|
||||
void switchScene(SceneType sceneType, void (*deleteContextFunc)(void*) = nullptr, void* sceneContext = nullptr);
|
||||
void switchScene(SceneType sceneType, void (*deleteContextFunc)(void*) = nullptr, void* sceneContext = nullptr, bool deleteHistory = false);
|
||||
|
||||
/**
|
||||
* @brief Switch back to the previous scene in the history stack
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ public:
|
|||
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
|
||||
|
||||
void advanceDialog();
|
||||
virtual void showDialog(DialogData* diagData);
|
||||
protected:
|
||||
virtual void setupFonts();
|
||||
virtual void setupDialog(DialogWidgetStyle& style);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public:
|
|||
void setPort(joypad_port_t port);
|
||||
|
||||
bool hasTransferPak();
|
||||
bool isPoweredOn() const;
|
||||
bool setPower(bool on);
|
||||
uint8_t getStatus();
|
||||
|
||||
|
|
@ -92,7 +93,7 @@ public:
|
|||
protected:
|
||||
private:
|
||||
joypad_port_t port_;
|
||||
bool wasPoweredAtLeastOnce_;
|
||||
bool isPoweredOn_;
|
||||
uint8_t currentSRAMBank_;
|
||||
uint16_t readBufferBankOffset_;
|
||||
uint16_t writeBufferSRAMBankOffset_;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
|
||||
#include "widget/VerticalList.h"
|
||||
#include "widget/MenuItemWidget.h"
|
||||
#include "core/Sprite.h"
|
||||
#include "core/RDPQGraphics.h"
|
||||
#include "widget/ListItemFiller.h"
|
||||
|
||||
#define DIALOG_TEXT_SIZE 512
|
||||
|
||||
|
|
@ -117,6 +116,7 @@ private:
|
|||
bool isAdvanceAllowed() const;
|
||||
|
||||
VerticalList dialogOptionList_;
|
||||
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> dialogOptionListFiller_;
|
||||
AnimationManager& animationManager_;
|
||||
Rectangle bounds_;
|
||||
DialogWidgetStyle style_;
|
||||
|
|
|
|||
|
|
@ -26,11 +26,7 @@ public:
|
|||
|
||||
~ListItemFiller()
|
||||
{
|
||||
for(ListItemWidgetType* item : widgets_)
|
||||
{
|
||||
delete item;
|
||||
}
|
||||
widgets_.clear();
|
||||
deleteWidgets();
|
||||
}
|
||||
|
||||
void addItems(ListDataType* dataList, size_t dataListSize, const MenuItemStyle& itemStyle)
|
||||
|
|
@ -44,6 +40,15 @@ public:
|
|||
list_.addWidget(itemWidget);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteWidgets()
|
||||
{
|
||||
for(ListItemWidgetType* item : widgets_)
|
||||
{
|
||||
delete item;
|
||||
}
|
||||
widgets_.clear();
|
||||
}
|
||||
protected:
|
||||
private:
|
||||
ListType& list_;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ protected:
|
|||
/**
|
||||
* Executes the onConfirmAction callback (if any)
|
||||
*/
|
||||
void execute();
|
||||
bool execute();
|
||||
private:
|
||||
MenuItemData data_;
|
||||
MenuItemStyle style_;
|
||||
|
|
|
|||
115
include/widget/ScrollWidget.h
Normal file
115
include/widget/ScrollWidget.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef _SCROLLWIDGET_H
|
||||
#define _SCROLLWIDGET_H
|
||||
|
||||
#include "widget/IWidget.h"
|
||||
#include "animations/IAnimation.h"
|
||||
#include <vector>
|
||||
|
||||
typedef std::vector<IWidget*> WidgetList;
|
||||
|
||||
class ScrollWidget;
|
||||
class AnimationManager;
|
||||
|
||||
typedef struct ScrollWidgetStyle
|
||||
{
|
||||
// it defines the amount of pixels we should scroll when the analog stick or dpad is used.
|
||||
uint8_t scrollStep;
|
||||
} ScrollWidgetStyle;
|
||||
|
||||
/**
|
||||
* @brief This Animation implementation is used internal in ScrollWidget
|
||||
* to move the "view window"'s x and y-coordinates.
|
||||
*
|
||||
* It's specific to the ScrollWidget
|
||||
*/
|
||||
class MoveScrollWidgetWindowAnimation : public AbstractAnimation
|
||||
{
|
||||
public:
|
||||
MoveScrollWidgetWindowAnimation(ScrollWidget* list);
|
||||
virtual ~MoveScrollWidgetWindowAnimation();
|
||||
|
||||
AnimationDistanceTimeFunctionType getDistanceTimeFunctionType() const override;
|
||||
|
||||
uint32_t getDurationInMs() const override;
|
||||
|
||||
void start(const Point& startPoint, const Point& endPoint);
|
||||
protected:
|
||||
void apply(float pos) override;
|
||||
private:
|
||||
ScrollWidget* list_;
|
||||
Point windowStartPoint_;
|
||||
Point windowEndPoint_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is a widget that contains other widgets and can scroll through it.
|
||||
* It differs from VerticalList in that ScrollWidget manages "unstructured" widgets instead of just a list in a certain direction.
|
||||
*
|
||||
* But this scroll widget does not allow you to select a widget and/or handle events on it. Every widget added will remain unfocused and won't handle keys
|
||||
* display only.
|
||||
*
|
||||
*/
|
||||
class ScrollWidget : public IWidget
|
||||
{
|
||||
public:
|
||||
ScrollWidget(AnimationManager& animManager);
|
||||
virtual ~ScrollWidget();
|
||||
|
||||
bool isFocused() const override;
|
||||
void setFocused(bool isFocused) override;
|
||||
|
||||
bool isVisible() const override;
|
||||
void setVisible(bool visible) override;
|
||||
|
||||
Rectangle getBounds() const override;
|
||||
void setBounds(const Rectangle& bounds) override;
|
||||
Dimensions getSize() const override;
|
||||
|
||||
void setStyle(const ScrollWidgetStyle& style);
|
||||
|
||||
void addWidget(IWidget* widget);
|
||||
void removeWidget(IWidget* widget);
|
||||
|
||||
bool handleUserInput(const joypad_inputs_t& userInput) override;
|
||||
void render(RDPQGraphics& gfx, const Rectangle& parentBounds) override;
|
||||
|
||||
/**
|
||||
* @brief Sets the view window start xy coords.
|
||||
* This is used by MoveScrollWidgetWindowAnimation
|
||||
*/
|
||||
void setWindowStart(const Point& windowStart);
|
||||
|
||||
/**
|
||||
* @brief Indicates the percentage at which the current window position is at horizontally
|
||||
* in relation to the entire window
|
||||
*/
|
||||
float getWindowProgressX() const;
|
||||
/**
|
||||
* @brief Indicates the percentage at which the current window position is at vertically
|
||||
* in relation to the entire window
|
||||
*/
|
||||
float getWindowProgressY() const;
|
||||
protected:
|
||||
private:
|
||||
/**
|
||||
* @brief This function will grow the maximum dimensions of the window
|
||||
* based on the specified IWidget
|
||||
*/
|
||||
void growWindow(IWidget* widget);
|
||||
/**
|
||||
* @brief Recalculates the entire width and height of the view window
|
||||
* This is necessary when removing a widget
|
||||
*/
|
||||
void recalculateWindowSize();
|
||||
|
||||
MoveScrollWidgetWindowAnimation windowAnimation_;
|
||||
WidgetList widgets_;
|
||||
ScrollWidgetStyle style_;
|
||||
AnimationManager& animManager_;
|
||||
Rectangle bounds_;
|
||||
Rectangle windowBounds_;
|
||||
bool visible_;
|
||||
bool focused_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,16 @@
|
|||
#include "core/Application.h"
|
||||
#include "scenes/IScene.h"
|
||||
|
||||
static Application* appInstance = nullptr;
|
||||
static void resetInterruptHandler()
|
||||
{
|
||||
if(!appInstance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
appInstance->onResetInterrupt();
|
||||
}
|
||||
|
||||
Application::Application()
|
||||
: graphics_()
|
||||
, animationManager_()
|
||||
|
|
@ -9,15 +19,21 @@ Application::Application()
|
|||
, sceneManager_(graphics_, animationManager_, fontManager_, tpakManager_)
|
||||
, sceneBounds_({0})
|
||||
{
|
||||
appInstance = this;
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
tpakManager_.setPower(false);
|
||||
unregister_RESET_handler(resetInterruptHandler);
|
||||
|
||||
graphics_.destroy();
|
||||
|
||||
display_close();
|
||||
timer_close();
|
||||
joypad_close();
|
||||
|
||||
appInstance = nullptr;
|
||||
}
|
||||
|
||||
void Application::init()
|
||||
|
|
@ -37,6 +53,8 @@ void Application::init()
|
|||
//display_init(RESOLUTION_320x240, DEPTH_16_BPP, 3, GAMMA_NONE, ANTIALIAS_RESAMPLE_FETCH_ALWAYS);
|
||||
|
||||
sceneManager_.switchScene(SceneType::INIT_TRANSFERPAK);
|
||||
|
||||
register_RESET_handler(resetInterruptHandler);
|
||||
}
|
||||
|
||||
void Application::run()
|
||||
|
|
@ -57,3 +75,8 @@ void Application::run()
|
|||
graphics_.finishAndShowFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::onResetInterrupt()
|
||||
{
|
||||
tpakManager_.setPower(false);
|
||||
}
|
||||
|
|
@ -10,6 +10,26 @@ Rectangle addOffset(const Rectangle &a, const Rectangle &b)
|
|||
return Rectangle{.x = a.x + b.x, .y = a.y + b.y, .width = a.width, .height = a.height};
|
||||
}
|
||||
|
||||
bool isPointInsideRectangle(const Rectangle& a, const Point& p)
|
||||
{
|
||||
const bool isInsideHorizontally = (p.x >= a.x) && (p.x < (a.x + a.width));
|
||||
const bool isInsideVertically = (p.y >= a.y) && (p.y < (a.y + a.width));
|
||||
return (isInsideHorizontally && isInsideVertically);
|
||||
}
|
||||
|
||||
bool doRectanglesOverlap(const Rectangle& a, const Rectangle& b)
|
||||
{
|
||||
if (a.x < (b.x + b.width) && (a.x + a.width) > b.x &&
|
||||
a.y + a.height > b.y && a.y < (b.y + b.height) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Dimensions getDimensions(const Rectangle &r)
|
||||
{
|
||||
return Dimensions{.width = r.width, .height = r.height};
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ MenuItemData gen2MenuEntries[] = {
|
|||
{
|
||||
.title = "PCNY Pokémon",
|
||||
.onConfirmAction = goToGen2PCNYDistributionPokemonMenu,
|
||||
},
|
||||
{
|
||||
.title = "Unlock Pikachu Bed",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_PIKACHU_BED
|
||||
},
|
||||
{
|
||||
.title = "Unlock Tentacool Doll",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -45,6 +55,16 @@ MenuItemData gen2CrystalMenuEntries[] = {
|
|||
{
|
||||
.title = "Unlock GS Ball",
|
||||
.onConfirmAction = gen2ReceiveGSBall
|
||||
},
|
||||
{
|
||||
.title = "Unlock Pikachu Bed",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_PIKACHU_BED
|
||||
},
|
||||
{
|
||||
.title = "Unlock Tentacool Doll",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
const Move MOVE_SURF = Move::SURF;
|
||||
const Move MOVE_FLY = Move::FLY;
|
||||
|
||||
// based on https://github.com/kwsch/PKHeX/blob/master/PKHeX.Core/Resources/text/script/gen2/flags_c_en.txt
|
||||
const uint16_t GEN2_EVENTFLAG_DECORATION_PIKACHU_BED = 679;
|
||||
const uint16_t GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL = 715;
|
||||
|
||||
typedef struct Gen1TeachPikachuParams
|
||||
{
|
||||
Move moveType;
|
||||
|
|
@ -71,9 +75,17 @@ static uint8_t gen1FindPikachuInParty(Gen1Party& party)
|
|||
return foundIndex;
|
||||
}
|
||||
|
||||
void printMessage(void* context, const void*)
|
||||
static const char* convertGen2EventFlagToString(uint16_t eventFlagIndex)
|
||||
{
|
||||
debugf((const char*)context);
|
||||
switch(eventFlagIndex)
|
||||
{
|
||||
case GEN2_EVENTFLAG_DECORATION_PIKACHU_BED:
|
||||
return "Pikachu Bed";
|
||||
case GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL:
|
||||
return "Tentacool doll";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void activateFrameLog(void* context, const void*)
|
||||
|
|
@ -84,6 +96,12 @@ void activateFrameLog(void* context, const void*)
|
|||
gfx.triggerDebugFrame();
|
||||
}
|
||||
|
||||
void advanceDialog(void* context, const void*)
|
||||
{
|
||||
MenuScene* scene = static_cast<MenuScene*>(context);
|
||||
scene->advanceDialog();
|
||||
}
|
||||
|
||||
void goToTestScene(void* context, const void* param)
|
||||
{
|
||||
MenuScene* scene = static_cast<MenuScene*>(context);
|
||||
|
|
@ -330,5 +348,37 @@ void gen2ReceiveGSBall(void* context, const void* param)
|
|||
|
||||
setDialogDataText(*messageData, "GS Ball event unlocked! Please go to the Golden Rod Pokémon Center and try to leave!", trainerName);
|
||||
|
||||
scene->showDialog(messageData);
|
||||
}
|
||||
|
||||
void gen2SetEventFlag(void* context, const void* param)
|
||||
{
|
||||
MenuScene* scene = static_cast<MenuScene*>(context);
|
||||
TransferPakManager& tpakManager = scene->getDependencies().tpakManager;
|
||||
TransferPakRomReader romReader(tpakManager);
|
||||
TransferPakSaveManager saveManager(tpakManager);
|
||||
Gen2GameReader gameReader(romReader, saveManager, static_cast<Gen2GameType>(scene->getDependencies().specificGenVersion));
|
||||
DialogData* messageData = new DialogData{
|
||||
.shouldDeleteWhenDone = true
|
||||
};
|
||||
const uint16_t eventFlagIndex = *static_cast<const uint16_t*>(param);
|
||||
|
||||
tpakManager.setRAMEnabled(true);
|
||||
|
||||
const char* trainerName = gameReader.getTrainerName();
|
||||
if(gameReader.getEventFlag(eventFlagIndex))
|
||||
{
|
||||
setDialogDataText(*messageData, "%s already has %s!", trainerName, convertGen2EventFlagToString(eventFlagIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
gameReader.setEventFlag(eventFlagIndex, true);
|
||||
gameReader.finishSave();
|
||||
tpakManager.finishWrites();
|
||||
|
||||
setDialogDataText(*messageData, "%s has unlocked %s!", trainerName, convertGen2EventFlagToString(eventFlagIndex));
|
||||
}
|
||||
|
||||
tpakManager.setRAMEnabled(false);
|
||||
scene->showDialog(messageData);
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
#include "core/DragonUtils.h"
|
||||
#include "widget/IWidget.h"
|
||||
|
||||
static uint16_t minimumTimeBetweenInputEventsInMs = 150;
|
||||
const uint16_t AbstractUIScene::MINIMUM_TIME_BETWEEN_INPUT_EVENTS = 150;
|
||||
|
||||
AbstractUIScene::AbstractUIScene(SceneDependencies& deps)
|
||||
: deps_(deps)
|
||||
|
|
@ -23,7 +23,7 @@ void AbstractUIScene::processUserInput()
|
|||
}
|
||||
|
||||
const uint64_t now = get_ticks();
|
||||
if(TICKS_TO_MS(now - lastInputHandleTime_) < minimumTimeBetweenInputEventsInMs)
|
||||
if(TICKS_TO_MS(now - lastInputHandleTime_) < MINIMUM_TIME_BETWEEN_INPUT_EVENTS)
|
||||
{
|
||||
// not enough time has passed since last handled input event. Ignore
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@ void InitTransferPakScene::onDialogDone()
|
|||
{
|
||||
menuContext = new MenuSceneContext({
|
||||
.menuEntries = gen1MenuEntries,
|
||||
.numMenuEntries = static_cast<uint32_t>(gen1MenuEntriesSize / sizeof(gen1MenuEntries[0]))
|
||||
.numMenuEntries = static_cast<uint32_t>(gen1MenuEntriesSize / sizeof(gen1MenuEntries[0])),
|
||||
.bButtonMeansUserWantsToSwitchCartridge = true
|
||||
});
|
||||
}
|
||||
else if(gen2Type != Gen2GameType::INVALID)
|
||||
|
|
@ -86,14 +87,16 @@ void InitTransferPakScene::onDialogDone()
|
|||
{
|
||||
menuContext = new MenuSceneContext({
|
||||
.menuEntries = gen2CrystalMenuEntries,
|
||||
.numMenuEntries = static_cast<uint32_t>(gen2CrystalMenuEntriesSize / sizeof(gen2CrystalMenuEntries[0]))
|
||||
.numMenuEntries = static_cast<uint32_t>(gen2CrystalMenuEntriesSize / sizeof(gen2CrystalMenuEntries[0])),
|
||||
.bButtonMeansUserWantsToSwitchCartridge = true
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
menuContext = new MenuSceneContext({
|
||||
.menuEntries = gen2MenuEntries,
|
||||
.numMenuEntries = static_cast<uint32_t>(gen2MenuEntriesSize / sizeof(gen2MenuEntries[0]))
|
||||
.numMenuEntries = static_cast<uint32_t>(gen2MenuEntriesSize / sizeof(gen2MenuEntries[0])),
|
||||
.bButtonMeansUserWantsToSwitchCartridge = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "scenes/MenuScene.h"
|
||||
#include "core/FontManager.h"
|
||||
#include "scenes/SceneManager.h"
|
||||
#include "transferpak/TransferPakManager.h"
|
||||
#include "menu/MenuFunctions.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
|
|
@ -78,7 +80,7 @@ void MenuScene::render(RDPQGraphics& gfx, const Rectangle& sceneBounds)
|
|||
.fontId = arialId_,
|
||||
.fontStyleId = fontStyleWhiteId_
|
||||
};
|
||||
gfx.drawText(Rectangle{40, 10, 280, 16}, "PokeMe64 by risingPhil. Very early version...", renderSettings);
|
||||
gfx.drawText(Rectangle{40, 10, 280, 16}, "PokeMe64 by risingPhil. Version 0.1", renderSettings);
|
||||
}
|
||||
|
||||
bool MenuScene::handleUserInput(joypad_port_t port, const joypad_inputs_t& inputs)
|
||||
|
|
@ -98,7 +100,22 @@ bool MenuScene::handleUserInput(joypad_port_t port, const joypad_inputs_t& input
|
|||
{
|
||||
// b button release occurred. Switch back to previous scene.
|
||||
bButtonPressed_ = false;
|
||||
deps_.sceneManager.goBackToPreviousScene();
|
||||
|
||||
if(context_->bButtonMeansUserWantsToSwitchCartridge)
|
||||
{
|
||||
DialogData* diag = new DialogData{
|
||||
.shouldDeleteWhenDone = true
|
||||
};
|
||||
|
||||
setDialogDataText(*diag, "Please turn the console off to switch gameboy cartridges!");
|
||||
|
||||
showDialog(diag);
|
||||
}
|
||||
else
|
||||
{
|
||||
// now do the actual switch back to the previous scene
|
||||
deps_.sceneManager.goBackToPreviousScene();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -199,10 +216,9 @@ void MenuScene::setupDialog(DialogWidgetStyle& style)
|
|||
|
||||
void MenuScene::showDialog(DialogData* diagData)
|
||||
{
|
||||
SceneWithDialogWidget::showDialog(diagData);
|
||||
menuList_.setVisible(false);
|
||||
cursorWidget_.setVisible(false);
|
||||
dialogWidget_.setVisible(true);
|
||||
dialogWidget_.setData(diagData);
|
||||
setFocusChain(&dialogFocusChainSegment_);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,11 +29,16 @@ SceneManager::~SceneManager()
|
|||
unloadScene(scene_);
|
||||
}
|
||||
|
||||
void SceneManager::switchScene(SceneType type, void (*deleteContextFunc)(void*), void* sceneContext)
|
||||
void SceneManager::switchScene(SceneType type, void (*deleteContextFunc)(void*), void* sceneContext, bool deleteHistory)
|
||||
{
|
||||
newSceneType_ = type;
|
||||
newSceneContext_ = sceneContext;
|
||||
|
||||
if(deleteHistory)
|
||||
{
|
||||
clearHistory();
|
||||
}
|
||||
|
||||
sceneHistory_.push_back(SceneHistorySegment{
|
||||
.type = type,
|
||||
.context = sceneContext,
|
||||
|
|
|
|||
|
|
@ -73,6 +73,12 @@ void SceneWithDialogWidget::advanceDialog()
|
|||
dialogWidget_.advanceDialog();
|
||||
}
|
||||
|
||||
void SceneWithDialogWidget::showDialog(DialogData* diagData)
|
||||
{
|
||||
dialogWidget_.setVisible(true);
|
||||
dialogWidget_.setData(diagData);
|
||||
}
|
||||
|
||||
void SceneWithDialogWidget::setupFonts()
|
||||
{
|
||||
arialId_ = deps_.fontManager.getFont("rom://Arial.font64");
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ static const uint16_t sramBankStartGBAddress = 0xA000;
|
|||
|
||||
TransferPakManager::TransferPakManager()
|
||||
: port_(JOYPAD_PORT_1)
|
||||
, wasPoweredAtLeastOnce_(false)
|
||||
, isPoweredOn_(false)
|
||||
, currentSRAMBank_(0)
|
||||
, readBufferBankOffset_(0xFFFF)
|
||||
, writeBufferSRAMBankOffset_(0xFFFF)
|
||||
|
|
@ -30,8 +30,11 @@ joypad_port_t TransferPakManager::getPort() const
|
|||
|
||||
void TransferPakManager::setPort(joypad_port_t port)
|
||||
{
|
||||
if(isPoweredOn_)
|
||||
{
|
||||
setPower(false);
|
||||
}
|
||||
port_ = port;
|
||||
wasPoweredAtLeastOnce_ = false;
|
||||
}
|
||||
|
||||
bool TransferPakManager::hasTransferPak()
|
||||
|
|
@ -47,31 +50,48 @@ bool TransferPakManager::hasTransferPak()
|
|||
return (type == JOYPAD_ACCESSORY_TYPE_TRANSFER_PAK);
|
||||
}
|
||||
|
||||
bool TransferPakManager::isPoweredOn() const
|
||||
{
|
||||
return isPoweredOn_;
|
||||
}
|
||||
|
||||
bool TransferPakManager::setPower(bool on)
|
||||
{
|
||||
uint8_t status;
|
||||
int ret;
|
||||
|
||||
if(!wasPoweredAtLeastOnce_)
|
||||
if(on)
|
||||
{
|
||||
if(!on)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ret = tpak_init(static_cast<int>(port_));
|
||||
if(ret)
|
||||
{
|
||||
debugf("[TransferPakManager]: %s: tpak_init got error %d\r\n", __FUNCTION__, ret);
|
||||
}
|
||||
wasPoweredAtLeastOnce_ = true;
|
||||
|
||||
status = getStatus();
|
||||
while(!(status | TPAK_STATUS_READY))
|
||||
{
|
||||
debugf("[TransferPakManager]: %s: ERROR: transfer pak not ready yet. Current status is %hu\r\n", __FUNCTION__, status);
|
||||
status = getStatus();
|
||||
}
|
||||
isPoweredOn_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = tpak_set_power(port_, on);
|
||||
ret = tpak_set_access(port_, false);
|
||||
if(ret)
|
||||
{
|
||||
debugf("[TransferPakManager]: %s: tpak_set_power got error %d\r\n", __FUNCTION__, ret);
|
||||
debugf("[TransferPakManager]: %s: tpak_set_access got error %d\r\n", __FUNCTION__, ret);
|
||||
}
|
||||
|
||||
ret = tpak_set_power(port_, false);
|
||||
if(ret)
|
||||
{
|
||||
debugf("[TransferPakManager]: %s: tpak_set_access got error %d\r\n", __FUNCTION__, ret);
|
||||
}
|
||||
isPoweredOn_ = false;
|
||||
}
|
||||
|
||||
return (!ret);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "widget/DialogWidget.h"
|
||||
#include "widget/ListItemFiller.h"
|
||||
#include "core/RDPQGraphics.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
|
@ -33,6 +32,7 @@ static void releaseEntry(DialogData* data, bool releaseAllEntries)
|
|||
|
||||
DialogWidget::DialogWidget(AnimationManager& animationManager)
|
||||
: dialogOptionList_(animationManager)
|
||||
, dialogOptionListFiller_(dialogOptionList_)
|
||||
, animationManager_(animationManager)
|
||||
, bounds_({0})
|
||||
, style_({0})
|
||||
|
|
@ -76,6 +76,8 @@ void DialogWidget::setStyle(const DialogWidgetStyle& style)
|
|||
void DialogWidget::setData(DialogData* data)
|
||||
{
|
||||
dialogOptionList_.clearWidgets();
|
||||
// the filler is the owner of the widgets. They need to be properly destroyed
|
||||
dialogOptionListFiller_.deleteWidgets();
|
||||
dialogOptionList_.setVisible(false);
|
||||
dialogOptionList_.setFocused(false);
|
||||
releaseEntry(data_, true);
|
||||
|
|
@ -84,9 +86,7 @@ void DialogWidget::setData(DialogData* data)
|
|||
|
||||
if(data_ && data->options.items)
|
||||
{
|
||||
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> filler(dialogOptionList_);
|
||||
|
||||
filler.addItems(data->options.items, data->options.number, style_.dialogOptions.style);
|
||||
dialogOptionListFiller_.addItems(data->options.items, data->options.number, style_.dialogOptions.style);
|
||||
dialogOptionList_.setVisible(true);
|
||||
dialogOptionList_.setFocused(focused_);
|
||||
}
|
||||
|
|
@ -171,13 +171,13 @@ void DialogWidget::advanceDialog()
|
|||
releaseEntry(oldEntry, false);
|
||||
|
||||
dialogOptionList_.clearWidgets();
|
||||
// the filler is the owner of the widgets. They need to be properly destroyed
|
||||
dialogOptionListFiller_.deleteWidgets();
|
||||
dialogOptionList_.setFocused(false);
|
||||
dialogOptionList_.setVisible(false);
|
||||
if(data_->options.items)
|
||||
{
|
||||
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> filler(dialogOptionList_);
|
||||
|
||||
filler.addItems(data_->options.items, data_->options.number, style_.dialogOptions.style);
|
||||
dialogOptionListFiller_.addItems(data_->options.items, data_->options.number, style_.dialogOptions.style);
|
||||
dialogOptionList_.setVisible(true);
|
||||
dialogOptionList_.setFocused(focused_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
MenuItemWidget::MenuItemWidget()
|
||||
: data_()
|
||||
, style_()
|
||||
, style_({0})
|
||||
, focused_(false)
|
||||
, visible_(true)
|
||||
, aButtonPressed_(false)
|
||||
|
|
@ -70,9 +70,8 @@ bool MenuItemWidget::handleUserInput(const joypad_inputs_t& userInput)
|
|||
}
|
||||
else if(aButtonPressed_)
|
||||
{
|
||||
execute();
|
||||
aButtonPressed_ = false;
|
||||
return true;
|
||||
return execute();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -104,7 +103,12 @@ void MenuItemWidget::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
|||
gfx.drawText(myBounds, data_.title, (focused_) ? style_.titleFocused : style_.titleNotFocused);
|
||||
}
|
||||
|
||||
void MenuItemWidget::execute()
|
||||
bool MenuItemWidget::execute()
|
||||
{
|
||||
data_.onConfirmAction(data_.context, data_.itemParam);
|
||||
if(data_.onConfirmAction)
|
||||
{
|
||||
data_.onConfirmAction(data_.context, data_.itemParam);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
310
src/widget/ScrollWidget.cpp
Normal file
310
src/widget/ScrollWidget.cpp
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
#include "widget/ScrollWidget.h"
|
||||
#include "core/common.h"
|
||||
#include "scenes/AbstractUIScene.h"
|
||||
#include "animations/AnimationManager.h"
|
||||
#include "core/RDPQGraphics.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
static uint8_t ANALOG_STICK_MIN_THRESHOLD = 30;
|
||||
static uint8_t ANALOG_STICK_MAX_THRESHOLD = 60;
|
||||
|
||||
static FloatVector determineScrollDirection(const joypad_inputs_t& userInput)
|
||||
{
|
||||
// this value should end up giving a semi-normalized vector when used for both x and y coords
|
||||
const float DPAD_2_DIRECTIONS_PRESSED_COORD = 0.7071f;
|
||||
const float STICK_RANGE_UNIT = 1.f / (ANALOG_STICK_MAX_THRESHOLD - ANALOG_STICK_MIN_THRESHOLD);
|
||||
|
||||
if(userInput.btn.d_left)
|
||||
{
|
||||
if(userInput.btn.d_up)
|
||||
{
|
||||
return FloatPoint{ -DPAD_2_DIRECTIONS_PRESSED_COORD, -DPAD_2_DIRECTIONS_PRESSED_COORD};
|
||||
}
|
||||
else if(userInput.btn.d_down)
|
||||
{
|
||||
return FloatPoint{ -DPAD_2_DIRECTIONS_PRESSED_COORD, DPAD_2_DIRECTIONS_PRESSED_COORD};
|
||||
}
|
||||
else
|
||||
{
|
||||
return FloatPoint{-1.f, 0.f};
|
||||
}
|
||||
}
|
||||
else if(userInput.btn.d_right)
|
||||
{
|
||||
if(userInput.btn.d_up)
|
||||
{
|
||||
return FloatPoint{ DPAD_2_DIRECTIONS_PRESSED_COORD, -DPAD_2_DIRECTIONS_PRESSED_COORD};
|
||||
}
|
||||
else if(userInput.btn.d_down)
|
||||
{
|
||||
return FloatPoint{ DPAD_2_DIRECTIONS_PRESSED_COORD, DPAD_2_DIRECTIONS_PRESSED_COORD};
|
||||
}
|
||||
else
|
||||
{
|
||||
return FloatPoint{1.f, 0.f};
|
||||
}
|
||||
}
|
||||
else if(userInput.btn.d_up)
|
||||
{
|
||||
return FloatPoint{0.f, -1.f};
|
||||
}
|
||||
else if(userInput.btn.d_down)
|
||||
{
|
||||
return FloatPoint{0.f, 1.f};
|
||||
}
|
||||
else
|
||||
{
|
||||
// analog stick
|
||||
int8_t absXVal = static_cast<int8_t>(abs(userInput.stick_x));
|
||||
int8_t absYVal = static_cast<int8_t>(abs(userInput.stick_y));
|
||||
if(absXVal < ANALOG_STICK_MIN_THRESHOLD && absYVal < ANALOG_STICK_MIN_THRESHOLD)
|
||||
{
|
||||
return FloatPoint{0.f, 0.f};
|
||||
}
|
||||
|
||||
// clamp the x and y values to ANALOG_STICK_MAX_THRESHOLD
|
||||
absXVal = (absXVal > ANALOG_STICK_MAX_THRESHOLD) ? ANALOG_STICK_MAX_THRESHOLD : absXVal;
|
||||
absYVal = (absYVal > ANALOG_STICK_MAX_THRESHOLD) ? ANALOG_STICK_MAX_THRESHOLD : absYVal;
|
||||
|
||||
// remove the dead zone and map the resulting range to 0.f-1.f
|
||||
float resultX = static_cast<float>(absXVal - ANALOG_STICK_MIN_THRESHOLD) * STICK_RANGE_UNIT;
|
||||
float resultY = static_cast<float>(absYVal - ANALOG_STICK_MIN_THRESHOLD) * STICK_RANGE_UNIT;
|
||||
|
||||
if(userInput.stick_x < 0)
|
||||
{
|
||||
resultX = -resultX;
|
||||
}
|
||||
if(userInput.stick_y > 0)
|
||||
{
|
||||
// up in the UI means negative y values
|
||||
resultY = -resultY;
|
||||
}
|
||||
return FloatPoint{resultX, resultY};
|
||||
}
|
||||
|
||||
return FloatPoint{0.f, 0.f};
|
||||
}
|
||||
|
||||
MoveScrollWidgetWindowAnimation::MoveScrollWidgetWindowAnimation(ScrollWidget* list)
|
||||
: AbstractAnimation(1.f)
|
||||
, list_(list)
|
||||
, windowStartPoint_({0})
|
||||
, windowEndPoint_({0})
|
||||
{
|
||||
}
|
||||
|
||||
MoveScrollWidgetWindowAnimation::~MoveScrollWidgetWindowAnimation()
|
||||
{
|
||||
}
|
||||
|
||||
AnimationDistanceTimeFunctionType MoveScrollWidgetWindowAnimation::getDistanceTimeFunctionType() const
|
||||
{
|
||||
return AnimationDistanceTimeFunctionType::LINEAR;
|
||||
}
|
||||
|
||||
uint32_t MoveScrollWidgetWindowAnimation::getDurationInMs() const
|
||||
{
|
||||
return AbstractUIScene::MINIMUM_TIME_BETWEEN_INPUT_EVENTS;
|
||||
}
|
||||
|
||||
void MoveScrollWidgetWindowAnimation::start(const Point& startPoint, const Point& endPoint)
|
||||
{
|
||||
windowStartPoint_ = startPoint;
|
||||
windowEndPoint_ = endPoint;
|
||||
}
|
||||
|
||||
void MoveScrollWidgetWindowAnimation::apply(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});
|
||||
}
|
||||
|
||||
ScrollWidget::ScrollWidget(AnimationManager& animManager)
|
||||
: windowAnimation_(this)
|
||||
, widgets_()
|
||||
, style_({0})
|
||||
, animManager_(animManager)
|
||||
, bounds_({0})
|
||||
, windowBounds_({0})
|
||||
, visible_(true)
|
||||
, focused_(false)
|
||||
{
|
||||
animManager_.add(&windowAnimation_);
|
||||
}
|
||||
|
||||
ScrollWidget::~ScrollWidget()
|
||||
{
|
||||
animManager_.remove(&windowAnimation_);
|
||||
}
|
||||
|
||||
bool ScrollWidget::isFocused() const
|
||||
{
|
||||
return focused_;
|
||||
}
|
||||
|
||||
void ScrollWidget::setFocused(bool isFocused)
|
||||
{
|
||||
focused_ = isFocused;
|
||||
}
|
||||
|
||||
bool ScrollWidget::isVisible() const
|
||||
{
|
||||
return visible_;
|
||||
}
|
||||
|
||||
void ScrollWidget::setVisible(bool visible)
|
||||
{
|
||||
visible_ = visible;
|
||||
}
|
||||
|
||||
Rectangle ScrollWidget::getBounds() const
|
||||
{
|
||||
return bounds_;
|
||||
}
|
||||
|
||||
void ScrollWidget::setBounds(const Rectangle& bounds)
|
||||
{
|
||||
bounds_ = bounds;
|
||||
}
|
||||
|
||||
Dimensions ScrollWidget::getSize() const
|
||||
{
|
||||
return Dimensions{.width = bounds_.width, .height = bounds_.height};
|
||||
}
|
||||
|
||||
void ScrollWidget::setStyle(const ScrollWidgetStyle& style)
|
||||
{
|
||||
style_ = style;
|
||||
}
|
||||
|
||||
void ScrollWidget::addWidget(IWidget* widget)
|
||||
{
|
||||
widgets_.push_back(widget);
|
||||
growWindow(widget);
|
||||
}
|
||||
|
||||
void ScrollWidget::removeWidget(IWidget* widget)
|
||||
{
|
||||
auto it = std::find(widgets_.begin(), widgets_.end(), widget);
|
||||
if(it != widgets_.end())
|
||||
{
|
||||
widgets_.erase(it);
|
||||
}
|
||||
recalculateWindowSize();
|
||||
}
|
||||
|
||||
bool ScrollWidget::handleUserInput(const joypad_inputs_t& userInput)
|
||||
{
|
||||
if(!focused_ || style_.scrollStep == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FloatVector scrollDirection = determineScrollDirection(userInput);
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScrollWidget::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
||||
{
|
||||
if(!visible_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Rectangle myAbsoluteBounds = addOffset(bounds_, parentBounds);
|
||||
|
||||
// we "correct" the x/y coords here because the child widget
|
||||
// will use addOffset() to apply its relative bounds (which may have higher xy coords than the width and height of the window) to the parentBounds
|
||||
// by doing this correction, we compensate for the window start offset
|
||||
const Rectangle absoluteWindowStartBounds = Rectangle{
|
||||
.x = myAbsoluteBounds.x - windowBounds_.x,
|
||||
.y = myAbsoluteBounds.y - windowBounds_.y,
|
||||
.width = myAbsoluteBounds.width,
|
||||
.height = myAbsoluteBounds.height
|
||||
};
|
||||
|
||||
// the bounds of the widget instances are all relative to the top of the scroll widget.
|
||||
// so is the windowStart_
|
||||
// so we need to define a rectangle to define the overlap in order to decide whether a widget needs to be rendered at all.
|
||||
const Rectangle relativeWindowRect = {
|
||||
.x = windowBounds_.x,
|
||||
.y = windowBounds_.y,
|
||||
.width = bounds_.width,
|
||||
.height = bounds_.height
|
||||
};
|
||||
|
||||
const Rectangle prevClip = gfx.getClippingRectangle();
|
||||
gfx.setClippingRectangle(myAbsoluteBounds);
|
||||
|
||||
for(IWidget* widget : widgets_)
|
||||
{
|
||||
if(doRectanglesOverlap(relativeWindowRect, widget->getBounds()))
|
||||
{
|
||||
widget->render(gfx, absoluteWindowStartBounds);
|
||||
}
|
||||
}
|
||||
gfx.setClippingRectangle(prevClip);
|
||||
}
|
||||
|
||||
void ScrollWidget::setWindowStart(const Point& windowStart)
|
||||
{
|
||||
windowBounds_.x = windowStart.x;
|
||||
windowBounds_.y = windowStart.y;
|
||||
}
|
||||
|
||||
float ScrollWidget::getWindowProgressX() const
|
||||
{
|
||||
const float maxX = windowBounds_.width - bounds_.width;
|
||||
return static_cast<float>(windowBounds_.x) / maxX;
|
||||
}
|
||||
|
||||
float ScrollWidget::getWindowProgressY() const
|
||||
{
|
||||
const double maxY = windowBounds_.height - bounds_.height;
|
||||
return static_cast<float>(windowBounds_.y) / maxY;
|
||||
}
|
||||
|
||||
void ScrollWidget::growWindow(IWidget* widget)
|
||||
{
|
||||
const Rectangle widgetBounds = widget->getBounds();
|
||||
|
||||
const int widgetMaxX = widgetBounds.x + widgetBounds.width;
|
||||
const int widgetMaxY = widgetBounds.y + widgetBounds.height;
|
||||
|
||||
if(widgetMaxX > windowBounds_.width)
|
||||
{
|
||||
windowBounds_.width = widgetMaxX;
|
||||
}
|
||||
if(widgetMaxY > windowBounds_.height)
|
||||
{
|
||||
windowBounds_.height = widgetMaxY;
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollWidget::recalculateWindowSize()
|
||||
{
|
||||
windowBounds_.width = 0;
|
||||
windowBounds_.height = 0;
|
||||
for(IWidget* widget : widgets_)
|
||||
{
|
||||
growWindow(widget);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user