Title-Bar Drag and Double-Click Gestures

mandatory

Como apps desktop Koder devem tratar arraste e duplo-clique na barra de título. Define o contrato cross-platform (Linux/Windows/macOS), proíbe o "gesture-arena hijack" do duplo-clique nos botões, e pinta os widgets canônicos do `engines/sdk/koder_kit` (`KoderTitleBar` + `KoderTitleBarFreeArea`).

Spec — TitleBar Drag and DoubleClick Gestures

Applicability

Toda variante desktop (Linux / Windows / macOS) de qualquer produto Koder que renderiza sua própria barra de título — kruze, kterm, hub desktop, kortex, mosaic, talk, drive, eye, koru desktop, sky, e adjacentes. Não se aplica a variantes mobile, TV, web ou CLI.

Required Behavior

A barra de título de uma janela Koder DEVE responder a:

  1. *rraste (pan)*em qualquer ponto da barra que não seja

    capturado por um widget interativo (botão, dropdown, controle de janela, célula de tab) — move a janela.

  2. *uplo-clique*em uma *rea livre*explicitamente declarada

    (espaço vazio decorativo, texto de título, gap entre tab strip e controles) — alterna entre maximizado e restaurado.

  3. *uplo-clique em um widget interativo*(botão fechar X da

    aba, botão + nova aba, dropdown , controle de janela minimizemaximizeclose, qualquer botão custom) NÃO dispara o toggle de maximize. O widget interativo trata o duplo-clique conforme sua semântica própria (geralmente: dispara onTap, ou um onDoubleTap distinto declarado pelo autor).

Implementation

A implementação canônica está em engines/sdk/koder_kit:

  • *KoderTitleBar({child, height, color})`*— wrapper externo da

    barra de título inteira. Declara apenas o gesto de arraste (onPanStart: (_) => windowManager.startDragging()). Não declara onDoubleTap — o gesto de toggle vive em KoderTitleBarFreeArea.

  • *KoderTitleBarFreeArea({child})`*— wrapper que marca uma

    região como área livre. Declara onDoubleTap e dispara windowManager.maximize() / unmaximize() conforme estado atual. Use behavior: HitTestBehavior.opaque. Posicione em Expanded(SizedBox.expand()) para o gap entre tab strip e controles, ou em volta de texto de título decorativo.

Layout canônico (kruze, kterm, hub):

KoderTitleBar(
  child: Row(
    children: [
      // Tab strip + botões internos (sized to content).
      Flexible(child: ReorderableListView.builder(...)),
      // Área livre — duplo-clique aqui alterna maximize/restore.
      Expanded(
        child: KoderTitleBarFreeArea(child: SizedBox.expand()),
      ),
      // Lado direito — dropdowns + window controls.
      DropdownAllTabs(),
      WindowControl(min),
      WindowControl(max),
      WindowControl(close),
    ],
  ),
)

Forbidden Patterns

❌ *ão*envolver a barra inteira em um GestureDetector com onDoubleTap + onPanStart:

// ANTI-PATTERN — Flutter gesture arena entrega o duplo-clique pro
// detector externo mesmo quando o usuário clica num botão filho.
// Usuário double-clica em "X" → janela maximiza em vez de fechar tab.
GestureDetector(
  onDoubleTap: _toggleMaximize,
  onPanStart: (_) => windowManager.startDragging(),
  child: TitleBarRow(...),
)

❌ *ão*declarar onDoubleTap no KoderTitleBar e em widgets filhos sem coordenar — gesture arena hijack reaparece.

❌ *ão*usar Caddy DragToMoveArea ou outras alternativas de window_manager que envolvem toda a área — perdem o split free-area / interactive.

Validation

Esta spec é enforcement-checked via testes estruturais:

  • engines/sdk/koder_kit/tests/regression/<NNN>-title-bar-widgets.test.sh

    — verifica que KoderTitleBar e KoderTitleBarFreeArea existem, que KoderTitleBar NÃO declara onDoubleTap, e que KoderTitleBarFreeArea chama windowManager.maximize/unmaximize.

  • Per-product structural tests no tests/regression/ de cada

    módulo desktop, verificando que o pattern legacy (GestureDetector(onDoubleTap:...) em volta da barra) não voltou.

Casos históricos consertados (registry regression-test-cases.md):

  • kterm v1.3.2 — close button da aba e botão close do Preferences

    dialog (cases #438, #439).

  • kruze 1.0.14 — botão X close de aba e botão + nova aba

    (caso #459 e seguintes; este commit).

  • specs/desktop-apps/title-bar.kmd — formato do nome do produto na

    barra de título (texto, não gesto).

  • specs/koder-app/behaviors.kmd — comportamentos cross-cutting de

    apps Koder (auth, telemetria, update etc.).

  • policies/reuse-first.kmd — sempre usar koder_kit em vez de

    reimplementar gestos por app.

Source: ../home/koder/dev/koder/meta/docs/stack/specs/desktop-apps/title-bar-double-click.kmd