mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 19:52:05 +01:00
Add RTC time adjustment menu
This commit is contained in:
parent
3f259377ce
commit
88528c1e72
@ -94,6 +94,7 @@ addExecutable(
|
||||
src/main/main.cpp
|
||||
src/main/uibase.cpp
|
||||
src/main/uicommon.cpp
|
||||
src/main/uimodals.cpp
|
||||
src/main/zs01.cpp
|
||||
src/main/app/app.cpp
|
||||
src/main/app/cartactions.cpp
|
||||
|
@ -323,6 +323,15 @@
|
||||
"name": "View IDE device and filesystem information",
|
||||
"prompt": "Display information about the connected IDE/ATAPI devices and mounted FAT filesystem (if any)."
|
||||
},
|
||||
"runExecutable": {
|
||||
"name": "Run executable from hard drive",
|
||||
"prompt": "Load and launch a System 573 executable file from the IDE hard drive or CF card connected as secondary drive (if any).",
|
||||
"filePrompt": "Note that PlayStation executables built without proper System 573 support will not run unless the watchdog is manually disabled."
|
||||
},
|
||||
"setRTCTime": {
|
||||
"name": "Set RTC date and time",
|
||||
"prompt": "Adjust the current date and time. Note that the time will not persist after a power cycle if the RTC's internal battery is empty."
|
||||
},
|
||||
"setResolution": {
|
||||
"name": "Change screen resolution",
|
||||
"prompt": "Switch to a different screen resolution and aspect ratio. Some monitors and upscalers may not support all resolutions."
|
||||
@ -331,11 +340,6 @@
|
||||
"name": "About this tool",
|
||||
"prompt": "View information about this tool, including open source licenses."
|
||||
},
|
||||
"runExecutable": {
|
||||
"name": "Run executable from hard drive",
|
||||
"prompt": "Load and launch a System 573 executable file from the IDE hard drive or CF card connected as secondary drive (if any).",
|
||||
"filePrompt": "Note that PlayStation executables built without proper System 573 support will not run unless the watchdog is manually disabled."
|
||||
},
|
||||
"ejectCD": {
|
||||
"name": "Eject CD-ROM",
|
||||
"prompt": "Open the CD-ROM drive's tray. You may use this option if the drive's eject button is not easily accessible on your 573."
|
||||
@ -383,6 +387,13 @@
|
||||
"640x480i": "640x480 (4:3), interlaced"
|
||||
},
|
||||
|
||||
"RTCTimeScreen": {
|
||||
"title": "Set RTC date and time",
|
||||
"body": "Enter the current date and time. Note that System 573 games only accept years in 1970-2069 range.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move the cursor, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted field.",
|
||||
"cancel": "Cancel",
|
||||
"ok": "Confirm"
|
||||
},
|
||||
|
||||
"StorageActionsScreen": {
|
||||
"title": "{CART_ICON} Storage device options",
|
||||
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include "common/ide.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "vendor/diskio.h"
|
||||
|
||||
/* FatFs library API glue */
|
||||
@ -87,5 +88,8 @@ extern "C" DRESULT disk_ioctl(uint8_t drive, uint8_t cmd, void *data) {
|
||||
}
|
||||
|
||||
extern "C" uint32_t get_fattime(void) {
|
||||
return io::getRTCTime();
|
||||
util::Date date;
|
||||
|
||||
io::getRTCTime(date);
|
||||
return date.toDOSTime();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/io.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "ps1/registers.h"
|
||||
#include "ps1/registers573.h"
|
||||
#include "ps1/system.h"
|
||||
@ -63,32 +64,53 @@ uint32_t getJAMMAInputs(void) {
|
||||
return inputs ^ 0x1fffffff;
|
||||
}
|
||||
|
||||
uint32_t getRTCTime(void) {
|
||||
void getRTCTime(util::Date &output) {
|
||||
SYS573_RTC_CTRL |= SYS573_RTC_CTRL_READ;
|
||||
|
||||
int year = SYS573_RTC_YEAR, month = SYS573_RTC_MONTH, day = SYS573_RTC_DAY;
|
||||
int hour = SYS573_RTC_HOUR, min = SYS573_RTC_MINUTE, sec = SYS573_RTC_SECOND;
|
||||
auto second = SYS573_RTC_SECOND, minute = SYS573_RTC_MINUTE;
|
||||
auto hour = SYS573_RTC_HOUR, day = SYS573_RTC_DAY;
|
||||
auto month = SYS573_RTC_MONTH, year = SYS573_RTC_YEAR;
|
||||
|
||||
SYS573_RTC_CTRL &= ~SYS573_RTC_CTRL_READ;
|
||||
|
||||
year = (year & 15) + 10 * ((year >> 4) & 15); // 0-99
|
||||
month = (month & 15) + 10 * ((month >> 4) & 1); // 1-12
|
||||
day = (day & 15) + 10 * ((day >> 4) & 3); // 1-31
|
||||
hour = (hour & 15) + 10 * ((hour >> 4) & 3); // 0-23
|
||||
min = (min & 15) + 10 * ((min >> 4) & 7); // 0-59
|
||||
sec = (sec & 15) + 10 * ((sec >> 4) & 7); // 0-59
|
||||
output.year = (year & 15) + 10 * ((year >> 4) & 15); // 0-99
|
||||
output.month = (month & 15) + 10 * ((month >> 4) & 1); // 1-12
|
||||
output.day = (day & 15) + 10 * ((day >> 4) & 3); // 1-31
|
||||
output.hour = (hour & 15) + 10 * ((hour >> 4) & 3); // 0-23
|
||||
output.minute = (minute & 15) + 10 * ((minute >> 4) & 7); // 0-59
|
||||
output.second = (second & 15) + 10 * ((second >> 4) & 7); // 0-59
|
||||
|
||||
// Return all values packed into a FAT/MS-DOS-style bitfield. Assume the
|
||||
// year is always in 1995-2094 range.
|
||||
int _year = (year >= 95) ? (year + 1900 - 1980) : (year + 2000 - 1980);
|
||||
output.year += (output.year < 70) ? 2000 : 1900;
|
||||
}
|
||||
|
||||
return 0
|
||||
| (_year << 25)
|
||||
| (month << 21)
|
||||
| (day << 16)
|
||||
| (hour << 11)
|
||||
| (min << 5)
|
||||
| (sec >> 1);
|
||||
void setRTCTime(const util::Date &value, bool stop) {
|
||||
//assert((value.year >= 1970) && (value.year <= 2069));
|
||||
|
||||
int _year = value.year % 100;
|
||||
int weekday = value.getDayOfWeek();
|
||||
|
||||
int year = (_year % 10) | (((_year / 10) & 15) << 4);
|
||||
int month = (value.month % 10) | (((value.month / 10) & 1) << 4);
|
||||
int day = (value.day % 10) | (((value.day / 10) & 3) << 4);
|
||||
int hour = (value.hour % 10) | (((value.hour / 10) & 3) << 4);
|
||||
int minute = (value.minute % 10) | (((value.minute / 10) & 7) << 4);
|
||||
int second = (value.second % 10) | (((value.second / 10) & 7) << 4);
|
||||
|
||||
SYS573_RTC_CTRL |= SYS573_RTC_CTRL_WRITE;
|
||||
|
||||
SYS573_RTC_SECOND = second
|
||||
| (stop ? SYS573_RTC_SECOND_STOP : 0);
|
||||
SYS573_RTC_MINUTE = minute;
|
||||
SYS573_RTC_HOUR = hour;
|
||||
SYS573_RTC_WEEKDAY = weekday
|
||||
| SYS573_RTC_WEEKDAY_CENTURY
|
||||
| SYS573_RTC_WEEKDAY_CENTURY_ENABLE;
|
||||
SYS573_RTC_DAY = day
|
||||
| SYS573_RTC_DAY_BATTERY_MONITOR;
|
||||
SYS573_RTC_MONTH = month;
|
||||
SYS573_RTC_YEAR = year;
|
||||
|
||||
SYS573_RTC_CTRL &= ~SYS573_RTC_CTRL_WRITE;
|
||||
}
|
||||
|
||||
bool isRTCBatteryLow(void) {
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "common/util.hpp"
|
||||
#include "ps1/registers.h"
|
||||
#include "ps1/registers573.h"
|
||||
|
||||
@ -159,7 +161,8 @@ static inline void setDIO1Wire(bool value) {
|
||||
|
||||
void init(void);
|
||||
uint32_t getJAMMAInputs(void);
|
||||
uint32_t getRTCTime(void);
|
||||
void getRTCTime(util::Date &output);
|
||||
void setRTCTime(const util::Date &value, bool stop = false);
|
||||
bool isRTCBatteryLow(void);
|
||||
|
||||
bool loadBitstream(const uint8_t *data, size_t length);
|
||||
|
@ -31,6 +31,69 @@ Hash hash(const uint8_t *data, size_t length) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Date and time class */
|
||||
|
||||
int Date::getDayOfWeek(void) const {
|
||||
// See https://datatracker.ietf.org/doc/html/rfc3339#appendix-B
|
||||
int _year = year, _month = month - 2;
|
||||
|
||||
if (_month <= 0) {
|
||||
_month += 12;
|
||||
_year--;
|
||||
}
|
||||
|
||||
int century = _year / 100;
|
||||
_year %= 100;
|
||||
|
||||
int weekday = 0
|
||||
+ day
|
||||
+ (_month * 26 - 2) / 10
|
||||
+ _year
|
||||
+ _year / 4
|
||||
+ century / 4
|
||||
+ century * 5;
|
||||
|
||||
return weekday % 7;
|
||||
}
|
||||
|
||||
int Date::getMonthDayCount(void) const {
|
||||
switch (month) {
|
||||
case 2:
|
||||
return isLeapYear() ? 29 : 28;
|
||||
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
return 30;
|
||||
|
||||
default:
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Date::toDOSTime(void) const {
|
||||
int _year = year + 2000 - 1980;
|
||||
|
||||
if ((_year < 0) || (_year > 127))
|
||||
return 0;
|
||||
|
||||
return 0
|
||||
| (_year << 25)
|
||||
| (month << 21)
|
||||
| (day << 16)
|
||||
| (hour << 11)
|
||||
| (minute << 5)
|
||||
| (second >> 1);
|
||||
}
|
||||
|
||||
size_t Date::toString(char *output) const {
|
||||
return sprintf(
|
||||
output, "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute,
|
||||
second
|
||||
);
|
||||
}
|
||||
|
||||
/* Tween/animation classes */
|
||||
|
||||
template<typename T, typename E> void Tween<T, E>::setValue(
|
||||
|
@ -158,6 +158,29 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/* Date and time class */
|
||||
|
||||
class Date {
|
||||
public:
|
||||
uint16_t year;
|
||||
uint8_t month, day;
|
||||
uint8_t hour, minute, second;
|
||||
|
||||
inline bool isLeapYear(void) const {
|
||||
if (year % 4)
|
||||
return false;
|
||||
if (!(year % 100) && (year % 400))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int getDayOfWeek(void) const;
|
||||
int getMonthDayCount(void) const;
|
||||
uint32_t toDOSTime(void) const;
|
||||
size_t toString(char *output) const;
|
||||
};
|
||||
|
||||
/* Tween/animation classes */
|
||||
|
||||
static constexpr int TWEEN_UNIT = 1 << 12;
|
||||
|
@ -187,14 +187,23 @@ void App::_interruptHandler(void) {
|
||||
_setupInterrupts();
|
||||
_loadResources();
|
||||
|
||||
_backgroundLayer.text = "v" VERSION_STRING;
|
||||
_ctx.background = &_backgroundLayer;
|
||||
char dateString[24];
|
||||
|
||||
_backgroundLayer.leftText = dateString;
|
||||
_backgroundLayer.rightText = "v" VERSION_STRING;
|
||||
|
||||
_ctx.background = &_backgroundLayer;
|
||||
#ifdef ENABLE_LOG_BUFFER
|
||||
_ctx.overlay = &_overlayLayer;
|
||||
_ctx.overlay = &_overlayLayer;
|
||||
#endif
|
||||
_ctx.show(_workerStatusScreen);
|
||||
|
||||
for (;;) {
|
||||
util::Date date;
|
||||
|
||||
io::getRTCTime(date);
|
||||
date.toString(dateString);
|
||||
|
||||
_ctx.update();
|
||||
_ctx.draw();
|
||||
|
||||
|
@ -60,6 +60,7 @@ class App {
|
||||
friend class StorageInfoScreen;
|
||||
friend class StorageActionsScreen;
|
||||
friend class IDEInfoScreen;
|
||||
friend class RTCTimeScreen;
|
||||
friend class ResolutionScreen;
|
||||
friend class AboutScreen;
|
||||
friend class CartInfoScreen;
|
||||
@ -84,6 +85,7 @@ private:
|
||||
StorageInfoScreen _storageInfoScreen;
|
||||
StorageActionsScreen _storageActionsScreen;
|
||||
IDEInfoScreen _ideInfoScreen;
|
||||
RTCTimeScreen _rtcTimeScreen;
|
||||
ResolutionScreen _resolutionScreen;
|
||||
AboutScreen _aboutScreen;
|
||||
CartInfoScreen _cartInfoScreen;
|
||||
|
@ -305,9 +305,7 @@ void SystemIDEntryScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_buttons[0] = STR("SystemIDEntryScreen.cancel");
|
||||
_buttons[1] = STR("SystemIDEntryScreen.ok");
|
||||
|
||||
_numButtons = 2;
|
||||
_locked = false;
|
||||
|
||||
_numButtons = 2;
|
||||
_bufferLength = 8;
|
||||
_separator = '-';
|
||||
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "main/cartdata.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
|
||||
/* Unlocked cartridge screens */
|
||||
|
||||
|
@ -342,9 +342,7 @@ void KeyEntryScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_buttons[0] = STR("KeyEntryScreen.cancel");
|
||||
_buttons[1] = STR("KeyEntryScreen.ok");
|
||||
|
||||
_numButtons = 2;
|
||||
_locked = false;
|
||||
|
||||
_numButtons = 2;
|
||||
_bufferLength = 8;
|
||||
_separator = '-';
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
|
||||
/* Pre-unlock cartridge screens */
|
||||
|
||||
|
@ -99,6 +99,14 @@ static const MenuEntry _MENU_ENTRIES[]{
|
||||
.name = "MainMenuScreen.ideInfo.name"_h,
|
||||
.prompt = "MainMenuScreen.ideInfo.prompt"_h,
|
||||
.target = &MainMenuScreen::ideInfo
|
||||
}, {
|
||||
.name = "MainMenuScreen.runExecutable.name"_h,
|
||||
.prompt = "MainMenuScreen.runExecutable.prompt"_h,
|
||||
.target = &MainMenuScreen::runExecutable
|
||||
}, {
|
||||
.name = "MainMenuScreen.setRTCTime.name"_h,
|
||||
.prompt = "MainMenuScreen.setRTCTime.prompt"_h,
|
||||
.target = &MainMenuScreen::setRTCTime
|
||||
}, {
|
||||
.name = "MainMenuScreen.setResolution.name"_h,
|
||||
.prompt = "MainMenuScreen.setResolution.prompt"_h,
|
||||
@ -107,10 +115,6 @@ static const MenuEntry _MENU_ENTRIES[]{
|
||||
.name = "MainMenuScreen.about.name"_h,
|
||||
.prompt = "MainMenuScreen.about.prompt"_h,
|
||||
.target = &MainMenuScreen::about
|
||||
}, {
|
||||
.name = "MainMenuScreen.runExecutable.name"_h,
|
||||
.prompt = "MainMenuScreen.runExecutable.prompt"_h,
|
||||
.target = &MainMenuScreen::runExecutable
|
||||
}, {
|
||||
.name = "MainMenuScreen.ejectCD.name"_h,
|
||||
.prompt = "MainMenuScreen.ejectCD.prompt"_h,
|
||||
@ -143,14 +147,6 @@ void MainMenuScreen::ideInfo(ui::Context &ctx) {
|
||||
ctx.show(APP->_ideInfoScreen, false, true);
|
||||
}
|
||||
|
||||
void MainMenuScreen::setResolution(ui::Context &ctx) {
|
||||
ctx.show(APP->_resolutionScreen, false, true);
|
||||
}
|
||||
|
||||
void MainMenuScreen::about(ui::Context &ctx) {
|
||||
ctx.show(APP->_aboutScreen, false, true);
|
||||
}
|
||||
|
||||
void MainMenuScreen::runExecutable(ui::Context &ctx) {
|
||||
APP->_filePickerScreen.setMessage(
|
||||
*this,
|
||||
@ -164,6 +160,18 @@ void MainMenuScreen::runExecutable(ui::Context &ctx) {
|
||||
APP->_filePickerScreen.loadRootAndShow(ctx);
|
||||
}
|
||||
|
||||
void MainMenuScreen::setRTCTime(ui::Context &ctx) {
|
||||
ctx.show(APP->_rtcTimeScreen, false, true);
|
||||
}
|
||||
|
||||
void MainMenuScreen::setResolution(ui::Context &ctx) {
|
||||
ctx.show(APP->_resolutionScreen, false, true);
|
||||
}
|
||||
|
||||
void MainMenuScreen::about(ui::Context &ctx) {
|
||||
ctx.show(APP->_aboutScreen, false, true);
|
||||
}
|
||||
|
||||
void MainMenuScreen::ejectCD(ui::Context &ctx) {
|
||||
APP->_setupWorker(&App::_atapiEjectWorker);
|
||||
ctx.show(APP->_workerStatusScreen, false, true);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
|
||||
/* Main menu screens */
|
||||
|
||||
@ -33,9 +34,10 @@ public:
|
||||
void cartInfo(ui::Context &ctx);
|
||||
void storageInfo(ui::Context &ctx);
|
||||
void ideInfo(ui::Context &ctx);
|
||||
void runExecutable(ui::Context &ctx);
|
||||
void setRTCTime(ui::Context &ctx);
|
||||
void setResolution(ui::Context &ctx);
|
||||
void about(ui::Context &ctx);
|
||||
void runExecutable(ui::Context &ctx);
|
||||
void ejectCD(ui::Context &ctx);
|
||||
void reboot(ui::Context &ctx);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <stdio.h>
|
||||
#include "common/file.hpp"
|
||||
#include "common/ide.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/spu.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
@ -107,6 +108,32 @@ void IDEInfoScreen::update(ui::Context &ctx) {
|
||||
|
||||
/* Misc. screens */
|
||||
|
||||
void RTCTimeScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_title = STR("RTCTimeScreen.title");
|
||||
_body = STR("RTCTimeScreen.body");
|
||||
_buttons[0] = STR("RTCTimeScreen.cancel");
|
||||
_buttons[1] = STR("RTCTimeScreen.ok");
|
||||
|
||||
_numButtons = 2;
|
||||
io::getRTCTime(_date);
|
||||
|
||||
DateEntryScreen::show(ctx, goBack);
|
||||
}
|
||||
|
||||
void RTCTimeScreen::update(ui::Context &ctx) {
|
||||
DateEntryScreen::update(ctx);
|
||||
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_START) &&
|
||||
(_activeButton >= _buttonIndexOffset)
|
||||
) {
|
||||
if (_activeButton == (_buttonIndexOffset + 1))
|
||||
io::setRTCTime(_date);
|
||||
|
||||
ctx.show(APP->_mainMenuScreen, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
struct Resolution {
|
||||
public:
|
||||
util::Hash name;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "common/util.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
|
||||
/* System information screens */
|
||||
|
||||
@ -20,6 +21,12 @@ public:
|
||||
|
||||
/* Misc. screens */
|
||||
|
||||
class RTCTimeScreen : public ui::DateEntryScreen {
|
||||
public:
|
||||
void show(ui::Context &ctx, bool goBack = false);
|
||||
void update(ui::Context &ctx);
|
||||
};
|
||||
|
||||
class ResolutionScreen : public ui::ListScreen {
|
||||
protected:
|
||||
const char *_getItemName(ui::Context &ctx, int index) const;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "common/util.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
|
||||
/* Modal screens */
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "common/rom.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
|
||||
/* Storage device submenu */
|
||||
|
||||
|
@ -216,17 +216,23 @@ void TiledBackground::draw(Context &ctx, bool active) const {
|
||||
tile.draw(ctx.gpuCtx, x, y);
|
||||
}
|
||||
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
gpu::RectWH rect;
|
||||
|
||||
int width = ctx.font.getStringWidth(text);
|
||||
rect.x = ctx.gpuCtx.width - (8 + width);
|
||||
rect.y = ctx.gpuCtx.height - (8 + ctx.font.metrics.lineHeight);
|
||||
rect.w = width;
|
||||
rect.h = ctx.font.metrics.lineHeight;
|
||||
ctx.font.draw(ctx.gpuCtx, text, rect, ctx.colors[COLOR_TEXT2]);
|
||||
rect.y = ctx.gpuCtx.height - (8 + ctx.font.metrics.lineHeight);
|
||||
rect.h = ctx.font.metrics.lineHeight;
|
||||
|
||||
if (leftText) {
|
||||
rect.x = 8;
|
||||
rect.w = ctx.gpuCtx.width - 16;
|
||||
ctx.font.draw(ctx.gpuCtx, leftText, rect, ctx.colors[COLOR_TEXT2]);
|
||||
}
|
||||
if (rightText) {
|
||||
int width = ctx.font.getStringWidth(rightText);
|
||||
|
||||
rect.x = ctx.gpuCtx.width - (8 + width);
|
||||
rect.w = width;
|
||||
ctx.font.draw(ctx.gpuCtx, rightText, rect, ctx.colors[COLOR_TEXT2]);
|
||||
}
|
||||
}
|
||||
|
||||
LogOverlay::LogOverlay(util::LogBuffer &buffer)
|
||||
|
@ -186,10 +186,10 @@ public:
|
||||
class TiledBackground : public Layer {
|
||||
public:
|
||||
gpu::Image tile;
|
||||
const char *text;
|
||||
const char *leftText, *rightText;
|
||||
|
||||
inline TiledBackground(void)
|
||||
: text(nullptr) {}
|
||||
: leftText(nullptr), rightText(nullptr) {}
|
||||
|
||||
void draw(Context &ctx, bool active = true) const;
|
||||
};
|
||||
|
@ -1,14 +1,13 @@
|
||||
|
||||
#include "common/defs.hpp"
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/gpufont.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uicommon.hpp"
|
||||
#include "ps1/gpucmd.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* Common higher-level screens */
|
||||
/* Common screens */
|
||||
|
||||
void PlaceholderScreen::draw(Context &ctx, bool active) const {
|
||||
_newLayer(ctx, 0, 0, ctx.gpuCtx.width, ctx.gpuCtx.height);
|
||||
@ -17,268 +16,6 @@ void PlaceholderScreen::draw(Context &ctx, bool active) const {
|
||||
);
|
||||
}
|
||||
|
||||
MessageBoxScreen::MessageBoxScreen(void)
|
||||
: ModalScreen(MODAL_WIDTH, MODAL_HEIGHT_FULL), _numButtons(0),
|
||||
_buttonIndexOffset(0), _locked(false) {}
|
||||
|
||||
void MessageBoxScreen::show(Context &ctx, bool goBack) {
|
||||
ModalScreen::show(ctx, goBack);
|
||||
|
||||
_activeButton = 0;
|
||||
_buttonAnim.setValue(_getButtonWidth());
|
||||
}
|
||||
|
||||
void MessageBoxScreen::draw(Context &ctx, bool active) const {
|
||||
ModalScreen::draw(ctx, active);
|
||||
|
||||
if (!active || !_numButtons)
|
||||
return;
|
||||
|
||||
int activeButton = _activeButton - _buttonIndexOffset;
|
||||
|
||||
int buttonX = _width / 8;
|
||||
int buttonY = TITLE_BAR_HEIGHT + _height - (BUTTON_HEIGHT + MODAL_PADDING);
|
||||
gpu::RectWH rect;
|
||||
|
||||
rect.y = buttonY + BUTTON_PADDING;
|
||||
rect.w = _getButtonWidth();
|
||||
rect.h = rect.y + ctx.font.metrics.lineHeight;
|
||||
//rect.h = BUTTON_HEIGHT - BUTTON_PADDING * 2;
|
||||
|
||||
for (int i = 0; i < _numButtons; i++) {
|
||||
rect.x = buttonX +
|
||||
(rect.w - ctx.font.getStringWidth(_buttons[i])) / 2;
|
||||
|
||||
if (_locked) {
|
||||
ctx.gpuCtx.drawRect(
|
||||
buttonX, buttonY, rect.w, BUTTON_HEIGHT,
|
||||
ctx.colors[COLOR_SHADOW], true
|
||||
);
|
||||
|
||||
ctx.font.draw(
|
||||
ctx.gpuCtx, _buttons[i], rect, ctx.colors[COLOR_TEXT2]
|
||||
);
|
||||
} else {
|
||||
if (i == activeButton) {
|
||||
ctx.gpuCtx.drawRect(
|
||||
buttonX, buttonY, rect.w, BUTTON_HEIGHT,
|
||||
ctx.colors[COLOR_HIGHLIGHT2]
|
||||
);
|
||||
ctx.gpuCtx.drawRect(
|
||||
buttonX, buttonY, _buttonAnim.getValue(ctx.time),
|
||||
BUTTON_HEIGHT, ctx.colors[COLOR_HIGHLIGHT1]
|
||||
);
|
||||
} else {
|
||||
ctx.gpuCtx.drawRect(
|
||||
buttonX, buttonY, rect.w, BUTTON_HEIGHT,
|
||||
ctx.colors[COLOR_WINDOW3]
|
||||
);
|
||||
}
|
||||
|
||||
ctx.font.draw(
|
||||
ctx.gpuCtx, _buttons[i], rect, ctx.colors[COLOR_TITLE]
|
||||
);
|
||||
}
|
||||
|
||||
buttonX += rect.w + BUTTON_SPACING;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageBoxScreen::update(Context &ctx) {
|
||||
if (_locked)
|
||||
return;
|
||||
|
||||
int numButtons = _buttonIndexOffset + _numButtons;
|
||||
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (_activeButton > 0))
|
||||
) {
|
||||
_activeButton--;
|
||||
if (_activeButton < 0) {
|
||||
_activeButton += numButtons;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
|
||||
_buttonAnim.setValue(ctx.time, 0, _getButtonWidth(), SPEED_FASTEST);
|
||||
}
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (_activeButton < (numButtons - 1)))
|
||||
) {
|
||||
_activeButton++;
|
||||
if (_activeButton >= numButtons) {
|
||||
_activeButton -= numButtons;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
|
||||
_buttonAnim.setValue(ctx.time, 0, _getButtonWidth(), SPEED_FASTEST);
|
||||
}
|
||||
}
|
||||
|
||||
HexEntryScreen::HexEntryScreen(void)
|
||||
: _bufferLength(0) {
|
||||
__builtin_memset(_buffer, 0, sizeof(_buffer));
|
||||
}
|
||||
|
||||
void HexEntryScreen::show(Context &ctx, bool goBack) {
|
||||
MessageBoxScreen::show(ctx, goBack);
|
||||
|
||||
_buttonIndexOffset = _bufferLength * 2;
|
||||
//__builtin_memset(_buffer, 0, _bufferLength);
|
||||
|
||||
_charIndex = 0;
|
||||
_cursorAnim.setValue(0);
|
||||
}
|
||||
|
||||
void HexEntryScreen::draw(Context &ctx, bool active) const {
|
||||
MessageBoxScreen::draw(ctx, active);
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
int boxY = TITLE_BAR_HEIGHT + _height -
|
||||
(BUTTON_HEIGHT + MODAL_PADDING) * 2;
|
||||
int boxWidth = _width - MODAL_PADDING * 2;
|
||||
|
||||
// Text box
|
||||
ctx.gpuCtx.drawRect(
|
||||
MODAL_PADDING, boxY, boxWidth, BUTTON_HEIGHT, ctx.colors[COLOR_BOX1]
|
||||
);
|
||||
|
||||
char text[128];
|
||||
gpu::Rect rect;
|
||||
|
||||
util::hexToString(text, _buffer, _bufferLength, _separator);
|
||||
|
||||
int digitWidth = ctx.font.getCharacterWidth('0');
|
||||
int textOffset = MODAL_PADDING +
|
||||
(boxWidth - ctx.font.getStringWidth(text)) / 2;
|
||||
|
||||
// Cursor
|
||||
if (_activeButton < _buttonIndexOffset)
|
||||
ctx.gpuCtx.drawGradientRectV(
|
||||
textOffset + _cursorAnim.getValue(ctx.time),
|
||||
boxY + BUTTON_HEIGHT / 2, digitWidth, BUTTON_HEIGHT / 2,
|
||||
ctx.colors[COLOR_BOX1], ctx.colors[COLOR_HIGHLIGHT1]
|
||||
);
|
||||
|
||||
// Text
|
||||
rect.x1 = textOffset;
|
||||
rect.y1 = boxY + BUTTON_PADDING;
|
||||
rect.x2 = _width - MODAL_PADDING;
|
||||
rect.y2 = boxY + BUTTON_PADDING + ctx.font.metrics.lineHeight;
|
||||
ctx.font.draw(ctx.gpuCtx, text, rect, ctx.colors[COLOR_TITLE]);
|
||||
|
||||
// Highlighted digit
|
||||
if (_activeButton < _buttonIndexOffset) {
|
||||
text[0] = text[_charIndex];
|
||||
text[1] = 0;
|
||||
|
||||
rect.x1 = textOffset + _cursorAnim.getTargetValue();
|
||||
ctx.font.draw(ctx.gpuCtx, text, rect, ctx.colors[COLOR_SUBTITLE]);
|
||||
}
|
||||
}
|
||||
|
||||
void HexEntryScreen::update(Context &ctx) {
|
||||
if (
|
||||
ctx.buttons.held(ui::BTN_START) && (_activeButton < _buttonIndexOffset)
|
||||
) {
|
||||
uint8_t *ptr = &_buffer[_activeButton / 2];
|
||||
int digit = (_activeButton % 2) ? (*ptr & 0x0f) : (*ptr >> 4);
|
||||
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (digit > 0))
|
||||
) {
|
||||
digit--;
|
||||
if (digit < 0) {
|
||||
digit = 0xf;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
}
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (digit < 0xf))
|
||||
) {
|
||||
digit++;
|
||||
if (digit > 0xf) {
|
||||
digit = 0;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
}
|
||||
|
||||
if (_activeButton % 2)
|
||||
*ptr = (*ptr & 0xf0) | digit;
|
||||
else
|
||||
*ptr = (*ptr & 0x0f) | (digit << 4);
|
||||
} else {
|
||||
int oldActive = _activeButton;
|
||||
|
||||
MessageBoxScreen::update(ctx);
|
||||
|
||||
// Update the cursor's position if necessary.
|
||||
if (oldActive != _activeButton) {
|
||||
int digitWidth = ctx.font.getCharacterWidth('0');
|
||||
int sepWidth = ctx.font.getCharacterWidth(_separator);
|
||||
|
||||
int offset = _activeButton, cursorX = 0;
|
||||
_charIndex = 0;
|
||||
|
||||
for (; offset >= 2; offset -= 2) {
|
||||
cursorX += digitWidth * 2 + sepWidth;
|
||||
_charIndex += 3;
|
||||
}
|
||||
for (; offset; offset--) {
|
||||
cursorX += digitWidth;
|
||||
_charIndex++;
|
||||
}
|
||||
|
||||
_cursorAnim.setValue(ctx.time, cursorX, SPEED_FASTEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProgressScreen::ProgressScreen(void)
|
||||
: ModalScreen(MODAL_WIDTH, MODAL_HEIGHT_REDUCED) {}
|
||||
|
||||
void ProgressScreen::show(Context &ctx, bool goBack) {
|
||||
ModalScreen::show(ctx, goBack);
|
||||
|
||||
_progressBarAnim.setValue(0);
|
||||
}
|
||||
|
||||
void ProgressScreen::draw(Context &ctx, bool active) const {
|
||||
ModalScreen::draw(ctx, active);
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
int fullBarWidth = _width - MODAL_PADDING * 2;
|
||||
|
||||
int barX = (_width - fullBarWidth) / 2;
|
||||
int barY = TITLE_BAR_HEIGHT + _height -
|
||||
(PROGRESS_BAR_HEIGHT + MODAL_PADDING);
|
||||
|
||||
_setBlendMode(ctx, GP0_BLEND_SEMITRANS, true);
|
||||
|
||||
ctx.gpuCtx.drawRect(
|
||||
barX, barY, fullBarWidth, PROGRESS_BAR_HEIGHT, ctx.colors[COLOR_WINDOW3]
|
||||
);
|
||||
ctx.gpuCtx.drawGradientRectH(
|
||||
barX, barY, _progressBarAnim.getValue(ctx.time), PROGRESS_BAR_HEIGHT,
|
||||
ctx.colors[COLOR_PROGRESS2], ctx.colors[COLOR_PROGRESS1]
|
||||
);
|
||||
}
|
||||
|
||||
TextScreen::TextScreen(void)
|
||||
: _title(nullptr), _body(nullptr), _prompt(nullptr) {}
|
||||
|
||||
@ -555,7 +292,7 @@ void ListScreen::update(Context &ctx) {
|
||||
}
|
||||
|
||||
_itemAnim.setValue(ctx.time, 0, _getItemWidth(ctx), SPEED_FAST);
|
||||
|
||||
|
||||
}
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||
|
@ -2,81 +2,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common/gpufont.hpp"
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/util.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* Common higher-level screens */
|
||||
/* Common screens */
|
||||
|
||||
class PlaceholderScreen : public AnimatedScreen {
|
||||
public:
|
||||
void draw(Context &ctx, bool active) const;
|
||||
};
|
||||
|
||||
class MessageBoxScreen : public ModalScreen {
|
||||
private:
|
||||
util::Tween<int, util::QuadOutEasing> _buttonAnim;
|
||||
|
||||
inline int _getButtonWidth(void) const {
|
||||
return ((_width / 5) * 4) / _numButtons - BUTTON_SPACING;
|
||||
}
|
||||
|
||||
protected:
|
||||
int _numButtons, _activeButton, _buttonIndexOffset;
|
||||
bool _locked;
|
||||
|
||||
const char *_buttons[4];
|
||||
|
||||
public:
|
||||
MessageBoxScreen(void);
|
||||
virtual void show(Context &ctx, bool goBack = false);
|
||||
virtual void draw(Context &ctx, bool active = true) const;
|
||||
virtual void update(Context &ctx);
|
||||
};
|
||||
|
||||
class HexEntryScreen : public MessageBoxScreen {
|
||||
private:
|
||||
int _charIndex;
|
||||
|
||||
util::Tween<int, util::QuadOutEasing> _cursorAnim;
|
||||
|
||||
protected:
|
||||
uint8_t _buffer[32];
|
||||
char _separator;
|
||||
|
||||
int _bufferLength;
|
||||
|
||||
public:
|
||||
HexEntryScreen(void);
|
||||
virtual void show(Context &ctx, bool goBack = false);
|
||||
virtual void draw(Context &ctx, bool active = true) const;
|
||||
virtual void update(Context &ctx);
|
||||
};
|
||||
|
||||
class ProgressScreen : public ModalScreen {
|
||||
private:
|
||||
util::Tween<int, util::QuadOutEasing> _progressBarAnim;
|
||||
|
||||
protected:
|
||||
inline void _setProgress(Context &ctx, int part, int total) {
|
||||
if (!total)
|
||||
total = 1;
|
||||
|
||||
int totalWidth = _width - MODAL_PADDING * 2;
|
||||
int partWidth = (totalWidth * part) / total;
|
||||
|
||||
if (_progressBarAnim.getTargetValue() != partWidth)
|
||||
_progressBarAnim.setValue(ctx.time, partWidth, SPEED_FASTEST);
|
||||
}
|
||||
|
||||
public:
|
||||
ProgressScreen(void);
|
||||
virtual void show(Context &ctx, bool goBack = false);
|
||||
virtual void draw(Context &ctx, bool active = true) const;
|
||||
};
|
||||
|
||||
class TextScreen : public AnimatedScreen {
|
||||
private:
|
||||
util::Tween<int, util::QuadOutEasing> _scrollAnim;
|
||||
|
446
src/main/uimodals.cpp
Normal file
446
src/main/uimodals.cpp
Normal file
@ -0,0 +1,446 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/gpu.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
#include "main/uimodals.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
MessageBoxScreen::MessageBoxScreen(void)
|
||||
: ModalScreen(MODAL_WIDTH, MODAL_HEIGHT_FULL), _numButtons(0),
|
||||
_buttonIndexOffset(0), _locked(false) {}
|
||||
|
||||
void MessageBoxScreen::show(Context &ctx, bool goBack) {
|
||||
ModalScreen::show(ctx, goBack);
|
||||
|
||||
_activeButton = 0;
|
||||
_buttonAnim.setValue(_getButtonWidth());
|
||||
}
|
||||
|
||||
void MessageBoxScreen::draw(Context &ctx, bool active) const {
|
||||
ModalScreen::draw(ctx, active);
|
||||
|
||||
if (!active || !_numButtons)
|
||||
return;
|
||||
|
||||
int activeButton = _activeButton - _buttonIndexOffset;
|
||||
|
||||
int buttonX = _width / 8;
|
||||
int buttonY = TITLE_BAR_HEIGHT + _height - (BUTTON_HEIGHT + MODAL_PADDING);
|
||||
gpu::RectWH rect;
|
||||
|
||||
rect.y = buttonY + BUTTON_PADDING;
|
||||
rect.w = _getButtonWidth();
|
||||
rect.h = rect.y + ctx.font.metrics.lineHeight;
|
||||
//rect.h = BUTTON_HEIGHT - BUTTON_PADDING * 2;
|
||||
|
||||
for (int i = 0; i < _numButtons; i++) {
|
||||
rect.x = buttonX +
|
||||
(rect.w - ctx.font.getStringWidth(_buttons[i])) / 2;
|
||||
|
||||
if (_locked) {
|
||||
ctx.gpuCtx.drawRect(
|
||||
buttonX, buttonY, rect.w, BUTTON_HEIGHT,
|
||||
ctx.colors[COLOR_SHADOW], true
|
||||
);
|
||||
|
||||
ctx.font.draw(
|
||||
ctx.gpuCtx, _buttons[i], rect, ctx.colors[COLOR_TEXT2]
|
||||
);
|
||||
} else {
|
||||
if (i == activeButton) {
|
||||
ctx.gpuCtx.drawRect(
|
||||
buttonX, buttonY, rect.w, BUTTON_HEIGHT,
|
||||
ctx.colors[COLOR_HIGHLIGHT2]
|
||||
);
|
||||
ctx.gpuCtx.drawRect(
|
||||
buttonX, buttonY, _buttonAnim.getValue(ctx.time),
|
||||
BUTTON_HEIGHT, ctx.colors[COLOR_HIGHLIGHT1]
|
||||
);
|
||||
} else {
|
||||
ctx.gpuCtx.drawRect(
|
||||
buttonX, buttonY, rect.w, BUTTON_HEIGHT,
|
||||
ctx.colors[COLOR_WINDOW3]
|
||||
);
|
||||
}
|
||||
|
||||
ctx.font.draw(
|
||||
ctx.gpuCtx, _buttons[i], rect, ctx.colors[COLOR_TITLE]
|
||||
);
|
||||
}
|
||||
|
||||
buttonX += rect.w + BUTTON_SPACING;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageBoxScreen::update(Context &ctx) {
|
||||
if (_locked)
|
||||
return;
|
||||
|
||||
int numButtons = _buttonIndexOffset + _numButtons;
|
||||
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (_activeButton > 0))
|
||||
) {
|
||||
_activeButton--;
|
||||
if (_activeButton < 0) {
|
||||
_activeButton += numButtons;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
|
||||
_buttonAnim.setValue(ctx.time, 0, _getButtonWidth(), SPEED_FASTEST);
|
||||
}
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (_activeButton < (numButtons - 1)))
|
||||
) {
|
||||
_activeButton++;
|
||||
if (_activeButton >= numButtons) {
|
||||
_activeButton -= numButtons;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
|
||||
_buttonAnim.setValue(ctx.time, 0, _getButtonWidth(), SPEED_FASTEST);
|
||||
}
|
||||
}
|
||||
|
||||
HexEntryScreen::HexEntryScreen(void)
|
||||
: _bufferLength(0) {
|
||||
__builtin_memset(_buffer, 0, sizeof(_buffer));
|
||||
}
|
||||
|
||||
void HexEntryScreen::show(Context &ctx, bool goBack) {
|
||||
MessageBoxScreen::show(ctx, goBack);
|
||||
|
||||
//__builtin_memset(_buffer, 0, _bufferLength);
|
||||
|
||||
_buttonIndexOffset = _bufferLength * 2;
|
||||
_charWidth = ctx.font.getCharacterWidth('0');
|
||||
_separatorWidth = ctx.font.getCharacterWidth(_separator);
|
||||
_stringWidth = 0
|
||||
+ _charWidth * (_bufferLength * 2)
|
||||
+ _separatorWidth * (_bufferLength - 1);
|
||||
|
||||
_cursorAnim.setValue(0);
|
||||
}
|
||||
|
||||
void HexEntryScreen::draw(Context &ctx, bool active) const {
|
||||
MessageBoxScreen::draw(ctx, active);
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
int boxY = TITLE_BAR_HEIGHT + _height -
|
||||
(BUTTON_HEIGHT + MODAL_PADDING) * 2;
|
||||
int boxWidth = _width - MODAL_PADDING * 2;
|
||||
|
||||
// Text box
|
||||
ctx.gpuCtx.drawRect(
|
||||
MODAL_PADDING, boxY, boxWidth, BUTTON_HEIGHT, ctx.colors[COLOR_BOX1]
|
||||
);
|
||||
|
||||
char string[128];
|
||||
gpu::Rect rect;
|
||||
|
||||
util::hexToString(string, _buffer, _bufferLength, _separator);
|
||||
|
||||
int stringOffset = MODAL_PADDING + (boxWidth - _stringWidth) / 2;
|
||||
int charIndex = _activeButton + _activeButton / 2;
|
||||
|
||||
// Cursor
|
||||
if (_activeButton < _buttonIndexOffset)
|
||||
ctx.gpuCtx.drawGradientRectV(
|
||||
stringOffset + _cursorAnim.getValue(ctx.time),
|
||||
boxY + BUTTON_HEIGHT / 2, _charWidth, BUTTON_HEIGHT / 2,
|
||||
ctx.colors[COLOR_BOX1], ctx.colors[COLOR_HIGHLIGHT1]
|
||||
);
|
||||
|
||||
// Current string
|
||||
rect.x1 = stringOffset;
|
||||
rect.y1 = boxY + BUTTON_PADDING;
|
||||
rect.x2 = _width - MODAL_PADDING;
|
||||
rect.y2 = boxY + BUTTON_PADDING + ctx.font.metrics.lineHeight;
|
||||
ctx.font.draw(ctx.gpuCtx, string, rect, ctx.colors[COLOR_TITLE]);
|
||||
|
||||
// Highlighted field
|
||||
if (_activeButton < _buttonIndexOffset) {
|
||||
auto ptr = &string[charIndex];
|
||||
ptr[1] = 0;
|
||||
|
||||
rect.x1 = stringOffset + _cursorAnim.getTargetValue();
|
||||
ctx.font.draw(ctx.gpuCtx, ptr, rect, ctx.colors[COLOR_SUBTITLE]);
|
||||
}
|
||||
}
|
||||
|
||||
void HexEntryScreen::update(Context &ctx) {
|
||||
if (
|
||||
ctx.buttons.held(ui::BTN_START) && (_activeButton < _buttonIndexOffset)
|
||||
) {
|
||||
auto ptr = &_buffer[_activeButton / 2];
|
||||
int value = (_activeButton % 2) ? (*ptr & 0x0f) : (*ptr >> 4);
|
||||
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (value > 0))
|
||||
) {
|
||||
if (--value < 0) {
|
||||
value = 0xf;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
}
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (value < 0xf))
|
||||
) {
|
||||
if (++value > 0xf) {
|
||||
value = 0;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
}
|
||||
|
||||
if (_activeButton % 2)
|
||||
*ptr = (*ptr & 0xf0) | value;
|
||||
else
|
||||
*ptr = (*ptr & 0x0f) | (value << 4);
|
||||
} else {
|
||||
int oldActive = _activeButton;
|
||||
|
||||
MessageBoxScreen::update(ctx);
|
||||
|
||||
// Update the cursor's position if necessary.
|
||||
if (oldActive != _activeButton) {
|
||||
int cursorX = 0
|
||||
+ _charWidth * _activeButton
|
||||
+ _separatorWidth * (_activeButton / 2);
|
||||
|
||||
_cursorAnim.setValue(ctx.time, cursorX, SPEED_FASTEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DateField {
|
||||
public:
|
||||
size_t offset;
|
||||
uint16_t minValue, maxValue;
|
||||
};
|
||||
|
||||
static const DateField _DATE_FIELDS[]{
|
||||
{
|
||||
.offset = offsetof(util::Date, year),
|
||||
.minValue = 1970,
|
||||
.maxValue = 2069
|
||||
}, {
|
||||
.offset = offsetof(util::Date, month),
|
||||
.minValue = 1,
|
||||
.maxValue = 12
|
||||
}, {
|
||||
.offset = offsetof(util::Date, day),
|
||||
.minValue = 1,
|
||||
.maxValue = 31
|
||||
}, {
|
||||
.offset = offsetof(util::Date, hour),
|
||||
.minValue = 0,
|
||||
.maxValue = 23
|
||||
}, {
|
||||
.offset = offsetof(util::Date, minute),
|
||||
.minValue = 0,
|
||||
.maxValue = 59
|
||||
}, {
|
||||
.offset = offsetof(util::Date, second),
|
||||
.minValue = 0,
|
||||
.maxValue = 59
|
||||
}
|
||||
};
|
||||
|
||||
DateEntryScreen::DateEntryScreen(void) {
|
||||
_date.year = 2000;
|
||||
_date.month = 1;
|
||||
_date.day = 1;
|
||||
_date.hour = 0;
|
||||
_date.minute = 0;
|
||||
_date.second = 0;
|
||||
}
|
||||
|
||||
void DateEntryScreen::show(Context &ctx, bool goBack) {
|
||||
MessageBoxScreen::show(ctx, goBack);
|
||||
|
||||
_buttonIndexOffset = 6;
|
||||
_charWidth = ctx.font.getCharacterWidth('0');
|
||||
|
||||
int dateSepWidth = ctx.font.getCharacterWidth('-');
|
||||
int spaceWidth = ctx.font.metrics.spaceWidth;
|
||||
int timeSepWidth = ctx.font.getCharacterWidth(':');
|
||||
|
||||
_fieldOffsets[0] = 0;
|
||||
_fieldOffsets[1] = _fieldOffsets[0] + _charWidth * 4 + dateSepWidth;
|
||||
_fieldOffsets[2] = _fieldOffsets[1] + _charWidth * 2 + dateSepWidth;
|
||||
_fieldOffsets[3] = _fieldOffsets[2] + _charWidth * 2 + spaceWidth;
|
||||
_fieldOffsets[4] = _fieldOffsets[3] + _charWidth * 2 + timeSepWidth;
|
||||
_fieldOffsets[5] = _fieldOffsets[4] + _charWidth * 2 + timeSepWidth;
|
||||
_stringWidth = _fieldOffsets[5] + _charWidth * 2;
|
||||
|
||||
_cursorAnim.setValue(0);
|
||||
}
|
||||
|
||||
void DateEntryScreen::draw(Context &ctx, bool active) const {
|
||||
MessageBoxScreen::draw(ctx, active);
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
int boxY = TITLE_BAR_HEIGHT + _height -
|
||||
(BUTTON_HEIGHT + MODAL_PADDING) * 2;
|
||||
int boxWidth = _width - MODAL_PADDING * 2;
|
||||
|
||||
// Text box
|
||||
ctx.gpuCtx.drawRect(
|
||||
MODAL_PADDING, boxY, boxWidth, BUTTON_HEIGHT, ctx.colors[COLOR_BOX1]
|
||||
);
|
||||
|
||||
char string[24];
|
||||
gpu::Rect rect;
|
||||
|
||||
_date.toString(string);
|
||||
|
||||
int stringOffset = MODAL_PADDING + (boxWidth - _stringWidth) / 2;
|
||||
int charIndex = _activeButton * 3;
|
||||
int fieldLength = 2;
|
||||
|
||||
// The first field (year) has 4 digits, while all others have 2.
|
||||
if (_activeButton)
|
||||
charIndex += 2;
|
||||
else
|
||||
fieldLength += 2;
|
||||
|
||||
// Cursor
|
||||
if (_activeButton < _buttonIndexOffset)
|
||||
ctx.gpuCtx.drawGradientRectV(
|
||||
stringOffset + _cursorAnim.getValue(ctx.time),
|
||||
boxY + BUTTON_HEIGHT / 2, _charWidth * fieldLength,
|
||||
BUTTON_HEIGHT / 2,ctx.colors[COLOR_BOX1],
|
||||
ctx.colors[COLOR_HIGHLIGHT1]
|
||||
);
|
||||
|
||||
// Current string
|
||||
rect.x1 = stringOffset;
|
||||
rect.y1 = boxY + BUTTON_PADDING;
|
||||
rect.x2 = _width - MODAL_PADDING;
|
||||
rect.y2 = boxY + BUTTON_PADDING + ctx.font.metrics.lineHeight;
|
||||
ctx.font.draw(ctx.gpuCtx, string, rect, ctx.colors[COLOR_TITLE]);
|
||||
|
||||
// Highlighted field
|
||||
if (_activeButton < _buttonIndexOffset) {
|
||||
auto ptr = &string[charIndex];
|
||||
ptr[fieldLength] = 0;
|
||||
|
||||
rect.x1 = stringOffset + _cursorAnim.getTargetValue();
|
||||
ctx.font.draw(ctx.gpuCtx, ptr, rect, ctx.colors[COLOR_SUBTITLE]);
|
||||
}
|
||||
}
|
||||
|
||||
void DateEntryScreen::update(Context &ctx) {
|
||||
if (
|
||||
ctx.buttons.held(ui::BTN_START) && (_activeButton < _buttonIndexOffset)
|
||||
) {
|
||||
auto &field = _DATE_FIELDS[_activeButton];
|
||||
int value;
|
||||
|
||||
// The year is the only 16-bit field.
|
||||
if (!_activeButton)
|
||||
value = _date.year;
|
||||
else
|
||||
value = *reinterpret_cast<uint8_t *>(
|
||||
reinterpret_cast<uintptr_t>(&_date) + field.offset
|
||||
);
|
||||
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_LEFT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_LEFT) && (value > field.minValue))
|
||||
) {
|
||||
if (--value < field.minValue) {
|
||||
value = field.maxValue;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
}
|
||||
if (
|
||||
ctx.buttons.pressed(ui::BTN_RIGHT) ||
|
||||
(ctx.buttons.repeating(ui::BTN_RIGHT) && (value < field.maxValue))
|
||||
) {
|
||||
if (++value > field.maxValue) {
|
||||
value = field.minValue;
|
||||
ctx.sounds[SOUND_CLICK].play();
|
||||
} else {
|
||||
ctx.sounds[SOUND_MOVE].play();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_activeButton)
|
||||
_date.year = value;
|
||||
else
|
||||
*reinterpret_cast<uint8_t *>(
|
||||
reinterpret_cast<uintptr_t>(&_date) + field.offset
|
||||
) = value;
|
||||
|
||||
// The day field must be fixed up after any date change.
|
||||
int maxDayValue = _date.getMonthDayCount();
|
||||
|
||||
if (_date.day > maxDayValue)
|
||||
_date.day = maxDayValue;
|
||||
} else {
|
||||
int oldActive = _activeButton;
|
||||
|
||||
MessageBoxScreen::update(ctx);
|
||||
|
||||
// Update the cursor's position if necessary.
|
||||
if (oldActive != _activeButton)
|
||||
_cursorAnim.setValue(
|
||||
ctx.time, _fieldOffsets[_activeButton], SPEED_FASTEST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProgressScreen::ProgressScreen(void)
|
||||
: ModalScreen(MODAL_WIDTH, MODAL_HEIGHT_REDUCED) {}
|
||||
|
||||
void ProgressScreen::show(Context &ctx, bool goBack) {
|
||||
ModalScreen::show(ctx, goBack);
|
||||
|
||||
_progressBarAnim.setValue(0);
|
||||
}
|
||||
|
||||
void ProgressScreen::draw(Context &ctx, bool active) const {
|
||||
ModalScreen::draw(ctx, active);
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
int fullBarWidth = _width - MODAL_PADDING * 2;
|
||||
|
||||
int barX = (_width - fullBarWidth) / 2;
|
||||
int barY = TITLE_BAR_HEIGHT + _height -
|
||||
(PROGRESS_BAR_HEIGHT + MODAL_PADDING);
|
||||
|
||||
_setBlendMode(ctx, GP0_BLEND_SEMITRANS, true);
|
||||
|
||||
ctx.gpuCtx.drawRect(
|
||||
barX, barY, fullBarWidth, PROGRESS_BAR_HEIGHT, ctx.colors[COLOR_WINDOW3]
|
||||
);
|
||||
ctx.gpuCtx.drawGradientRectH(
|
||||
barX, barY, _progressBarAnim.getValue(ctx.time), PROGRESS_BAR_HEIGHT,
|
||||
ctx.colors[COLOR_PROGRESS2], ctx.colors[COLOR_PROGRESS1]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
90
src/main/uimodals.hpp
Normal file
90
src/main/uimodals.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common/util.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* Common modal screens */
|
||||
|
||||
class MessageBoxScreen : public ModalScreen {
|
||||
private:
|
||||
util::Tween<int, util::QuadOutEasing> _buttonAnim;
|
||||
|
||||
inline int _getButtonWidth(void) const {
|
||||
return ((_width / 5) * 4) / _numButtons - BUTTON_SPACING;
|
||||
}
|
||||
|
||||
protected:
|
||||
int _numButtons, _activeButton, _buttonIndexOffset;
|
||||
bool _locked;
|
||||
|
||||
const char *_buttons[4];
|
||||
|
||||
public:
|
||||
MessageBoxScreen(void);
|
||||
virtual void show(Context &ctx, bool goBack = false);
|
||||
virtual void draw(Context &ctx, bool active = true) const;
|
||||
virtual void update(Context &ctx);
|
||||
};
|
||||
|
||||
class HexEntryScreen : public MessageBoxScreen {
|
||||
private:
|
||||
uint8_t _charWidth, _separatorWidth, _stringWidth;
|
||||
|
||||
util::Tween<int, util::QuadOutEasing> _cursorAnim;
|
||||
|
||||
protected:
|
||||
uint8_t _buffer[32];
|
||||
char _separator;
|
||||
|
||||
int _bufferLength;
|
||||
|
||||
public:
|
||||
HexEntryScreen(void);
|
||||
virtual void show(Context &ctx, bool goBack = false);
|
||||
virtual void draw(Context &ctx, bool active = true) const;
|
||||
virtual void update(Context &ctx);
|
||||
};
|
||||
|
||||
class DateEntryScreen : public MessageBoxScreen {
|
||||
private:
|
||||
uint8_t _charWidth, _stringWidth, _fieldOffsets[6];
|
||||
|
||||
util::Tween<int, util::QuadOutEasing> _cursorAnim;
|
||||
|
||||
protected:
|
||||
util::Date _date;
|
||||
|
||||
public:
|
||||
DateEntryScreen(void);
|
||||
virtual void show(Context &ctx, bool goBack = false);
|
||||
virtual void draw(Context &ctx, bool active = true) const;
|
||||
virtual void update(Context &ctx);
|
||||
};
|
||||
|
||||
class ProgressScreen : public ModalScreen {
|
||||
private:
|
||||
util::Tween<int, util::QuadOutEasing> _progressBarAnim;
|
||||
|
||||
protected:
|
||||
inline void _setProgress(Context &ctx, int part, int total) {
|
||||
if (!total)
|
||||
total = 1;
|
||||
|
||||
int totalWidth = _width - MODAL_PADDING * 2;
|
||||
int partWidth = (totalWidth * part) / total;
|
||||
|
||||
if (_progressBarAnim.getTargetValue() != partWidth)
|
||||
_progressBarAnim.setValue(ctx.time, partWidth, SPEED_FASTEST);
|
||||
}
|
||||
|
||||
public:
|
||||
ProgressScreen(void);
|
||||
virtual void show(Context &ctx, bool goBack = false);
|
||||
virtual void draw(Context &ctx, bool active = true) const;
|
||||
};
|
||||
|
||||
}
|
@ -91,6 +91,13 @@ typedef enum {
|
||||
SYS573_RTC_SECOND_STOP = 1 << 7
|
||||
} Sys573RTCSecondFlag;
|
||||
|
||||
typedef enum {
|
||||
SYS573_RTC_WEEKDAY_UNITS_BITMASK = 7 << 0,
|
||||
SYS573_RTC_WEEKDAY_CENTURY = 1 << 4,
|
||||
SYS573_RTC_WEEKDAY_CENTURY_ENABLE = 1 << 5,
|
||||
SYS573_RTC_WEEKDAY_FREQUENCY_TEST = 1 << 6
|
||||
} Sys573RTCWeekdayFlag;
|
||||
|
||||
typedef enum {
|
||||
SYS573_RTC_DAY_UNITS_BITMASK = 15 << 0,
|
||||
SYS573_RTC_DAY_TENS_BITMASK = 3 << 4,
|
||||
|
Loading…
x
Reference in New Issue
Block a user