Files
mice-3ds/source/gui.c
2025-12-07 15:39:55 -06:00

394 lines
8.9 KiB
C

#include <3ds.h>
#include <citro2d.h>
#include <citro3d.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "gui.h"
#include "metadata.h"
static C3D_RenderTarget* topTarget = NULL;
static C3D_RenderTarget* bottomTarget = NULL;
static C2D_TextBuf textBuf = NULL;
/**
* Initialize the GUI system
*/
int guiInit(void)
{
gfxInitDefault();
C3D_Init(C3D_DEFAULT_CMDBUF_SIZE);
C2D_Init(C2D_DEFAULT_MAX_OBJECTS);
C2D_Prepare();
/* Create render targets for top and bottom screens */
topTarget = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT);
bottomTarget = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT);
if(!topTarget || !bottomTarget)
return -1;
/* Create text buffer */
textBuf = C2D_TextBufNew(4096);
if(!textBuf)
return -1;
return 0;
}
/**
* Clean up and exit the GUI system
*/
void guiExit(void)
{
if(textBuf)
C2D_TextBufDelete(textBuf);
C2D_Fini();
C3D_Fini();
gfxExit();
}
/**
* Begin rendering a frame
*/
void guiBeginFrame(void)
{
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
}
/**
* End rendering a frame and display it
*/
void guiEndFrame(void)
{
C3D_FrameEnd(0);
}
/**
* Clear the top screen
*/
void guiClearTopScreen(void)
{
C2D_TargetClear(topTarget, GUI_COLOR_BG_TOP);
}
/**
* Clear the bottom screen
*/
void guiClearBottomScreen(void)
{
C2D_TargetClear(bottomTarget, GUI_COLOR_BG_BOTTOM);
}
/**
* Draw a simple text string at specified position
*/
void guiDrawText(gfxScreen_t screen, float x, float y, const char* text, u32 color, float scale)
{
if(!text || !textBuf)
return;
C2D_Text c2dText;
C2D_TextBufClear(textBuf);
C2D_TextParse(&c2dText, textBuf, text);
C2D_TextOptimize(&c2dText);
C3D_RenderTarget* target = (screen == GFX_TOP) ? topTarget : bottomTarget;
C2D_SceneBegin(target);
C2D_DrawText(&c2dText, C2D_WithColor, x, y, 0.5f, scale, scale, color);
}
/**
* Display metadata on the top screen
*/
void guiDisplayMetadata(struct metadata_t* metadata, const char* filename)
{
if(!metadata || !filename || !textBuf)
return;
C2D_SceneBegin(topTarget);
/* Extract just the filename without path and extension for fallback */
const char* basename = strrchr(filename, '/');
if(!basename)
basename = filename;
else
basename++;
/* Remove file extension for display */
char displayName[64];
strncpy(displayName, basename, sizeof(displayName) - 1);
displayName[sizeof(displayName) - 1] = '\0';
char* dot = strrchr(displayName, '.');
if(dot) *dot = '\0';
C2D_Text text;
float y = 10.0f;
float scale = 0.6f;
float lineHeight = 20.0f;
C2D_TextBufClear(textBuf);
/* Draw title */
if(metadata->title[0])
{
char titleBuf[64];
snprintf(titleBuf, sizeof(titleBuf), "%.47s", metadata->title);
C2D_TextParse(&text, textBuf, titleBuf);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, scale, scale, GUI_COLOR_TEXT);
}
else
{
char titleBuf[64];
snprintf(titleBuf, sizeof(titleBuf), "%.47s", displayName);
C2D_TextParse(&text, textBuf, titleBuf);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, scale, scale, GUI_COLOR_TEXT);
}
y += lineHeight;
/* Draw artist */
if(metadata->artist[0])
{
char artistBuf[64];
snprintf(artistBuf, sizeof(artistBuf), "%.45s", metadata->artist);
C2D_TextParse(&text, textBuf, artistBuf);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, scale * 0.8f, scale * 0.8f, GUI_COLOR_TEXT_DIM);
}
else
{
C2D_TextParse(&text, textBuf, "Unknown Artist");
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, scale * 0.8f, scale * 0.8f, GUI_COLOR_TEXT_DIM);
}
y += lineHeight;
/* Draw album */
if(metadata->album[0])
{
char albumBuf[64];
snprintf(albumBuf, sizeof(albumBuf), "%.45s", metadata->album);
C2D_TextParse(&text, textBuf, albumBuf);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, scale * 0.8f, scale * 0.8f, GUI_COLOR_TEXT_DIM);
}
else
{
C2D_TextParse(&text, textBuf, "Unknown Album");
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, scale * 0.8f, scale * 0.8f, GUI_COLOR_TEXT_DIM);
}
}
/**
* Display log messages on the top screen
*/
void guiDisplayLog(const char** messages, int count, int scroll)
{
if(!messages || count <= 0 || !textBuf)
return;
C2D_SceneBegin(topTarget);
C2D_Text text;
float y = 70.0f; /* Start below metadata area */
float scale = 0.4f;
float lineHeight = 12.0f;
int maxLines = 14;
C2D_TextBufClear(textBuf);
for(int i = scroll; i < count && (i - scroll) < maxLines; i++)
{
if(messages[i])
{
C2D_TextParse(&text, textBuf, messages[i]);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, scale, scale, GUI_COLOR_TEXT);
y += lineHeight;
}
}
}
/**
* Display file list on the bottom screen
*/
void guiDisplayFileList(const char** files, int count, int selected, int scroll)
{
if(!files || count <= 0 || !textBuf)
return;
C2D_SceneBegin(bottomTarget);
C2D_Text text;
float y = 18.0f; /* Start below path display */
float scale = 0.5f;
float lineHeight = 16.0f;
int maxLines = 13; /* One less line due to path at top */
C2D_TextBufClear(textBuf);
for(int i = scroll; i < count && (i - scroll) < maxLines; i++)
{
if(files[i])
{
/* Check if this is a directory */
size_t len = strlen(files[i]);
bool isDir = (len > 0 && files[i][len-1] == '/');
/* Draw selection highlight */
if(i == selected)
{
C2D_DrawRectSolid(5.0f, y - 2.0f, 0.5f, 310.0f, lineHeight, GUI_COLOR_HIGHLIGHT);
}
/* Truncate if too long */
char displayName[48];
snprintf(displayName, sizeof(displayName), "%.40s", files[i]);
C2D_TextParse(&text, textBuf, displayName);
C2D_TextOptimize(&text);
/* Use different color for directories */
u32 color;
if(i == selected)
color = GUI_COLOR_ACCENT;
else if(isDir)
color = C2D_Color32(100, 200, 255, 255); /* Light blue for folders */
else
color = GUI_COLOR_TEXT;
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, scale, scale, color);
y += lineHeight;
}
}
}
/**
* Display playback controls and status on the top screen
*/
void guiDisplayPlaybackStatus(bool isPlaying, bool isPaused, float position, float duration)
{
if(!textBuf)
return;
C2D_SceneBegin(topTarget);
C2D_Text text;
C2D_TextBufClear(textBuf);
/* Display status and time at bottom of top screen */
float y = 215.0f;
/* Display status */
char statusBuf[64];
if(isPlaying)
{
if(isPaused)
snprintf(statusBuf, sizeof(statusBuf), "Paused");
else
snprintf(statusBuf, sizeof(statusBuf), "Playing");
}
else
{
snprintf(statusBuf, sizeof(statusBuf), "Stopped");
}
C2D_TextParse(&text, textBuf, statusBuf);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 10.0f, y, 0.5f, 0.5f, 0.5f, GUI_COLOR_TEXT);
/* Display time if playing */
if(isPlaying && duration > 0)
{
char timeBuf[32];
int posMin = (int)position / 60;
int posSec = (int)position % 60;
int durMin = (int)duration / 60;
int durSec = (int)duration % 60;
snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d / %02d:%02d", posMin, posSec, durMin, durSec);
C2D_TextParse(&text, textBuf, timeBuf);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 280.0f, y, 0.5f, 0.5f, 0.5f, GUI_COLOR_TEXT);
}
}
/**
* Display version text and credits at bottom of bottom screen
*/
void guiDisplayVersion(const char* version)
{
if(!textBuf)
return;
C2D_SceneBegin(bottomTarget);
C2D_Text text;
C2D_TextBufClear(textBuf);
/* Display "mice - by sillyangel" at bottom center */
const char* credits = "mice - by sillyangel";
C2D_TextParse(&text, textBuf, credits);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 80.0f, 220.0f, 0.5f, 0.45f, 0.45f, GUI_COLOR_TEXT_DIM);
}
/**
/**
* Display progress bar on top screen
*/
void guiDisplayProgressBar(float position, float duration)
{
if(duration <= 0)
return;
C2D_SceneBegin(topTarget);
/* Progress bar at bottom of top screen */
float barY = 205.0f;
float barX = 10.0f;
float barWidth = 380.0f;
float barHeight = 6.0f;
/* Background bar */
C2D_DrawRectSolid(barX, barY, 0.5f, barWidth, barHeight, C2D_Color32(50, 50, 60, 255));
/* Progress fill */
float progress = position / duration;
if(progress > 1.0f) progress = 1.0f;
if(progress < 0.0f) progress = 0.0f;
float fillWidth = barWidth * progress;
if(fillWidth > 0)
{
C2D_DrawRectSolid(barX, barY, 0.5f, fillWidth, barHeight, GUI_COLOR_ACCENT);
}
}
/**
* Display current directory path on bottom screen
*/
void guiDisplayCurrentPath(const char* path)
{
if(!path || !textBuf)
return;
C2D_SceneBegin(bottomTarget);
C2D_Text text;
C2D_TextBufClear(textBuf);
/* Display path at top of bottom screen */
char pathBuf[64];
snprintf(pathBuf, sizeof(pathBuf), "%.55s", path);
C2D_TextParse(&text, textBuf, pathBuf);
C2D_TextOptimize(&text);
C2D_DrawText(&text, C2D_WithColor, 5.0f, 2.0f, 0.5f, 0.35f, 0.35f, GUI_COLOR_TEXT_DIM);
}