Title-Bar Drag and Double-Click Gestures
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:
- *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.
- *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.
- *uplo-clique em um widget interativo*(botão fechar
Xdaaba, 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: disparaonTap, ou umonDoubleTapdistinto 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 declaraonDoubleTap— o gesto de toggle vive emKoderTitleBarFreeArea.
- *KoderTitleBarFreeArea({child})`*— wrapper que marca uma
região como área livre. Declara
onDoubleTape disparawindowManager.maximize() / unmaximize()conforme estado atual. Usebehavior: HitTestBehavior.opaque. Posicione emExpanded(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
KoderTitleBareKoderTitleBarFreeAreaexistem, queKoderTitleBarNÃO declaraonDoubleTap, e queKoderTitleBarFreeAreachamawindowManager.maximize/unmaximize.- Per-product structural tests no
tests/regression/de cadamó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
Xclose de aba e botão+nova aba(caso #459 e seguintes; este commit).
Related
specs/desktop-apps/title-bar.kmd— formato do nome do produto nabarra de título (texto, não gesto).
specs/koder-app/behaviors.kmd— comportamentos cross-cutting deapps Koder (auth, telemetria, update etc.).
policies/reuse-first.kmd— sempre usarkoder_kitem vez dereimplementar gestos por app.