Como o comportamento "Desfazer digitação" deve se comportar?

12

Estou implementando um aplicativo Java que inclui uma pilha de Desfazer / Refazer. Percebi que alguns aplicativos (como o TextEdit no Mac OS X) permitem escolher "Desfazer digitação" no menu Editar depois de digitar algum texto. Gostaria de implementar esse tipo de coisa também no meu aplicativo, mas estou tendo muita dificuldade em encontrar diretrizes sobre como ele deve se comportar.

Com algumas tentativas e erros, meu melhor palpite sobre como se comporta o Undo Typing do TextEdit é:

  • Quando o usuário digitar um novo caractere (ou digitar a chave de exclusão), mescle-o no item de Anulação de digitação anterior, se houver um na parte superior da pilha de Anulação, a menos que ocorra uma das seguintes situações
  • Sempre crie um novo item de Desfazer digitação depois que o usuário continuar digitando após pelo menos 15 segundos de inatividade
  • Sempre crie um novo item de Desfazer digitação depois que o usuário estiver digitando por um longo período de tempo e alguma condição for atendida (não foi possível descobrir se isso foi baseado no tempo ou na contagem de caracteres).
  • Sempre crie um novo item de Desfazer digitação quando qualquer texto for selecionado e, em seguida, excluído ou sobrescrito (selecionar texto, não fazer alterações, retornar ao ponto de inserção original e continuar digitando não dispara isso)

Na prática, a estratégia da Apple parece funcionar (pelo menos funciona para mim quando digito), mas, como observado no último ponto, não consegui realmente descobrir as regras. Além disso, parece que outros programas seguem regras diferentes, como o Microsoft Word. O Google não criou uma lista definida de regras para nenhuma implementação de Undo Typing e não encontrei nenhuma prática recomendada sobre como ele deve se comportar. Então, como deve se comportar? Ou depende apenas dos caprichos do programador?

EDIT: Apenas para esclarecer, não estou interessado nos detalhes da implementação no momento. Estou especialmente curioso para saber se existe ou não uma referência autorizada (por exemplo, práticas recomendadas ou documento de interface do usuário) descrevendo isso ou uma descrição de como ela é implementada em vários produtos.

Forja do Trovão
fonte
Minha sugestão: comprima as modificações até o ponto em que ainda é possível reconstruir a seqüência exata de teclas pressionadas a partir da informação de desfazer, e não mais. Por exemplo, isso significa que, se o usuário digitar algo e usar imediatamente o backspace para excluí-lo, deverá haver um ponto de desfazer no meio.
Ambroz Bizjak
Talvez você também possa adicionar um novo "item de digitação de desfazer" toda vez que o usuário criar uma nova linha e talvez sempre que a barra de espaço for usada imediatamente após a entrada dos caracteres. O tempo de espera da OMI 15 segundos antes de um novo "item de digitação de desfazer" pode demorar um pouco, mas sou apenas eu. (Eu iria para cerca de 5 segundos),
user82529
Ou talvez "a sequência exata de teclas pressionadas" deva ser relaxada para "o estado do texto após cada modificação". A idéia é impedir que qualquer texto seja perdido devido à compactação.
Ambroz Bizjak
Parece-me que o TextEdit mescla espaços e exclui o último item de Undo Typing, desde que as outras condições não sejam atendidas. Então 124<delete>3, desfazer e refazê-lo resulta em 123. Eu acho que a vantagem disso é que resulta no estado final do texto do usuário, um pouco como a sugestão acima.
Thunderforge
Você já tentou pesquisar patentes? (As regras são geralmente codificada na camada de biblioteca, não exposto a código de utilizador.)
Donal Fellows

Respostas:

5

Se você está procurando uma fonte autorizada, acho que o melhor material relacionado ao Mac será encontrado no documento Undo Architecture da Apple.

Não acho que você encontre uma lista de regras sobre quando deve ou não se unir para desfazer eventos. O que parece certo para um aplicativo não fará necessariamente sentido para outro. Por exemplo, combinações de teclas combinadas fazem sentido em um editor de texto porque o usuário provavelmente verá a digitação de um parágrafo como uma ação única e não como 539 ações separadas, e também porque você não deseja que o usuário precise desfazer 539 vezes apenas para obter ao ponto em que estavam antes de digitarem esse parágrafo. Mas e as operações de movimentação em uma forma em um programa de desenho? Ou ajustes seqüenciais em uma cor de preenchimento? Você pode argumentar que esses itens são coalescentes ou não, dependendo da natureza do seu programa.

Sempre crie um novo item de Desfazer digitação depois que o usuário estiver digitando por um longo período de tempo e alguma condição for atendida (não foi possível descobrir se isso foi baseado no tempo ou na contagem de caracteres).

É baseado no salvamento automático. Para sua sorte, o código fonte do TextEdit está disponível e é bem comentado. Acho que, se você der uma olhada, terá uma idéia melhor do que está acontecendo e por quê. Por exemplo:

- (void)saveToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName forSaveOperation:(NSSaveOperationType)saveOperation completionHandler:(void (^)(NSError *error))handler {
    // Note that we do the breakUndoCoalescing call even during autosave, which 
    // means the user's undo of long typing will take them back to the last spot an 
    // autosave occured. This might seem confusing, and a more elaborate solution may 
    // be possible (cause an autosave without having to breakUndoCoalescing), but since 
    // this change is coming late in Leopard, we decided to go with the lower risk fix.
    [[self windowControllers] makeObjectsPerformSelector:@selector(breakUndoCoalescing)];
 ...

Sei que você disse que ainda não está interessado nos detalhes da implementação, mas observar como a Apple implementou o TextEdit pode informar as decisões que você toma para seu próprio aplicativo.

Caleb
fonte
1
Eu acho que vou declarar essa a melhor resposta. Agradeço por fornecer o link para a implementação da Apple, bem como algum código para o exemplo específico que forneci. Eu acho que você está certo, porém, geralmente é baseado nas necessidades do aplicativo e ninguém realmente fez um esforço para padronizar quais são essas necessidades e como abordá-las.
Thunderforge
1

no pressionamento de tecla -> o temporizador que representa seu início ocioso

em keydown / timer-running -> redefinir temporizador

no pressionamento de tecla / sem execução do timer -> reajuste os blocos de células para se preparar para um novo estado preservado conforme a posição muda

temporizador ocioso é executado -> Estabelece um novo estado de desfazer

Eu não rastrearia as identidades das teclas pressionadas. Eu dividia em blocos de texto celulares (por contagem de caracteres) que permitem rastrear a posição com deslocamentos das posições iniciais da célula mais próxima, para que você não precise salvar todo o estado de um romance de Tolstói toda vez que um timer ocioso se esgotar . Reajustar esses deslocamentos quando as células antes de outras células serem editadas é a parte complicada.

Erik Reppen
fonte