fix(desktop): corrige pacing e corridas na animação de bounds da janela - #13
Merged
Conversation
- Deriva o indice do frame do tempo decorrido em vez de incrementar: um frame que estourava o orcamento deixava o contador atras do relogio e o loop rodava varias iteracoes sem dormir, transformando um frame atrasado numa rajada de SetWindowPos. - Sobe a resolucao do timer do sistema para 1ms enquanto a animacao roda (timeBeginPeriod/timeEndPeriod via guard RAII). Sleep() arredonda para o tick de ~15,6ms por padrao, o que sozinho explica periodo de frame variavel na faixa dos 40fps. - Pula frames que arredondam para os mesmos pixels. Na cauda do ease-out eles sao a maioria e cada um custa um relayout completo do WebView2. - Serializa checagem de geracao e SetWindowPos sob um mutex, para um frame em voo nunca pousar depois dos bounds finais de set_window_bounds. - Enfileira os frames com SWP_ASYNCWINDOWPOS em vez de bloquear na fila de mensagens da thread dona da HWND. set_window_bounds segue sincrono (o front depende disso) e reposta os bounds atras de frames ja enfileirados, porque mensagens enviadas passam na frente das postadas. - Faz snap no alvo antes de propagar erro no meio da animacao. - Unifica TargetBounds e WorkArea em Bounds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_1736078c-8194-45f4-996d-c4e82e37c568) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Aplica a revisão focada no loop de
run_animation(apps/desktop/src-tauri/src/lib.rs).Contexto: as correções tinham sido escritas antes do merge do #12 mas se perderam — só a feature
Win32_MedianoCargo.tomle o fix de borrow emcapture.rsentraram. HojemaindeclaraWin32_Mediasem nada que use. Este PR reaplica o resto.Bugs corrigidos
1. Contador de frame acumulava atraso e virava rajada. O deadline era absoluto, mas o índice do frame era incrementado em vez de derivado do relógio. Um frame que estourasse o orçamento (com relayout do WebView2, acontece) deixava
frameatrás do tempo:checked_duration_sinceretornavaNonevárias iterações seguidas e o loop rodava sem dormir até recuperar. Agora o índice sai deelapsed / FRAME_INTERVAL + 1— frame perdido é pulado, não recuperado.2. Resolução do timer no Windows.
thread::sleepviraSleep(), que arredonda para o tick do sistema (~15,6ms por padrão): um sleep de 16,6ms podia virar 31,2ms. Isso sozinho explica período de frame variável na faixa dos 40fps. Guard RAII comtimeBeginPeriod(1)/timeEndPeriod(1)em volta da animação.3. Frames que não mudavam nada. Na cauda do
ease_out_cubicvários frames seguidos arredondam para os mesmos pixels, e o frame emt = 0reaplicava os bounds atuais. Cada um custava um relayout completo do DOM de graça. Agora só aplica quando os bounds mudam.4. Corrida entre
set_window_boundse um frame em voo. A checagem de geração e oSetWindowPosnão eram atômicos:set_window_boundspodia entrar no meio e aplicar os bounds finais, e o frame já calculado pousava por cima. A animação reportava cancelamento corretamente, mas a janela ficava parada num estado intermediário. As duas mutações agora compartilham um mutex.5.
SetWindowPoscross-thread era síncrono. A animação roda emspawn_blocking, numa thread que não é dona da HWND, então cada frame bloqueava até o message pump processar o relayout inteiro do WebView2. Os frames agora usamSWP_ASYNCWINDOWPOS.6. Erro no meio deixava a janela no meio do caminho. Agora faz snap no alvo antes de propagar o
Err.8.
TargetBoundseWorkAreaeram o mesmo shape. Unificados emBounds— é oPartialEqdele que viabiliza o item 3.Desvio consciente da revisão (item 5)
A revisão sugeria só adicionar
SWP_ASYNCWINDOWPOSàs flags. Misturar chamadas síncronas e enfileiradas na mesma HWND não é seguro:SetWindowPossíncrono cruza a thread como mensagem enviada, e mensagens enviadas passam na frente das postadas, então uma chamada síncrona posterior ultrapassa frames já na fila. A implementação aqui:set_window_boundssegue síncrono — o front depende disso,floating-quick-menu-mode.ts:177só dispara o morph de CSS depois que a janela está no tamanho final — e faz um repost enfileirado dos mesmos bounds em seguida, que cai atrás de qualquer frame velho. Bounds idênticos não geramWM_SIZE, então o repost não custa relayout.Não incluído
set_bounds(Rect)não serve: emtauri-2.11.5/src/webview/mod.rs:1509ele é oWebview::set_bounds, que mexe na área de cliente do webview, não no frame da janela. Ficou documentado no código, junto com o apontamento de que o caminho nativo no macOS éNSAnimationContext. A implementação AppKit precisa de depsobjc2e não dá para compilar/verificar neste ambiente.SetWindowPos+ morph em CSS.animate_window_boundssobrou em dois lugares —enterFloatingMode(login → flutuante) e o passomoveFirstda expansão. Matá-los é o próximo passo natural, e aí os itens 1-6 somem junto.Instant::now() - deadline).Verificação
cargo check --liblimpo.window-animation,floating-compact-bounds,window-work-area,enter-floating-mode,BarApp).🤖 Generated with Claude Code
Note
Medium Risk
Altera comportamento nativo de janela e threading Win32 (
SetWindowPossíncrono vs enfileirado); impacto visível em transições, mas escopo concentrado emlib.rscom contrato explícito para o front.Overview
Reaplica e endurece a animação nativa de resize/move da janela principal no Tauri (
lib.rs), com bump de versão do crate desktop para 0.1.4.Pacing e custo por frame: o índice de frame passa a ser derivado do tempo decorrido (frames atrasados são pulados em vez de virarem rajada de
SetWindowPos). No Windows, um guard RAII comtimeBeginPeriod(1)estabilizathread::sleep. Frames só disparam resize quando os bounds interpolados mudam de fato (Boundsunificado comPartialEq, no lugar deTargetBounds/WorkArea).Concorrência e Win32: mutações de bounds compartilham
WINDOW_MUTATION; geração da animação e aplicação de frame ficam atômicas. Frames usamSWP_ASYNCWINDOWPOS;set_window_boundscontinua bloqueante (contrato do front) e reposta os mesmos bounds na fila para não perder para frames antigos. Erro no meio da animação faz snap no destino antes de propagar o erro.Reviewed by Cursor Bugbot for commit 8145917. Configure here.