Skip to content

fix(linux/x11): ajustar la ventana del detail al contenido bajo XWayland (#215) - #224

Merged
egdev6 merged 11 commits into
egdev6:mainfrom
SrStamm:fix/x11-detail-window-fit
Oct 31, 2026
Merged

egdev6 merged 11 commits into
egdev6:mainfrom
SrStamm:fix/x11-detail-window-fit

Conversation

@SrStamm

@SrStamm SrStamm commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Related issue / Issue relacionada

Issue: Refs #215
ODD: `odd/tasks/x11-detail-fit.md` — alcance aprobado por el mantenedor en el hilo de la issue (dos cuidados aplicados: no se comparte el ciclo/validación/anclaje/generación del widget y la primera apertura no se bloquea con el read-back).

Scope / Alcance

  • Con `GDK_BACKEND=x11` (XWayland en GNOME) el aviso de API key mostraba la caja de la ventana contenedora: GTK retiene un piso `PMinSize` sticky con `resizable(false)` y el resize externo es rechazado en el ancho (330 vs contenido de 314).
  • El `detail` pasa por el mismo ciclo de la tira (fix(linux/x11): la tira alcanza el borde del área de trabajo (#179) #205), en módulo propio `detail_placement.rs`: una sola llamada al hilo de UI hace la pista de tamaño + pistas `min == max == pedido` + permiso de resize abierto sólo alrededor del resize y confirmado cerrado; luego posición explícita (sin anclaje) y read-back de GDK con presupuesto acotado y tolerancia ±1 px sólo sobre geometría con read-back.
  • Generación de cancelación propia del detalle (no comparte la del widget); Windows, macOS y Wayland nativo conservan la ruta anterior (`detail_geometry::apply`, informe `unverified` con `bounds = null`).
  • `set_detail_bounds` devuelve el informe `{status, bounds, reason, read_back}` espejo de `set_widget_bounds`; el frontend cachea sólo geometría confirmada por read-back y reintenta en silencio tras `show()` ante `not_viewable` (`readPlacementWithRetries` de fix(linux): not_viewable tras show() se reintenta en silencio y los comentarios no prometen confirmaciones (#179) #221). Motivos como códigos ASCII, sin textos nuevos de UI.
  • Exclusiones: no se toca la tira ni su colocación (sólo visibilidad `pub(crate)` de `on_ui`/`transport_on_ui`; fix(linux): not_viewable tras show() se reintenta en silencio y los comentarios no prometen confirmaciones (#179) #221 ya en base); no se cambian catálogos ni inventario a mano (regenerado con el script).

Coordination / Coordinación

Contributor / Contribuidor: @SrStamm
Claim or agreement evidence / Evidencia de asignación o acuerdo: alcance aprobado por el mantenedor en el comentario de #215; implementación según ODD y los dos cuidados citados.

Acceptance and validation / Aceptación y validación

  • Criterios cumplidos: geometría efectiva ajustada al contenido (ancho nativo 314 = \`panelWidthFor(48)\`), sin caja, sin resize manual (pistas `min == max`), permiso de resize cerrado, sin `resizable(true)` permanente, primera apertura sin bloqueo y sin cachear la petición como confirmada.
  • Comprobaciones ejecutadas y resultados:
    • `cargo test --locked --offline`: 401 passed / 0 failed / 4 ignored (TDD: RED observado en ambas unidades; 17 tests deterministas + 1 en vivo nuevos del módulo).
    • Prueba en vivo GTK+WebKit2 real (`GDK_BACKEND=x11` sobre XWayland): tamaño 314×379 exacto concedido, permiso confirmado cerrado, tamaño sobrevive a la retirada.
    • `npm test`: 822 vitest (65 ficheros) + 298 node. `npm run typecheck`: limpio.
    • `cargo fmt --all -- --check`: limpio. `cargo clippy --locked --offline --all-targets`: mismos avisos que la base, 0 en `detail_placement*`.
    • `node scripts/i18n-inventory.mjs --check`: limpio tras la regeneración (sólo strings de error/test de Rust visibles al escáner, nada de interfaz).
    • Sesión real GNOME/XWayland + app de la rama, validación humana por SrStamm + medición instrumental: detail \`314×379+69+258\`, \`WM_NORMAL_HINTS\` min = max = base = 314×379, tira intacta \`61×238+0+273\`, sin caja translúcida en el aviso de API key.
  • Comprobaciones no ejecutadas y motivo: la reseña nativa del candidato devolvió consentimiento declinado (no se creó línea de review); el camino de riesgo se cubrió con verificador independiente sobre ambas unidades (sin hallazgos bloqueantes). Reducción/crecimiento/cambios de proveedor y DPI en vivo quedan cubiertos por tests deterministas y el dedup del read-back; acreditación humana extra no correrá por defecto.
  • Remaining blockers / Bloqueos pendientes: assignees de la issue/PR (mantenedor) y label \`status:approved\` en la issue, según la política del repositorio.

Una PR abierta no implica integración. Release, secretos y firma requieren autorización separada.

…and (egdev6#215)

La ventana detail nace resizable(false) y GTK retiene un piso PMinSize sticky,
así que el resize externo es rechazado en el ancho y el aviso de API key muestra
la caja del contenedor (~16 px de banda). El detalle pasa por el mismo ciclo X11
de la tira (egdev6#205): una sola llamada al hilo de UI hace la pista de tamaño, las
pistas min == max == pedido, el permiso de resize abierto sólo alrededor del
resize y confirmado cerrado; luego la posición explícita y un read-back de GDK
con presupuesto acotado. Generación de cancelación propia del detail (no
comparte la del widget), sin anclaje y con informe {status, bounds, reason,
read_back} espejo de set_widget_bounds; Windows, macOS y Wayland nativo quedan
en su ruta anterior (unverified, bounds=null).
…gdev6#215)

DetallePanel cachea sólo la geometría confirmada por read-back (nunca la pedida),
repide en silencio tras show() ante not_viewable con el presupuesto acotado de
egdev6#221 (readPlacementWithRetries reutilizado) y deja intacto el flujo de Wayland
nativo/Windows/macOS (unverified sin read-back, comparación exacta). La apertura
no se bloquea: place → clip → positioned → show conserva su orden.
Secciones nuevas de detail_placement.rs y detail_placement_test.rs (cuerpos de
error y nombres de test, nada de interfaz de usuario) tras los commits 9a6622d
y 15aea55; el frontend no añade textos.
…gdev6#215)

Ejecutar la prueba en vivo contra el XWayland real marcó el hallazgo del
fixture: movía la ventana 314×379 a (1859,899), fuera de pantalla, y el gestor
la reubicaba (1606,701) para que quepa. La petición ahora es (100,100) y el
resto de la secuencia queda intacto: tamaño exacto concedido, permiso cerrado y
tamaño sobreviviente a la retirada.
…6#215)

Ciclo, decisiones (D1–D3), TDD por unidad, hallazgo del fixture de la prueba
en vivo y evidencia de sesión GNOME/XWayland (geometría 314×379, pistas
min == max, sin caja translúcica) según los criterios de aceptación (egdev6#215).
@egdev6

egdev6 commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Gracias por el trabajo, @SrStamm. Revisé 026e020 y encontré un punto para corregir antes de aprobar.

P2: posición incorrecta con escala HiDPI. En src-tauri/src/detail_placement.rs:377–386, request_position recibe coordenadas físicas y las pasa directamente a gtk.move_(x, y), que utiliza coordenadas lógicas. El tamaño sí se convierte mediante to_logical, pero la posición no. Con escala GTK ×2, una solicitud física (200,300) se interpreta como (200,300) lógicos, aproximadamente (400,600) físicos, desplazando el panel respecto de la tira. El read-back detecta position_refused, pero no corrige esa conversión. El adaptador existente del widget ya convierte ambos ejes antes de mover (widget_placement.rs:702–712).

Propongo convertir ambos ejes a coordenadas lógicas y añadir cobertura del adaptador con escala ×2, porque el doble de geometría prefijada no detecta esta omisión. Los cinco checks de CI están verdes. El hallazgo está contrastado con el recorrido del código, no con una reproducción gráfica; no ejecuté suites locales en esta revisión.

…6#215)

request_position pasaba la petición física tal cual a gtk.move_, que
mide en píxeles lógicos: a escala ×2 el panel quedaba desplazado
aproximadamente al doble (P2 del review de PR egdev6#224, diagnosticado por
el mantenedor y contrastado con el recorrido del código).

Se introduce el seam testeable del propio archivo (Positioner +
GtkPositioner + position_call, espejo de Sizable/GtkSizable): la
conversión con to_logical cubre los dos ejes antes del movimiento, el
mismo mapeo que el adaptador del widget. La cobertura nueva (escala
×2: (200,300) físico -> (100,150) lógico) falla contra el código
anterior, así que la omisión queda atrapada por test.

Verificación: cargo test 402 + 4 ignoradas (0 fallos), fmt limpio,
clippy sin avisos nuevos en detail_placement*; verificador
independiente en PASS sobre los 5 checks.
@SrStamm

SrStamm commented Oct 31, 2026

Copy link
Copy Markdown
Contributor Author

Tenés razón: request_position pasaba la petición física tal cual a gtk.move_, que mide en píxeles lógicos.

Corregido en 29e7c77: la solicitud de posición convierte ambos ejes con to_logical antes del movimiento, con el mismo mapeo que el adaptador del widget. Para que el cambio quede bien revisable, la conversión vive ahora en el seam propio del archivo (Positioner + GtkPositioner + position_call, espejo de Sizable/GtkSizable) y el test nuevo lo ejercita con escala 2.0: (200,300) físico debe aterrizar como (100,150) lógico (más un caso de identidad en escala 1.0). Ese test falla contra el código anterior, así que la omisión queda atrapada por test.

Verificación: suite en 402 pasando + 4 ignoradas (0 fallos), fmt limpio y clippy sin avisos nuevos en detail_placement* (un verificador independiente corrió los mismos checks sobre el diff y pasó). Además de los tests, validé la colocación a mano en la app de la rama y la veo bien.

@egdev6

egdev6 commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Gracias por la corrección y por añadir la cobertura de regresión, @SrStamm. Revisé d61cc30: el hallazgo HiDPI queda resuelto en el adaptador de producción, que convierte ambos ejes a coordenadas lógicas usando la escala real de la ventana antes del movimiento. Los tests de escalas ×1 y ×2 pasan en CI.

No encontré nuevos hallazgos bloqueantes y los cinco checks están verdes. No ejecuté pruebas locales ni GUI; la prueba nativa X11 sigue ignorada en CI, por lo que no doy esa validación por realizada.

Por mi parte, recomiendo aprobar. El merge queda a cargo del mantenedor. ¡Gracias por el trabajo y por atender la revisión!

@egdev6
egdev6 merged commit d3b3bd6 into egdev6:main Oct 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants