Annulation NSBEzierPath

RadadaRadada Membre
21:34 modifié dans API AppKit #1
Salut :)

comme dis sur un précédent post, je suis en train de réaliser un exercice du bouquin de Aaron Hilleglass. Il faut faire une toute petite appli qui permet de dessiner des ovales (avec une NSBezierPath).
Pour les courageux (moi donc ^^), il faut ajouter la sauvegarde et l'annulation.

Pour la sauvegarde, j'ai à  peu près réussi à  m'en sortir.

Par contre c'est une autre paire de manche pour l'annulation. J'ai décidé (peut être à  tort) d'utiliser un seul objet NSBezierPath et de faire des appendBezierPathWithOvalInRect et de faire ré-afficher le path quand besoin.

Du coup, je n'arrive pas à  voir comment implémenter l'annulation. Il n'y a pas de conception KVC, donc pas possible de réailser un addObserver (enfin, je pense) et comme il n'y a pas de moyen d'enlever (enfin, à  ma connaissance) un seul des ovales, je ne peux pas utiliser le NSUndoManager prepareInvocationTarget.

D'où ma question : peut-on réaliser un mécanisme d'annulation avec de type d'objet et comment?
Sinon, j'avais pensé à  une autre solution : plutôt qu'utiliser un appendBezierPathWithOvalInRect, je pourrais gérer un NSMutableArray de path et gérer un NSBezierPath par ovale dessiné. Du coup, je devrais pouvoir gérer l'ajout de NSBezierPath dans le vecteur et l'annulation se ferait par suppression du(des) dernier(s) path dans le NSMutableArray. Lors de l'affichage, il me suffirait de boucler sur les objets du NSMutableArray et de les afficher tour à  tour. Mais cette méthode, si elle me semble correcte pour l'annulation, ne me parait pas terrible tant en conception qu'en perf non?

Pour voir le code --> voir la PJ. Ou sinon, voici la gestion des NZBezierPath dans mon NSView :

<br />#pragma mark INITIALISATIONS<br />- (id)initWithFrame:(NSRect)frame {<br />&nbsp; &nbsp; self = [super initWithFrame:frame];<br />&nbsp; &nbsp; if (self) {<br />&nbsp; &nbsp; &nbsp; &nbsp; // Initialization code here.<br />&nbsp; &nbsp; &nbsp; &nbsp; path = [[NSBezierPath alloc]init]; <br />&nbsp; &nbsp; &nbsp; &nbsp; color = [NSColor blackColor];<br />&nbsp; &nbsp; &nbsp; &nbsp; currentPoint = NSZeroPoint;<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return self;<br />}<br /><br />#pragma mark DESSINS<br />- (void)drawRect:(NSRect)rect {<br />&nbsp; &nbsp; // Drawing code here.<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; NSRect bounds = [self bounds];<br />&nbsp; &nbsp; [[NSColor whiteColor] set];<br />&nbsp; &nbsp; [NSBezierPath fillRect:bounds];<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; //[self addObserver:path forKeyPath:@&quot;path&quot; options:NSKeyValueObservingOptionOld context:nil];<br />&nbsp; &nbsp; if (currentPoint.x &gt; 0 &amp;&amp; currentPoint.y &gt; 0)<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; [path appendBezierPathWithOvalInRect:[self currentRect]];<br />&nbsp; &nbsp; &nbsp; &nbsp; [color set];<br />&nbsp; &nbsp; &nbsp; &nbsp; [path fill];<br />&nbsp; &nbsp; }<br />}<br /><br />-(NSRect)currentRect<br />{<br />&nbsp; &nbsp; float minX = MIN(originPoint.x, currentPoint.x);<br />&nbsp; &nbsp; float maxX = MAX(originPoint.x, currentPoint.x);<br />&nbsp; &nbsp; float minY = MIN(originPoint.y, currentPoint.y);<br />&nbsp; &nbsp; float maxY = MAX(originPoint.y, currentPoint.y);<br />&nbsp; &nbsp; return NSMakeRect(minX, minY, maxX-minX, maxY-minY);<br />}<br /><br />


Merci :p

Réponses

  • Philippe49Philippe49 Membre
    21:34 modifié #2
    dans 1250622444:

    Sinon, j'avais pensé à  une autre solution : plutôt qu'utiliser un appendBezierPathWithOvalInRect, je pourrais gérer un NSMutableArray de path et gérer un NSBezierPath par ovale dessiné. Du coup, je devrais pouvoir gérer l'ajout de NSBezierPath dans le vecteur et l'annulation se ferait par suppression du(des) dernier(s) path dans le NSMutableArray. Lors de l'affichage, il me suffirait de boucler sur les objets du NSMutableArray et de les afficher tour à  tour. Mais cette méthode, si elle me semble correcte pour l'annulation, ne me parait pas terrible tant en conception qu'en perf non?

    C'est en effet une première idée. Que faire d'autre ? Faire une NSView par ovale et faire des addSubview, c'est nettement moins économique !
  • RadadaRadada Membre
    21:34 modifié #3
    dans 1250627329:

    C'est en effet une première idée. Que faire d'autre ? Faire une NSView par ovale et faire des addSubview, c'est nettement moins économique !


    Oki doki, merci. Donc selon toi, il n'y a pas d'autre solution plus économique?
    Merci Philippe  :p
  • CéroceCéroce Membre, Modérateur
    21:34 modifié #4
    Stocker tous les ovales dans un NSMutableArray est la manière classique de procéder pour ce genre de choses, ne serait-ce que parce que ça permet de changer le plan sur lequel se trouvent les figures (envoyer au fond, ou envoyer au premier plan).

    Pour enregistrer un état auprès de l'undo manager, tu utilises typiquement sa méthode -registerUndoWithTarget:selector:object:.
    Cette méthode va envoyer un [retain] à  object.

    Tes deux méthodes sont possibles (un seul, ou un tableau de Bezier paths), mais selon le cas, object sera une référence sur le NSBezierPath ajouté, ou sur une copie (parce que si tu n'as qu'un seul NSBezierPath, ajouter un oval modifie le bezier path, mais pas la référence).

    Relis le chapitre du livre sur l'Undo, c'est la meilleure explication qu'il m'ait été donné de lire. Il faut juste comprendre que chaque méthode de modification, doit avoir une méthode d'annulation totalement réciproque pour que l'annulation et le rétablissement fonctionnent tous les deux.

    Bon, je sais pas si je suis très clair.
  • RadadaRadada Membre
    21:34 modifié #5
    dans 1250663307:

    Stocker tous les ovales dans un NSMutableArray est la manière classique de procéder pour ce genre de choses, ne serait-ce que parce que ça permet de changer le plan sur lequel se trouvent les figures (envoyer au fond, ou envoyer au premier plan).

    Pour enregistrer un état auprès de l'undo manager, tu utilises typiquement sa méthode -registerUndoWithTarget:selector:object:.
    Cette méthode va envoyer un [retain] à  object.

    Tes deux méthodes sont possibles (un seul, ou un tableau de Bezier paths), mais selon le cas, object sera une référence sur le NSBezierPath ajouté, ou sur une copie (parce que si tu n'as qu'un seul NSBezierPath, ajouter un oval modifie le bezier path, mais pas la référence).

    Relis le chapitre du livre sur l'Undo, c'est la meilleure explication qu'il m'ait été donné de lire. Il faut juste comprendre que chaque méthode de modification, doit avoir une méthode d'annulation totalement réciproque pour que l'annulation et le rétablissement fonctionnent tous les deux.

    Bon, je sais pas si je suis très clair.


    Salut Ceroce et merci de ta réponse.

    Justement mon problème était que je ne voyais pas comment faire une méthode réciproque de l'ajout, d'où l'idée du NSMutableArray. Je ne trouvais pas ça très propre (en tout cas en comparaison de ce que j'ai l'habitude de faire en c++), mais ça me rassure de savoir que c'est la bonne méthode. Je vais m'y mettre cet AM :)
    Merci à  tous en tout cas...
  • AliGatorAliGator Membre, Modérateur
    21:34 modifié #6
    Eh oui, c'est la bonne méthode car la seule qui te permette de séparer chacun de tes ovales pour justement donc annuler le dernier par exemple.
    Mais ça a aussi l'avantage comme le note Céroce de rendre tes ovales individuels : modifier leur ordre de dessin (premier-plan/arrière-plan), modifier leur couleur de remplissage ou de trait (car si tu dessines tout en tant qu'un seul BezierPath, tu ne peux pas mettre des couleurs différentes puisque c'est un BezierPath unique), ...
Connectez-vous ou Inscrivez-vous pour répondre.