Title-Bar Drag and Double-Click Gestures
desktop-apps specs/desktop-apps/title-bar-double-click.kmd
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`).
When this spec applies
All triggers
- Implementar barra de título customizada em app desktop Koder
- Adicionar duplo-clique pra maximizar/restaurar janela
- Adicionar comportamento de arraste no título
- Detectar / corrigir bug onde duplo-clique em botão da barra de título dispara comportamento da janela
Specification body
Spec — Title-Bar Drag and Double-Click 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:
- Arraste (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.
- Duplo-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.
- Duplo-clique em um widget interativo (botão fechar
Xda aba, botão+nova aba, dropdown▼, controle de janela minimize/maximize/close, 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. DeclaraonDoubleTape 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
❌ Nã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(...),
)
❌ Não declarar onDoubleTap no KoderTitleBar e em widgets
filhos sem coordenar — gesture arena hijack reaparece.
❌ Nã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 queKoderTitleBareKoderTitleBarFreeAreaexistem, queKoderTitleBarNÃO declaraonDoubleTap, e queKoderTitleBarFreeAreachamawindowManager.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
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 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 usarkoder_kitem vez de reimplementar gestos por app.
References
specs/desktop-apps/title-bar.kmdspecs/koder-app/behaviors.kmdpolicies/reuse-first.kmd