Skip to content

TableView es

Edgar Mesquita edited this page Oct 20, 2026 · 4 revisions

Vista de tabla

🌐 Esta página en: English · Português · Español · Français · Deutsch

TableView es una ventana a una cuadrícula mayor que cualquier pantalla, en AMBOS ejes, como lo es el TableView de Flutter: describes hasta dónde llega cada fila y cada columna, le entregas un constructor que responde la celda en una fila y una columna, y dices cuántas de las primeras filas y columnas quedan fijas. Construye solo las celdas a la vista más un margen a su alrededor, así que la hoja entera de Excel, 1 048 576 filas por 16 384 columnas, cuesta lo que cuesta una pantalla. Es write-once: el mismo C# corre como pintura de GPU en Photon y como DOM en la web.

TableView(
    rows: new UniformTableSpans(1_048_576 + 1, 28),
    columns: new SparseTableSpans(16_384 + 1, 96, new Dictionary<int, double> { [0] = 48, [3] = 180 }),
    cellBuilder: (row, column) => Text(row == 0 ? $"{column}" : $"{row}:{column}", TypeRole.Caption),
    pinnedRows: 1,
    pinnedColumns: 1,
    width: SizeValue.Fill,
    height: SizeValue.Fill,
    label: "Sheet")

La pantalla /table del sample del dashboard es esta cuadrícula, con cada celda mostrando su propia dirección, una fila de cabecera y una columna de números de fila fijas.

Filas y columnas

Cada eje es un TableSpans: cuántas filas (o columnas) hay, hasta dónde llegan en total, y las dos preguntas que hace el viewport, dónde empieza el índice i (OffsetOf) y qué índice hay en un desplazamiento (IndexAt).

  • UniformTableSpans(count, extent): toda fila (o columna) llega igual de lejos. Todo desplazamiento es una multiplicación.
  • SparseTableSpans(count, extent, extents): una extensión por defecto, y las pocas que tienen la suya, como en una hoja donde alguien ensanchó la columna D. Las excepciones se mantienen ordenadas con una suma acumulada, así que un desplazamiento cuesta una búsqueda binaria sobre ellas, por largo que sea el eje.
  • La tuya: deriva de TableSpans y responde Count, Total, ExtentOf, OffsetOf e IndexAt.

Los desplazamientos son double en los dos destinos: un millón de filas de 28 llegan a 29 360 128, más allá de donde un float aún cuenta punto a punto.

La ventana que construye

La tabla construye las filas y columnas que muestra el viewport más overscan (3 por defecto) más allá de cada borde. La ventana tiene histéresis: un scroll que mantiene lo visible dentro de las celdas ya construidas no construye nada, y el viewport mueve esas celdas por sí mismo. La tabla construye de nuevo solo cuando las filas o columnas visibles salen de ellas.

Una celda tiene por clave su posición ("row:column"), así que una celda que sigue en la ventana conserva su elemento, y su estado, cuando la ventana se mueve.

El primer frame se construye para una ventana común (1 024 × 768) en lugar de para nada, así que ya muestra celdas; el viewport reporta entonces su tamaño real y el siguiente frame construye para él.

Filas y columnas fijas

pinnedRows y pinnedColumns mantienen las primeras filas arriba y las primeras columnas al inicio mientras el resto hace scroll. El viewport dibuja cuatro bandas, cada una bajo su propio recorte: la esquina, que nunca se mueve; las filas fijas, que se mueven de lado junto con la cuadrícula; las columnas fijas, que bajan junto con ella; y el cuerpo, que se mueve en los dos sentidos. Una celda se dibuja en la banda de su primera fila y su primera columna.

Marcas

Las marks se dibujan POR ENCIMA de las celdas, sobre el rango que cubre cada una, y no reciben pulsación ni foco: una selección, un contorno, la banda de una búsqueda. Cada una es un TableViewCell hecho con mark: true, y sus rows y columns dicen cuántas filas y columnas cubre:

TableViewCell(row: 4, column: 2, Box(new BoxStyle { BorderWidth = 2, BorderColor = theme.FocusRing }),
    mark: true, rows: 3, columns: 2)

Una marca que cruza el borde de una banda fija se corta ahí, un trozo por banda, así que se ve en cada banda que alcanza y se oculta donde pasa por debajo de una.

Scroll

El viewport ES DUEÑO de su desplazamiento, en los dos destinos: el navegador no hace scroll de nada. No es cuestión de gusto. Un navegador dibuja un elemento de como mucho 16 777 214 px de alto, y las filas de Excel llegan a 29 millones, así que una cuadrícula cuyo scroll lo hiciera el navegador nunca alcanzaría su última fila. La tabla mueve sus celdas por transform en su lugar, y dibuja sus propias barras de desplazamiento.

  • Una rueda y un trackpad mueven los dos ejes. Los deltas precisos de un trackpad mueven la cuadrícula como lo hacen los dedos; un paso de la rueda del ratón se desliza hasta donde llega. Shift convierte una rueda vertical en horizontal.
  • Las barras de desplazamiento están en franjas en los bordes final e inferior. Un pulgar se arrastra, y una pulsación en la pista mueve una página hacia el puntero.
  • Un dedo que recorre más allá de un slop de 12 puntos desplaza la cuadrícula, y levantarlo mientras se mueve la lanza con inercia. Una pulsación que nunca recorre distancia sigue siendo un toque en la celda que tiene debajo.
  • Lo que la cuadrícula no puede absorber en un borde pasa al scroller que la rodea, así que una página sigue haciendo scroll cuando la cuadrícula llega a su final.
  • El teclado: la tabla es ella misma una parada del Tab, antes de los controles de sus celdas. Con el foco, las flechas mueven un paso, RePág y AvPág una página, Inicio y Fin hasta la primera y la última fila, y con Control o Command hasta la primera y la última columna también. El Tab visita los controles de las celdas fila a fila.
  • Un ratón o el puntero de un trackpad en un teléfono o una tableta pulsa las celdas, como en un escritorio; solo un dedo desplaza.
  • Con movimiento reducido, cada paso llega de una vez.

request hace scroll por ti, una vez por versión:

request: new TableScrollRequest(version) { Reveal = new TableRange(1_048_575, 0) }

Offset pone la cuadrícula en un desplazamiento; Reveal trae un rango a la vista, libre de las bandas fijas. Una solicitud se atiende cuando su Version es nueva, así que entregar la misma en cada build no hace nada dos veces. Vale hasta que la persona mueve la cuadrícula, así que un layout que se asienta en dos pasadas, una fila que mide la tabla antes y después de sus hermanos fijos, todavía la lleva adonde dice la última pasada.

onScrolled dice dónde está la cuadrícula (un TableOffset, en horizontal y en vertical) y onViewportChanged qué tamaño tiene la parte en la que las celdas hacen scroll, cada uno una vez por cambio.

Darle una ventana

Una tabla ES una ventana, así que dale una: un tamaño fijo, o SizeValue.Fill dentro de algo que tenga tamaño. Un eje Fill sin límite, que es lo que una página que hace scroll en ese eje entrega a su contenido, toma la extensión visible del scroller más cercano que la rodea, o la de la ventana, y el log de desarrollo lo avisa una vez, nombrando la extensión que tomó. Su propio contenido nunca decide su tamaño, y un ancho propio mayor que el espacio que le da su padre se limita a ese espacio, en los dos destinos.

Accesibilidad

El viewport es un grupo nombrado por label, sobre las celdas que contiene. Las celdas que no contiene no están en el árbol en absoluto, en ninguno de los dos destinos. El Tab alcanza los controles de las celdas que contiene, y un control que recibe el foco fuera de la ventana se desplaza hasta quedar a la vista, libre de las bandas fijas. Una marca nunca es una parada del Tab, y un lector de pantalla no la alcanza.

Bajo el componente

TableView es un componente sobre un nodo del vocabulario, TableViewport, que coloca los TableViewCell que recibe y lleva el modelo de scroll (ITableScrollModel, implementado por TableScroll). Un componente que decide su propia ventana, como lo hará el Spreadsheet, puede componer el nodo directamente con el mismo modelo: los realizadores le dan al modelo la rueda, el puntero y el reloj, y dibujan lo que responde.

Clone this wiki locally