La barra de menús
Lo primero que delata a una aplicación web disfrazada. Cómo se construye un menú nativo, qué espera cada sistema, y el menú sin el cual copiar y pegar dejan de funcionar.
Lo que cada sistema espera
La quinta fila es la que causa el fallo más desconcertante de este nivel y merece su apartado.
El menú de edición en macOS
En macOS, los atajos de copiar, pegar, cortar, deshacer y seleccionar todo funcionan porque están en el menú. El sistema los enruta a través de él, no directamente a la ventana.
Consecuencia: una aplicación de Tauri en macOS sin menú, o con un menú propio que no incluye edición, deja al usuario sin poder copiar ni pegar con el teclado — en una aplicación hecha con tecnología web, donde eso debería funcionar solo.
Y es un fallo especialmente malo porque:
- No da ningún error. El atajo simplemente no hace nada.
- Funciona en desarrollo, si tu configuración deja el menú por omisión.
- Y el usuario no lo reporta como un fallo del menú, sino como «no puedo pegar», que suena a otra cosa.
La solución es incluir los elementos predefinidos de edición, que Tauri ofrece hechos:
let edicion = SubmenuBuilder::new(app, "Edición") .undo().redo() .separator() .cut().copy().paste().select_all() .build()?;Usar los predefinidos y no entradas propias es lo correcto: traen el atajo estándar, el texto traducido al idioma del sistema, y el comportamiento que el sistema espera.
Construir la barra
let menu = MenuBuilder::new(app) .items(&[ &SubmenuBuilder::new(app, "Archivo") .text("nuevo", "Nuevo proyecto") .text("abrir", "Abrir…") .separator() .text("guardar", "Guardar") .build()?, &edicion, &SubmenuBuilder::new(app, "Ayuda").text("docs", "Documentación").build()?, ]) .build()?;app.set_menu(menu)?;app.on_menu_event(|app, ev| match ev.id().as_ref() { "nuevo" => { /* … */ } "abrir" => { /* … */ } _ => {}});Tres detalles:
- Los identificadores son cadenas, y conviene que sean constantes de un enumerado en vez de literales sueltos — el mismo argumento del nivel 47.1.
- Los puntos suspensivos en «Abrir…» son una convención real: significan que la acción abre un diálogo en vez de hacer algo directamente. Los usuarios lo leen aunque no sepan que lo leen.
- Y el manejador vive en el core, así que puede hacer el trabajo o emitir un evento al frontend. Ver el nivel 49.3.
Conectar el menú con el estado
Lo que separa un menú decorativo de uno útil: las entradas se activan y desactivan según lo que se pueda hacer.
- «Guardar» desactivado si no hay cambios.
- «Deshacer» desactivado si no hay nada que deshacer.
- «Cerrar proyecto» desactivado si no hay ninguno abierto.
Un menú donde todo está siempre activo obliga al usuario a probar para averiguar qué se puede hacer, y produce mensajes de error que no deberían existir. Mantener el estado sincronizado cuesta un poco y es lo que hace que un menú informe además de ejecutar.