Add Menu translation support and fix compilation error

This commit is contained in:
2026-03-30 16:52:28 -06:00
parent eefc67e50a
commit 2ac274a4c7
3 changed files with 1371 additions and 206 deletions

View File

@@ -1,204 +1,10 @@
{
"language": "Español",
"strings": {
"Settings": "Configuración",
"Enhancements": "Mejoras",
"Randomizer": "Randomizer",
"Network": "Red",
"Dev Tools": "Desarrollo",
"General": "General",
"General Settings": "Configuración General",
"Graphics": "Gráficos",
"Audio": "Audio",
"Controls": "Controles",
"Input Viewer": "Visor de Entrada",
"Notifications": "Notificaciones",
"Mod Menu": "Menú de Módos",
"About": "Acerca de",
"Enabled": "Activado",
"Disabled": "Desactivado",
"On": "Activado",
"Off": "Desactivado",
"Yes": "Sí",
"No": "No",
"Apply": "Aplicar",
"Cancel": "Cancelar",
"Resolution": "Resolución",
"FPS Limit": "Límite de FPS",
"VSync": "Sincronización Vertical",
"Master Volume": "Volumen Principal",
"Master Volume: %d %%": "Volumen Principal: %d %%",
"Main Music Volume: %d %%": "Volumen de Música Principal: %d %%",
"Sub Music Volume: %d %%": "Volumen de Música Secondary: %d %%",
"Fanfare Volume: %d %%": "Volumen de Fanfarria: %d %%",
"Sound Effects Volume: %d %%": "Volumen de Efectos: %d %%",
"Music Volume": "Volumen de Música",
"SFX Volume": "Volumen de Efectos",
"Anti-aliasing (MSAA)": "Antialiasing (MSAA)",
"Menu Settings": "Configuración del Menú",
"Menu Theme": "Tema del Menú",
"Menu Controller Navigation": "Navegación con Mando",
"Allow background inputs": "Permitir entradas en segundo plano",
"Menu Background Opacity": "Opacidad del Fondo del Menú",
"General Settings": "Configuración General",
"Cursor Always Visible": "Cursor Siempre Visible",
"Search In Sidebar": "Buscar en Barra Lateral",
"Search Input Autofocus": "Autofoco en Búsqueda",
"Reset Button Combination:": "Combinación de Botón de Reseteo:",
"Open App Files Folder": "Abrir Carpeta de Archivos",
"Boot": "Arranque",
"Boot Sequence": "Secuencia de Arranque",
"Languages": "Idiomas",
"Translate Title Screen": "Traducir Pantalla de Título",
"Language": "Idioma",
"UI Translation": "Traducción de Interfaz",
"Accessibility": "Accesibilidad",
"Text to Speech": "Texto a Voz",
"Disable Idle Camera Re-Centering": "Desactivar Recentrado de Cámara",
"Disable Screen Flash for Finishing Blow": "Desactivar Flash de Pantalla",
"Disable Jabu Wobble": "Desactivar Tambaleo de Jabu",
"EXPERIMENTAL": "EXPERIMENTAL",
"ImGui Menu Scaling": "Escala del Menú ImGui",
"Ship Of Harkinian": "Ship Of Harkinian",
"Graphics Options": "Opciones de Gráficos",
"Toggle Fullscreen": "Alternar Pantalla Completa",
"Internal Resolution": "Resolución Interna",
"Current FPS": "FPS Actuales",
"Match Refresh Rate": "Coincidir Tasa de Refresco",
"Renderer API (Needs reload)": "API de Renderizado (Requiere Recarga)",
"Enable Vsync": "Activar Vsync",
"Windowed Fullscreen": "Pantalla Completa en Ventana",
"Allow multi-windows": "Permitir Multi-ventanas",
"Texture Filter (Needs reload)": "Filtro de Textura (Requiere Recarga)",
"Advanced Graphics Options": "Opciones Avanzadas de Gráficos",
"Clear Devices": "Limpiar Dispositivos",
"Controller Bindings": "Asignaciones de Mando",
"Popout Bindings Window": "Ventana de Asignaciones",
"Input Viewer Settings": "Configuración del Visor de Entrada",
"Popout Input Viewer Settings": "Ventana de Configuración",
"Position": "Posición",
"Duration (seconds):": "Duración (segundos):",
"Background Opacity": "Opacidad del Fondo",
"Size:": "Tamaño:",
"Test Notification": "Probar Notificación",
"Mute Notification Sound": "Silenciar Sonido de Notificación",
"Popout Mod Menu Window": "Ventana de Menú de Módos",
"Saving": "Guardado",
"Autosave": "Guardado Automático",
"Notification on Autosave": "Notificación de Guardado Automático",
"Remember Save Location": "Recordar Ubicación de Guardado",
"Containers Match Contents": "Contenedores Corresponden al Contenido",
"Containers of Agony": "Contenedores de Agonía",
"Time of Day": "Hora del Día",
"Nighttime GS Always Spawn": "GS Nocturnos Siempre Aparecen",
"Pull Grave During the Day": "Tumbar Durante el Día",
"Dampe Appears All Night": "Dampe Aparece Toda la Noche",
"Exit Market at Night": "Salir del Mercado de Noche",
"Shops and Games Always Open": "Tiendas y Juegos Siempre Abiertos",
"Pause Menu": "Menú de Pausa",
"Allow the Cursor to be on Any Slot": "Permitir Cursor en Cualquier Ranura",
"Pause Warp": "Teletransporte de Pausa",
"Answer Navi Prompt with L Button": "Responder a Navi con Botón L",
"Don't Require Input for Credits Sequence": "No Requiere Input para Secuencia de Créditos",
"Include Held Inputs at the Start of Pause Buffer Input Window": "Incluir Inputs Sostenidos",
"Pause Buffer Input Window: %d frames": "Ventana de Input de Pausa: %d frames",
"Simulated Input Lag: %d frames": "Lag de Input Simulado: %d frames",
"Reworked Targeting": "Cambio de Objetivo Revisado",
"Target Switch Button Combination:": "Combinación de Botón de Cambio de Objetivo:",
"Item Count Messages": "Mensajes de Cantidad de Objetos",
"Gold Skulltula Tokens": "Tokens de Skulltula de Oro",
"Pieces of Heart": "Piezas de Corazón",
"Heart Containers": "Contenedores de Corazón",
"Misc": "Varios",
"Disable Crit Wiggle": "Desactivar Crujido Crítico",
"Better Owl": "Mejor Búho",
"Convenience": "Comodidad",
"Quit Fishing at Door": "Salir de Pescar en Puerta",
"Instant Putaway": "Guardar Instantáneo",
"Navi Timer Resets on Scene Change": "Temporizador de Navi Resetea",
"Link's Cow in Both Time Periods": "Vaca de Link en Ambos Períodos",
"Play Zelda's Lullaby to Open Sleeping Waterfall": "Canción de Zelda Abre Cascada",
"Skip Feeding Jabu-Jabu": "Saltar Alimentar Jabu-Jabu",
"Cutscenes": "Escenas",
"All##Skips": "Todas##Saltos",
"None##Skips": "Ninguna##Saltos",
"Skip Intro": "Saltar Intro",
"Great Fairies": "Hadas Grandes",
"Horse": "Caballo",
"Ganon": "Ganon",
"Dampé": "Dampé",
"Title Screen": "Pantalla de Título",
"File Select": "Selección de Archivo",
"Boss Rush": "Combate de Jefes",
"Skips": "Saltos",
"Rainbow Bridge": "Puente Arcoíris",
"Bridge Requirement": "Requisito del Puente",
"Randomizer": "Randomizer",
"Enhancements": "Mejoras",
"Cheats": "Trampas",
"Randomizer Settings": "Configuración del Randomizer",
"Keyshuffle": "Mezcla de Llaves",
"Maps & Compasses": "Mapas y Brújulas",
"Small Keys": "Llaves Pequeñas",
"Boss Keys": "Llaves de Jefe",
"Skulltulas": "Skulltulas",
"Tokens": "Tokens",
"Stones": "Piedras",
"Medallions": "Medallones",
"Dungeon Items": "Objetos de Mazmorra",
"Start with Consumables": "Empezar con Consumibles",
"Start with Max Rupees": "Empezar con Rupias Máximas",
"Start with Deku Equipment": "Equipamiento Deku Inicial",
"Open Deku Tree": "Abrir Árbol Deku",
"Open Door of Time": "Abrir Puerta del Tiempo",
"Open Kak Bridge": "Abrir Puente de Kakariko",
"Open Market Entrance": "Abrir Entrada del Mercado",
"Open Castle Gate": "Abrir Puerta del Castillo",
"Network": "Red",
"Connect to Server": "Conectar al Servidor",
"Disconnect": "Desconectar",
"Server Address": "Dirección del Servidor",
"Username": "Nombre de Usuario",
"Room ID": "ID de Sala",
"Game Mode": "Modo de Juego",
"Co-op": "Cooperativo",
"Adventure": "Aventura",
"Time Sync": "Sincronización de Tiempo",
"Lag Compensation": "Compensación de Lag",
"Dev Tools": "Herramientas de Desarrollo",
"General": "General",
"Game Interaction": "Interacción del Juego",
"Visual": "Visual",
"Audio": "Audio",
"Cheats": "Trampas",
"Cosmetics": "Cosméticos",
"Restrict Debug Mode": "Restringir Modo Debug",
"Free Camera": "Cámara Libre",
"Frame Advance": "Avance de Fotograma",
"Pause Game": "Pausar Juego",
"Log Object Ages": "Registrar Edades de Objetos",
"Visual Cheats": "Trampas Visuales",
"No UI": "Sin Interfaz",
"Cheat Cheats": "Trampas de Trampas",
"Infinite Gold": "Oro Infinito",
"Infinite Health": "Salud Infinita",
"Infinite Magic": "Magia Infinita",
"Infinite Nails": "Uñas Infinitas",
"Infinite Eggs": "Huevos Infinitos",
"Infinite Arrows": "Flechas Infinitas",
"Unbreakable Umbrella": "Paraguas Irrompible",
"Cosmetics": "Cosméticos",
"Tunic Color": "Color del Túnico",
"Skin Color": "Color de Piel",
"Mirror Shield Frame": "Marco del Escudo Espejo",
"Link's Age": "Edad de Link",
"Default": "Predeterminado",
"Adult": "Adulto",
"Child": "Niño",
"Small": "Pequeño",
"Normal": "Normal",
"Large": "Grande",
"X-Large": "Extra Grande",
"English": "Inglés",
"German": "Alemán",
"French": "Francés",
"Japanese": "Japonés",
"Red": "Rojo",
"Dark Red": "Rojo Oscuro",
"Orange": "Naranja",
@@ -216,15 +22,411 @@
"Three-Point": "Tres Puntos",
"Linear": "Lineal",
"None": "Ninguno",
"Top Left": "Arriba Izquierda",
"Top Right": "Arriba Derecha",
"Bottom Left": "Abajo Izquierda",
"Bottom Right": "Abajo Derecha",
"Trace": "Rastro",
"Debug": "Depuración",
"Info": "Información",
"Warn": "Advertencia",
"Error": "Error",
"Critical": "Crítico",
"Off": "Desactivado",
"Top Left": "Superior Izquierda",
"Top Right": "Superior Derecha",
"Bottom Left": "Inferior Izquierda",
"Bottom Right": "Inferior Derecha",
"Hidden": "Oculto",
"Default": "Predeterminado",
"Normal": "Normal",
"Unbreakable": "Inquebrantable",
"Unbreakable + Always on Fire": "Inquebrantable + Siempre en Llamas",
"Navi": "Navi",
"NPCs": "NPCs",
"All": "Todos",
"Disabled": "Desactivado",
"Junk Items": "Objetos Basura",
"All Items": "Todos los Objetos",
"Both": "Ambos",
"Texture Only": "Solo Textura",
"Size Only": "Solo Tamaño",
"Ocarina of Time": "Ocarina del Tiempo",
"Any Ocarina": "Cualquier Ocarina",
"Always": "Siempre",
"Once": "Una vez",
"Never": "Nunca",
"Vanilla (1x)": "Original (1x)",
"Double (2x)": "Doble (2x)",
"Quadruple (4x)": "Cuádruple (4x)",
"Octuple (8x)": "Óctuple (8x)",
"Foolish (16x)": "Absurdo (16x)",
"Ridiculous (32x)": "Ridículo (32x)",
"Merciless (64x)": "Cruel (64x)",
"Pure Torture (128x)": "Pura Tortura (128x)",
"OHKO (256x)": "KO (256x)",
"No Damage": "Sin Daño",
"0.25 Hearts": "0.25 Corazones",
"0.5 Hearts": "0.5 Corazones",
"1 Heart": "1 Corazón",
"2 Hearts": "2 Corazones",
"4 Hearts": "4 Corazones",
"8 Hearts": "8 Corazones",
"Only in Rando": "Solo en Rando",
"Child Toggle": "Cambiar Niño",
"Both Ages": "Ambas Edades",
"Consistent Vanish": "Desvanecer Consistente",
"No Vanish": "Sin Desvanecer",
"Always": "Siempre",
"Random": "Aleatorio",
"Random (Seeded)": "Aleatorio (Semilla)",
"Dungeons": "Mazmorras",
"Dungeons (Vanilla)": "Mazmorras (Original)",
"Dungeons (MQ)": "Mazmorras (MQ)",
"Dungeons Random": "Mazmorras Aleatorio",
"Dungeons Random (Seeded)": "Mazmorras Aleatorio (Semilla)",
"Off": "Apagado",
"Vanilla": "Original",
"Maxed": "Maximizado",
"Default": "Por Defecto",
"Authentic": "Auténtico",
"File Select": "Selección de Archivo",
"Debug Warp Screen": "Pantalla de Teletransporte Debug",
"Warp Point": "Punto de Teletransporte"
"Vanilla Plus": "Original Plus",
"Enhanced": "Mejorado",
"Randomizer": "Randomizer",
"Small": "Pequeño",
"Large": "Grande",
"X-Large": "Extra Grande",
"NTSC 1.0": "NTSC 1.0",
"NTSC 1.1": "NTSC 1.1",
"NTSC 1.2": "NTSC 1.2",
"NTSC-U GC": "NTSC-U GC",
"NTSC-J GC": "NTSC-J GC",
"NTSC-J GC (Collector's Edition)": "NTSC-J GC (Edición Coleccionista)",
"NTSC-U MQ": "NTSC-U MQ",
"NTSC-J MQ": "NTSC-J MQ",
"PAL 1.0": "PAL 1.0",
"PAL 1.1": "PAL 1.1",
"PAL GC": "PAL GC",
"PAL MQ": "PAL MQ",
"PAL GC-D": "PAL GC-D",
"PAL MQ-D": "PAL MQ-D",
"IQUE CN": "IQUE CN",
"IQUE TW": "IQUE TW",
"UNKNOWN": "DESCONOCIDO",
"Changes the Theme of the Menu Widgets.": "Cambia el tema de los widgets del menú.",
"Allows controller navigation of the port menu": "Permite navegación con controlador del menú de puertos",
"CAUTION: This will disable game inputs while the menu is visible.": "ADVERTENCIA: Esto deshabilitará las entradas del juego mientras el menú sea visible.",
"D-pad to move between items, A to select, B to move up in scope.": "D-pad para mover entre elementos, A para seleccionar, B para subir.",
"Sets the opacity of the background of the port menu.": "Establece la opacidad del fondo del menú de puertos.",
"Makes the cursor always visible, even in full screen.": "Hace el cursor siempre visible, incluso en pantalla completa.",
"Displays the Search menu as a sidebar entry in Settings instead of in the header.": "Muestra el menú de búsqueda como entrada lateral en Configuración.",
"Search input box gets autofocus when visible.": "La caja de búsqueda obtiene enfoque automático cuando es visible.",
"Allows pressing the Tab key to toggle alternate assets": "Permite presionar Tab para alternar assets alternativos",
"Opens the folder that contains the save and mods folders, etc.": "Abre la carpeta que contiene guardados y mods.",
"Configure what happens when starting or resetting the game.": "Configura qué sucede al iniciar o reiniciar el juego.",
"Default: LUS logo -> N64 logo": "Por defecto: Logo LUS -> Logo N64",
"Authentic: N64 logo only": "Auténtico: Solo logo N64",
"File Select: Skip to file select menu": "Selección de Archivo: Saltar al menú de selección",
"Enables text to speech for in game dialog": "Habilita texto a voz para diálogos del juego",
"Disables the automatic re-centering of the camera when idle.": "Deshabilita el recentrado automático de la cámara.",
"EXPERIMENTAL": "EXPERIMENTAL",
"Changes the scaling of the ImGui menu elements.": "Cambia la escala de los elementos del menú ImGui.",
"Ship Of Harkinian": "Ship Of Harkinian",
"Branch": "Rama",
"Commit": "Commit",
"Master Volume: %d %%": "Volumen Principal: %d %%",
"Main Music Volume: %d %%": "Volumen de Música Principal: %d %%",
"Sub Music Volume: %d %%": "Volumen de Música Secundaria: %d %%",
"Fanfare Volume: %d %%": "Volumen de Fanfarria: %d %%",
"Sound Effects Volume: %d %%": "Volumen de Efectos de Sonido: %d %%",
"Audio API (Needs reload)": "API de Audio (Necesita recargar)",
"Uses Matrix Interpolation to create extra frames": "Usa Interpolación de Matrices para crear cuadros extra",
"Toggles Fullscreen On/Off.": "Alternar Pantalla Completa.",
"Multiplies your output resolution by the value inputted": "Multiplica tu resolución de salida por el valor introducido",
"Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x": "Activa MSAA (antialiasing multiamuestra) de 2x hasta 8x",
"Original (%d)": "Original (%d)",
"Match interpolation value to the refresh rate of your display.": "Coincidir valor de interpolación con tasa de refresco.",
"Renderer API (Needs reload)": "API del Renderizador (Necesita recargar)",
"Removes tearing, but clamps your max FPS to your displays refresh rate.": "Elimina tearing, pero limita FPS máximos.",
"Enables Windowed Fullscreen Mode.": "Habilita Modo Ventana Pantalla Completa.",
"Allows multiple windows to be opened at once.": "Permite múltiples ventanas a la vez.",
"Sets the applied Texture Filtering.": "Establece el filtro de textura aplicado.",
"Advanced Graphics Options": "Opciones Avanzadas de Gráficos",
"Controller Bindings": "Botones del Controlador",
"Enables the separate Bindings Window.": "Habilita la ventana de botones separada.",
"Input Viewer": "Visor de Entrada",
"Toggles the Input Viewer.": "Alterna el visor de entrada.",
"Input Viewer Settings": "Configuración del Visor de Entrada",
"Which corner of the screen notifications appear in.": "En qué esquina aparecen las notificaciones.",
"Duration (seconds):": "Duración (segundos):",
"How long notifications are displayed for.": "Cuánto tiempo se muestran las notificaciones.",
"Background Opacity": "Opacidad del Fondo",
"How opaque the background of notifications is.": "Qué tan opaco es el fondo de notificaciones.",
"Size:": "Tamaño:",
"How large notifications are.": "Qué tan grandes son las notificaciones.",
"Test Notification": "Probar Notificación",
"This is a test.": "Esto es una prueba.",
"Displays a test notification.": "Muestra una notificación de prueba.",
"Quality of Life": "Calidad de Vida",
"Saving": "Guardado",
"Save the game automatically on a 3 minute interval": "Guardar automáticamente cada 3 minutos",
"When loading a save, places Link at the last entrance": "Al cargar, coloca a Link en la última entrada",
"Containers Match Contents": "Contenedores equal Contenido",
"Chests of Agony": "Cofres de Agonía",
"Time of Day": "Hora del Día",
"Nighttime GS Always Spawn": "GS Nocturno Siempre Aparece",
"Pull Grave During the Day": "Tumbar de Día",
"Dampe Appears All Night": "Dampe Aparece Toda la Noche",
"Exit Market at Night": "Salir del Mercado de Noche",
"Shops and Games Always Open": "Tiendas y Juegos Siempre Abiertos",
"Pause Menu": "Menú de Pausa",
"Allow the Cursor to be on Any Slot": "Permitir Cursor en Cualquier Ranura",
"Pause Warp": "Teletransporte de Pausa",
"Controls": "Controles",
"Answer Navi Prompt with L Button": "Responder a Navi con Botón L",
"Don't Require Input for Credits Sequence": "No Requerir Input para Secuencia de Créditos",
"Include Held Inputs at the Start of Pause Buffer Input Window": "Incluir Inputs Sostenidos al Inicio de Ventana de Pausa",
"Pause Buffer Input Window: %d frames": "Ventana de Input de Pausa: %d cuadros",
"Simulated Input Lag: %d frames": "Lag de Input Simulado: %d cuadros",
"Item Count Messages": "Mensajes de Cantidad de Objetos",
"Gold Skulltula Tokens": "Tokens de Gold Skulltula",
"Pieces of Heart": "Piezas de Corazón",
"Heart Containers": "Contenedores de Corazón",
"Misc": "Varios",
"Disable Crit Wiggle": "Desactivar Crítico",
"Better Owl": "Mejor Búho",
"Quit Fishing at Door": "Salir de Pescar en Puerta",
"Instant Putaway": "Guardar Instantáneo",
"Navi Timer Resets on Scene Change": "Temporizador de Navi Resetea al Cambiar Escena",
"Link's Cow in Both Time Periods": "Vaca de Link en Ambos Períodos",
"Play Zelda's Lullaby to Open Sleeping Waterfall": "Tocar Canción de Zelda para Abrir Cascada Dormida",
"Skips & Speed-ups": "Saltos y Aceleraciones",
"Cutscenes": "Cinematicas",
"All": "Todos",
"Skip Intro": "Saltar Introducción",
"Skip Entrance Cutscenes": "Saltar Cinemáticas de Entrada",
"Skip Story Cutscenes": "Saltar Cinemáticas de Historia",
"Skip Song Cutscenes": "Saltar Cinemáticas de Canciones",
"Skip Boss Introductions": "Saltar Introducciones de Jefes",
"Quick Boss Deaths": "Muertes Rápidas de Jefes",
"Skip One Point Cutscenes": "Saltar Cinemáticas de Un Punto",
"Skip Owl Interactions": "Saltar Interacciones del Búho",
"Skip Misc Interactions": "Saltar Interacciones Varias",
"Disable Title Card": "Desactivar Tarjeta de Título",
"Exclude Glitch-Aiding Cutscenes": "Excluir Cinemáticas de Glitch",
"Text": "Texto",
"Skip Pickup Messages": "Saltar Mensajes de Recolección",
"Skip Forced Dialog": "Saltar Diálogo Forzado",
"Skip Text": "Saltar Texto",
"Text Speed: %dx": "Velocidad de Texto: %dx",
"Slow Text Speed: %dx": "Velocidad de Texto Lento: %dx",
"Animations": "Animaciones",
"Faster Heavy Block Lift": "Levantar Bloque Pesado Más Rápido",
"Fast Chests": "Cofres Rápidos",
"Skip Water Take Breath Animation": "Saltar Animación de Respirar Bajo Agua",
"Vine/Ladder Climb Speed +%d": "Velocidad de Escalar Enredadera/Escalera +%d",
"Block Pushing Speed +%d": "Velocidad de Empujar Bloques +%d",
"Crawl Speed %dx": "Velocidad de Arrastre %dx",
"King Zra Speed: %.2fx": "Velocidad de Rey Zora: %.2fx",
"Skip Child Stealth": "Saltar Sigilo de Niño",
"Skip Tower Escape": "Saltar Escape de Torre",
"Skip Scarecrow's Song": "Saltar Canción del Espantapájaros",
"Faster Rupee Accumulator": "Acumulador de Rupias Más Rápido",
"No Skulltula Freeze": "Sin Congelamiento de Skulltula",
"Skip Save Confirmation": "Saltar Confirmación de Guardado",
"Link as Default File Name": "Link como Nombre de Archivo por Defecto",
"Biggoron Forge Time: %d days": "Tiempo de Forja de Biggoron: %d días",
"Mods": "Mods",
"Use Alternate Assets": "Usar Assets Alternativos",
"Disable Bomb Billboarding": "Desactivar Billboard de Bombas",
"Disable Grotto Fixed Rotation": "Desactivar Rotación Fija de Grotto",
"Ingame Text Spacing: %d": "Espaciado de Texto en Juego: %d",
"Models & Textures": "Modelos y Texturas",
"Disable LOD": "Desactivar LOD",
"Enemy Health Bars": "Barras de Salud de Enemigos",
"Enable 3D Dropped Items/Projectiles": "Habilitar Objetos 3D Lanzados",
"Animated Link in Pause Menu": "Link Animado en Menú de Pausa",
"Show Age-Dependent Equipment": "Mostrar Equipo Dependiente de Edad",
"Scale Adult Equipment as Child": "Escalar Equipo de Adulto como Niño",
"Show Gauntlets in First Person": "Mostrar Guanteletes en Primera Persona",
"Show Chains on Both Sides of Locked Doors": "Mostrar Cadenas en Ambos Lados de Puertas Cerradas",
"Color Temple of Time's Medallions": "Colorear Medallones del Templo del Tiempo",
"UI": "Interfaz",
"Minimal UI": "Interfaz Mínima",
"Disable Hot/Underwater Warning Text": "Desactivar Texto de Advertencia Calor/Submarino",
"Remember Minimap State Between Areas": "Recordar Estado del Minimapa Entre Áreas",
"Visual Stone of Agony": "Piedra Visual de Agonía",
"Disable HUD Heart Animations": "Desactivar Animaciones de Corazones HUD",
"Glitch Line-up Tick": "Ticks de Alineación de Glitch",
"Disable Black Bar Letterboxes": "Desactivar Carteleras Negras",
"Dynamic Wallet Icon": "Icono de Billetera Dinámico",
"Always Show Dungeon Entrances": "Siempre Mostrar Entradas de Mazmorras",
"More Info in File Select": "Más Info en Selección de Archivo",
"Better Ammo Rendering in Pause Menu": "Mejor Renderizado de Munición en Pausa",
"Enable Passage of Time on File Select": "Habilitar Paso del Tiempo en Selección",
"Misc.": "Varios",
"N64 Mode": "Modo N64",
"Remove Spin Attack Darkness": "Remover Oscuridad de Ataque Giratorio",
"Draw Distance": "Distancia de Dibujo",
"Increase Actor Draw Distance: %dx": "Aumentar Distancia de Dibujo de Actores: %dx",
"Kokiri Draw Distance": "Distancia de Dibujo Kokiri",
"Widescreen Actor Culling": "Culling de Actores Widescreen",
"Cull Glitch Useful Actors": "Culling de Actores Útiles para Glitch",
"Items": "Objetos",
"Equipment": "Equipo",
"Equip Items on Dpad": "Equipar Objetos en Dpad",
"Assignable Tunics and Boots": "Túnicas y Botas Asignables",
"Equipment Toggle": "Alternar Equipo",
"Allow Strength Equipment to be Toggled": "Permitir Alternar Equipo de Fuerza",
"Sword Toggle Options": "Opciones de Alternar Espada",
"Ask to Equip New Items": "Preguntar para Equipar Nuevos Objetos",
"Ocarina": "Ocarina",
"Prevent Dropped Ocarina Inputs": "Prevenir Inputs de Ocarina Suelta",
"Fast Ocarina Playback": "Reproducción Rápida de Ocarina",
"Time Travel with Song of Time": "Viajar en el Tiempo con Canción del Tiempo",
"Masks": "Máscaras",
"Bunny Hood Effect": "Efecto de Máscara de Conejo",
"Masks Equippable as Adult": "Máscaras Equipables como Adulto",
"Persistent Masks": "Máscaras Persistentes",
"Invisible Bunny Hood": "Máscara de Conejo Invisible",
"Mask Select in Inventory": "Selección de Máscara en Inventario",
"Explosives": "Explosivos",
"Deku Nuts Explode Bombs": "Nueces Deku Explotan Bombas",
"Remove Explosive Limit": "Remover Límite de Explosivos",
"Static Explosion Radius": "Radio de Explosión Estático",
"Prevent Bombchus Forcing First-Person": "Prevenir Bombchus Forzando Primera Persona",
"Better Bombchu Shopping": "Mejor Compra de Bombchus",
"Bow / Slingshot": "Arco/Honda",
"Equip Multiple Arrows at Once": "Equipar Múltiples Flechas a la Vez",
"Skip Magic Arrow Equip Animation": "Saltar Animación de Equipar Flecha Mágica",
"Blue Fire Arrows": "Flechas de Fuego Azul",
"Sunlight Arrows": "Flechas de Luz Solar",
"Bow as Child/Slingshot as Adult": "Arco como Niño/Honda como Adulto",
"Aiming Reticle for the Bow/Slingshot": "Retícula de Puntería para Arco/Honda",
"Hookshot": "Gancho",
"Targetable Hookshot Reticle": "Retícula de Gancho Dirigible",
"Boomerang": "Bumerang",
"Instant Boomerang Recall": "Retorno Instantáneo de Bumerang",
"Aim Boomerang in First-Person Mode": "Apuntar Bumerang en Modo Primera Persona",
"Aiming Reticle for Boomerang": "Retícula de Puntería para Bumerang",
"Magic Spells": "Hechizos",
"Better Farore's Wind": "Mejor Viento de Farore",
"Faster Farore's Wind": "Viento de Farore Más Rápido",
"Fixes": "Arreglos",
"Gameplay Fixes": "Arreglos de Jugabilidad",
"Fix the Gravedigging Tour Glitch": "Arreglar Glitch de Excursión de Excavación",
"Fix Raised Floor Switches": "Arreglar Interruptores de Piso Elevado",
"Popout Randomizer Settings Window": "Abrir Ventana de Configuración de Randomizer",
"Randomizer Settings": "Configuración de Randomizer",
"Enables the separate Randomizer Settings Window.": "Habilita la ventana separada de configuración de Randomizer.",
"Randomizer Enhancements": "Mejoras de Randomizer",
"Rando-Relevant Navi Hints": "Pistas de Navi Relevantes para Rando",
"Random Rupee Names": "Nombres Aleatorios de Rupias",
"Use Custom Key Models": "Usar Modelos de Llaves Personalizados",
"Map & Compass Colors Match Dungeon": "Colores de Mapa y Brújula igual a Mazmorra",
"Quest Item Fanfares": "Fanfarrias de Objetos de Misión",
"Mysterious Shuffled Items": "Objetos Mezclados Misteriosos",
"Simpler Boss Soul Models": "Modelos de Almas de Jefe Simples",
"Skip Get Item Animations": "Saltar Animaciones de Obtener Objeto",
"Item Scale: %.2f": "Escala de Objeto: %.2f",
"Popout Plandomizer Window": "Abrir Ventana de Plandomizer",
"Plandomizer Editor": "Editor de Plandomizer",
"Item Tracker": "Rastreador de Objetos",
"Toggle Item Tracker": "Alternar Rastreador de Objetos",
"Item Tracker Settings": "Configuración de Rastreador de Objetos",
"Popout Item Tracker Settings": "Abrir Configuración de Rastreador de Objetos",
"Entrance Tracker": "Rastreador de Entradas",
"Toggle Entrance Tracker": "Alternar Rastreador de Entradas",
"Entrance Tracker Settings": "Configuración de Rastreador de Entradas",
"Popout Entrance Tracker Settings": "Abrir Configuración de Rastreador de Entradas",
"Check Tracker": "Rastreador de Checks",
"Toggle Check Tracker": "Alternar Rastreador de Checks",
"Check Tracker Settings": "Configuración de Rastreador de Checks",
"Popout Check Tracker Settings": "Abrir Configuración de Rastreador de Checks",
"Popout Menu": "Abrir Menú",
"Debug Mode": "Modo Depuración",
"OoT Registry Editor": "Editor de Registro de OoT",
"Debug Save File Mode": "Modo de Archivo de Guardado de Depuración",
"OoT Skulltula Debug": "Depuración de Skulltula de OoT",
"Better Debug Warp Screen": "Mejor Pantalla de Teletransporte de Depuración",
"Debug Warp Screen Translation": "Traducción de Pantalla de Teletransporte",
"Resource logging": "Registro de Recursos",
"Frame Advance": "Avance de Cuadro",
"Advance 1": "Avanzar 1",
"Advance (Hold)": "Avanzar (Mantener)",
"Popout Stats Window": "Abrir Ventana de Estadísticas",
"Stats": "Estadísticas",
"Popout Console": "Abrir Consola",
"Console": "Consola",
"Popout Save Editor": "Abrir Editor de Guardado",
"Save Editor": "Editor de Guardado",
"Popout Hook Debugger": "Abrir Depurador de Hooks",
"Hook Debugger": "Depurador de Hooks",
"Popout Collision Viewer": "Abrir Visor de Colisiones",
"Collision Viewer": "Visor de Colisiones",
"Popout Actor Viewer": "Abrir Visor de Actores",
"Actor Viewer": "Visor de Actores",
"Popout Display List Viewer": "Abrir Visor de Lista de Display",
"Display List Viewer": "Visor de Lista de Display",
"Popout Value Viewer": "Abrir Visor de Valores",
"Value Viewer": "Visor de Valores",
"Popout Message Viewer": "Abrir Visor de Mensajes",
"Message Viewer": "Visor de Mensajes",
"Popout Gfx Debugger": "Abrir Depurador de Gráficos",
"Gfx Debugger": "Depurador de Gráficos",
"DirectX": "DirectX",
"OpenGL": "OpenGL",
"Metal": "Metal",
"Resolution Presets": "Preajustes de Resolución",
"Off": "Apagado",
"Custom": "Personalizado",
"Original (4:3)": "Original (4:3)",
"Widescreen (16:9)": "Pantalla Amplia (16:9)",
"Nintendo 3DS (5:3)": "Nintendo 3DS (5:3)",
"16:10 (8:5)": "16:10 (8:5)",
"Ultrawide (21:9)": "Ultrawide (21:9)",
"Pixel Count Presets": "Preajustes de Cantidad de Píxeles",
"Native N64 (240p)": "Nativo N64 (240p)",
"2x (480p)": "2x (480p)",
"3x (720p)": "3x (720p)",
"4x (960p)": "4x (960p)",
"5x (1200p)": "5x (1200p)",
"6x (1440p)": "6x (1440p)",
"Full HD (1080p)": "Full HD (1080p)",
"4K (2160p)": "4K (2160p)",
"Set fixed vertical resolution": "Establecer resolución vertical fija",
"Horiz. Pixel Count": "Cantidad de Píxeles Horiz.",
"Force aspect ratio": "Forzar proporción de aspecto",
"Click to resolve": "Clic para resolver",
"Vertical Pixel Count": "Cantidad de Píxeles Vertical",
"Integer Scaling Settings": "Configuración de Escala Entera",
"Pixel Perfect Mode": "Modo Pixel Perfecto",
"Integer scale factor: {}": "Factor de escala entera: {}",
"Window exceeded.": "Ventana excedida.",
"Automatically scale image to fit viewport": "Escalar automáticamente imagen para ajustar",
"Additional Settings": "Configuración Adicional",
"If the image is stretched and you don't know why, click this.": "Si la imagen está estirada y no sabes por qué, haz clic.",
"Click to reenable aspect correction.": "Clic para rehabilitar corrección de aspecto.",
"Show a horizontal resolution field": "Mostrar campo de resolución horizontal",
"Allow integer scale factor to go +1 above maximum screen bounds.": "Permitir factor de escala entera +1 arriba del máximo.",
"A scroll bar may become visible if screen bounds are exceeded.": "Barra de desplazamiento puede aparecer.",
"Click to reset a console variable that may be causing this.": "Clic para reiniciar variable.",
"Viewport dimensions: {} x {}": "Dimensiones de viewport: {} x {}",
"Internal resolution: {} x {}": "Resolución interna: {} x {}",
"Enable advanced settings.": "Habilitar configuración avanzada.",
"Significant frame rate (FPS) drops may be occuring.": "Caídas significativas de FPS pueden estar ocurriendo.",
"N64 Mode is overriding these settings.": "Modo N64 está sobrescribiendo estas configuraciones.",
"Click to disable N64 mode": "Clic para desactivar modo N64",
"Aspect Ratio": "Proporción de Aspecto",
"Disabling VSync not supported": "Desactivar VSync no soportado",
"Windowed Fullscreen not supported": "Pantalla Completa en Ventana no soportada",
"Multi-viewports not supported": "Multi-viewports no soportado",
"Available Only on DirectX": "Solo Disponible en DirectX",
"Not Available on DirectX": "No Disponible en DirectX",
"Match Refresh Rate is Enabled": "Coincidir Tasa de Refresco está Habilitado",
"Advanced Resolution Enabled": "Resolución Avanzada Habilitada",
"Vertical Resolution Toggle Enabled": "Alternar Resolución Vertical Habilitado",
"N64 Mode Enabled": "Modo N64 Habilitado",
"Save Not Loaded": "Guardado No Cargado",
"Debug Mode is Disabled": "Modo Depuración está Desactivado",
"Frame Advance is Disabled": "Avance de Cuadro está Desactivado",
"Advanced Resolution is Disabled": "Resolución Avanzada está Desactivada",
"Vertical Resolution Toggle is Off": "Alternar Resolución Vertical está Apagado"
}
}

962
soh/soh/SohGui/Menu.cpp Normal file
View File

@@ -0,0 +1,962 @@
#include "Menu.h"
#include "LanguageManager.h"
#include "UIWidgets.hpp"
#include "soh/OTRGlobals.h"
#include <ship/window/gui/GuiMenuBar.h>
#include <ship/window/gui/GuiElement.h>
#include "SohModals.h"
#include <variant>
#include <spdlog/fmt/fmt.h>
#include <tuple>
extern "C" {
#include "z64.h"
extern PlayState* gPlayState;
}
std::vector<ImVec2> windowTypeSizes = { {} };
extern std::unordered_map<s16, const char*> warpPointSceneList;
extern void Warp();
namespace SohGui {
extern std::shared_ptr<SohModalWindow> mModalWindow;
}
std::vector<SearchWidget> extraSearchWidgets = {};
namespace Ship {
std::string disabledTempTooltip;
const char* disabledTooltip;
bool disabledValue = false;
bool navigateToWidget = false;
const char* navigateMainEntry = "";
const char* navigateSidebar = "";
std::string navigateWidgetName = "";
bool highlightWidget = false;
bool operator==(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
return l.r == r.r && l.g == r.g && l.b == r.b;
}
bool operator==(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
return l.r == r.r && l.g == r.g && l.b == r.b && l.a == r.a;
}
bool operator<(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
return (l.r < r.r && l.g <= r.g && l.b <= r.b) || (l.r <= r.r && l.g < r.g && l.b <= r.b) ||
(l.r <= r.r && l.g <= r.g && l.b < r.b);
}
bool operator<(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
return (l.r < r.r && l.g <= r.g && l.b <= r.b && l.a <= r.a) ||
(l.r <= r.r && l.g < r.g && l.b <= r.b && l.a <= r.a) ||
(l.r <= r.r && l.g <= r.g && l.b < r.b && l.a <= r.a) ||
(l.r <= r.r && l.g <= r.g && l.b <= r.b && l.a < r.a);
}
bool operator>(Color_RGB8 const& l, Color_RGB8 const& r) noexcept {
return (l.r > r.r && l.g >= r.g && l.b >= r.b) || (l.r >= r.r && l.g > r.g && l.b >= r.b) ||
(l.r >= r.r && l.g >= r.g && l.b > r.b);
}
bool operator>(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept {
return (l.r > r.r && l.g >= r.g && l.b >= r.b && l.a >= r.a) ||
(l.r >= r.r && l.g > r.g && l.b >= r.b && l.a >= r.a) ||
(l.r >= r.r && l.g >= r.g && l.b > r.b && l.a >= r.a) ||
(l.r >= r.r && l.g >= r.g && l.b >= r.b && l.a > r.a);
}
uint32_t GetVectorIndexOf(std::vector<std::string>& vector, std::string value) {
return std::distance(vector.begin(), std::find(vector.begin(), vector.end(), value));
}
static bool raceDisableActive = false;
void Menu::InsertSidebarSearch() {
menuEntries["Settings"].sidebars.emplace("Search", searchSidebarEntry);
uint32_t curIndex = 0;
if (!Ship_IsCStringEmpty(CVarGetString(menuEntries["Settings"].sidebarCvar, ""))) {
curIndex = GetVectorIndexOf(menuEntries["Settings"].sidebarOrder,
CVarGetString(menuEntries["Settings"].sidebarCvar, ""));
}
menuEntries["Settings"].sidebarOrder.insert(menuEntries["Settings"].sidebarOrder.begin() + searchSidebarIndex,
"Search");
if (curIndex > searchSidebarIndex) {
CVarSetString(menuEntries["Settings"].sidebarCvar, menuEntries["Settings"].sidebarOrder.at(curIndex).c_str());
}
}
void Menu::RemoveSidebarSearch() {
uint32_t curIndex = GetVectorIndexOf(menuEntries["Settings"].sidebarOrder,
CVarGetString(menuEntries["Settings"].sidebarCvar, "General"));
menuEntries["Settings"].sidebars.erase("Search");
std::erase_if(menuEntries["Settings"].sidebarOrder, [](std::string& name) { return name == "Search"; });
if (curIndex > searchSidebarIndex) {
curIndex--;
} else if (curIndex >= menuEntries["Settings"].sidebarOrder.size()) {
curIndex = menuEntries["Settings"].sidebarOrder.size() - 1;
}
CVarSetString(menuEntries["Settings"].sidebarCvar, menuEntries["Settings"].sidebarOrder.at(curIndex).c_str());
}
void Menu::UpdateWindowBackendObjects() {
Ship::WindowBackend runningWindowBackend = Ship::Context::GetInstance()->GetWindow()->GetWindowBackend();
int32_t configWindowBackendId = Ship::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1);
if (Ship::Context::GetInstance()->GetWindow()->IsAvailableWindowBackend(configWindowBackendId)) {
configWindowBackend = static_cast<Ship::WindowBackend>(configWindowBackendId);
} else {
configWindowBackend = runningWindowBackend;
}
availableWindowBackends = Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends();
for (auto& backend : *availableWindowBackends) {
availableWindowBackendsMap[backend] = windowBackendsMap.at(backend);
}
}
bool Menu::IsMenuPopped() {
return popped;
}
UIWidgets::Colors Menu::GetMenuThemeColor() {
return menuThemeIndex;
}
Menu::Menu(const std::string& cVar, const std::string& name, uint8_t searchSidebarIndex_,
UIWidgets::Colors defaultThemeIndex_)
: GuiWindow(cVar, name), searchSidebarIndex(searchSidebarIndex_), defaultThemeIndex(defaultThemeIndex_) {
}
void Menu::InitElement() {
popped = CVarGetInteger(CVAR_SETTING("Menu.Popout"), 0);
poppedSize.x = CVarGetInteger(CVAR_SETTING("Menu.PoppedWidth"), 1280);
poppedSize.y = CVarGetInteger(CVAR_SETTING("Menu.PoppedHeight"), 800);
poppedPos.x = CVarGetInteger(CVAR_SETTING("Menu.PoppedPos.x"), 0);
poppedPos.y = CVarGetInteger(CVAR_SETTING("Menu.PoppedPos.y"), 0);
menuThemeIndex = static_cast<UIWidgets::Colors>(CVarGetInteger(CVAR_SETTING("Menu.Theme"), defaultThemeIndex));
UpdateWindowBackendObjects();
}
void Menu::UpdateElement() {
menuThemeIndex = static_cast<UIWidgets::Colors>(CVarGetInteger(CVAR_SETTING("Menu.Theme"), defaultThemeIndex));
}
bool ModernMenuSidebarEntry(std::string label) {
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiStyle& style = ImGui::GetStyle();
ImVec2 pos = window->DC.CursorPos;
const ImGuiID sidebarId = window->GetID(std::string(label + "##Sidebar").c_str());
ImVec2 labelSize = ImGui::CalcTextSize(label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true);
pos.y += style.FramePadding.y;
pos.x = window->WorkRect.GetCenter().x - labelSize.x / 2;
ImRect bb = { pos - style.FramePadding, pos + labelSize + style.FramePadding };
ImGui::ItemSize(bb, style.FramePadding.y);
ImGui::ItemAdd(bb, sidebarId);
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, sidebarId, &hovered, &held);
if (pressed) {
ImGui::MarkItemEdited(sidebarId);
}
window->DrawList->AddRectFilled(pos - style.FramePadding, pos + labelSize + style.FramePadding,
ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive
: hovered ? ImGuiCol_ButtonHovered
: ImGuiCol_Button),
3.0f);
UIWidgets::RenderText(pos, label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true);
return pressed;
}
bool ModernMenuHeaderEntry(std::string label) {
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiStyle& style = ImGui::GetStyle();
ImVec2 pos = window->DC.CursorPos;
const ImGuiID headerId = window->GetID(std::string(label + "##Header").c_str());
ImVec2 labelSize = ImGui::CalcTextSize(label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true);
ImRect bb = { pos, pos + labelSize + style.FramePadding * 2 };
ImGui::ItemSize(bb, style.FramePadding.y);
ImGui::ItemAdd(bb, headerId);
bool hovered, held;
bool pressed = ImGui::ButtonBehavior(bb, headerId, &hovered, &held);
window->DrawList->AddRectFilled(bb.Min, bb.Max,
ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive
: hovered ? ImGuiCol_ButtonHovered
: ImGuiCol_Button),
3.0f);
pos += style.FramePadding;
UIWidgets::RenderText(pos, label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true);
return pressed;
}
uint32_t Menu::DrawSearchResults(std::string& menuSearchText) {
int searchCount = 0;
std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower);
menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), menuSearchText.end());
ImGui::SetNextWindowSizeConstraints({ ImGui::GetContentRegionAvail().x / 2, 0 },
{ ImGui::GetContentRegionAvail().x / 2, ImGui::GetContentRegionAvail().y });
if (ImGui::BeginChild("Search Results Col 1", { ImGui::GetContentRegionAvail().x / 2, 0 },
ImGuiChildFlags_AutoResizeY, ImGuiWindowFlags_NoTitleBar)) {
for (auto& menuLabel : menuOrder) {
auto& menuEntry = menuEntries.at(menuLabel);
for (auto& sidebarLabel : menuEntry.sidebarOrder) {
auto& sidebar = menuEntry.sidebars[sidebarLabel];
for (size_t i = 0; i < sidebar.columnWidgets.size(); i++) {
auto& column = sidebar.columnWidgets.at(i);
for (auto& info : column) {
if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR ||
info.type == WIDGET_SEPARATOR_TEXT || info.isHidden || info.hideInSearch) {
continue;
}
const char* tooltip = info.options->tooltip;
std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : "");
std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower);
widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end());
if (widgetStr.find(menuSearchText) != std::string::npos) {
UIWidgets::ComponentAlignments backupAlignment;
UIWidgets::LabelPositions backupLabelPos;
if (info.type == WIDGET_COMBOBOX || info.type == WIDGET_CVAR_COMBOBOX) {
backupAlignment =
std::static_pointer_cast<UIWidgets::ComboboxOptions>(info.options)->alignment;
backupLabelPos =
std::static_pointer_cast<UIWidgets::ComboboxOptions>(info.options)->labelPosition;
std::static_pointer_cast<UIWidgets::ComboboxOptions>(info.options)->alignment =
UIWidgets::ComponentAlignments::Left;
std::static_pointer_cast<UIWidgets::ComboboxOptions>(info.options)->labelPosition =
UIWidgets::LabelPositions::Above;
}
MenuDrawItem(info, 400, menuThemeIndex);
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray));
std::string origin =
fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1);
ImGui::Text("%s", origin.c_str());
ImGui::PopStyleColor();
searchCount++;
if (info.type == WIDGET_COMBOBOX || info.type == WIDGET_CVAR_COMBOBOX) {
std::static_pointer_cast<UIWidgets::ComboboxOptions>(info.options)->alignment =
backupAlignment;
std::static_pointer_cast<UIWidgets::ComboboxOptions>(info.options)->labelPosition =
backupLabelPos;
}
}
}
}
}
}
for (auto& entry : extraSearchWidgets) {
if (entry.info.type == WIDGET_SEARCH || entry.info.type == WIDGET_SEPARATOR ||
entry.info.type == WIDGET_SEPARATOR_TEXT || entry.info.isHidden || entry.info.hideInSearch) {
continue;
}
std::string widgetStr =
entry.info.name + entry.info.options->tooltip + entry.extraTerms + entry.sidebarName;
std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower);
widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end());
if (widgetStr.find(menuSearchText) != std::string::npos) {
MenuDrawItem(entry.info, 400, menuThemeIndex);
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray));
std::string origin = fmt::format(" ({} -> {}, {})", entry.menuName, entry.sidebarName, entry.location);
ImGui::Text("%s", origin.c_str());
ImGui::PopStyleColor();
searchCount++;
}
}
ImGui::EndChild();
}
return searchCount;
}
void Menu::AddMenuEntry(std::string entryName, const char* entryCvar) {
std::string translatedName = SohGui::LanguageManager::Instance().GetString(entryName);
menuEntries.emplace(entryName, MainMenuEntry{ translatedName, entryCvar });
menuOrder.push_back(entryName);
}
void Menu::AddSearchWidget(SearchWidget widget) {
extraSearchWidgets.push_back(widget);
}
std::unordered_map<uint32_t, disabledInfo>& Menu::GetDisabledMap() {
return disabledMap;
}
void Menu::MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors menuThemeIndex) {
disabledTempTooltip = "This setting is disabled because: \n";
disabledValue = false;
disabledTooltip = " ";
if (widget.preFunc != nullptr) {
widget.ResetDisables();
widget.preFunc(widget);
if (widget.isHidden) {
return;
}
if (!widget.activeDisables.empty()) {
widget.options->disabled = true;
for (auto option : widget.activeDisables) {
disabledTempTooltip += std::string("\n- ") + disabledMap.at(option).reason;
}
widget.options->disabledTooltip = disabledTempTooltip.c_str();
}
}
if (widget.raceDisable && raceDisableActive) {
widget.options->disabled = true;
disabledTempTooltip += std::string("\n- Race Lockout Active");
widget.options->disabledTooltip = disabledTempTooltip.c_str();
}
if (widget.sameLine) {
ImGui::SameLine();
}
try {
switch (widget.type) {
case WIDGET_CHECKBOX: {
bool* pointer = std::get<bool*>(widget.valuePointer);
if (pointer == nullptr) {
SPDLOG_ERROR("Checkbox Widget requires a value pointer, currently nullptr");
assert(false);
return;
}
auto options = std::static_pointer_cast<UIWidgets::CheckboxOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::Checkbox(UIWidgets::WrappedText(widget.name.c_str(), width).c_str(), pointer,
*options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
}
} break;
case WIDGET_CVAR_CHECKBOX: {
auto options = std::static_pointer_cast<UIWidgets::CheckboxOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::CVarCheckbox(UIWidgets::WrappedText(widget.name.c_str(), width).c_str(), widget.cVar,
*options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
};
} break;
case WIDGET_AUDIO_BACKEND: {
auto currentAudioBackend = Ship::Context::GetInstance()->GetAudio()->GetCurrentAudioBackend();
UIWidgets::ComboboxOptions options = {};
options.color = menuThemeIndex;
options.tooltip = "Sets the audio API used by the game. Requires a relaunch to take effect.";
options.disabled = Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1;
options.disabledTooltip = "Only one audio API is available on this platform.";
if (UIWidgets::Combobox("Audio API", &currentAudioBackend, audioBackendsMap, options)) {
Ship::Context::GetInstance()->GetAudio()->SetCurrentAudioBackend(currentAudioBackend);
}
} break;
case WIDGET_VIDEO_BACKEND: {
UIWidgets::ComboboxOptions options = {};
options.color = menuThemeIndex;
options.tooltip = "Sets the renderer API used by the game.";
options.disabled = availableWindowBackends->size() <= 1;
options.disabledTooltip = "Only one renderer API is available on this platform.";
if (UIWidgets::Combobox("Renderer API (Needs reload)", &configWindowBackend, availableWindowBackendsMap,
options)) {
Ship::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id",
(int32_t)(configWindowBackend));
Ship::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name",
windowBackendsMap.at(configWindowBackend));
Ship::Context::GetInstance()->GetConfig()->Save();
UpdateWindowBackendObjects();
}
} break;
case WIDGET_SEPARATOR: {
ImGui::Separator();
} break;
case WIDGET_SEPARATOR_TEXT: {
auto options = std::static_pointer_cast<UIWidgets::TextOptions>(widget.options);
if (options->color != UIWidgets::Colors::NoColor) {
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(options->color));
}
ImGui::SeparatorText(widget.name.c_str());
if (options->color != UIWidgets::Colors::NoColor) {
ImGui::PopStyleColor();
}
} break;
case WIDGET_TEXT: {
auto options = std::static_pointer_cast<UIWidgets::TextOptions>(widget.options);
if (options->color != UIWidgets::Colors::NoColor) {
ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(options->color));
}
ImGui::AlignTextToFramePadding();
ImGui::TextWrapped("%s", widget.name.c_str());
if (options->color != UIWidgets::Colors::NoColor) {
ImGui::PopStyleColor();
}
} break;
case WIDGET_COMBOBOX: {
int32_t* pointer = std::get<int32_t*>(widget.valuePointer);
if (pointer == nullptr) {
SPDLOG_ERROR("Combobox Widget requires a value pointer, currently nullptr");
assert(false);
return;
}
auto options = std::static_pointer_cast<UIWidgets::ComboboxOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::Combobox(widget.name.c_str(), pointer, options->comboMap, *options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
};
} break;
case WIDGET_CVAR_COMBOBOX: {
auto options = std::static_pointer_cast<UIWidgets::ComboboxOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::CVarCombobox(widget.name.c_str(), widget.cVar, options->comboMap, *options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
}
} break;
case WIDGET_SLIDER_INT: {
int32_t* pointer = std::get<int32_t*>(widget.valuePointer);
if (pointer == nullptr) {
SPDLOG_ERROR("int32 Slider Widget requires a value pointer, currently nullptr");
assert(false);
return;
}
auto options = std::static_pointer_cast<UIWidgets::IntSliderOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::SliderInt(widget.name.c_str(), pointer, *options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
};
} break;
case WIDGET_CVAR_SLIDER_INT: {
auto options = std::static_pointer_cast<UIWidgets::IntSliderOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::CVarSliderInt(widget.name.c_str(), widget.cVar, *options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
};
} break;
case WIDGET_SLIDER_FLOAT: {
float* pointer = std::get<float*>(widget.valuePointer);
if (pointer == nullptr) {
SPDLOG_ERROR("float Slider Widget requires a value pointer, currently nullptr");
assert(false);
return;
}
auto options = std::static_pointer_cast<UIWidgets::FloatSliderOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::SliderFloat(widget.name.c_str(), pointer, *options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
}
} break;
case WIDGET_CVAR_SLIDER_FLOAT: {
auto options = std::static_pointer_cast<UIWidgets::FloatSliderOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::CVarSliderFloat(widget.name.c_str(), widget.cVar, *options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
}
} break;
case WIDGET_CVAR_BTN_SELECTOR: {
auto options = std::static_pointer_cast<UIWidgets::BtnSelectorOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::CVarBtnSelector(widget.name.c_str(), widget.cVar, *options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
}
} break;
case WIDGET_BUTTON: {
auto options = std::static_pointer_cast<UIWidgets::ButtonOptions>(widget.options);
options->color = menuThemeIndex;
if (UIWidgets::Button(widget.name.c_str(), *options)) {
if (widget.callback != nullptr) {
widget.callback(widget);
}
}
} break;
case WIDGET_CUSTOM: {
if (widget.customFunction != nullptr) {
widget.customFunction(widget);
}
} break;
case WIDGET_WINDOW_BUTTON: {
if (widget.windowName == nullptr || widget.windowName[0] == '\0') {
std::string msg =
fmt::format("Error drawing window contents for {}: windowName not defined", widget.name);
SPDLOG_ERROR(msg.c_str());
break;
}
auto window = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow(widget.windowName);
if (!window) {
std::string msg =
fmt::format("Error drawing window contents: windowName {} does not exist", widget.windowName);
SPDLOG_ERROR(msg.c_str());
break;
}
auto options = std::static_pointer_cast<UIWidgets::WindowButtonOptions>(widget.options);
options->color = menuThemeIndex;
if (options->showButton) {
UIWidgets::WindowButton(widget.name.c_str(), widget.cVar, window, *options);
}
if (!window->IsVisible() && options->embedWindow) {
window->DrawElement();
}
} break;
case WIDGET_CVAR_COLOR_PICKER: {
auto options = std::static_pointer_cast<UIWidgets::ColorPickerOptions>(widget.options);
uint32_t modifiers = 0;
if (options->showLock)
modifiers |= UIWidgets::ColorPickerLockCheck;
if (options->showRandom)
modifiers |= UIWidgets::ColorPickerRandomButton;
if (options->showReset)
modifiers |= UIWidgets::ColorPickerResetButton;
if (options->showRainbow)
modifiers |= UIWidgets::ColorPickerRainbowCheck;
UIWidgets::CVarColorPicker(widget.name.c_str(), widget.cVar, options->defaultValue, options->useAlpha,
modifiers, options->color);
} break;
case WIDGET_SEARCH: {
UIWidgets::PushStyleButton(menuThemeIndex);
if (ImGui::Button("Clear")) {
menuSearch.Clear();
}
ImGui::SameLine();
if (CVarGetInteger(CVAR_SETTING("Menu.SearchAutofocus"), 0) &&
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() &&
!ImGui::IsMouseClicked(0)) {
ImGui::SetKeyboardFocusHere(0);
}
UIWidgets::PushStyleCombobox(menuThemeIndex);
ImGui::PushStyleColor(ImGuiCol_Border, UIWidgets::ColorValues.at(menuThemeIndex));
menuSearch.Draw();
ImGui::PopStyleColor();
UIWidgets::PopStyleCombobox();
UIWidgets::PopStyleButton();
std::string menuSearchText(menuSearch.InputBuf);
if (menuSearchText == "") {
ImGui::Text("Start typing to see results.");
return;
}
DrawSearchResults(menuSearchText);
} break;
default:
break;
}
if (widget.postFunc != nullptr) {
widget.postFunc(widget);
}
} catch (const std::bad_variant_access& e) {
SPDLOG_ERROR("Failed to draw menu item \"{}\" due to: {}", widget.name, e.what());
assert(false);
}
}
void Menu::Draw() {
if (!IsVisible()) {
return;
}
DrawElement();
// Sync up the IsVisible flag if it was changed by ImGui
SyncVisibilityConsoleVariable();
}
static bool freshOpen = true;
void Menu::DrawElement() {
if (OTRGlobals::Instance->fontStandardLargest == nullptr) {
return;
}
for (auto& [reason, info] : disabledMap) {
info.active = info.evaluation(info);
}
const char* headerCvar = CVAR_SETTING("Menu.ActiveHeader");
if (navigateToWidget) {
if (menuEntries.contains(navigateMainEntry) &&
menuEntries.at(navigateMainEntry).sidebars.contains(navigateSidebar)) {
menuSearch.Clear();
CVarSetString(headerCvar, navigateMainEntry);
const char* sidebarCvar = menuEntries.at(navigateMainEntry).sidebarCvar;
CVarSetString(sidebarCvar, navigateSidebar);
highlightWidget = true;
}
navigateToWidget = false;
}
raceDisableActive = CVarGetInteger(CVAR_SETTING("DisableChanges"), 0);
windowHeight = ImGui::GetMainViewport()->WorkSize.y;
windowWidth = ImGui::GetMainViewport()->WorkSize.x;
auto windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
bool popout = CVarGetInteger(CVAR_SETTING("Menu.Popout"), 0) && allowPopout;
if (popout) {
windowFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDocking;
}
if (popout != popped) {
if (popout) {
windowHeight = poppedSize.y;
windowWidth = poppedSize.x;
ImGui::SetNextWindowSize({ static_cast<float>(windowWidth), static_cast<float>(windowHeight) },
ImGuiCond_Always);
ImGui::SetNextWindowPos(poppedPos, ImGuiCond_Always);
} else if (popped) {
CVarSetFloat(CVAR_SETTING("Menu.PoppedWidth"), poppedSize.x);
CVarSetFloat(CVAR_SETTING("Menu.PoppedHeight"), poppedSize.y);
CVarSave();
}
}
popped = popout;
auto windowCond = ImGuiCond_Always;
if (!popout) {
ImGui::SetNextWindowSize({ static_cast<float>(windowWidth), static_cast<float>(windowHeight) }, windowCond);
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), windowCond, { 0.5f, 0.5f });
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
}
ImGui::PushStyleColor(ImGuiCol_WindowBg,
ImVec4(0, 0, 0, CVarGetFloat(CVAR_SETTING("Menu.BackgroundOpacity"), 0.85f)));
if (!ImGui::Begin("Main Menu", NULL, windowFlags)) {
if (!popout) {
ImGui::PopStyleVar();
}
freshOpen = true;
ImGui::PopStyleColor();
ImGui::End();
return;
}
ImGui::PopStyleColor();
if (popped != popout) {
if (!popout) {
ImGui::PopStyleVar();
}
ImGui::PopStyleColor();
CVarSetInteger(CVAR_SETTING("Menu.Popout"), popped);
CVarSetFloat(CVAR_SETTING("Menu.PoppedWidth"), poppedSize.x);
CVarSetFloat(CVAR_SETTING("Menu.PoppedHeight"), poppedSize.y);
CVarSetFloat(CVAR_SETTING("Menu.PoppedPos.x"), poppedSize.x);
CVarSetFloat(CVAR_SETTING("Menu.PoppedPos.y"), poppedSize.y);
CVarSave();
ImGui::End();
return;
}
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiStyle& style = ImGui::GetStyle();
windowHeight = window->WorkRect.GetHeight();
windowWidth = window->WorkRect.GetWidth();
ImGui::PushFont(OTRGlobals::Instance->fontStandardLargest);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f));
std::string headerIndex = CVarGetString(headerCvar, "Settings");
ImVec2 pos = window->DC.CursorPos;
float centerX = pos.x + windowWidth / 2 - (style.ItemSpacing.x * (menuEntries.size() + 1));
std::vector<ImVec2> headerSizes;
float headerWidth = 0.0f;
bool headerSearch = !CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0);
if (headerSearch) {
headerWidth += 200.0f;
}
for (auto& label : menuOrder) {
ImVec2 size = ImGui::CalcTextSize(label.c_str());
headerSizes.push_back(size);
headerWidth += size.x + style.FramePadding.x * 2 + style.ItemSpacing.x;
}
// Full screen menu with widths below 1280, heights below 800.
// 5% of screen width/height padding on both sides above those resolutions.
// Menu width will never exceed a 16:9 aspect ratio.
ImVec2 menuSize = { windowWidth, windowHeight };
if (windowWidth > 1280) {
menuSize.x = std::fminf(windowWidth * 0.9f, (windowHeight * 1.77f));
}
if (windowHeight > 800) {
menuSize.y = windowHeight * 0.9f;
}
pos += window->WorkRect.GetSize() / 2 - menuSize / 2;
ImGui::SetNextWindowPos(pos);
ImGui::BeginChild("Menu Block", menuSize,
ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar);
std::unordered_map<std::string, SidebarEntry>* sidebar;
float headerHeight = headerSizes.at(0).y + style.FramePadding.y * 2;
ImVec2 buttonSize = ImGui::CalcTextSize(ICON_FA_TIMES_CIRCLE) + style.FramePadding * 2;
bool scrollbar = false;
if (headerWidth > menuSize.x - buttonSize.x * 3 - style.ItemSpacing.x * 3) {
headerHeight += style.ScrollbarSize;
scrollbar = true;
}
ImGui::SetNextWindowSizeConstraints({ 0, headerHeight }, { headerWidth, headerHeight });
ImVec2 headerSelSize = { menuSize.x - buttonSize.x * 3 - style.ItemSpacing.x * 3, headerHeight };
if (scrollbar) {
headerSelSize.y += style.ScrollbarSize;
}
bool autoFocus = CVarGetInteger(CVAR_SETTING("Menu.SearchAutofocus"), 0);
ImGui::BeginChild("Header Selection", headerSelSize,
ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_HorizontalScrollbar);
uint8_t curIndex = 0;
for (auto& label : menuOrder) {
if (curIndex != 0) {
ImGui::SameLine();
}
auto& entry = menuEntries.at(label);
std::string displayLabel = entry.label;
if (displayLabel != label) {
displayLabel = SohGui::LanguageManager::Instance().GetString(label);
}
std::string nextIndex = label;
UIWidgets::PushStyleButton(menuThemeIndex);
if (headerIndex != label) {
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 });
}
if (ModernMenuHeaderEntry(displayLabel.c_str())) {
if (headerSearch) {
menuSearch.Clear();
}
CVarSetString(headerCvar, label.c_str());
CVarSave();
nextIndex = label;
}
if (headerIndex != label) {
ImGui::PopStyleColor();
}
UIWidgets::PopStyleButton();
if (headerIndex == label) {
sidebar = &entry.sidebars;
}
if (nextIndex != label) {
headerIndex = nextIndex;
}
curIndex++;
}
std::string menuSearchText = "";
if (headerSearch) {
ImGui::SameLine();
if (autoFocus && freshOpen) {
ImGui::SetKeyboardFocusHere();
}
auto color = UIWidgets::ColorValues.at(menuThemeIndex);
color.w = 0.6f;
ImGui::PushStyleColor(ImGuiCol_FrameBg, color);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
menuSearch.Draw("##search", 200.0f);
menuSearchText = menuSearch.InputBuf;
menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), menuSearchText.end());
if (menuSearchText.length() < 1) {
ImGui::SameLine(headerWidth - 200.0f + style.ItemSpacing.x);
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search...");
}
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
ImGui::EndChild();
ImGui::SameLine(menuSize.x - (buttonSize.x * 3) - (style.ItemSpacing.x * 2));
UIWidgets::ButtonOptions options3 = {};
options3.color = UIWidgets::Colors::Red;
options3.size = UIWidgets::Sizes::Inline;
options3.tooltip = "Quit SoH";
if (UIWidgets::Button(ICON_FA_POWER_OFF, options3)) {
SohGui::mModalWindow->RegisterPopup(
"Quit SoH", "Are you sure you want to quit SoH?", "Quit", "Cancel",
[]() {
std::shared_ptr<Menu> menu =
static_pointer_cast<Menu>(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetMenu());
if (!menu->IsMenuPopped()) {
menu->ToggleVisibility();
}
Ship::Context::GetInstance()->GetWindow()->Close();
},
nullptr);
}
ImGui::PopStyleVar();
ImGui::SameLine();
UIWidgets::ButtonOptions options2 = {};
options2.color = UIWidgets::Colors::Red;
options2.size = UIWidgets::Sizes::Inline;
options2.tooltip = "Reset"
#ifdef __APPLE__
" (Command-R)"
#elif !defined(__SWITCH__) && !defined(__WIIU__)
" (Ctrl+R)"
#else
""
#endif
;
if (UIWidgets::Button(ICON_FA_UNDO, options2)) {
std::reinterpret_pointer_cast<Ship::ConsoleWindow>(
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))
->Dispatch("reset");
}
ImGui::SameLine();
UIWidgets::ButtonOptions options = {};
options.size = UIWidgets::Sizes::Inline;
options.tooltip = "Close Menu (Esc)";
if (UIWidgets::Button(ICON_FA_TIMES_CIRCLE, options)) {
ToggleVisibility();
// Update gamepad navigation after close based on if other menus are still visible
auto mImGuiIo = &ImGui::GetIO();
if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) &&
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetMenuOrMenubarVisible()) {
mImGuiIo->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
} else {
mImGuiIo->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
}
}
pos.y += headerHeight + style.ItemSpacing.y;
pos.x = centerX - menuSize.x / 2 + (style.ItemSpacing.x * (menuEntries.size() + 1));
window->DrawList->AddRectFilled(pos, pos + ImVec2{ menuSize.x, 4 }, ImGui::GetColorU32({ 255, 255, 255, 255 }),
true, style.WindowRounding);
pos.y += style.ItemSpacing.y;
float sectionHeight = menuSize.y - headerHeight - 4 - style.ItemSpacing.y * 2;
float columnHeight = sectionHeight - style.ItemSpacing.y * 4;
ImGui::SetNextWindowPos(pos + style.ItemSpacing * 2);
// Increase sidebar width on larger screens to accomodate people scaling their menus.
float sidebarWidth = 200 - style.ItemSpacing.x;
if (menuSize.x > 1600) {
sidebarWidth = menuSize.x * 0.15f;
}
const char* sidebarCvar = menuEntries.at(headerIndex).sidebarCvar;
std::string sectionIndex = CVarGetString(sidebarCvar, "");
if (!sidebar->contains(sectionIndex)) {
sectionIndex = menuEntries.at(headerIndex).sidebarOrder.at(0);
}
float sectionCenterX = pos.x + (sidebarWidth / 2);
float topY = pos.y;
ImGui::SetNextWindowSizeConstraints({ sidebarWidth, 0 }, { sidebarWidth, columnHeight });
ImGui::BeginChild((menuEntries.at(headerIndex).label + " Section").c_str(), { sidebarWidth, columnHeight * 3 },
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoTitleBar);
for (auto& sidebarLabel : menuEntries.at(headerIndex).sidebarOrder) {
std::string displayLabel = sidebarLabel;
auto& sidebarEntry = menuEntries.at(headerIndex).sidebars.at(sidebarLabel);
if (!sidebarEntry.translatedName.empty() && sidebarEntry.translatedName != sidebarLabel) {
displayLabel = sidebarEntry.translatedName;
}
std::string nextIndex = "";
UIWidgets::PushStyleButton(menuThemeIndex);
if (sectionIndex != sidebarLabel) {
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 });
}
if (ModernMenuSidebarEntry(displayLabel.c_str())) {
if (headerSearch) {
menuSearch.Clear();
}
CVarSetString(sidebarCvar, sidebarLabel.c_str());
CVarSave();
nextIndex = sidebarLabel;
}
if (sectionIndex != sidebarLabel) {
ImGui::PopStyleColor();
}
UIWidgets::PopStyleButton();
if (nextIndex != "") {
sectionIndex = nextIndex;
}
}
ImGui::EndChild();
ImGui::PopFont();
pos = ImVec2{ sectionCenterX + (sidebarWidth / 2), topY } + style.ItemSpacing * 2;
window->DrawList->AddRectFilled(pos, pos + ImVec2{ 4, sectionHeight - style.FramePadding.y * 2 },
ImGui::GetColorU32({ 255, 255, 255, 255 }), true, style.WindowRounding);
pos.x += 4 + style.ItemSpacing.x;
ImGui::SetNextWindowPos(pos + style.ItemSpacing);
float sectionWidth = menuSize.x - sidebarWidth - 4 - style.ItemSpacing.x * 4;
std::string sectionMenuId = sectionIndex + " Settings";
size_t columns = sidebar->at(sectionIndex).columnCount;
size_t columnFuncs = sidebar->at(sectionIndex).columnWidgets.size();
if (windowWidth < 800) {
columns = 1;
}
float columnWidth = (sectionWidth - style.ItemSpacing.x * columns) / columns;
bool useColumns = columns > 1;
if (!useColumns || (headerSearch && menuSearchText.length() > 0)) {
ImGui::SameLine();
ImGui::SetNextWindowSizeConstraints({ sectionWidth, 0 }, { sectionWidth, columnHeight });
ImGui::BeginChild(sectionMenuId.c_str(), { sectionWidth, windowHeight * 4 }, ImGuiChildFlags_AutoResizeY,
ImGuiWindowFlags_NoTitleBar);
}
if (headerSearch && menuSearchText.length() > 0) {
ImGui::AlignTextToFramePadding();
ImGui::PushFont(OTRGlobals::Instance->fontMonoLargest);
ImGui::Text("Search Results");
ImGui::PopFont();
ImGui::SameLine();
UIWidgets::ButtonOptions clearBtnOpts = {};
clearBtnOpts.size = UIWidgets::Sizes::Inline;
if (UIWidgets::Button("Clear Search", clearBtnOpts)) {
menuSearch.Clear();
}
ImGui::BeginChild("searchSeparator", ImVec2(ImGui::GetContentRegionAvail().x / 2, 20),
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY);
UIWidgets::Separator(true, true, 0, 10);
ImGui::EndChild();
uint32_t searchCount = DrawSearchResults(menuSearchText);
if (searchCount == 0) {
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize("No results found").x) / 2);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f);
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "No results found");
}
} else {
std::string menuLabel = menuEntries.at(headerIndex).label;
if (MenuInit::GetUpdateFuncs().contains(menuLabel)) {
if (MenuInit::GetUpdateFuncs()[menuLabel].contains(sectionIndex)) {
for (auto& updateFunc : MenuInit::GetUpdateFuncs()[menuLabel][sectionIndex]) {
updateFunc();
}
}
}
for (size_t i = 0; i < columnFuncs; i++) {
std::string sectionId = fmt::format("{} Column {}", sectionMenuId, i);
if (useColumns) {
ImGui::SetNextWindowSizeConstraints({ columnWidth, 0 }, { columnWidth, columnHeight });
ImGui::BeginChild(sectionId.c_str(), { columnWidth, windowHeight * 4 }, ImGuiChildFlags_AutoResizeY,
ImGuiWindowFlags_NoTitleBar);
}
// for (auto& entryName : sidebar->at(sectionIndex).sidebarOrder) {
for (auto& entry : sidebar->at(sectionIndex).columnWidgets.at(i)) {
MenuDrawItem(entry, 90 / sidebar->at(sectionIndex).columnCount, menuThemeIndex);
}
//}
if (useColumns) {
ImGui::EndChild();
}
if (i < columns - 1) {
ImGui::SameLine();
}
}
}
if (!useColumns || menuSearchText.length() > 0) {
ImGui::EndChild();
}
if (!popout) {
ImGui::PopStyleVar();
}
ImGui::EndChild();
if (popout) {
poppedSize = ImGui::GetWindowSize();
poppedPos = ImGui::GetWindowPos();
}
if (freshOpen) {
freshOpen = false;
}
ImGui::End();
}
} // namespace Ship

View File

@@ -19,7 +19,8 @@ using namespace UIWidgets;
void SohMenu::AddSidebarEntry(std::string sectionName, std::string sidebarName, uint32_t columnCount) {
assert(!sectionName.empty());
assert(!sidebarName.empty());
menuEntries.at(sectionName).sidebars.emplace(sidebarName, SidebarEntry{ .columnCount = columnCount });
std::string translatedSidebar = LanguageManager::Instance().GetString(sidebarName);
menuEntries.at(sectionName).sidebars.emplace(sidebarName, SidebarEntry{ .columnCount = columnCount, .translatedName = translatedSidebar });
menuEntries.at(sectionName).sidebarOrder.push_back(sidebarName);
}