Row background customized !

09:30 modifié dans API AppKit #1
Hello,

C'est assez simple, je cherche à  personaliser le background de la selectedRow d'une tableView :D
Notez par exemple sur Adiumx depuis la version 0.80, il y a un petit gestionnaire pour l'envoi et reception de fichiers, lorsqu'on sélectionne une row du tableau, ça fait un beau dégradé perso.
J'ai parlé à  Adam (l'un des dev maà®tre de Adium) il m'a parlé d'une subclass de ne je sais quoi :p

Merci d'avance,
Louka.
«1

Réponses

  • Eddy58Eddy58 Membre
    09:30 modifié #2
    Tu peux récupérer le code d'Adium je crois non ? :)
  • 09:30 modifié #3
    Regarde là : http://iratescotsman.com/products/source/

    Ce qui t'intéresse est sous le titre iTableView.

    PS: Utilise l'outil de recherche, ce n'est pas la première fois que je mets ce lien...
  • 09:30 modifié #4
    dans 1119693887:

    Tu peux récupérer le code d'Adium je crois non ? :)


    Il y avait à  l'époque de de la version 0.63 de Adium environ 1700 classes. Je ne doute pas que ça monte encore régulièrement...
  • Eddy58Eddy58 Membre
    09:30 modifié #5
    dans 1119693974:

    Il y avait à  l'époque de de la version 0.63 de Adium environ 1700 classes. Je ne doute pas que ça monte encore régulièrement...

    Ha oui quand même...:P
  • 09:30 modifié #6
    C'est vrai que les sources d'adium ont bcp de classes mais ce soft a largement la classe :D
    J'ai fait une recherche sur OC avant de poster (oui oui je m'y fait) et j'ai rien trouvé concernant ce que je cherchais.
    Dsl alors Renaud. Merci pour ton lien je regarde de suite
  • 09:30 modifié #7
    Toutes mes excuses, j'avais effectivement déjà  mis le lien, mais c'était pour autre chose...
  • 09:30 modifié #8
    ^^
    J'ai compilé l'app, c'est effectivement ce que je veux !
    Je trifouille le code ce soir !
    Merci encore Renaud ;)
  • Eddy58Eddy58 Membre
    09:30 modifié #9
    En tout cas, très sympa ce bout de code. Je verrais bien une amélioration de cette classe en choisissant la couleur de base qu'on veut pour le dégradé, qui serait alors tracé avec NSBezierPath. Et puis les checkboxes colorisées une belle trouvaille aussi... :)
  • maconnectmaconnect Membre
    09:30 modifié #10
    non, ce code est plutôt bugué. Genre dès que tu change la hauteur des rows ou que tu mets une image plus grande ou que tu veux que la row soit éditable ou que la colonne soit redimentionnable. Dans tous ces cas y'aura des bugs visuels ou sémantiques.
    Pas top du tout en fait :(
  • muqaddarmuqaddar Administrateur
    09:30 modifié #11
    dans 1119693974:

    dans 1119693887:

    Tu peux récupérer le code d'Adium je crois non ? :)


    Il y avait à  l'époque de de la version 0.63 de Adium environ 1700 classes. Je ne doute pas que ça monte encore régulièrement...


    Parenthèse : adium plante chez moi dès qu'on envoie un fichier, enfin, une fois sur deux... il faudrait se concentrer sur les bases avant de tout pouvoir customizer...
  • 09:30 modifié #12
    Réponse à  la parenthèse. Il ne faut pas tout mettre dans le même sac. Adium n'est qu'une interface Cocoa pour libgaim. Les problèmes de type envoi de fichiers sont imputables à  la libgaim, ce dont ils ne s'occupent pas (et refusent de s'occuper, ce n'est pas dans leurs cordes).
  • muqaddarmuqaddar Administrateur
    09:30 modifié #13
    dans 1121160264:

    Réponse à  la parenthèse. Il ne faut pas tout mettre dans le même sac. Adium n'est qu'une interface Cocoa pour libgaim. Les problèmes de type envoi de fichiers sont imputables à  la libgaim, ce dont ils ne s'occupent pas (et refusent de s'occuper, ce n'est pas dans leurs cordes).


    C'est bien dommage, c'est quand même des fonctionnalités essentielles...
  • Eddy58Eddy58 Membre
    09:30 modifié #14
    dans 1121151524:

    non, ce code est plutôt bugué. Genre dès que tu change la hauteur des rows ou que tu mets une image plus grande ou que tu veux que la row soit éditable ou que la colonne soit redimentionnable. Dans tous ces cas y'aura des bugs visuels ou sémantiques.
    Pas top du tout en fait :(

    Oui c'est normal, le dégradé n'est rien de plus qu'une image d'un pixel de large sur une hauteur définie, qui est dupliquée selon la longueur voulue. En améliorant cette classe et en dessinant le dégradé par NSBezierPath pour qu'il s'adapte automatiquement à  toutes les hauteurs, ce serait une bonne chose, en plus on pourrait choisir la couleur du dégradé. :)
  • maconnectmaconnect Membre
    09:30 modifié #15
    En fait cela n'est pas un problème puisqu'il suffit de changer la taille de l'image ([image setSize:]).
    Le plus gros problème c'est que les rows ne sont plus éditables après coup, et aussi qu'on voit la sélection normale en dessous de la sélection dégradée selon la hauteur des rows. Et aussi si on met des images le résultat ne sera pas satisfaisant selon la hauteur des rows.
    Bref, dans l'exemple fourni ça marche, mais c'est le seul cas. Inutilisable a mon avis, il faut s'y prendre autrement.
  • juillet 2005 modifié #16
    Bon Okay, la prochaine fois, je donnerai que du code que je vérifierai, ce sera à  mon avis plus sûr, quand j'ai vu comment c'était fait, j'ai failli hurler...

    Voici le code de la sous-classe:

    [tt]#import <Cocoa/Cocoa.h>

    static NSImage* _gradientGray;
    static NSImage* _gradientBlue;

    @implementation GradientTableView
    +(void)initialize {
    _gradientBlue = [[NSImage imageNamed:@highlight_blue.tiff] retain];
    [_gradientBlue setFlipped:YES];
    _gradientGray = [[NSImage imageNamed:@highlight_grey.tiff] retain];
    [_gradientGray setFlipped:YES];

    [super initialize];
    }

    - (void)highlightSelectionInClipRect:(NSRect)clipRect {
    NSImage* gradient;
    if ([[self window] firstResponder] == self || [[self window] isKeyWindow]) {
            gradient = _gradientBlue;
        } else {
            gradient = _gradientGray;
        }

    NSEnumerator* e = [self selectedRowEnumerator];
    NSNumber* selRow;
    while ((selRow = [e nextObject])) {
    NSRect r = [self rectOfRow:[selRow intValue]];
    if (NSIntersectsRect(clipRect,r)) {
    [gradient drawInRect:r
    fromRect:NSZeroRect
       operation:NSCompositeSourceOver
    fraction:1.0];
    }
    }
    }

    - (id)_highlightColorForCell:(NSCell *)cell { return nil; }

    @end[/tt]

    [EDIT] Code pour avoir le dégradé au lieu d'une couleur quelconque. Maintenant comparez le nombre de lignes de code, et vous aurez compris pourquoi j'ai voulu hurler. Les images proviennet de l'exemple donné plus haut, mais rien ne vous empêche d'en utiliser d'autres.

    Ceci dit il y a un problème: le texte reste en noir même dans la sélection. La méthode privée surchargée fait visiblement plus que ce que son nom le suggère... Pour résoudre ce problème, j'ai fait une sous classe perso de nstextfieldcell, mais il y a certainement mieux, donc je ne le mets pas...
  • BruBru Membre
    09:30 modifié #17
    Voici la méthode que j'utilisais à  une certaine époque (10.2).
    Il suffit juste de faire une sous classe de NSTableView.

    BRUTableView.h :
    <br />#import &lt;Cocoa/Cocoa.h&gt;<br /><br />@interface BRUTableView : NSTableView<br />{<br />}<br /><br />@end<br />
    


    BRUTableView.m :
    <br />#import &quot;BRUTableView.h&quot;<br /><br />void DrawSelectionBackGround(NSRect rect);<br /><br />@implementation BRUTableView<br /><br />- (void)highlightSelectionInClipRect:(NSRect)clipRect<br />{<br />   //désactivation de cette méthode car j&#39;ai ma propre méthode de sélection à  moi...<br />   return;<br />}<br /><br />- (void)drawRow:(int)rowIndex clipRect:(NSRect)clipRect<br />{<br />   NSArray *listcol;<br />   NSTableColumn *tcol;<br />   NSCell *cell;<br />   int icol;<br />   id obj;<br />   NSColor *colsav;<br /><br />   // la row en cours d&#39;affichage est sélectionnée :<br />   //  1. on affiche le dégradé de sélection<br />   //  2. on affiche les NSCell composant la ligne (en modifiant la textColor pour les NSTextFieldCell)<br />   if ([self isRowSelected:rowIndex])<br />   {<br />      // affichage du dégradé de sélection.<br />      DrawSelectionBackGround([self rectOfRow:rowIndex]);<br /><br />      // parcours des colonnes de la table<br />      listcol=[self tableColumns];<br />      for (icol=0; icol&lt;[listcol count]; icol++)<br />      {<br />         tcol=[listcol objectAtIndex:icol];<br />         cell=[tcol dataCell];<br /><br />         // on appelle la méthode du dataSource pour récupérer l&#39;objet à  afficher dans la NSCell.<br />         obj=[[self dataSource] tableView:self objectValueForTableColumn:tcol row:rowIndex];<br />         [cell setObjectValue:obj];<br /><br />         // si la NSCell est NSTextFieldCell, alors on sauvegarde la textColor<br />         // et on met la ouleur du texte à  blanc.<br />         colsav=nil;<br />         if ([cell isKindOfClass:[NSTextFieldCell class]])<br />         {<br />            colsav=[[(NSTextFieldCell *)cell textColor] retain];<br />            [(NSTextFieldCell *)cell setTextColor:[NSColor whiteColor]];<br />         }<br /><br />         // maintenant, on peut réafficher la NSCell dans la tableView (par dessus le dégradé de sélection).<br />         [cell drawWithFrame:[self frameOfCellAtColumn:icol row:rowIndex] inView:self];<br /><br />         // si la textColor a été modifiée, on restitue son ancienne valeur.<br />         if (colsav)<br />         {<br />            [(NSTextFieldCell *)cell setTextColor:colsav];<br />            [colsav release];<br />         }<br />      }<br />   }<br /><br />   // la row en cours d&#39;affichage n&#39;est pas sélectionnée : on laisse faire la méthode standard.<br />   else [super drawRow:rowIndex clipRect:clipRect];<br />}<br />@end<br /><br />// méthode d&#39;affichage d&#39;un rectangle dégradé (du noir au blanc)<br />void DrawSelectionBackGround(NSRect rect)<br />{<br />   int ix;<br />   float f;<br />   NSPoint pointd, pointf;<br /><br />   for (ix=0; ix&lt;(int)rect.size.height; ix++)<br />   {<br />      f=0.9-(ix/rect.size.height);<br />      [[NSColor colorWithDeviceWhite:f alpha:1] set];<br />      pointd=NSMakePoint(rect.origin.x, rect.origin.y+ix);<br />      pointf=NSMakePoint(rect.origin.x+rect.size.width, rect.origin.y+ix);<br />      [NSBezierPath strokeLineFromPoint:pointd toPoint:pointf];<br />   }<br />}
    


    Bon, ok c'est pas parfait. Mon dégradé va du noir au blanc (ça peut se changer), et le dégradé est linéaire (alors que s'il était courbe, ça fera peut être mieux, mais je ne suis pas matheux).

    Mais, l'avantage, c'est que ça fonctionne sans bug (nop nop Maconnect...), et que le code n'est pas trop pourri (juste pour éviter le Renaud de s'énerver).

    Si je me souviens bien, la bidouille ne fonctionne pas avec les sélections de colonne.

    .
  • Eddy58Eddy58 Membre
    09:30 modifié #18
    Hé bien Bru, apparemment ce à  quoi je pensais plus haut trainait dans tes cartons depuis un moment ! ;)
  • muqaddarmuqaddar Administrateur
    09:30 modifié #19
    dans 1121276705:

    Hé bien Bru, apparemment ce à  quoi je pensais plus haut trainait dans tes cartons depuis un moment ! ;)


    Ah, toi t'as jamais vu le grenier de Bru !
    Il paraà®t qu'on trouve de tout !  :o
  • Eddy58Eddy58 Membre
    09:30 modifié #20
    Le grenier de Bru vaut surement le détour, mais la cave ne doit pas être sans intérêt non plus.:P
  • muqaddarmuqaddar Administrateur
    09:30 modifié #21
    dans 1121291523:

    Le grenier de Bru vaut surement le détour, mais la cave ne doit pas être sans intérêt non plus.:P


    Oui, il a quelques bouteilles au milieu de ces anciennes palmes et combinaisons, m'a t-il signifié un jour.
    Désolé pour le troll...
  • maconnectmaconnect Membre
    09:30 modifié #22
    Merci Bru.
    Tiens, en bleu et avec une fonction quadratique ça donne ça:
    void DrawSelectionBackGround(NSRect rect)<br />{<br />   int ix;<br />   float f;<br />   NSPoint pointd, pointf;<br /><br />   for (ix=1; ix&lt;(int)rect.size.height; ix++)<br />   {<br />      f=0.9-(ix/rect.size.height);<br />	  f*=f;<br />	  [[NSColor colorWithCalibratedRed:0 green:f/2. blue:1 alpha:1] set];<br />      pointd=NSMakePoint(rect.origin.x, rect.origin.y+ix);<br />      pointf=NSMakePoint(rect.origin.x+rect.size.width, rect.origin.y+ix);<br />      [NSBezierPath strokeLineFromPoint:pointd toPoint:pointf];<br />   }<br />}
    

    J'ai mis ix=1 car sinon il y a un petit bug (pas taper) de rafraà®chissement
  • BruBru Membre
    09:30 modifié #23
    dans 1121530088:

    J'ai mis ix=1 car sinon il y a un petit bug (pas taper) de rafraà®chissement


    Ce n'est pas un bug, c'est une paresse de ma part.

    La ligne qui apparait parfois, c'est tout simplement l'emplacement de la horizontal grid line lorsque qu'elle n'est pas affichée.

    Pour faire les chose propres, c'est normalement à  moi aussi de réafficher cette grid line comme je l'ai fait pour les cells. Mais bon, faut bien que j'en laisse pour les autres...

    .
  • UniXUniX Membre
    09:30 modifié #24
    Salut.

    J'ai essayé d'implanter ça dans une de mes NSTableView, mais j'ai un pb ...
    Le fond se dessine parfaitement, mais le texte ne s'écrit pas ... D'ou ça peut venir ?
  • BruBru Membre
    09:30 modifié #25
    dans 1126804476:

    J'ai essayé d'implanter ça dans une de mes NSTableView, mais j'ai un pb ...
    Le fond se dessine parfaitement, mais le texte ne s'écrit pas ... D'ou ça peut venir ?


    Si tu utilises le delegate tableView:willDisplayCell:forTableColumn:row: pour modifier les attriburs de la cell, alors ça ne marche pas.

    Par contre, si tu n'utilises pas le delegate, alors le problème ne peut venir que de ton code (car la cell n'affiche rien). Es tu sûr que ton datasource renvoie bien un objet à  afficher ?

    .
  • UniXUniX Membre
    09:30 modifié #26
    J'utilise les bindings. Ca vient peut être de là  ?
  • UniXUniX Membre
    09:30 modifié #27
    Salut.

    Bon, je suis en train de tenter un truc : d'utiliser dans une NSTableView le fond perso décrit dans ce topic, et des cell ImageAndTextCell utilisées par Apple dans plusieurs de ses exemples.

    Ca marche presque bien.
    J'explique :
    - dans le controlleur, j'ai le delegate - tableView: willDisplayCell: forTableColumn: row: qui attribut au cell son image avec un simple [(ImageAndTextCell*)cell setImage:monImage]

    - dans la NSTableView perso, j'ai le code de Bru dans lequel j'ai enlevé la partie qui testait si la cell est une NSTextFieldCell pour mettre le texte en blanc

    - enfin, le code de ImageAndTextCell est par exemple ici : http://developer.apple.com/samplecode/QTSSInspector/listing6.html

    Ca s'affiche presque correctement, sauf que lorsque je change de ligne sélectionnée, l'image reste celle de la ligne précedemment sélectionnée. Et tout fonctionne bien si j'enlève la méthode pour faire le fond custo .... Les 2 ne font pas bon ménage ... :(
  • BruBru Membre
    09:30 modifié #28
    dans 1127456680:

    - dans le controlleur, j'ai le delegate - tableView: willDisplayCell: forTableColumn: row: qui attribut au cell son image avec un simple [(ImageAndTextCell*)cell setImage:monImage]


    Je le répète :  ma classe ne gère pas le delegate tableView:willDisplayCell:forTableColumn:row:, donc ta méthode ne peut pas correctement fonctionner !

    En fait, il suffit d'apporter une légère modification au code de ma classe pour implémenter l'appel à  ce delegate :
    <br />- (void)drawRow:(int)rowIndex clipRect:(NSRect)clipRect<br />{<br />&nbsp;  NSArray *listcol;<br />&nbsp;  NSTableColumn *tcol;<br />&nbsp;  NSCell *cell;<br />&nbsp;  int icol;<br />&nbsp;  id obj;<br />&nbsp;  NSColor *colsav;<br />&nbsp;  id delegate;<br /><br />&nbsp;  // la row en cours d&#39;affichage est sélectionnée :<br />&nbsp;  //&nbsp; 1. on affiche le dégradé de sélection<br />&nbsp;  //&nbsp; 2. on appelle le delegate&nbsp; tableView:willDisplayCell:forTableColumn:row:<br />&nbsp;  //&nbsp; 3. on affiche les NSCell composant la ligne (en modifiant la textColor pour les NSTextFieldCell)<br />&nbsp;  if ([self isRowSelected:rowIndex])<br />&nbsp;  {<br />&nbsp; &nbsp; &nbsp; // affichage du dégradé de sélection.<br />&nbsp; &nbsp; &nbsp; DrawSelectionBackGround([self rectOfRow:rowIndex]);<br /><br />&nbsp; &nbsp; &nbsp; // parcours des colonnes de la table<br />&nbsp; &nbsp; &nbsp; listcol=[self tableColumns];<br />&nbsp; &nbsp; &nbsp; for (icol=0; icol&lt;[listcol count]; icol++)<br />&nbsp; &nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp;  tcol=[listcol objectAtIndex:icol];<br />&nbsp; &nbsp; &nbsp; &nbsp;  cell=[tcol dataCell];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp;  // on appelle la méthode du dataSource pour récupérer l&#39;objet à  afficher dans la NSCell.<br />&nbsp; &nbsp; &nbsp; &nbsp;  obj=[[self dataSource] tableView:self objectValueForTableColumn:tcol row:rowIndex];<br />&nbsp; &nbsp; &nbsp; &nbsp;  [cell setObjectValue:obj];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp;  // si la NSCell est NSTextFieldCell, alors on sauvegarde la textColor<br />&nbsp; &nbsp; &nbsp; &nbsp;  // et on met la ouleur du texte à  blanc.<br />&nbsp; &nbsp; &nbsp; &nbsp;  colsav=nil;<br />&nbsp; &nbsp; &nbsp; &nbsp;  if ([cell isKindOfClass:[NSTextFieldCell class]])<br />&nbsp; &nbsp; &nbsp; &nbsp;  {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; colsav=[[(NSTextFieldCell *)cell textColor] retain];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [(NSTextFieldCell *)cell setTextColor:[NSColor whiteColor]];<br />&nbsp; &nbsp; &nbsp; &nbsp;  }<br /><br />&nbsp; &nbsp; &nbsp; &nbsp;  // si le delegate existe, alors on l&#39;appelle :<br />&nbsp; &nbsp; &nbsp; &nbsp;  delegate=[self delegate];<br />&nbsp; &nbsp; &nbsp; &nbsp;  if (delegate) [delegate&nbsp; tableView:self willDisplayCell:cell forTableColumn:tcol row:rowIndex];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp;  // maintenant, on peut réafficher la NSCell dans la tableView (par dessus le dégradé de sélection).<br />&nbsp; &nbsp; &nbsp; &nbsp;  [cell drawWithFrame:[self frameOfCellAtColumn:icol row:rowIndex] inView:self];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp;  // si la textColor a été modifiée, on restitue son ancienne valeur.<br />&nbsp; &nbsp; &nbsp; &nbsp;  if (colsav)<br />&nbsp; &nbsp; &nbsp; &nbsp;  {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [(NSTextFieldCell *)cell setTextColor:colsav];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [colsav release];<br />&nbsp; &nbsp; &nbsp; &nbsp;  }<br />&nbsp; &nbsp; &nbsp; }<br />&nbsp;  }<br /><br />&nbsp;  // la row en cours d&#39;affichage n&#39;est pas sélectionnée : on laisse faire la méthode standard.<br />&nbsp;  else [super drawRow:rowIndex clipRect:clipRect];<br />}<br />
    


    .
  • UniXUniX Membre
    09:30 modifié #29
    ;D ;D ;D

    C'était donc si simple ! Merci Bru  ;)
  • UniXUniX Membre
    09:30 modifié #30
    Et dernière question sur le sujet, lorsque on clique 2 fois pour éditer la cell, il y a un premier état qui réaffiche le fond de colonne standard (la couleur définie dans les prefs système), et ensuite il faut re-cliquer pour avoir le texte surligné pour effectuer la modif.

    Comment faire pour supprimer le premier état ? Il doit y avoir une méthode à  inhiber, mais je ne trouve pas laquelle ...
  • BruBru Membre
    09:30 modifié #31
    dans 1127754017:

    Et dernière question sur le sujet, lorsque on clique 2 fois pour éditer la cell, il y a un premier état qui réaffiche le fond de colonne standard (la couleur définie dans les prefs système), et ensuite il faut re-cliquer pour avoir le texte surligné pour effectuer la modif.

    Comment faire pour supprimer le premier état ? Il doit y avoir une méthode à  inhiber, mais je ne trouve pas laquelle ...


    Lorsque qu'une NSTextFieldCell est en mode "édition", le fond devient automatiquement de la même couleur que celle du contrôle associé (donc la couleur background de la tableView), et le texte est sélectionné entièrement.
    C'est le fonctionnement standard (et non modifiable) de ce type de NSCell.

    Maintenant, si tu ne désires pas que tes NSTextFields soient modifiables, alors utilise setEditables:NO pour qu'un double clic ne les mette pas en mode édition.

    .
Connectez-vous ou Inscrivez-vous pour répondre.