Skip to content

TableView pt BR

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

Visualização de tabela

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

TableView é uma janela para uma grade maior que qualquer tela, nos DOIS eixos, como o TableView do Flutter: você descreve até onde cada linha e cada coluna alcança, entrega a ele um construtor que responde a célula numa linha e numa coluna, e diz quantas das primeiras linhas e colunas ficam fixadas. Ele constrói só as células à vista mais uma margem ao redor, então a planilha inteira do Excel, 1 048 576 linhas por 16 384 colunas, custa o que uma tela cheia custa. É write-once: o mesmo C# roda como pintura de GPU no Photon e como DOM no 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")

A tela /table do sample do dashboard é esta grade, com cada célula mostrando o próprio endereço, uma linha de cabeçalho e uma coluna de números de linha fixadas.

Linhas e colunas

Cada eixo é um TableSpans: quantas linhas (ou colunas) há, até onde elas alcançam no total, e as duas perguntas que o viewport faz, onde o índice i começa (OffsetOf) e qual índice está num deslocamento (IndexAt).

  • UniformTableSpans(count, extent): toda linha (ou coluna) alcança a mesma distância. Todo deslocamento é uma multiplicação.
  • SparseTableSpans(count, extent, extents): uma extensão padrão, e as poucas que têm a sua própria, como numa planilha em que alguém alargou a coluna D. As exceções ficam ordenadas com uma soma acumulada, então um deslocamento custa uma busca binária sobre elas, por mais longo que seja o eixo.
  • O seu próprio: derive de TableSpans e responda Count, Total, ExtentOf, OffsetOf e IndexAt.

Os deslocamentos são double nos dois alvos: um milhão de linhas de 28 chegam a 29 360 128, além de onde um float ainda conta ponto a ponto.

A janela que ele constrói

A tabela constrói as linhas e colunas que o viewport mostra mais overscan (3 por padrão) além de cada borda. A janela tem histerese: uma rolagem que mantém o que está visível dentro das células já construídas não constrói nada, e o viewport move essas células por conta própria. A tabela só constrói de novo quando as linhas ou colunas visíveis saem delas.

Uma célula tem como chave a sua posição ("row:column"), então uma célula que continua na janela mantém o elemento, e o estado, quando a janela se move.

O primeiro frame é construído para uma janela comum (1 024 × 768) em vez de para nada, então já mostra células; o viewport então reporta o tamanho real dele e o frame seguinte constrói para esse tamanho.

Linhas e colunas fixadas

pinnedRows e pinnedColumns mantêm as primeiras linhas no topo e as primeiras colunas no início enquanto o resto rola. O viewport desenha quatro faixas, cada uma sob o próprio corte: o canto, que nunca se move; as linhas fixadas, que se movem para o lado junto com a grade; as colunas fixadas, que descem junto com ela; e o corpo, que se move nos dois sentidos. Uma célula é desenhada na faixa da sua primeira linha e da sua primeira coluna.

Marcas

As marks são desenhadas POR CIMA das células, sobre o intervalo que cada uma cobre, e não recebem toque nem foco: uma seleção, um contorno, a faixa de uma busca. Cada uma é um TableViewCell feito com mark: true, e seus rows e columns dizem quantas linhas e colunas ela cobre:

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

Uma marca que cruza a borda de uma faixa fixada é cortada ali, um pedaço por faixa, então aparece em cada faixa que alcança e se esconde onde passa por baixo de uma.

Rolagem

O viewport É DONO do próprio deslocamento, nos dois alvos: o navegador não rola nada. Isso não é questão de gosto. Um navegador desenha um elemento com no máximo 16 777 214 px de altura, e as linhas do Excel chegam a 29 milhões, então uma grade que o navegador rolasse nunca alcançaria a última linha. A tabela move as células por transform no lugar disso, e desenha as próprias barras de rolagem.

  • Uma roda e um trackpad movem os dois eixos. Os deltas precisos de um trackpad movem a grade como os dedos movem; um passo da roda do mouse desliza até onde chega. Shift vira uma roda vertical para o outro eixo.
  • As barras de rolagem ficam em faixas nas bordas final e inferior. Um polegar arrasta, e um toque na trilha move uma página em direção ao ponteiro.
  • Um dedo que percorre mais que um slop de 12 pontos arrasta a grade, e soltar o dedo enquanto ela se move a lança com inércia. Um toque que nunca percorre distância continua sendo um toque na célula sob ele.
  • O que a grade não consegue absorver numa borda passa para o rolador ao redor, então uma página continua rolando quando a grade chega ao fim.
  • O teclado: a tabela é ela mesma uma parada do Tab, antes dos controles das suas células. Com o foco, as setas movem um passo, PageUp e PageDown uma página, Home e End até a primeira e a última linha, e com Control ou Command até a primeira e a última coluna também. O Tab visita os controles das células linha a linha.
  • Um mouse ou o ponteiro de um trackpad num celular ou num tablet pressiona as células, como num desktop; só um dedo arrasta.
  • Com movimento reduzido, cada passo chega de uma vez.

request rola em seu nome, uma vez por versão:

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

Offset põe a grade num deslocamento; Reveal traz um intervalo à vista, livre das faixas fixadas. Uma requisição é atendida quando a sua Version é nova, então entregar a mesma a cada build não faz nada duas vezes. Ela vale até a pessoa mover a grade, então um layout que se assenta em duas passadas, uma linha que mede a tabela antes e depois dos seus irmãos fixos, ainda a leva aonde a última passada diz.

onScrolled diz onde a grade está (um TableOffset, na horizontal e na vertical) e onViewportChanged qual o tamanho da parte em que as células rolam, cada um uma vez por mudança.

Dando uma janela a ele

Uma tabela É uma janela, então dê uma a ela: um tamanho fixo, ou SizeValue.Fill dentro de algo que tenha tamanho. Um eixo Fill sem limite, que é o que uma página que rola naquele eixo entrega ao seu conteúdo, assume a extensão visível do rolador mais próximo ao redor, ou a da janela, e o log de desenvolvimento avisa isso uma vez, nomeando a extensão que assumiu. O conteúdo dela nunca decide o tamanho dela, e uma largura própria maior que o espaço que o pai dá é limitada a esse espaço, nos dois alvos.

Acessibilidade

O viewport é um grupo nomeado por label, sobre as células que ele mantém. As células que ele não mantém não estão na árvore de jeito nenhum, em nenhum dos alvos. O Tab alcança os controles das células que ele mantém, e um controle que recebe o foco fora da janela é rolado até ficar à vista, livre das faixas fixadas. Uma marca nunca é parada do Tab, e um leitor de tela não a alcança.

Por baixo do componente

TableView é um componente sobre um nó do vocabulário, TableViewport, que posiciona os TableViewCells que recebe e carrega o modelo de rolagem (ITableScrollModel, implementado por TableScroll). Um componente que decide a própria janela, como o Spreadsheet vai fazer, pode compor o nó diretamente com o mesmo modelo: os realizadores alimentam o modelo com a roda, o ponteiro e o relógio, e desenham o que ele responde.

Clone this wiki locally