Editeur de format d'édition
Bonjour à tous,
Je vous décrit rapidement le projet.
Mon application, permet de générer un fichier XML (UTF-8) contenant les détails d'une mise en page d'impression. Je m'explique, grâce à un moteur d'édition, le fichier XML qui va bien et un tableau de valeur, l'impression ce réaliser et se met en page.
Le fichier XML contient, les coordonnées des lignes, cadre, emplacement de valeur etc...
Je cherche à faire en sorte que lors de l'édition ou la création d'un fichier de format, il soit possible de visualiser le résultat. En clair, quel objet faut t'il utiliser pour arriver à faire une image de preview et pouvoir dessiner les informations saisie par l'utilisateur (trait, rectangle, rond, texte, etc...) (si l'image pouvait être enregistrable cela serai super).
Je sais pas si j'ai été clair. Le but avoué de ce projet est de me facilité la tâche au travail. J'ai déjà un programme qui fait à peut de chose près la même chose sauf qu'il est prévu pour générer un code REALBasic et il est codé avec REALbasic (j'ai fait tellement vite que c'est irrécupérable. Sauf le principe
).
Je vous remercie d'avance de votre aide
Je vous décrit rapidement le projet.
Mon application, permet de générer un fichier XML (UTF-8) contenant les détails d'une mise en page d'impression. Je m'explique, grâce à un moteur d'édition, le fichier XML qui va bien et un tableau de valeur, l'impression ce réaliser et se met en page.
Le fichier XML contient, les coordonnées des lignes, cadre, emplacement de valeur etc...
Je cherche à faire en sorte que lors de l'édition ou la création d'un fichier de format, il soit possible de visualiser le résultat. En clair, quel objet faut t'il utiliser pour arriver à faire une image de preview et pouvoir dessiner les informations saisie par l'utilisateur (trait, rectangle, rond, texte, etc...) (si l'image pouvait être enregistrable cela serai super).
Je sais pas si j'ai été clair. Le but avoué de ce projet est de me facilité la tâche au travail. J'ai déjà un programme qui fait à peut de chose près la même chose sauf qu'il est prévu pour générer un code REALBasic et il est codé avec REALbasic (j'ai fait tellement vite que c'est irrécupérable. Sauf le principe

Je vous remercie d'avance de votre aide

Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Pour ce qui est des tracés, regardes du côté de la classe NSBezierPath.
Pour l'affichage, il te suffit ensuite d'envoyer ton instance NSImage vers une NSImageView.
Et NSImage a bien sûr tout ce qu'il faut pour sauvegarder dans différents formats.
Plutôt que d'avoir une image à générer et à afficher, je souhaite que l'utilisateur puisse interagir avec les objets. De la même façon que OmniGraffle !
En clair faire un éditeur vectoriel avec la possibilité de changer le "zoom" d'affichage pour permettre la vue global ou plus précise des objets et bien sûr la gestion des plans (premier, dernier, etc...).
Comment s'y prendre ? Doit-je créer des surcharges de NSImageView que je placerait dans une vue et qui contiendrais l'objet ?
Existe t-il un objet qui permet de gérer d'autre objet et l'affichage de dessin vectoriel ?
Merci de vos réponse
Il faut créer une sous-classe de NSView et dessiner grâce à NSBezierPath ou autres. J'ai déjà dit que ce n'était pas simple ?
Pour l'ordre des figures, ce n'est par contre pas très compliqué: elles sont organisées dans un NSArray, celle se trouvant à l'index 0 étant la première dessinée, il suffit de changer l'ordre dans la liste pour changer de plan.
Et pour le zoom ? Tu veux zoomer à 200%: il faut tout dessiner deux fois plus grand.
Le plus dur dans mon cas va être de dessiner avec NSBezierPath. Je ne l'ai jamais fait !
La gestion des objets n'est pas très compliqué au final
J'ai en soit 4 objets simples (trait, texte, image, rectangle). Ces objet permettent la construction des objet plus complexe comme un tableau, texte + bordure, etc...
Au final, mon logiciel très limitatif (et ultra-lourd) écrit en REALbasic vas vite sauté avec son remplaçant Cocoa !
Pour l'affichage des objets tu ajouter à la NSView principale les autres NSView en tant que enfants ? Mais pour garder l'ordre des plans il ne faut pas ajouter du dernier plan vers le premier ?
Non, pas du tout, dessiner avec NSBezierPath n'a rien de sorcier.
Le plus dur, et de très loin, va être de synchroniser le dessin avec le modèle.
Non, je n'utilise qu'une seule vue, pour deux raisons.
La première c'est qu'Apple déconseille, pour des raisons de performances, d'utiliser trop de NSViews (chaque changement de vue produit un changement de Graphic Context).
La seconde, c'est que tu vas devoir dessiner des rectangles de sélection et des poignées pour déplacer tes vues. Ceci amène beaucoup de questions: sur quelle vue les afficher ? De plus, toutes les vues possèdent des systèmes de coordonnées différents: faire les conversions va te pourrir la vie.
Si, très exactement, ce qui apparaà®t au fond (= au dernier plan), est ce qui est dessiné en premier. Les autres plans le recouvrent.
Consulte le Cocoa Drawing Guide, c'est une doc bien fichue.
Comment récupérer les nouvelles propriétés ?
En PJ : une image écran de la vue dessiné.
Il te suffit de boucler sur tous tes bezier paths (que tu stokes dans un tableau) et tester cette méthode jusqu'à ce qu'il réponde "YES", et tu as alors trouvé ton BezierPath et donc ton objet cliqué.
Attention, si tu utilises un NSArray pour stocker toutes tes pièces et donc tous tes BezierPath, si tu parcourres ce tableau dans le sens normal pour demander à chaque pièce de se dessiner, il faut alors parcourir le tableau dans le sens inverse pour tester le clic. En effet, si certains de ces objets se recouvrent/chevauchent, il faut bien tester d'abord les objets au premier plan avant ceux en arrière plan, donc tester ceux qui sont dessinés en dernier d'abord et ceux dessiné en premier à la fin
Pour les déplacement il faut gérer avec les évènements de la souris sur la NSView ? Et selon les mouvements on redessine le focus et l'objet qui a bougé ? J'imagine qu'il faut tout redessiner à chaque changement (ou les changement sur l'objet change l'affichage) ?
Pour le cas de l'affichage des "poignées", je peux te dévoiler la technique qu'a utilisé Apple pour IB 1 et 2 (pour IB3, vu les modifications importantes, je ne sais pas).
Pour des raisons non dévoilables, j'avais fait un peu de reverse-engineering sur IB.
Le dessin des lignes (pour relier 2 objets) ou des poignées de redimensionnement était fait dans une fenêtre recouvrant toutes les autres, et dont le fond était transparent.
Je crois avoir aussi lu quelque part que le Finder faisait de même pour créer ses rectangles de sélection.
Bien sûr, cette méthode introduit aussi pas mal de complexité entre synchronisation des coordonnées du dessin des poignées/lignes et de celles du dessin "en dessous".
Il te faut supplanter les méthodes -mouseDown:, -mouseUp: et -mouseDragged: de NSView.
Pour l'exemple de l'ajout de poignées, dans -mouseDown:
- tu regardes si le clic a eu lieu dans une figure (grâce à la méthode containsPoint, comme indiqué par Ali, en énumérant à l'envers)
- si c'est dans une figure, et si elle ne faisait pas partie de la sélection courante, elle devient la sélection (sauf si la touche Maj. est appuyée, auquel cas elle agrandit la sélection...) => C'est à ce moment que tu utilises la méthode -bounds des NSBezierPath pour concocter un rectangle englobant toutes les figures.
- sinon, (clic dans le vide) la sélection devient vide.
Tu vois l'idée, c'est à toi de gérer tous les cas !
Par exemple, pour gérer de déplacement d'une figure:
- Dans mouseDown: tu as mémorisé les coordonnées de premier clic
- dans mouseDragged: tu fais la différence des coordonnées du clic et coordonnées courantes. Tu appliques ensuite ces nouvelles coordonnées dans l'objet (couche modèle) représenté par ton NSBezierPath. À cet instant, il faudra alors redessiner la figure. Pour commencer, je te conseille de redessiner toute la vue, plutôt que seulement les figures impactées par le déplacement.
Forcément, ça n'est pas simple, ça fait des mois que je travaille dessus à plein temps, il y a bien une raison.
J'avais lu quelque part que c'était ainsi que IB faisait pour afficher les lignes représentant les outlets. Mais en y réflechissant, ça apporte pas mal d'inconvénients. Par exemple, si la poignée arrive en bord de page, tu ne profites plus du défilement automatique de NSScrollView.
Oui.
Je pense que la solution de la fenêtre transparente n'a pour seul avantage que de pouvoir dessiner quelque chose entre 2 fenêtres (et donc forcément entre des views différentes).
EDIT :
Je viens de terminé la mie en place du zoom et dé-zoom ! Cela ce fait assez simplement et demande de tout redessiner!
Et du coup, j'ai un NSArray contenant toutes mes "AliPieces", et dans le "drawRect" de la vue je parcoure ce tableau pour appeler le "draw" de chaque AliPiece... et dans mon event mouseDown (enfin touchBegan vu que c'est iPhone) je parcoure le tableau en sens inverse pour appeler le containsPoint.
Tout ça pour dire que l'intérêt d'avoir un tableau c'est pas que pour le hitTest pour savoir quelle pièce est sélectionnée, mais aussi parce que bien souvent ce n'est pas un tableau de BezierPath mais un tableau d'objets contenant entre autres des bezierpath (mais aussi d'autres trucs représentant ta "pièce"), et que ça permet donc de gérer tes pièces de façon indépendantes.[/list]
Désolé avec mes questions bidon mais j'aime comprendre ce que je fait et comme l'anglais n'est pas mon for la doc est dur à comprendre pour moi :-\\
Donc pour te donner une idée, dans mon "drawRect" de ma vue j'ai : et la méthode "draw" de mon AliPiece ressemble à : Bon c'est du code pour iPhone, qui fait appel directement aux méthode CoreGraphics, donc ça va p'tet pas te parler : toi tu utiliserais plutôt des NSAffineTransforms pour créer et appliquer tes transformations (translation, rotation), et des NSBezierPath pour dessiner ta pièce au lieu des CGMutablePathRef que moi j'utilise... donc bon ça sera pas les mêmes méthodes mais l'idée est là .
Comme j'applique une transformation, une fois fini je restaure l'état pour éviter que la transformation reste appliquée pour la pièce suivante, quoi.
Un graphic context tout propre est donné à ta vue à chaque fois que la méthode drawRect: est appelée. À cet instant, tu peux dessiner.
Le contexte possède ce qu'ils appellent une Current Transform Matrix (CTM): il s'agit d'une matrice de transformation affine, qui s'applique à tout ce qui est dessiné. Par exemple, si tu voulais dessiner deux fois plus gros, tu pourrais modifier la CTM, mais il faudrait rétablir ensuite la CTM dans son état initial pour afficher correctement les poignées (sinon, elles apparaà®traient aussi 2x plus grosses).
Note que tant que tu te restreins aux NSBezierPaths, tu peux appliquer une transformation affine avec la méthode -transformUsingAffineTransform:.
Comme tu peux le voir, il y a beaucoup de méthodes envisageables. C'est à toi de voir ce qui convient le mieux à ton application.
• Nombre de bits par pixels (plans de l'écran),
• Fonctions logiques pour la combinaison des bits pour chaque pixel,
• Masques de plans,
• Bouts et jointures des lignes,
• Clippage,
• Définition du système de coordonnées (la NSAffineTransform par exemple)
etc ...
Mais maintenant, ma fonction draw dessine selon l'origine qui est placé en bas à gauche mais il est souvent plus commode de le mettre en haut à gauche. De plus, quand le dessin est plus petit que la zone d'affichage j'aimerai centré mon dessin. Style photoshop
Seulement je ne vois pas comment faire ! et vous ?
Pour la PJ:
Le cadre rouge représente les bords de ma NSView. Dans ce cas j'aimerais que la NSView soit centrée dans la zone d'affichage, est-ce possible ? si non, est t'il possible de la mettre au moins en haut à gauche ?
méthode de NSView : isFlipped
On fait une arborescence de vues, où les deux premières sont fixes :
contentView -> zoneDessin -> vueOùOnDessine
et à chaque mise à jour,
vueOùOnDessine.center=zoneDessin.center
Je comprend pas ton arborescence. Ma zone de droite est une scrool view qui contiens la vue dans lequel je dessine.
Je sais bien que NSScoolView contiens une vue de type NSClipView qui contiens ma NSView.
Je doit donc centrer NSView selon NSClipView ?
j'ai pas trouvé non plus de méthode "center" NSView mais "centerScanRect:" qui permet de centrer un rectangle
Bon tu as choisi pour ce que j'appelle zoneDessin une scroll-view, ce qui donne l'arborescence
contentView -> scrollView -> clipView --> vueOùOnDessine
Oui sorry, sur la plate-forme Mac OS (je suis sur iPhone actuellement), la property center n'est disponible que via les CALayer, qui n'arrangera pas ton coup ici.
Le fait de mettre ceci dans une ScrollView change la donne, car désormais ce n'est plus ta vue qui va se déplacer mais le rectangle bounds de la clip view.
Cherche donc du côté des méthodes de la clip view scrollToPoint: et documentRect/documentVisibleRect
Je ne sais pas comment on fait. J'ai essayé plusieurs tactique, sans succès.
Le problème, c'est que cette méthode a pour conséquence de modifier la CTM.
Les images se retrouvent alors la tête en bas.
C'est ce que j'ai cru voir, comment faire alors ? J'ai essayez plein de chose sans sucés !
Pour le centrage c'est du détail et c'est pas très important mais le point en haut à gauche est plus pratique qu'en bas à gauche ???
Cependant avec les view "flipped" ou non, cela peut rester opaque par l'utilisation des méthodes convertPoint: convertRect: ... de NSView.
Là je ne suis pas d'accord : c'est comme cela qu'on fait à la main ...
Je trouve que le graphisme sous Cocoa possède deux grandes qualités en ce qui concerne le repérage : celle de faire les choses à l'endroit, et celle d'utiliser les flottants au lieu des entiers.
Un grand merci à vous tous, je vais continuer de coder et le reviens si j'ai des questions !
aller une petite tourné