Comment doit se dérouler la «suppression de la frappe»?

12

J'implémente une application Java qui inclut une pile Annuler / Rétablir. J'ai remarqué que certaines applications (telles que TextEdit sur Mac OS X) vous permettent de choisir "Annuler la saisie" dans le menu Edition après avoir tapé du texte. J'aimerais également implémenter ce genre de chose dans mon application, mais j'ai beaucoup de mal à trouver des directives sur la façon dont elle devrait se comporter.

Avec quelques essais et erreurs, ma meilleure estimation du comportement de TextEdit Undo Typing est:

  • Lorsque l'utilisateur tape un nouveau caractère (ou tape la clé de suppression), fusionnez-le dans l'élément de frappe d'annulation précédent si l'un se trouve en haut de la pile d'annulation, sauf si l'une des situations suivantes se produit
  • Créez toujours un nouvel élément Annuler la frappe après que l'utilisateur continue de taper après au moins 15 secondes d'inactivité
  • Créez toujours un nouvel élément Annuler la frappe après que l'utilisateur a tapé pendant une longue période et qu'une condition est remplie (impossible de déterminer si cela était basé sur le temps ou sur le nombre de caractères).
  • Créez toujours un nouvel élément Annuler la frappe lorsqu'un texte est sélectionné, puis supprimé ou écrasé (sélectionner du texte, ne pas apporter de modification, puis revenir au point d'insertion d'origine et continuer à taper ne déclenche pas cela)

Dans la pratique, la stratégie d'Apple semble fonctionner (au moins, elle fonctionne pour moi lorsque je tape), mais comme indiqué par le dernier point, je n'ai pas vraiment pu comprendre les règles. En outre, il semble que d'autres programmes suivent des règles différentes, telles que Microsoft Word. Google n'a pas fourni de liste définie de règles pour toute implémentation de la fonction d'annulation de la frappe et je n'ai trouvé aucune meilleure pratique sur la façon dont il devrait se comporter. Alors, comment devrait-il se comporter? Ou est-ce juste aux caprices du programmeur?

EDIT: Juste pour clarifier, je ne suis pas intéressé par les détails de mise en œuvre pour le moment. Je suis particulièrement curieux de savoir s'il existe ou non une référence faisant autorité (par exemple, les meilleures pratiques ou un document d'interface utilisateur) décrivant cela ou une description de la façon dont il est mis en œuvre sur plusieurs produits.

Thunderforge
la source
Ma suggestion: compresser les modifications au point où il est toujours possible de reconstruire la séquence exacte des touches enfoncées à partir des informations d'annulation, et pas plus. Par exemple, cela signifie que si l'utilisateur tape quelque chose et utilise immédiatement un retour arrière pour le supprimer, il devrait y avoir un point d'annulation entre les deux.
Ambroz Bizjak
Peut-être pourriez-vous également ajouter un nouvel "élément d'annulation de frappe" à chaque fois que l'utilisateur crée une nouvelle ligne et peut-être à chaque fois que la barre d'espace est utilisée immédiatement après la saisie des caractères. OMI 15 secondes de temps d'attente avant un nouvel «élément d'annulation de frappe» pourrait être un peu long, mais ce n'est que moi. (
J'irais
Ou peut-être que "la séquence exacte des touches enfoncées" devrait être assouplie à "l'état du texte après chaque modification". L'idée est d'empêcher tout texte de se perdre à cause de la compression.
Ambroz Bizjak
Il me semble que TextEdit fusionne les espaces et les supprime avec le dernier élément Annuler la frappe, à condition que les autres conditions ne soient pas remplies. Donc 124<delete>3, l'annuler et le refaire entraîne 123. Je suppose que l'avantage de ceci est qu'il en résulte l'état final du texte de l'utilisateur, un peu comme la suggestion ci-dessus.
Thunderforge
Avez-vous déjà essayé de rechercher des brevets? (Les règles sont généralement encodées dans la couche bibliothèque, pas exposées au code utilisateur.)
Donal Fellows

Réponses:

5

Si vous recherchez une source faisant autorité, je pense que le meilleur matériel lié à Mac se trouve dans le document Annuler l'architecture d'Apple.

Je ne pense pas que vous allez trouver une liste de règles sur le moment où vous devez ou non fusionner les événements d'annulation, cependant. Ce qui convient à une application n'aura pas nécessairement de sens pour une autre. Par exemple, la coalescence des frappes est logique dans un éditeur de texte car l'utilisateur verra probablement la saisie d'un paragraphe comme une action unique et non comme 539 actions distinctes, et aussi parce que vous ne voulez pas que l'utilisateur doive annuler 539 fois juste pour obtenir au point où ils en étaient avant de taper ce paragraphe. Mais qu'en est-il des opérations de déplacement sur une forme dans un programme de dessin? Ou des ajustements séquentiels à une couleur de remplissage? Vous pourriez faire un bon argument en faveur de leur fusion ou non, selon la nature de votre programme.

Créez toujours un nouvel élément Annuler la frappe après que l'utilisateur a tapé pendant une longue période et qu'une condition est remplie (impossible de déterminer si cela était basé sur le temps ou sur le nombre de caractères).

Il est basé sur l'enregistrement automatique. Heureusement pour vous, le code source de TextEdit est disponible et bien commenté. Je pense que si vous y jetez un œil, vous aurez une meilleure idée de ce qui se passe et pourquoi. Par exemple:

- (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)];
 ...

Je sais que vous avez dit que vous n'êtes pas encore intéressé par les détails de la mise en œuvre, mais en regardant la façon dont Apple a implémenté TextEdit peut éclairer les décisions que vous prenez pour votre propre application.

Caleb
la source
1
Je pense que je vais déclarer cela la meilleure réponse. Je vous remercie d'avoir donné le lien vers l'implémentation d'Apple ainsi que du code pour l'exemple spécifique que j'ai fourni. Je pense que vous avez raison cependant, il est généralement basé sur les besoins de l'application et personne n'a vraiment fait d'effort pour normaliser ces besoins et comment y répondre.
Thunderforge
1

au keydown -> minuterie représentant vos démarrages inactifs

sur keydown / timer-running -> reset timer

sur keydown / pas de temporisation -> réajuster les blocs de cellules pour se préparer à un nouvel état préservé lorsque la position change

la minuterie d'inactivité est épuisée -> Établir un nouvel état d'annulation

Je ne suivrais pas les identités des touches. Je me diviserais en blocs cellulaires de texte (par nombre de caractères) qui vous permettent de suivre la position par décalages à partir des positions de départ de la cellule la plus proche afin que vous n'ayez pas à enregistrer l'intégralité de l'état d'un roman tolstoy à chaque fois qu'une minuterie inactive s'épuise . Réajuster ces décalages lorsque les cellules avant que d'autres cellules ne soient modifiées est la partie délicate.

Erik Reppen
la source