feat: Implement Change View and Renderer Options menus
- Added functionality to display and navigate the Change View menu in SixelRenderer and SoftwareRenderer. - Introduced methods to draw the Change View and Renderer Options menus, including handling cursor and selection states. - Updated WolfClassicMenuArt to include a customize label for the new menu. - Enhanced WolfMenuScreen to support new menu states. - Created tests for Change View menu interactions, ensuring proper transitions and renderer settings toggling. - Implemented persistence for renderer settings in Flutter, allowing settings to be saved and loaded from a local file. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -642,6 +642,163 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (engine.menuManager.activeMenu == WolfMenuScreen.changeView) {
|
||||
_drawCustomizeMenuHeader(art, headingColor, bgColor);
|
||||
final cursor = art.mappedPic(
|
||||
engine.menuManager.isCursorAltFrame(engine.timeAliveMs) ? 9 : 8,
|
||||
);
|
||||
final selectedMarker = art.selectedMarker;
|
||||
final unselectedMarker = art.unselectedMarker;
|
||||
const int rowYStart = 66;
|
||||
const int rowStep = 18;
|
||||
const int cursorX = 62;
|
||||
const int markerX = 92;
|
||||
const int textX = 122;
|
||||
final entries = engine.menuManager.changeViewEntries;
|
||||
final optionEntries = engine.menuManager.rendererOptionEntries;
|
||||
final int modeCount = entries.length;
|
||||
final int optionCount = optionEntries.length;
|
||||
|
||||
const int modesPanelY = 52;
|
||||
final int modesContentHeight = modeCount <= 0
|
||||
? 0
|
||||
: ((modeCount - 1) * rowStep) + 12;
|
||||
final int modesPanelHeight = math.max(56, modesContentHeight + 14);
|
||||
final int sectionHeaderY = modesPanelY + modesPanelHeight + 6;
|
||||
final int optionsPanelY = sectionHeaderY + 14;
|
||||
final int optionsContentHeight = optionCount <= 0
|
||||
? 0
|
||||
: ((optionCount - 1) * 15) + 12;
|
||||
final int optionsPanelHeight = math.max(30, optionsContentHeight + 10);
|
||||
|
||||
_fillRect320(46, modesPanelY, 228, modesPanelHeight, panelColor);
|
||||
_fillRect320(46, optionsPanelY, 228, optionsPanelHeight, panelColor);
|
||||
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
final int y = rowYStart + (i * rowStep);
|
||||
final bool isSelected = i == engine.menuManager.selectedChangeViewIndex;
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImageAscii(cursor, cursorX, y - 2);
|
||||
}
|
||||
|
||||
final entry = entries[i];
|
||||
final marker = entry.isChecked ? selectedMarker : unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImageAscii(marker, markerX, y);
|
||||
}
|
||||
final int textColor = entry.isEnabled
|
||||
? (isSelected ? selectedTextColor : unselectedTextColor)
|
||||
: disabledTextColor;
|
||||
_drawMenuLabelAdaptive(
|
||||
typography: menuTypography,
|
||||
text: entry.label,
|
||||
textX320: textX,
|
||||
y200: y + 1,
|
||||
color: textColor,
|
||||
panelX320: 46,
|
||||
panelW320: 228,
|
||||
panelColor: panelColor,
|
||||
);
|
||||
}
|
||||
|
||||
_drawMenuSectionHeader(
|
||||
text: engine.menuManager.rendererOptionsTitle,
|
||||
y200: sectionHeaderY,
|
||||
);
|
||||
|
||||
const int optionsRowStep = 15;
|
||||
final int optionsRowsHeight = optionCount <= 0
|
||||
? 0
|
||||
: ((optionCount - 1) * optionsRowStep) + 10;
|
||||
final int optionsRowStart =
|
||||
optionsPanelY +
|
||||
((optionsPanelHeight - optionsRowsHeight) ~/ 2).clamp(0, 200);
|
||||
for (int i = 0; i < optionEntries.length; i++) {
|
||||
final int optionIndex = modeCount + i;
|
||||
final bool isSelected =
|
||||
optionIndex == engine.menuManager.selectedChangeViewIndex;
|
||||
final int y = optionsRowStart + (i * optionsRowStep);
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImageAscii(cursor, cursorX, y - 2);
|
||||
}
|
||||
final entry = optionEntries[i];
|
||||
final marker = entry.isChecked ? selectedMarker : unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImageAscii(marker, markerX, y);
|
||||
}
|
||||
final int textColor = entry.isEnabled
|
||||
? (isSelected ? selectedTextColor : unselectedTextColor)
|
||||
: disabledTextColor;
|
||||
_drawMenuLabelAdaptive(
|
||||
typography: menuTypography,
|
||||
text: entry.label,
|
||||
textX320: textX,
|
||||
y200: y + 1,
|
||||
color: textColor,
|
||||
panelX320: 46,
|
||||
panelW320: 228,
|
||||
panelColor: panelColor,
|
||||
);
|
||||
}
|
||||
|
||||
_drawCenteredMenuFooter();
|
||||
_applyMenuFade(engine.menuManager.transitionAlpha, bgColor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (engine.menuManager.activeMenu == WolfMenuScreen.rendererOptions) {
|
||||
_drawCustomizeMenuHeader(art, headingColor, bgColor);
|
||||
_fillRect320(56, 52, 208, 120, panelColor);
|
||||
_drawMenuTextCentered(
|
||||
engine.menuManager.rendererOptionsTitle,
|
||||
46,
|
||||
headingColor,
|
||||
scale: 1,
|
||||
);
|
||||
|
||||
final cursor = art.mappedPic(
|
||||
engine.menuManager.isCursorAltFrame(engine.timeAliveMs) ? 9 : 8,
|
||||
);
|
||||
final selectedMarker = art.selectedMarker;
|
||||
final unselectedMarker = art.unselectedMarker;
|
||||
const int rowYStart = 68;
|
||||
const int rowStep = 20;
|
||||
const int cursorX = 62;
|
||||
const int markerX = 92;
|
||||
const int textX = 122;
|
||||
final entries = engine.menuManager.rendererOptionEntries;
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
final int y = rowYStart + (i * rowStep);
|
||||
final bool isSelected =
|
||||
i == engine.menuManager.selectedRendererOptionIndex;
|
||||
final entry = entries[i];
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImageAscii(cursor, cursorX, y - 2);
|
||||
}
|
||||
final marker = entry.isChecked ? selectedMarker : unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImageAscii(marker, markerX, y);
|
||||
}
|
||||
final int textColor = entry.isEnabled
|
||||
? (isSelected ? selectedTextColor : unselectedTextColor)
|
||||
: disabledTextColor;
|
||||
_drawMenuLabelAdaptive(
|
||||
typography: menuTypography,
|
||||
text: entry.label,
|
||||
textX320: textX,
|
||||
y200: y + 1,
|
||||
color: textColor,
|
||||
panelX320: 56,
|
||||
panelW320: 208,
|
||||
panelColor: panelColor,
|
||||
);
|
||||
}
|
||||
|
||||
_drawCenteredMenuFooter();
|
||||
_applyMenuFade(engine.menuManager.transitionAlpha, bgColor);
|
||||
return;
|
||||
}
|
||||
|
||||
final int selectedDifficultyIndex =
|
||||
engine.menuManager.selectedDifficultyIndex;
|
||||
|
||||
@@ -997,7 +1154,10 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
|
||||
int scale = 1,
|
||||
}) {
|
||||
if (y200 == _headerHeadingY) {
|
||||
y200 = _centerHeaderTitleInBlackBand(defaultY: y200, scale: scale);
|
||||
y200 = _centerHeaderTitleInBlackBand(
|
||||
defaultY: y200,
|
||||
scale: scale,
|
||||
);
|
||||
}
|
||||
final int textWidth = WolfMenuFont.measureTextWidth(text, scale);
|
||||
final int x320 = ((320 - textWidth) ~/ 2).clamp(0, 319);
|
||||
@@ -1156,6 +1316,103 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
|
||||
}
|
||||
}
|
||||
|
||||
void _drawCustomizeMenuHeader(
|
||||
WolfClassicMenuArt art,
|
||||
int headingColor,
|
||||
int backgroundColor,
|
||||
) {
|
||||
_drawHeaderBarStack(
|
||||
headingY200: _headerHeadingY,
|
||||
backgroundColor: backgroundColor,
|
||||
barColor: ColorPalette.vga32Bit[0],
|
||||
);
|
||||
|
||||
final VgaImage? heading = art.customizeLabel ?? art.optionsLabel;
|
||||
if (heading != null) {
|
||||
final int headingX = ((320 - heading.width) ~/ 2).clamp(0, 319);
|
||||
_blitVgaImageAscii(heading, headingX, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_drawMenuTextCentered('CUSTOMIZE', _headerHeadingY, headingColor, scale: 2);
|
||||
}
|
||||
|
||||
void _drawMenuSectionHeader({
|
||||
required String text,
|
||||
required int y200,
|
||||
}) {
|
||||
final int textW = WolfMenuFont.measureTextWidth(text, 1);
|
||||
final int boxW = (textW + 28).clamp(110, 250);
|
||||
final int x = ((320 - boxW) ~/ 2).clamp(0, 319);
|
||||
final int outer = _rgbToPaletteColor(0x727272);
|
||||
final int inner = _rgbToPaletteColor(0x8E8E8E);
|
||||
final int fill = _rgbToPaletteColor(0xC9C9C9);
|
||||
final int textColor = _rgbToPaletteColor(0x5A5A5A);
|
||||
|
||||
_fillRect320(x, y200, boxW, 12, outer);
|
||||
_fillRect320(x + 1, y200 + 1, boxW - 2, 10, inner);
|
||||
_fillRect320(x + 2, y200 + 2, boxW - 4, 8, fill);
|
||||
_drawMenuLabelAdaptive(
|
||||
typography: _resolveMenuTypography(),
|
||||
text: text,
|
||||
textX320: x + 8,
|
||||
y200: y200 + 2,
|
||||
color: textColor,
|
||||
panelX320: x + 2,
|
||||
panelW320: boxW - 4,
|
||||
panelColor: fill,
|
||||
centerWhenCompact: true,
|
||||
);
|
||||
}
|
||||
|
||||
void _drawMenuLabelAdaptive({
|
||||
required _AsciiMenuTypography typography,
|
||||
required String text,
|
||||
required int textX320,
|
||||
required int y200,
|
||||
required int color,
|
||||
required int panelX320,
|
||||
required int panelW320,
|
||||
required int panelColor,
|
||||
bool centerWhenCompact = false,
|
||||
}) {
|
||||
if (!typography.usesCompactRows) {
|
||||
_drawMenuText(text, textX320, y200, color);
|
||||
return;
|
||||
}
|
||||
|
||||
if (centerWhenCompact) {
|
||||
final int panelX = _menuX320ToColumn(panelX320);
|
||||
final int panelW = math.max(
|
||||
1,
|
||||
((panelW320 / 320.0) * projectionWidth).toInt(),
|
||||
);
|
||||
final int textW = text.length;
|
||||
final int centeredLeft = panelX + math.max(0, ((panelW - textW) ~/ 2));
|
||||
_writeLeftClipped(
|
||||
_menuY200ToRow(y200),
|
||||
text,
|
||||
color,
|
||||
panelColor,
|
||||
math.max(1, panelW - 2),
|
||||
centeredLeft,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_drawMinimalMenuRows(
|
||||
rows: <String>[text],
|
||||
selectedIndex: -1,
|
||||
rowYStart200: y200,
|
||||
rowStep200: 1,
|
||||
textX320: textX320,
|
||||
panelX320: panelX320,
|
||||
panelW320: panelW320,
|
||||
panelColor: panelColor,
|
||||
colorForRow: (_, _) => color,
|
||||
);
|
||||
}
|
||||
|
||||
void _drawCenteredMenuFooter() {
|
||||
if (_usesTerminalLayout && !_emitAnsi) {
|
||||
_drawFlutterGridMenuFooter();
|
||||
|
||||
@@ -526,6 +526,146 @@ class SixelRenderer extends CliRendererBackend<String> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (engine.menuManager.activeMenu == WolfMenuScreen.changeView) {
|
||||
_drawCustomizeMenuHeader(art, headingIndex, bgColor);
|
||||
final cursor = art.mappedPic(
|
||||
engine.menuManager.isCursorAltFrame(engine.timeAliveMs) ? 9 : 8,
|
||||
);
|
||||
final selectedMarker = art.selectedMarker;
|
||||
final unselectedMarker = art.unselectedMarker;
|
||||
const int rowYStart = 66;
|
||||
const int rowStep = 18;
|
||||
const int cursorX = 62;
|
||||
const int markerX = 92;
|
||||
const int textX = 122;
|
||||
final entries = engine.menuManager.changeViewEntries;
|
||||
final optionEntries = engine.menuManager.rendererOptionEntries;
|
||||
final int modeCount = entries.length;
|
||||
final int optionCount = optionEntries.length;
|
||||
|
||||
const int modesPanelY = 52;
|
||||
final int modesContentHeight = modeCount <= 0
|
||||
? 0
|
||||
: ((modeCount - 1) * rowStep) + 12;
|
||||
final int modesPanelHeight = math.max(56, modesContentHeight + 14);
|
||||
final int sectionHeaderY = modesPanelY + modesPanelHeight + 6;
|
||||
final int optionsPanelY = sectionHeaderY + 14;
|
||||
final int optionsContentHeight = optionCount <= 0
|
||||
? 0
|
||||
: ((optionCount - 1) * 15) + 12;
|
||||
final int optionsPanelHeight = math.max(30, optionsContentHeight + 10);
|
||||
|
||||
_fillRect320(46, modesPanelY, 228, modesPanelHeight, panelColor);
|
||||
_fillRect320(46, optionsPanelY, 228, optionsPanelHeight, panelColor);
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
final int y = rowYStart + (i * rowStep);
|
||||
final bool isSelected = i == engine.menuManager.selectedChangeViewIndex;
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImage(cursor, cursorX, y - 2);
|
||||
}
|
||||
|
||||
final entry = entries[i];
|
||||
final marker = entry.isChecked ? selectedMarker : unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImage(marker, markerX, y);
|
||||
}
|
||||
_drawMenuText(
|
||||
entry.label,
|
||||
textX,
|
||||
y + 1,
|
||||
entry.isEnabled
|
||||
? (isSelected ? selectedTextIndex : unselectedTextIndex)
|
||||
: disabledTextIndex,
|
||||
scale: 1,
|
||||
);
|
||||
}
|
||||
|
||||
_drawMenuSectionHeader(
|
||||
text: engine.menuManager.rendererOptionsTitle,
|
||||
y200: sectionHeaderY,
|
||||
);
|
||||
|
||||
const int optionsRowStep = 15;
|
||||
final int optionsRowsHeight = optionCount <= 0
|
||||
? 0
|
||||
: ((optionCount - 1) * optionsRowStep) + 10;
|
||||
final int optionsRowStart =
|
||||
optionsPanelY +
|
||||
((optionsPanelHeight - optionsRowsHeight) ~/ 2).clamp(0, 200);
|
||||
for (int i = 0; i < optionEntries.length; i++) {
|
||||
final int optionIndex = modeCount + i;
|
||||
final bool isSelected =
|
||||
optionIndex == engine.menuManager.selectedChangeViewIndex;
|
||||
final int y = optionsRowStart + (i * optionsRowStep);
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImage(cursor, cursorX, y - 2);
|
||||
}
|
||||
final entry = optionEntries[i];
|
||||
final marker = entry.isChecked ? selectedMarker : unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImage(marker, markerX, y);
|
||||
}
|
||||
_drawMenuText(
|
||||
entry.label,
|
||||
textX,
|
||||
y + 1,
|
||||
entry.isEnabled
|
||||
? (isSelected ? selectedTextIndex : unselectedTextIndex)
|
||||
: disabledTextIndex,
|
||||
scale: 1,
|
||||
);
|
||||
}
|
||||
_applyMenuFade(engine.menuManager.transitionAlpha, bgColor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (engine.menuManager.activeMenu == WolfMenuScreen.rendererOptions) {
|
||||
_drawCustomizeMenuHeader(art, headingIndex, bgColor);
|
||||
_fillRect320(56, 52, 208, 120, panelColor);
|
||||
_drawMenuTextCentered(
|
||||
engine.menuManager.rendererOptionsTitle,
|
||||
46,
|
||||
headingIndex,
|
||||
scale: 1,
|
||||
);
|
||||
|
||||
final cursor = art.mappedPic(
|
||||
engine.menuManager.isCursorAltFrame(engine.timeAliveMs) ? 9 : 8,
|
||||
);
|
||||
final selectedMarker = art.selectedMarker;
|
||||
final unselectedMarker = art.unselectedMarker;
|
||||
const int rowYStart = 68;
|
||||
const int rowStep = 20;
|
||||
const int cursorX = 62;
|
||||
const int markerX = 92;
|
||||
const int textX = 122;
|
||||
final entries = engine.menuManager.rendererOptionEntries;
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
final int y = rowYStart + (i * rowStep);
|
||||
final bool isSelected =
|
||||
i == engine.menuManager.selectedRendererOptionIndex;
|
||||
final entry = entries[i];
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImage(cursor, cursorX, y - 2);
|
||||
}
|
||||
final marker = entry.isChecked ? selectedMarker : unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImage(marker, markerX, y);
|
||||
}
|
||||
_drawMenuText(
|
||||
entry.label,
|
||||
textX,
|
||||
y + 1,
|
||||
entry.isEnabled
|
||||
? (isSelected ? selectedTextIndex : unselectedTextIndex)
|
||||
: disabledTextIndex,
|
||||
scale: 1,
|
||||
);
|
||||
}
|
||||
_applyMenuFade(engine.menuManager.transitionAlpha, bgColor);
|
||||
return;
|
||||
}
|
||||
|
||||
final int selectedDifficultyIndex =
|
||||
engine.menuManager.selectedDifficultyIndex;
|
||||
_drawHeaderBarStack(
|
||||
@@ -580,6 +720,45 @@ class SixelRenderer extends CliRendererBackend<String> {
|
||||
_applyMenuFade(engine.menuManager.transitionAlpha, bgColor);
|
||||
}
|
||||
|
||||
void _drawCustomizeMenuHeader(
|
||||
WolfClassicMenuArt art,
|
||||
int headingIndex,
|
||||
int backgroundColor,
|
||||
) {
|
||||
_drawHeaderBarStack(
|
||||
headingY200: _headerHeadingY,
|
||||
backgroundColor: backgroundColor,
|
||||
barColor: 0,
|
||||
);
|
||||
|
||||
final VgaImage? heading = art.customizeLabel ?? art.optionsLabel;
|
||||
if (heading != null) {
|
||||
final int headingX = ((320 - heading.width) ~/ 2).clamp(0, 319);
|
||||
_blitVgaImage(heading, headingX, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_drawMenuTextCentered('CUSTOMIZE', _headerHeadingY, headingIndex, scale: 2);
|
||||
}
|
||||
|
||||
void _drawMenuSectionHeader({
|
||||
required String text,
|
||||
required int y200,
|
||||
}) {
|
||||
final int textW = WolfMenuFont.measureTextWidth(text, 1);
|
||||
final int boxW = (textW + 28).clamp(110, 250);
|
||||
final int x = ((320 - boxW) ~/ 2).clamp(0, 319);
|
||||
final int outer = _rgbToPaletteIndex(0x727272);
|
||||
final int inner = _rgbToPaletteIndex(0x8E8E8E);
|
||||
final int fill = _rgbToPaletteIndex(0xC9C9C9);
|
||||
final int textColor = _rgbToPaletteIndex(0x5A5A5A);
|
||||
|
||||
_fillRect320(x, y200, boxW, 12, outer);
|
||||
_fillRect320(x + 1, y200 + 1, boxW - 2, 10, inner);
|
||||
_fillRect320(x + 2, y200 + 2, boxW - 4, 8, fill);
|
||||
_drawMenuTextCentered(text, y200 + 2, textColor, scale: 1);
|
||||
}
|
||||
|
||||
void _drawMenuFooterArt(WolfClassicMenuArt art) {
|
||||
final bottom = art.mappedPic(15);
|
||||
if (bottom == null) {
|
||||
|
||||
@@ -209,6 +209,28 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
|
||||
unselectedTextColor,
|
||||
);
|
||||
break;
|
||||
case WolfMenuScreen.changeView:
|
||||
_drawChangeViewMenu(
|
||||
engine,
|
||||
art,
|
||||
panelColor,
|
||||
headingColor,
|
||||
selectedTextColor,
|
||||
unselectedTextColor,
|
||||
disabledTextColor,
|
||||
);
|
||||
break;
|
||||
case WolfMenuScreen.rendererOptions:
|
||||
_drawRendererOptionsMenu(
|
||||
engine,
|
||||
art,
|
||||
panelColor,
|
||||
headingColor,
|
||||
selectedTextColor,
|
||||
unselectedTextColor,
|
||||
disabledTextColor,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
_applyMenuFade(engine.menuManager.transitionAlpha, bgColor);
|
||||
@@ -410,6 +432,216 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
|
||||
}
|
||||
}
|
||||
|
||||
void _drawChangeViewMenu(
|
||||
WolfEngine engine,
|
||||
WolfClassicMenuArt art,
|
||||
int panelColor,
|
||||
int headingColor,
|
||||
int selectedTextColor,
|
||||
int unselectedTextColor,
|
||||
int disabledTextColor,
|
||||
) {
|
||||
_drawHeaderBarStack(
|
||||
headingY200: _headerHeadingY,
|
||||
backgroundColor: _rgbToFrameColor(engine.menuManager.menuBackgroundRgb),
|
||||
barColor: ColorPalette.vga32Bit[0],
|
||||
);
|
||||
|
||||
const int modesPanelX = 46;
|
||||
const int modesPanelY = 52;
|
||||
const int modesPanelW = 228;
|
||||
const int modesPanelH = 74;
|
||||
_fillCanonicalRect(
|
||||
modesPanelX,
|
||||
modesPanelY,
|
||||
modesPanelW,
|
||||
modesPanelH,
|
||||
panelColor,
|
||||
);
|
||||
|
||||
const int optionsPanelX = 46;
|
||||
const int optionsPanelY = 146;
|
||||
const int optionsPanelW = 228;
|
||||
const int optionsPanelH = 42;
|
||||
_fillCanonicalRect(
|
||||
optionsPanelX,
|
||||
optionsPanelY,
|
||||
optionsPanelW,
|
||||
optionsPanelH,
|
||||
panelColor,
|
||||
);
|
||||
|
||||
final VgaImage? heading = art.customizeLabel ?? art.optionsLabel;
|
||||
if (heading != null) {
|
||||
final int headingX = ((320 - heading.width) ~/ 2).clamp(0, 319);
|
||||
_blitVgaImage(heading, headingX, 0);
|
||||
} else {
|
||||
_drawCanonicalMenuTextCentered(
|
||||
'CUSTOMIZE',
|
||||
_headerHeadingY,
|
||||
headingColor,
|
||||
scale: 2,
|
||||
);
|
||||
}
|
||||
|
||||
final VgaImage? selectedMarker = art.selectedMarker;
|
||||
final VgaImage? unselectedMarker = art.unselectedMarker;
|
||||
final VgaImage? cursor = art.mappedPic(
|
||||
engine.menuManager.isCursorAltFrame(engine.timeAliveMs) ? 9 : 8,
|
||||
);
|
||||
|
||||
const int rowYStart = 66;
|
||||
const int rowStep = 18;
|
||||
const int cursorX = 62;
|
||||
const int markerX = 92;
|
||||
const int textX = 122;
|
||||
|
||||
final entries = engine.menuManager.changeViewEntries;
|
||||
final optionEntries = engine.menuManager.rendererOptionEntries;
|
||||
final int modeCount = entries.length;
|
||||
final int selectedIndex = engine.menuManager.selectedChangeViewIndex;
|
||||
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
final bool isSelected = i == selectedIndex;
|
||||
final int y = rowYStart + (i * rowStep);
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImage(cursor, cursorX, y - 2);
|
||||
}
|
||||
|
||||
final entry = entries[i];
|
||||
final VgaImage? marker = entry.isChecked
|
||||
? selectedMarker
|
||||
: unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImage(marker, markerX, y);
|
||||
}
|
||||
|
||||
_drawCanonicalMenuText(
|
||||
entry.label,
|
||||
textX,
|
||||
y + 1,
|
||||
entry.isEnabled
|
||||
? (isSelected ? selectedTextColor : unselectedTextColor)
|
||||
: disabledTextColor,
|
||||
);
|
||||
}
|
||||
|
||||
_drawMenuSectionHeader(
|
||||
text: engine.menuManager.rendererOptionsTitle,
|
||||
y200: 132,
|
||||
textColor: ColorPalette.vga32Bit[8],
|
||||
);
|
||||
|
||||
const int optionsRowStart = 159;
|
||||
const int optionsRowStep = 15;
|
||||
for (int i = 0; i < optionEntries.length; i++) {
|
||||
final int optionIndex = modeCount + i;
|
||||
final bool isSelected = optionIndex == selectedIndex;
|
||||
final int y = optionsRowStart + (i * optionsRowStep);
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImage(cursor, cursorX, y - 2);
|
||||
}
|
||||
|
||||
final entry = optionEntries[i];
|
||||
final VgaImage? marker = entry.isChecked
|
||||
? selectedMarker
|
||||
: unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImage(marker, markerX, y);
|
||||
}
|
||||
_drawCanonicalMenuText(
|
||||
entry.label,
|
||||
textX,
|
||||
y + 1,
|
||||
entry.isEnabled
|
||||
? (isSelected ? selectedTextColor : unselectedTextColor)
|
||||
: disabledTextColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _drawRendererOptionsMenu(
|
||||
WolfEngine engine,
|
||||
WolfClassicMenuArt art,
|
||||
int panelColor,
|
||||
int headingColor,
|
||||
int selectedTextColor,
|
||||
int unselectedTextColor,
|
||||
int disabledTextColor,
|
||||
) {
|
||||
_drawHeaderBarStack(
|
||||
headingY200: _headerHeadingY,
|
||||
backgroundColor: _rgbToFrameColor(engine.menuManager.menuBackgroundRgb),
|
||||
barColor: ColorPalette.vga32Bit[0],
|
||||
);
|
||||
|
||||
const int panelX = 56;
|
||||
const int panelY = 52;
|
||||
const int panelW = 208;
|
||||
const int panelH = 120;
|
||||
_fillCanonicalRect(panelX, panelY, panelW, panelH, panelColor);
|
||||
|
||||
final VgaImage? heading = art.customizeLabel ?? art.optionsLabel;
|
||||
if (heading != null) {
|
||||
final int headingX = ((320 - heading.width) ~/ 2).clamp(0, 319);
|
||||
_blitVgaImage(heading, headingX, 0);
|
||||
} else {
|
||||
_drawCanonicalMenuTextCentered(
|
||||
'CUSTOMIZE',
|
||||
_headerHeadingY,
|
||||
headingColor,
|
||||
scale: 2,
|
||||
);
|
||||
}
|
||||
|
||||
final VgaImage? selectedMarker = art.selectedMarker;
|
||||
final VgaImage? unselectedMarker = art.unselectedMarker;
|
||||
final VgaImage? cursor = art.mappedPic(
|
||||
engine.menuManager.isCursorAltFrame(engine.timeAliveMs) ? 9 : 8,
|
||||
);
|
||||
|
||||
_drawCanonicalMenuTextCentered(
|
||||
engine.menuManager.rendererOptionsTitle,
|
||||
46,
|
||||
headingColor,
|
||||
scale: 1,
|
||||
);
|
||||
|
||||
const int rowYStart = 68;
|
||||
const int rowStep = 20;
|
||||
const int cursorX = 62;
|
||||
const int markerX = 92;
|
||||
const int textX = 122;
|
||||
|
||||
final entries = engine.menuManager.rendererOptionEntries;
|
||||
final int selectedIndex = engine.menuManager.selectedRendererOptionIndex;
|
||||
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
final entry = entries[i];
|
||||
final bool isSelected = i == selectedIndex;
|
||||
final int y = rowYStart + (i * rowStep);
|
||||
if (isSelected && cursor != null) {
|
||||
_blitVgaImage(cursor, cursorX, y - 2);
|
||||
}
|
||||
|
||||
final VgaImage? marker = entry.isChecked
|
||||
? selectedMarker
|
||||
: unselectedMarker;
|
||||
if (marker != null) {
|
||||
_blitVgaImage(marker, markerX, y);
|
||||
}
|
||||
|
||||
_drawCanonicalMenuText(
|
||||
entry.label,
|
||||
textX,
|
||||
y + 1,
|
||||
entry.isEnabled
|
||||
? (isSelected ? selectedTextColor : unselectedTextColor)
|
||||
: disabledTextColor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _drawGameSelectMenu(
|
||||
WolfEngine engine,
|
||||
WolfClassicMenuArt art,
|
||||
@@ -461,6 +693,26 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
|
||||
}
|
||||
}
|
||||
|
||||
void _drawMenuSectionHeader({
|
||||
required String text,
|
||||
required int y200,
|
||||
required int textColor,
|
||||
}) {
|
||||
final int textW = WolfMenuFont.measureTextWidth(text, 1);
|
||||
final int boxW = (textW + 28).clamp(110, 250);
|
||||
final int x = ((320 - boxW) ~/ 2).clamp(0, 319);
|
||||
final int y = y200.clamp(0, 199);
|
||||
final int outer = _rgbToFrameColor(0x727272);
|
||||
final int inner = _rgbToFrameColor(0x8E8E8E);
|
||||
final int fill = _rgbToFrameColor(0xC9C9C9);
|
||||
final int menuTextColor = _rgbToFrameColor(0x5A5A5A);
|
||||
|
||||
_fillCanonicalRect(x, y, boxW, 12, outer);
|
||||
_fillCanonicalRect(x + 1, y + 1, boxW - 2, 10, inner);
|
||||
_fillCanonicalRect(x + 2, y + 2, boxW - 4, 8, fill);
|
||||
_drawCanonicalMenuTextCentered(text, y + 2, menuTextColor, scale: 1);
|
||||
}
|
||||
|
||||
void _drawEpisodeSelectMenu(
|
||||
WolfEngine engine,
|
||||
WolfClassicMenuArt art,
|
||||
|
||||
Reference in New Issue
Block a user