NSOutlineView : hauteur de ligne variable

skimpyskimpy Membre
09:58 modifié dans API AppKit #1
Bonjour,

Je viens de créer une NSOutlineView mais j'aimerais définir des hauteurs de lignes différentes : par exemple, tout ce qui est au niveau 0 de l'outline fait X et tout ce qui est de niveau 1 fait Y (le plus concret et de lancer l'application Mail pour voir le résultat).
Dans la doc Apple, j'ai trouvé la méthode setRowHeight: et le delegate outlineView:heightOfRowByItem: mais j'ai l'impression que ça s'adresse uniquement à  l'outline complète et pas à  une ligne.

Savez-vous comment faire ?

Merci.

Réponses

  • Eddy58Eddy58 Membre
    09:58 modifié #2
    J'ai jamais utilisé, mais je suppose qu'il faut renvoyer une valeur en fonction de l'item. :)
    [tt]
    - (float)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item
    {
        if (item==unItem)
        {
              return 14.0f;
        }
        else
        {
              return 18.0f;
        }
    }
    [/tt]
  • skimpyskimpy Membre
    09:58 modifié #3
    Euh .... bien vu Eddy ! Ca fonctionne impec'. J'étais parti sur des trucs bien plus compliqués alors que les lignes que tu as mises suffisent amplement.
    Il ne me reste plus qu'à  centrer le texte verticalement maintenant (car là , il se situe tout en haut). Vous pensez que je dois descendre au niveau de la cellule pour effectuer ce genre d'opérations ?
  • Eddy58Eddy58 Membre
    février 2006 modifié #4
    J'ai jamais utilisé non plus ;), mais je pense que je passerais par la méthode delegate ci-dessous, pour ensuite utiliser NSAttributedString en jouant sur l'attribut NSBaselineOffsetAttributeName. :)
    [tt]
    - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
    {
         NSAttributedString *cellAttrString;
         NSDictionary *cellAttrDict;
         float offset=-4.0; // Négatif je pense (à  essayer).

          if (item==unItem)
         {
              cellAttrDict=[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:offset] forKey:NSBaselineOffsetAttributeName];
              cellAttrString=[[NSAttributedString alloc] initWithString:[cell stringValue] attributes:cellAttrDict];
              [cell setAttributedStringValue:cellAttrString];
              [cellAttrString release];
         }   
    }
    [/tt]
  • skimpyskimpy Membre
    09:58 modifié #5
    Merci Eddy, je viens d'essayer mais cette fois, ça ne fonctionne pas ; le texte reste en haut de la cellule.

    J'ai fait quelques modifications pour être sûr que le code était bien exécuté :
    NSColor * txtColor = [NSColor redColor];
    NSFont * txtFont = [NSFont boldSystemFontOfSize:14];

    cellAttrDict=[NSDictionary dictionaryWithObjectsAndKeys:txtFont, NSFontAttributeName, txtColor, NSForegroundColorAttributeName, [NSNumber numberWithFloat:offset] forKey:NSBaselineOffsetAttributeName, nil];

    Le texte s'affiche bien en rouge et en taille plus grosse mais je n'ai pas de centrage. J'ai essayé des valeurs positives et négatives pour offset mais ça n'y change rien.
  • Eddy58Eddy58 Membre
    février 2006 modifié #6
    Je comprend pas pourquoi ce n'est pas pris en compte...??? En faisant le tour des attributs je n'en vois pas d'autre en rapport avec le décalage vertical.
    Tu peux toujours ajouter la ligne ci-dessous après le release pour essayer, mais je pense pas que ça fasse grand chose.
    [tt]
    [outlineView updateCell:cell];
    [/tt]

    Sinon, je vois rien d'autre, à  part surcharger la méthode de dessin de la cell et donc prendre tout son tracé en charge.
  • skimpyskimpy Membre
    09:58 modifié #7
    Comme je voulais mettre une image dans mon OutlineView, j'ai utilisé la classe imageAndTextCell fournie par Apple. C'est bizarre car l'image est bien centrée mais le texte reste tout le temps en haut de la ligne ...
  • ChachaChacha Membre
    février 2006 modifié #8
    dans 1140121847:

    Comme je voulais mettre une image dans mon OutlineView, j'ai utilisé la classe imageAndTextCell fournie par Apple. C'est bizarre car l'image est bien centrée mais le texte reste tout le temps en haut de la ligne ...

    Salut, si je ne me trompe pas, NSImageAndTextCell dérive de NSTextFieldCell, et l'affichage du texte est pris en compte par un appel à  [super drawWithFrame:cellFrame inView:controlView] dans -(void) drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
    De ce fait, pour centrer ton texte, tu pourrais modifier le cellFrame pour le décaler vers le bas, genre avec

    <br />NSRect textFrame = NSMakeRect(cellFrame.origin.x, cellFrame.origin.y+(cellFrame.size.height-hauteurDuTexte)/2, cellFrame.size.width, hauteurDuTexte);<br />[super drawWithFrame:textFrame inView:controlView] <br />
    


    Tu nous tiens au courant ?
    +
    Chacha

    [edit]
    Tiens, y avait des problèmes de copier/coller dans mon post. Corrigé
    [/edit]
  • skimpyskimpy Membre
    09:58 modifié #9
    Chacha, merci, tu as trouvé LA solution ! ça fonctionne ! Le texte est bien centré verticalement.

    Pour les personnes qui auraient un jour besoin du code, il faut mettre :
    [super drawWithFrame:textFrame inView:controlView];

    Par contre Chacha, comme je débute, est-ce que tu pourrais m'expliquer le cellFrame ? Il s'agit d'un cadre contenu dans une cellule ? Concrètement, le texte est placé dans un cadre (le cellFrame) qui lui même se situe dans une cellule de la NSOutlineView ?

    Encore merci !
  • ChachaChacha Membre
    09:58 modifié #10
    dans 1140123512:

    Concrètement, le texte est placé dans un cadre (le cellFrame) qui lui même se situe dans une cellule de la NSOutlineView ?

    En lisant la doc, j'ai compris la même chose ! Pas grand chose à  rajouter, donc...
    Bonne continuation

    +
    Chacha

  • Eddy58Eddy58 Membre
    février 2006 modifié #11
    dans 1140121847:

    Comme je voulais mettre une image dans mon OutlineView, j'ai utilisé la classe imageAndTextCell fournie par Apple. C'est bizarre car l'image est bien centrée mais le texte reste tout le temps en haut de la ligne ...

    Effectivement ça change tout ! :P
    Je trouvais curieux que ça ne fonctionne pas. C'est normal la méthode de dessin de la cell est surchargée (sans prendre tout les cas en compte). Quand tu utilises des classes autres que celles du framework Cocoa, il faut le spécifier sinon ça met carrément dedans. :o
  • skimpyskimpy Membre
    09:58 modifié #12
    Eddy, je pense que je me suis mal exprimé. En fait j'ai rajouté la fonctionnalité imageAndTextCell après les différents essais.
    Là , je viens de supprimer toutes les références à  imageAndTextCell pour voir si le paramètre NSBaselineOffsetAttributeName est pris en compte mais ce n'est pas le cas (seules la taille et la couleur sont modifiées).

    Il faut peut-être comme tu le précises surcharger la méthode de dessin.

    Maintenant, si je veux vraiment être dans le look Apple, il faut que je fasse un dégradé quand une ligne est selectionnée et je comptais également mettre dans la colonne de droite, un chiffre encadré d'une forme ovale (comme lorsque Mail indique le nombre de message non lu). Pour le dégradé, je pense savoir comment faire, mais pour la forme ovale encadrant le texte, je ne vois pas. Vous auriez une piste ?

    Encore merci pour votre précieuse aide.
  • ChachaChacha Membre
    février 2006 modifié #13
    dans 1140159529:

    Maintenant, si je veux vraiment être dans le look Apple, il faut que je fasse un dégradé quand une ligne est selectionnée et je comptais également mettre dans la colonne de droite, un chiffre encadré d'une forme ovale (comme lorsque Mail indique le nombre de message non lu). Pour le dégradé, je pense savoir comment faire, mais pour la forme ovale encadrant le texte, je ne vois pas. Vous auriez une piste ?
    Encore merci pour votre précieuse aide.

    Pour le dégradé, voici un code sympa , pour le rond, voici un début de réponse que tu peux adapter.

    +
    Chacha
  • LeChatNoirLeChatNoir Membre, Modérateur
    09:58 modifié #14
    Euh je crois qu'il voulait plutot parler d'un compteur apparaissant à  droite dans la table view.
    Pas de l'image lors du drag&drop...

    Je pencherai pour la même solution que NSImageAndTextCell. Reprend cette classe et ajoutes ton image à  droite dans le DrawRect de la cell. Tu compose ton image avec ton ovale (soit tracer en live, soit une image) dans laquel tu compose ton texte (donc ton compteur) via un drawTextMachinChose (entre un lockFocus/unlockFous).

    Enfin, y a peut être plus simple....

    a+
  • février 2006 modifié #15
    dans 1140159529:

    je comptais également mettre dans la colonne de droite, un chiffre encadré d'une forme ovale (comme lorsque Mail indique le nombre de message non lu).


    2 solutions (qui nécessitent toutes les 2 une cell perso):
    - tu rajoutes une colonne avec le numéro
    - pour la cell perso que tu crées, tu rajoutes une variable d'instance qui représente le numéro que tu veux indiquer, que tu peux modifier lors de l'appel de la méthode de delegate suivante: [tt]outlineView:willDisplayCell:forTableColumn:item:[/tt]

    Les 2 sont tout aussi valables, mais présentent des avantages et des inconvénients: pour faire simple, la première est plus simple à  mettre en ½uvre que la seconde, mais c'est moins joli (à  condition de faire la seconde dans les règles, genre si le texte est trop long et qu'il n'y a pas de numéro à  indiquer, faire en sorte que le texte puisse déborder dans la zone normalement réservée au numéro).

    Dans les 2 cas, tu dois faire une cell perso et surcharger la méthode [tt]drawInteriorWithFrame:inView:[/tt] et dessiner le numéro dans la forme de ton choix, dans le premier cas à  partie de la intValue de la cell et dans le second à  partir de la variable d'instance que tu auras rajouté.

    dans 1140165821:

    Je pencherai pour la même solution que NSImageAndTextCell. Reprend cette classe et ajoutes ton image à  droite dans le DrawRect de la cell. Tu compose ton image avec ton ovale (soit tracer en live, soit une image) dans laquel tu compose ton texte (donc ton compteur) via un drawTextMachinChose (entre un lockFocus/unlockFous).


    Il n'est pas nécessaire que le texte soit dans l'image. Il peut d'abord rajouter le fond (que ce soit une image ou une courbe de bezier, qu'il peut choisir en fonction de la longueur) et dessiner le texte directement au dessus (avec le méthode [tt]drawAtPoint:attributes[/tt] de NSString) - et donc pas besoin de s'ennuyer avec les lockFocus/unlockFocus.
  • skimpyskimpy Membre
    09:58 modifié #16
    Merci pour tous vos conseils ; c'est vraiment agréable d'avoir autant de retour. Comme je débute, je vais y aller petit à  petit et dans un premier temps, je vais simplement mettre le numéro dans la colonne de droite (maintenant que j'ai eu des informations pour faire le cercle, je n'ai plus qu'à  potasser).

    Par contre, une fonction essentielle et je voulais savoir si je faisais juste : quand je sélectionne un élément dans la NSOutlineView, il faut que je déclenche une fonction (pour charger des données dans une NSTableView). Pour intercepter le clic dans la NSOutlineView, il faut bien que j'utilise outlineViewSelectionIsChanging ? Est-ce qu'il est possible aussi d'intercepter un double clic sur un élément de la NSOutline (par exemple que la table ne soit chargée que s'il y a eu 2x clic sur l'élément de l'Outline).

    Merci.
  • LeChatNoirLeChatNoir Membre, Modérateur
    09:58 modifié #17
    Slt,
    Je crois qu'un simple
    - (void)setDoubleAction:(SEL)aSelector
    suffit.
    Il faut sous classer ton NSOutlineView et que celle ci ne soit pas éditable mais ca semble être le cas.
    a+


  • Eddy58Eddy58 Membre
    09:58 modifié #18
    dans 1140200369:

    Pour intercepter le clic dans la NSOutlineView, il faut bien que j'utilise outlineViewSelectionIsChanging ?

    Oui, alors pour les delegates et notifications, il faut faire attention aux temps des verbes qui composent les noms de méthodes. Dans le cas de outlineViewSelectionIsChanging, c'est au présent, donc le changement est en cours, le bouton de la souris est appuyé. Pour outlineViewSelectionDidChange, c'est du passé, donc le clique a été effectué. A toi de voir quelles méthodes utiliser selon tes besoins. :)
  • skimpyskimpy Membre
    09:58 modifié #19
    Donc en fait, pour le double clic, j'ai procédé comme ceci (je n'ai pas sous classé ma NSOutlineView) :

    dans mon awakeFromNib, j'ai rajouté :

    [outlineView setTarget:self];
    [outlineView setDoubleAction:@selector(loadTable)];

    Et si j'ai bien saisi, le outlineViewSelectionDidChange suffit amplement lorsqu'un simple clic a été effectué.

    Eddy58, merci pour tes précisions sur les temps employés dans les delegates ; est-ce que tu pourrais également m'éclaicir sur les delegate avec des noms en should et will (ils arrivent à  prévoir le futur  B) ?)
  • LeChatNoirLeChatNoir Membre, Modérateur
    09:58 modifié #20
    Ah ouais, pas besoin de sous classer j'suis bête...

    Pour should et will, c'est assez simple.

    Imaginons que tu sélectionnes un item de ton outlineView.
    Dans l'ordre, l'outline envoie à  son delegate :
    * selectionWillChange : ca le prévient que le click a été déclenché et que la sélection va changer.
    * selectionShouldChange : le delegate est informé que la sélection va changer et décide si oui ou non, l'action doit se faire,
    * selection isChanging : on est en train de le faire,
    * selectionDidChange : a y est, c'est fait.

    Bon il y a bien d'autres notifications et cet exemple est bidon car pour une tableView, je crois pas qu'il y ait de selectionWillChange.
    Mais c'était pour illustrer.

    Tu as aussi par exemple un WillDisplayCell qui est envoyé au delegate lorsque la table va afficher une de ses cellules. Tu peux alors, dans le delegate, interagir juste avant l'affichage.

    a+
  • skimpyskimpy Membre
    09:58 modifié #21
    Je viens de m'apercevoir qu'il existait aussi un setAction (quand on clique simplement sur une ligne) : mais quelle est alors la différence entre le setAction et le delegate outlineViewSelectionDidChange ?
  • LeChatNoirLeChatNoir Membre, Modérateur
    09:58 modifié #22
    ben le setAction permet d'indiquer à  l'outline que quand on clique 1 fois (doubleAction pour double clique), ca doit déclencher l'appelle à  la méthode que tu indiques en paramètre.

    Tu cliques et paf, l'action est appelée. Même si tu cliques sur un item déjà  sélectionné.

    outlineViewSelectionDidChange n'est appelé que si tu changes d'item je pense.

    Par exemple, si tu changes programatiquement d'item ou peut être aussi via les touches (flèches haut/bas), SelectionDidChange sera appelée. Mais la méthode que tu as indiquée dans le setAction, non.

    Mais on commence à  s'éloigner du sujet initial là ... Faudrait peut être refaire un topic sur les outlines et tablesView...

    a+

  • skimpyskimpy Membre
    09:58 modifié #23
    Oui tu as raison LeChatNoir, je m'égare du sujet initial. Pour mes futures questions, j'ouvrirai un autre thread.
    Sinon j'ai bien saisi grâce à  ton explication la différence entre le delegate et le setAction.

    Merci.
  • Eddy58Eddy58 Membre
    09:58 modifié #24
    J'ajouterais aux explications du matou ;), que la méthode setAction permet aussi de redéfinir dynamiquement un sélecteur, ce qui peut être utile. :)
Connectez-vous ou Inscrivez-vous pour répondre.