TableView & Menu Contextuel: sélectionner la bonne ligne

ClicCoolClicCool Membre
22:03 modifié dans API AppKit #1
Bonjour,

J'ai une NSTableView autorisant les sélections multiples à  laquelle je souhaite associer un menu contextuel s'appliquant à  la seule ligne cliquée qui doit donc alors apparaà®tre comme la seule ligne sélectionnée.

Le problème c'est que lorsqu'on Ctrl-Clic (eh oui! j'ai pas de clic droit) la ligne cliquée est désignée par un joli contour bleu mais n'est pas sélectionnée.
Donc je ne peut me contenter d'appliquer l'action du contextuel menu à  la ligne sélectionnée.
Je peux, bien sur récupérer avec [tt]- (NSInteger)clickedRow[/tt] de tableView la bonne ligne, mais c'est pas très intuitif pour l'utilisateur qui déclenche une action sur une ligne qui n'est pas celle(s) sélectionnée(s).
De plus le statut enable (bindé) des menuItems dépend de la première ligne effectivement sélectionée et pas comme il se devrait de celle cliquée ...

J'ai bien essayé d'implémenter une action sur simple-clic sur la tableView, qui (si keyModifier & NSControlKeyMask = TRUE) remplace la sélection actuelle par la lgne cliquée seule.
ça marche, sauf que dès que je "couple" la tableView avec le menu contextuel, mon action n'est plus appelée.

Et quoique j'ai essayé:
  • l'interface n'est pas claire en n'indiquant pas la ligne sélectionnée
  • le statut "enable" des items correspond à  (aux) ligne(s) précédemment sélectionnée(s)


Pourtant, le comportement que je recherche se retrouve dans les App d'apple, comme Mail, l'appel d'un menu contextuel sélectionne bien la bonne ligne (et déselectionne la (les) ligne(s) précédemment sélectionnées) immédiatement AVANT l'apparition du menu contextuel...

J'ai loupé un truc simple ?  ???

Réponses

  • mpergandmpergand Membre
    22:03 modifié #2
    J'utilise ça:
    #import &quot;CTableView.h&quot;<br /><br /><br />@implementation NSTableView (CTableView)<br /><br />// sélection par clic droit<br /><br />- (NSMenu *)menuForEvent:(NSEvent *)theEvent<br />{<br />	int row = [self rowAtPoint: [self convertPoint: [theEvent locationInWindow] fromView: nil]];<br />	<br />	if (row != -1) <br />		[self selectRow: row byExtendingSelection: NO];<br />		<br />	return [self menu];<br />}<br /><br /><br />@end<br />
    


    Je crois que ce n'est plus utile dans Léo ou/et snow (?)


  • ClicCoolClicCool Membre
    22:03 modifié #3
    Ah ! merci Mpergand :)

    J'avais cru que [tt]menuForEvent[/tt] permettait "juste" d'adapter le menu au contexte, mais pas de changer la sélection de la tableView.
    Je vais essayer ça !

    PS je suis sous SnowLeopard que veux-tu dire par plus utile ?
  • mpergandmpergand Membre
    22:03 modifié #4
    Je viens de vérifier, ce qui change en fait à  partir de léopard c'est que la ligne est entourée en bleu, mais n'est pas sélectionnée (je pensais le contraire).

    Donc, faut toujours bidouiller  ::)
  • ClicCoolClicCool Membre
    22:03 modifié #5
    dans 1258325966:
    .../...
    Donc, faut toujours bidouiller  ::)


    En effet, mais le plus important c'est qu'avec ton approche ça marche impécable, exactement comme dans mail  :brule:
  • ClicCoolClicCool Membre
    22:03 modifié #6
    Si ça peut en intéresser certains, voici donc l'approche que j'ai utilisée grâce à  la suggestion de Mpergand.
    • Etablissement d'une catégorie de NSTableView.
      imposant la sélection de la ligne ctrl-cliquée de toutes mes tableView
      Et permettant au délégate (s'il existe et implémente la méthode) de customiser le menu par défaut
    • Etablissement d'un protocole delegate spécial pour ces catégories.
      Permettant de déporter sur le délégate l'établissement du menu contextuel adapté


    Le protocole se présente ainsi:
    #import &lt;Cocoa/Cocoa.h&gt;<br /><br /><br />@protocol CCContextualMenuSetter<br />@required<br />- (NSMenu*) tableView:(NSTableView *)tableView menuForEvent: (NSEvent *)theEvent withDefaultMenu: (NSMenu*) defaultMenu;<br /><br />@end<br />
    


    Le header de la catégorie de NSTableView est tout aussi simple:
    #import &lt;Cocoa/Cocoa.h&gt;<br /><br /><br />@interface&nbsp; NSTableView (TableView_ContextualMenu) <br /><br /><br />- (NSMenu *)menuForEvent:(NSEvent *)theEvent;<br />@end<br />
    


    Son implémentation est pas bien plus compliquée:
    #import &quot;TableView_ContextualMenu.h&quot;<br />#import &quot;CCContextualMenuSetter.h&quot;<br /><br />@implementation NSTableView (TableView_ContextualMenu)<br /><br />// Methode de Class renvoyant le menu contextuel par défault<br />+ (NSMenu *)defaultMenu {<br />&nbsp; &nbsp; NSMenu *theMenu = [[[NSMenu alloc] initWithTitle:@&quot;Contextual Menu&quot;] autorelease];<br />&nbsp; &nbsp; [theMenu insertItemWithTitle:@&quot;Insert Element&quot; action:@selector(insertElement:) keyEquivalent:@&quot;&quot; atIndex:0];<br />&nbsp; &nbsp; return theMenu;<br />}<br />// Appelée lors d&#39;un Ctrl-Clic (ou clic-droit)<br />- (NSMenu *)menuForEvent:(NSEvent *)theEvent {<br />	NSMenu *theMenu;<br />	int row = [self rowAtPoint: [self convertPoint: [theEvent locationInWindow] fromView: nil]];<br />	// Si on est bien sur une ligne de la tableView<br />	if (row != -1)&nbsp; { <br />		// Sélectionne cette ligne et elle seule<br />		NSIndexSet * selectedLine = [NSIndexSet	indexSetWithIndex:row];<br />		[self selectRowIndexes: selectedLine byExtendingSelection: NO];<br />		// Appèle le délégate pour Adapter le menu au contexte<br />		if ( [self.delegate respondsToSelector:@selector(tableView: menuForEvent: withDefaultMenu:)] ) {<br />			// Evitons les warning en typeCastant le delegate<br />			id &lt;CCContextualMenuSetter&gt; myContextualDelegate = (id &lt;CCContextualMenuSetter&gt;) self.delegate;<br />			theMenu = [myContextualDelegate tableView: self menuForEvent:theEvent withDefaultMenu:[[self class] defaultMenu] ];<br />		} else {<br />			// Pas de délégate ou méthode spécifique non implémentée<br />			// On utilise le menu par défaut<br />			theMenu = [[self class] defaultMenu];<br />		}<br />	}<br />	return theMenu;}<br /><br />@end
    


    Et enfin, un exemple d'implémentation de la méthode déléguée déclarée:
    - (NSMenu*) tableView:(NSTableView *)tableView menuForEvent: (NSEvent *)theEvent withDefaultMenu: (NSMenu*) contextualMenu { <br />	NSInteger tableViewTag = [tableView tag];<br />	NSMenuItem * newItem;<br />	NSString * menuItemTitle;<br />	Resultat * rsltAEnvoyer;<br />	 switch (tableViewTag) {<br />		 case _TableView_Resultats_:<br />			 rsltAEnvoyer = [[ResultatACTRL selectedObjects] objectAtIndex:0];<br />			 menuItemTitle = [NSString stringWithFormat:@&quot;email %@&quot;, ResultatACTRL ];<br />			 newItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle<br />										action:@selector(emailSelectedResult:)<br />								 keyEquivalent:@&quot;&quot;] autorelease];<br />			 [newItem setTarget:self];<br />			 <br />			 [contextualMenu insertItem:newItem<br />						&nbsp; atIndex:0<br />			&nbsp; ];<br />			 <br />			 break;<br />.../...<br />	 }<br />	return contextualMenu;<br />}<br />
    


    Cette méthode ici se base sur le Tag de la TableView pour l'identifier et customiser le menu selon le contexte.



    Si vous voyer des améliorations à  apporter n'hésitez pas ;)
  • uocramuocram Membre
    22:03 modifié #7
    Amélioration du comportement très appréciée! Merci

    À noter, plutôt utiliser :
    [self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];<br />//		[self selectRow:row byExtendingSelection:NO];	deprecated in Mac OS 10.3<br />
    
Connectez-vous ou Inscrivez-vous pour répondre.