Ajouter une row et mettre à  jour firstResponder

MickMick Membre
20:19 modifié dans API AppKit #1
Bonjour à  tous,

Je me permets de relancer un nouveau sujet sur les problèmes de firstResponder dans une tableView.
J'ai une NSTableView gérée par une dataSource. Pas de soucis sur le fonctionnement. Une méthode add: d'un controleur permet d'ajouter un objet, qui par voie de conséquence entraine l'ajout d'une row => Dans la méthode add, j'ai donc un reloadData.

Le soucis est le suivant : je souhaite que lorsque l'utilisateur a terminé d'éditer la dernière cell de la tableView, la méthode add: soit appelée, afin de créer une nouvelle row, et que les nouvelles cellules soient les nouveaux responder (si l'utilisateur a appuyé sur entrée, la cellule active doit etre celle en-dessous de celle qu'il éditait, et si il a appuyé sur tab, c'est celle de la première colonne qui est active en édition). Logiquement, j'ai donc overridé la méthode textDidEndEditing, dans laquelle je teste si c'est bien la dernière cellule. Pas de soucis, mon nouvel objet est bien créé et entraine bien une nouvelle row, Mais l'édition est donnée à  la première ligne. J'ai tenté un selectRow, puis un editColumn: row: => Au débug, ligne par ligne ça colle, jusqu'à  la fin de la méthode textDidEndEditing => Le focus est donné à  la première row juste après avoir été donné à  la bonne !! C'est lorsque la méthode textDidEndEditing est terminée qu'il se passe un truc qui redonne le focus à  la 1ere row. I don't understand what happens !!

Quand j'essaye un makeFirstReponder, message d'erreur . J'essaye de donner le responder de la façon suivante :
<br />[[self window] makeFirstResponder:[laColonne dataCellForRow:laRow]];<br />

M'y prends-je correctement ? Dans la doc d'Apple j'ai compris qu'il n'y avait qu'un dataCell... Comment accéder au textFieldCell's ?  :'(

Réponses

  • ClicCoolClicCool Membre
    20:19 modifié #2
    Et avec tout simplement:
    [maTable editColumn:theColumn row:theLastRow withEvent:nil select:YES];<br />
    
    ?
  • MickMick Membre
    20:19 modifié #3
    Oui, mais non !!

    Après la sortie de la méthode textDidEndEditing, la cellule que je force à  éditer avec editColumn: row: withEvent: select: perd le focus !!
    Au débug, ligne par ligne cela se voit : elle a le focus, puis le perd juste après...
  • ClicCoolClicCool Membre
    20:19 modifié #4
    T'as pas un bout de code qui traine juste après l'édition de la cell ?
    Au débug c'est dans quelle méthode que ça zappe ?


    Et si t'envoyais une notification à  ton contrôleur qui la récupère juste après, et lance alors l'édition de la bonne colonne/ligne ?
  • MickMick Membre
    20:19 modifié #5
    Re bonjour,

    Justement, je ne vois pas ce qui cloche.
    La structure du projet : j'ai un controleur (subclass de NSObject) qui gère l'ajout et la suppression des objets associés à  une row. Il y a donc une méthode add: dans ce controleur qui est donc appelée par la méthode textDidEndEditing si la editedColumn: et la editedRow sont les dernières.

    <br />- (void)add:(id)sender {<br />	NSArray *lesVariables=[leControleurDeVariables valueForKey:@&quot;arrangedObjects&quot;];<br />	NSEnumerator *enumVariables=[lesVariables objectEnumerator];<br />	NSMutableDictionary *uneVariable;<br />	NSMutableDictionary *uneMesure=[[NSMutableDictionary alloc] init];<br />	while (uneVariable=[enumVariables nextObject]) {<br />		[uneMesure setObject:[NSNumber numberWithDouble:0.0] forKey:[uneVariable valueForKey:@&quot;varID&quot;]];<br />	}<br />	[leControleurDeMesures addObject:uneMesure];<br />	[uneMesure release];<br />	[laTable reloadData];<br />}<br />
    


    Après l'appel, je force la sélection de cette dernière row, puis l'édition avec editColumn: row: withAction: select:
    Et ensuite, la méthode textDidEndEditing se termine, et c'est le drame... (je n'appelle aucune autre méthode....)
  • NseaProtectorNseaProtector Membre
    20:19 modifié #6
    A la fin de la méthode textDidEndEditing tu as essayé un [return false]; Je ne suis pas sur de ce que j'avance mais si tu valide que c'est la fin de l'édition cela me semble normal que l'édition se termine, parcontre si tu renvois false... Tu peux toujours essayer en attendant que les pros nous sortent le truc évident que personne ne voit !!!
  • MickMick Membre
    20:19 modifié #7
    ClicCool, je vais tenter la notification. Elle sera reçue APRES la fin de l'édition ?
    J'avoue ne pas trop utiliser ces notifications. Il va falloir que je me penche dessus. Si ClicCool tu as un exemple je suis preneur.
  • mpergandmpergand Membre
    20:19 modifié #8
    La soluce: ne pas utiliser textDidEndEditing  :P
    http://forums.macgeneration.com/5322483-post4.html

    Une façon un peu bidouille de se sortir de ce genre de problème, c'est de faire un performSelectorAfterDelay.
  • ClicCoolClicCool Membre
    20:19 modifié #9
    dans 1261337887:
    .../...
    Une façon un peu bidouille de se sortir de ce genre de problème, c'est de faire un performSelectorAfterDelay.


    Oui,

    Je préfère quand même la voie de la Notification qui sera exécuté immédiatement à  la sortie du runLoop (à  l'entrée dans le loop suivant au moment de la lecture des évènements en attente si j'ai bien saisi leur fonctionnement) l'ayant envoyée ;)
  • AliGatorAliGator Membre, Modérateur
    20:19 modifié #10
    Non, ça c'est le fonctionnement de performSelectorOnThread qui empile les appels qui sont dépilés à  chaque boucle de la runLoop, tout comme des timers d'ailleurs.
    NSNotification c'est le pattern "Observer" (ou "Listener"), et normalement dans ce pattern dès que tu envoies une notification (postNotificationName:...) et que le NSNotificationCenter la reçoit, elle sort de son chapeau le NSArray d'Observers associés à  cette notification, et appelle la méthode qui va bien sur ces objets... directement.

    @implementation NotifCenter<br />-(void)addObserver:(id)obs { [arrayOfListeners addObject:obs]; }<br />-(void)postNotif { for(id obs in arrayOfListeners) [obj didReceiveNotif]; }<br />@end
    
  • ClicCoolClicCool Membre
    20:19 modifié #11
    merci pour la précision  :o
  • MickMick Membre
    20:19 modifié #12
    Effectivement, ça fonctionne super si j'envoie mon message "add:" à  mon controleur dans la méthode setObject.
    En fait, dans la méthode didEndEditing, le"did" ne veut pas tout-à -fait dire "did"... Comprenne qui voudra !
  • wiskywisky Membre
    20:19 modifié #13
    Il me semble que la fonction "did" est appelé après la fonction qui te permet de gérer les modifications de contenu ! Will c'est avant (elle permet de refuser les modifications) !  ;)
  • ClicCoolClicCool Membre
    20:19 modifié #14
    C'est vrai que c'est quand même étonnant de pas pouvoir faire ça avec [tt]textDidEndEditing[/tt] alors que la doc nous dit qu'est bien là  qu'il faut le faire ...  ???

    Updates the data source based on the newly edited value and selects another cell for editing if possible according to the character that ended editing (Return, Tab, Backtab).

  • wiskywisky Membre
    20:19 modifié #15
    dans 1261386717:

    C'est vrai que c'est quand même étonnant de pas pouvoir faire ça avec [tt]textDidEndEditing[/tt] alors que la doc nous dit qu'est bien là  qu'il faut le faire ...  ???

    Updates the data source based on the newly edited value and selects another cell for editing if possible according to the character that ended editing (Return, Tab, Backtab).



    à‰tonnant en effet ! Il doit y avoir une valeur qui n'est pas mise à  jour !
    Pour ma part, le fait que la ligne soit absente de la table avant l'appel de textDidEndEditing est source de problème. Mac OS X doit vérifier la valeur countRow et voyant qu'il est à  la dernière il retourne à  la première ligne.
  • ClicCoolClicCool Membre
    20:19 modifié #16
    dans 1261394019:
    .../...
    Pour ma part, le fait que la ligne soit absente de la table avant l'appel de textDidEndEditing est source de problème. Mac OS X doit vérifier la valeur countRow et voyant qu'il est à  la dernière il retourne à  la première ligne.


    A priori non puisqu'initialement ça marche.
    dans 1261331335:
    .../...
    Au débug, ligne par ligne cela se voit : elle a le focus, puis le perd juste après...


    C'est en sortant du [tt]textDidEndEditing[/tt] que le focus change sans crier gare.
  • MickMick Membre
    20:19 modifié #17
    Oui, je crois que le nextResponder est donné avant la fin de l'édition à  la cellule de la première ligne, puisque la dernière n'existe pas encore. Du coup, j'ai beau m'exciter (en tout bien tout honneur....) à  faire des editColumn, dès la fin de l'édition la window donne le focus au nextReponder.

    J'ai résolu mon problèle en modifiant le setObject: de la dataSource, mais par curiosité j'aimerais essayer de changer le nextResponder dans la méthode textDidEndEditing pour voir. Le soucis est d'avoir accès au textFieldCell fraichement créé : how can I do ?
    D'après la doc, une tableColumn a un dataCell et un headerCell. => Comment accéder au textFieldCell's, afin d'envoyer le message
    <br />[[self window] makeFirstResponder:leTextFieldCellQueJArrivePasAChopper];<br />
    
  • mpergandmpergand Membre
    20:19 modifié #18
    dans 1261420736:

    Le soucis est d'avoir accès au textFieldCell fraichement créé : how can I do ?



    On pourrait essayer de voir du coté de dataCell:
    <br />- (void)controlTextDidEndEditing:(NSNotification *)aNotification<br />{<br />	printf(&quot;end edit&#092;n&quot;);<br />	<br />	NSTableColumn* col=[[oTableView tableColumns] objectAtIndex:[oTableView editedColumn]];<br />	NSCell* cell=[col dataCell];<br />	NSLog(@&quot;%@ %@&quot;,cell,[cell stringValue]);<br />}
    


    Sauf que la valeur des cells est inexploitable en dehors des méthodes concues à  cet effet (willDisplayCell, etc)

    Par contre le field editor semble plus intéressant:
    <br />- (void)controlTextDidEndEditing:(NSNotification *)aNotification<br />{<br />	printf(&quot;end edit&#092;n&quot;);<br />	NSText* editor=[[oTableView window] fieldEditor:NO forObject:nil];<br />	NSLog(@&quot;%@ %@&quot;,editor,[editor string]);<br />	<br />}
    


    La valeur est correcte, ceci dit, je vois pas ce que tu pourrais faire à  partir de ça pour résoudre ton problème ...
    Je pense pas qu'il soit possible de faire quelque chose puisque le reloadData est ignoré par tableView à  ce niveau.

    La seule solution c'est d'intercepter les tab au niveau de keyDown et de gérer soi-même l'édition des cellules.

    Good luck  ;)
  • MickMick Membre
    20:19 modifié #19
    Je laisse tomber ma curiosité. Ca marche bien avec setObject: de la dataSource, donc je ne vais plus me prendre la tete.

    Par contre, dans quels cas serait-il judicieux d'overrider textDidEndEditing ? Quelqu'un a des exemples ? (peut-etre cela peut me servir un jour)
  • wiskywisky Membre
    20:19 modifié #20
    dans 1261428065:

    Je laisse tomber ma curiosité. Ca marche bien avec setObject: de la dataSource, donc je ne vais plus me prendre la tete.

    Par contre, dans quels cas serait-il judicieux d'overrider textDidEndEditing ? Quelqu'un a des exemples ? (peut-etre cela peut me servir un jour)

    Je l'ai fait pour sortir de l'édition mais rester sur la ligne sélectionnée !
  • mpergandmpergand Membre
    20:19 modifié #21
    Le retour de la vengeance  :D

    Une méthode plus propre serait d'intercepter doCommandBySelector.

    Dans une sous classe de NSTableView ajouter:
    - (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector;<br />{<br />	if (commandSelector == @selector(insertTab:))<br />		{<br />		int editedRow=[self editedRow];<br />		int editedCol=[self editedColumn];<br />		<br />		[[self delegate] tableViewWillEditNextCell:self];<br />		[[self selectedCell] endEditing:textView];&nbsp; &nbsp; // sur les conseils de Edit58 (ça marche mais c&#39;est zarbi, selected=edited en fait ?)<br />		<br />		editedCol++;<br />		<br />		if(editedCol&gt;=[self numberOfColumns])<br />			{<br />			editedCol=0;<br />			editedRow++;<br />			}<br />		<br />		[self selectRow:editedRow byExtendingSelection:NO];<br />		[self editColumn:editedCol row:editedRow withEvent:nil select:YES];<br />		<br />		return YES;<br />		}<br />	else if (commandSelector==@selector(insertBacktab:))<br />		{<br />		int editedRow=[self editedRow];<br />		int editedCol=[self editedColumn];<br />		<br />		editedCol--;<br />		<br />		if(editedCol&lt;0)<br />			{<br />			editedCol=0;<br />			editedRow--;<br />			<br />			if(editedRow&gt;=0)<br />				{<br />				[[self selectedCell] endEditing:textView];<br />				[self selectRow:editedRow byExtendingSelection:NO];<br />				[self editColumn:editedCol row:editedRow withEvent:nil select:YES];<br />				return YES;<br />				}<br />			}<br />		<br />		}<br />	<br />	return [super textView:textView doCommandBySelector:commandSelector];<br />}<br /><br />
    


    Ca gère les tabs et backtabs.
    Une nouvelle méthode delegate "tableViewWillEditNextCell:" est appelé lors d'un tab.
    C'est donc dans cette méthode qu'il faut tester si la cellule en édition est la dernière et agir en conséquence. (addRow reloadData etc)

    Pour éviter le warning sur la nouvelle méthode, on peut définir un protocole, ou faire un simple performSelectorWithObject.
  • MickMick Membre
    20:19 modifié #22
    Je ais tester cela.
    Bien vu. Mpergand où as-tu trouvé cette méthode textView doCommandBySelector dans la doc ?
Connectez-vous ou Inscrivez-vous pour répondre.