NSTreeController

Philippe49Philippe49 Membre
01:51 modifié dans API AppKit #1
Bonjour à  tous

Peu de documentation sur les NSTreeController, et j'essaie d'ouvrir cette boà®te noire qui n'a pas l'air de fonctionner aussi bien qu'on pourrait l'espérer.

Le premier pas est résumé dans le pdf joint, avec le code source associé.

Qu'en pensez-vous ?

[Fichier joint supprimé par l'administrateur]

Réponses

  • Philippe49Philippe49 Membre
    01:51 modifié #2

    Cela se poursuit par une brève étude de NSIndexPath

    [Fichier joint supprimé par l'administrateur]
  • Philippe49Philippe49 Membre
    avril 2007 modifié #3
    puis par l'étude des méthodes add et remove de NSTreeController




    [Fichier joint supprimé par l'administrateur]
  • laurrislaurris Membre
    01:51 modifié #4
    Merci pour ces éclaircissements.
    Moi je ne reproche qu'une chose à  NSTreeController, c'est qu'il ne soit pas mieux accompagné.
    On ne sait pas ce qui se passe à  l'intérieur, contrairement à  NSArrayController dont on peut observer les éléments associés avec le KVO ou avec les accesseurs indexés.
    Ici, on sait qu'il faut lui donner des noeuds mais on ne peut pas obtenir d'infos plus fines sur la façon dont les noeuds changent.
    Je rève d'une classe NSTreeNode qui soit KVO compliant. On pourrait, après avoir modifié l'arbre, obtenir le NSIndexPath affecté par le changement dans le change dictionnary, comme pour un NSMutableArray.

    D'aileurs, plutôt que de rêver, j'ai commencé à  écrire cette classe NSTreeNode. C'est loin d'être fini mais si ça intéresse, je peux poster mon code en l'état pour susciter des idées.
  • Philippe49Philippe49 Membre
    avril 2007 modifié #5


    dans 1177179313:

    D'aileurs, plutôt que de rêver, j'ai commencé à  écrire cette classe NSTreeNode. C'est loin d'être fini mais si ça intéresse, je peux poster mon code en l'état pour susciter des idées.

    Ne rêvons plus ...
    poste en l'état , c'est un service public naturellement !

  • laurrislaurris Membre
    avril 2007 modifié #6
    A la demande générale ;) je poste ma classe de noeud:

    Attention, il ne s'agit que d'une possibilité, utilisée dans un certain contexte (mon noeud est destiné à  représenter un NSCompoundPredicate), donc ce n'est peut-être pas valable dans tous les cas.

    En gros, ça se passe comme ça:
    A chaque fois qu'un noeud est créé, il observe ses enfants. Quand un enfant s'insère ou se barre, le parent en informe une instance qui a été créée à  l'initialisation (_observer).
    De ce fait, on récupère les infos sur les enfants et leurs indexes relatifs dans une classe externe. C'est pour respecter le MVC.

    A faire: - au lieu des indexes relatifs, récupérer les NSIndexPath relatifs au rootNode. Et aussi, tant qu'à  faire récupérer les niveaux d'imbrication.

    Last but not least: un genre de classe dans ce gout là  existera dans leopard, comme quoi ça manquait vraiment.

    Voici:
    <br /><br />#import &quot;TreeNode.h&quot;<br /><br />@implementation NSTreeNode<br /><br />+ (id)treeNodeWithRepresentedObject:(id)modelObject observer:(id)observer{<br />&nbsp; &nbsp; return [[[NSTreeNode alloc] initWithObject:modelObject parent:nil children:[NSArray array] observer:(id)observer] autorelease]; <br />}<br /><br />- (id)initWithObject:(id)modelObject parent:(NSTreeNode*)parent children:(NSArray*)children observer:(id)observer{<br />&nbsp; &nbsp; self = [super init];<br />&nbsp; &nbsp; if (self){		<br />		_representedObject = [modelObject retain];<br />		_childNodes = [[NSMutableArray arrayWithArray:children] retain];<br />		_parentNode = parent;<br />		_observer = [observer retain];<br />		[self addObserver:self forKeyPath:@&quot;childNodes&quot; options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:self];<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return self;<br />}<br /><br />- (void)dealloc {<br />	[self removeObserver:self forKeyPath:@&quot;childNodes&quot;];<br />	[_observer release];<br />&nbsp; &nbsp; [_representedObject release];<br />&nbsp; &nbsp; [_childNodes release];<br />	_observer = nil;<br />&nbsp; &nbsp; _representedObject = nil;<br />&nbsp; &nbsp; _childNodes = nil;<br />&nbsp; &nbsp; [super dealloc];<br />}<br /><br />- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{<br />	[_observer observeValueForKeyPath:keyPath ofObject:object change:change context:NULL];	<br />}<br /><br />// ================================================================<br />// Methods used to manage a node and its children.<br />// ================================================================<br /><br />- (void)setRepresentedObject:(id)modelObject{<br />	if(modelObject!=_representedObject){<br />		[_representedObject release]; <br />		_representedObject = [modelObject retain];<br />	}<br />}<br /><br />- (id)representedObject { <br />&nbsp; &nbsp; return _representedObject; <br />}<br /><br />- (void)setParentNode:(NSTreeNode*)parent {<br />&nbsp; &nbsp; _parentNode = parent; <br />}<br /><br />- (NSTreeNode*)parentNode { <br />&nbsp; &nbsp; return _parentNode; <br />}<br /><br /> // Insert<br />- (void)insertObject:(NSTreeNode*)child inChildNodesAtIndex:(int)index {<br />&nbsp; &nbsp; [_childNodes insertObject:child atIndex:index];<br />&nbsp; &nbsp; [child setParentNode: self];<br />}<br /><br />- (void)insertChildNodes:(NSArray*)children atIndexes:(NSIndexSet*)indexes {<br />&nbsp; &nbsp; &nbsp;[children makeObjectsPerformSelector:@selector(setParentNode:) withObject:self];<br />	 [_childNodes insertObjects:children atIndexes:indexes];<br />}<br /><br />- (void)insertChildNodes:(NSArray*)children atIndex:(int)index{<br />	NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index,[children count])];<br />&nbsp; &nbsp; [children makeObjectsPerformSelector:@selector(setParentNode:) withObject:self];<br />	[self insertChildNodes:(NSArray*)children atIndexes:(NSIndexSet*)indexes];<br />}<br /><br />- (void)insertRepresentedObjects:(NSArray*)objects atIndex:(int)index{<br />	[self insertRepresentedObjects:(NSArray*)objects atIndex:(int)index observable:(BOOL)YES];<br />}<br /><br />- (void)insertRepresentedObjects:(NSArray*)objects atIndex:(int)index observable:(BOOL)observable{<br />	NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(index,[objects count])];<br />	[self insertRepresentedObjects:(NSArray*)objects atIndexes:(NSIndexSet*)indexes observable:(BOOL)observable];<br />}<br /><br />- (void)insertRepresentedObjects:(NSArray*)objects atIndexes:(NSIndexSet*)indexes{<br />	[self insertRepresentedObjects:(NSArray*)objects atIndexes:(NSIndexSet*)indexes observable:(BOOL)YES];<br />}<br /><br />- (void)insertRepresentedObjects:(NSArray*)objects atIndexes:(NSIndexSet*)indexes observable:(BOOL)observable{<br /><br />	NSMutableArray *nodes = [NSMutableArray array];	<br />	NSEnumerator *e = [objects objectEnumerator];	<br />	id object;<br />	while (object= [e nextObject])<br />	{<br />		NSTreeNode *node = [NSTreeNode treeNodeWithRepresentedObject:object observer:(id)_observer];<br />		[node setChildrenKeyPath:[self childrenKeyPath]];<br />		[nodes addObject:node];<br />		<br />		SEL children_selector = NSSelectorFromString([self childrenKeyPath]);<br />		BOOL hasChildren = [object respondsToSelector:children_selector];		<br />		if (hasChildren)<br />		{<br />			NSArray *subobjects = [object performSelector:children_selector];			<br />			[node insertRepresentedObjects:(NSArray*)subobjects atIndex:0 observable:NO];<br />		}<br />	}<br />	<br />	[nodes makeObjectsPerformSelector:@selector(setParentNode:) withObject:self];<br />	<br />	if(observable)[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@&quot;childNodes&quot;];<br />	[_childNodes insertObjects:nodes atIndexes:indexes];<br />	if(observable)[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@&quot;childNodes&quot;];<br />}<br /><br />- (void)replaceObjectInChildNodesAtIndex:(unsigned)index withObject:(NSTreeNode*)child{<br />	[child setParentNode:self];<br />	[_childNodes replaceObjectAtIndex:index withObject:child];<br />}<br /><br />- (void)replaceChildNodesAtIndexes:(NSIndexSet*)indexes with:(NSArray*)children{<br />	[children makeObjectsPerformSelector:@selector(setParentNode:) withObject:self];<br />	[_childNodes replaceObjectsAtIndexes:indexes withObjects:children];<br />}<br /><br />-(NSArray*)allNodes{<br />	return [self allNodes:YES];	<br />}<br /><br />-(NSArray*)allNodes:(BOOL)reset{<br />	static NSMutableArray *nodes_accu = nil; <br />	if(nodes_accu==nil) nodes_accu = [NSMutableArray array];<br />	<br />	[nodes_accu addObject:self];<br />	<br />	NSEnumerator *e = [[self childNodes] objectEnumerator];	<br />	id object;	<br />	while (object= [e nextObject])<br />		[object allNodes:NO];<br />	<br />	NSArray *finalnodes = [NSArray arrayWithArray:nodes_accu];<br />	if(reset) nodes_accu = nil;<br />	<br />	return finalnodes;<br />}<br /><br />- (int)level{<br />	int level = 0;<br />	NSTreeNode *node = self;<br />	<br />	while(node=[node parentNode])<br />		level ++;<br />	<br />	return level;<br />}<br /><br />// remove<br /><br />- (void)removeObjectFromChildNodesAtIndex:(int)index{<br />	NSTreeNode *child = [_childNodes objectAtIndex:index];<br />	[child removeAllChilds];<br />	[child setParentNode:nil];<br />	[_childNodes removeObjectAtIndex:index];<br />}<br /><br />- (void)removeChildNodesAtIndexes:(NSIndexSet*)indexes{<br />	NSArray *childs = [_childNodes objectsAtIndexes:indexes];<br />	[childs makeObjectsPerformSelector:@selector(removeAllChilds)];<br />	[childs makeObjectsPerformSelector:@selector(setParentNode:) withObject:nil];	<br />	[_childNodes removeObjectsAtIndexes:indexes];<br />}<br /><br />-(void)removeAllChilds{<br />	int numChilds = [self numberOfChildren];<br />	NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,numChilds)];<br />	if(numChilds&gt;0)[self removeChildNodesAtIndexes:indexes];	<br />}<br /><br />- (void)removeChild:(NSTreeNode*)child {<br />&nbsp; &nbsp; int index = [self indexOfChild: child];<br />&nbsp; &nbsp; if (index!=NSNotFound) {<br />		[child setParentNode:nil];<br />		[self removeObjectFromChildNodesAtIndex:index];<br />&nbsp; &nbsp; }<br />}<br /><br />- (void)removeFromParent {<br />	[[self parentNode] removeChild:self];<br />}<br /><br />// Get infos about node<br /><br />- (NSArray *)childNodes {<br />&nbsp; &nbsp; return&nbsp; _childNodes;<br />}<br /><br />- (NSMutableArray *)mutableChildNodes {<br />&nbsp; &nbsp; return [NSMutableArray arrayWithArray: _childNodes];<br />}<br /><br />- (int)numberOfChildren {<br />&nbsp; &nbsp; return [self countOfChildNodes];<br />}<br /><br />- (int)countOfChildNodes{<br />	return [_childNodes count];	<br />}<br /><br />- (NSTreeNode*)childAtIndex:(int)index {<br />&nbsp; &nbsp; return [self ObjectInChildNodesAtIndex:index];<br />}<br /><br />- (NSTreeNode*)ObjectInChildNodesAtIndex:(int)index {<br />&nbsp; &nbsp; return [_childNodes objectAtIndex:index];<br />}<br /><br />- (int)indexOfChild:(NSTreeNode*)child {<br />&nbsp; &nbsp; return [_childNodes indexOfObject:child];<br />}<br /><br />- (int)indexOfChildIdenticalTo:(NSTreeNode*)child {<br />&nbsp; &nbsp; return [_childNodes indexOfObjectIdenticalTo:child];<br />}<br /><br />- (BOOL)isLeaf{<br />	return ([self countOfChildNodes] == 0);	<br />}<br /><br />- (NSTreeNode*)firstChild {<br />&nbsp; &nbsp; return [_childNodes objectAtIndex:0];<br />}<br /><br />- (NSTreeNode*)lastChild {<br />&nbsp; &nbsp; return [_childNodes lastObject];<br />}<br /><br />- (BOOL)isDescendantOfNode:(NSTreeNode*)node {<br />&nbsp; &nbsp; // returns YES if &#39;node&#39; is an ancestor.<br />&nbsp; &nbsp; // Walk up the tree, to see if any of our ancestors is &#39;node&#39;.<br />&nbsp; &nbsp; NSTreeNode *parent = self;<br />&nbsp; &nbsp; while(parent) {<br />&nbsp; &nbsp; &nbsp; &nbsp; if(parent==node) return YES;<br />&nbsp; &nbsp; &nbsp; &nbsp; parent = [parent parentNode];<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return NO;<br />}<br /><br />- (BOOL)isDescendantOfNodeInArray:(NSArray*)nodes {<br />&nbsp; &nbsp; // returns YES if any &#39;node&#39; in the array &#39;nodes&#39; is an ancestor of ours.<br />&nbsp; &nbsp; // For each node in nodes, if node is an ancestor return YES.&nbsp; If none is an<br />&nbsp; &nbsp; // ancestor, return NO.<br />&nbsp; &nbsp; NSEnumerator *nodeEnum = [nodes objectEnumerator];<br />&nbsp; &nbsp; NSTreeNode *node = nil;<br />&nbsp; &nbsp; while((node=[nodeEnum nextObject])) {<br />&nbsp; &nbsp; &nbsp; &nbsp; if([self isDescendantOfNode:node]) return YES;<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return NO;<br />}<br /><br />- (NSString*)childrenKeyPath{<br />	return 	childrenKeyPath;<br />}<br /><br />- (void)setChildrenKeyPath:(NSString*)path{<br />	if(childrenKeyPath!=path){<br />		[childrenKeyPath release];<br />		childrenKeyPath = [path retain];<br />	}<br />}<br /><br />- (NSString*)description{<br />&nbsp; &nbsp; // Return something that will be useful for debugging.<br />&nbsp; &nbsp; return [NSString stringWithFormat: @&quot;&#092;nRepresentedObject: {%@}&#092;nSubNodes: {%@}&#092;nisLeaf: %d&quot;, _representedObject,_childNodes,[self isLeaf]];<br />}<br /><br />- (int)indexOfNode:(NSTreeNode*)node{	<br />	return [[self allNodes] indexOfObject:node];<br />}<br /><br />@end<br /><br />
    


    Header:
    <br />//<br />//&nbsp; TreeNode.h<br />//<br />//&nbsp; Copyright (c) 2001-2002, Apple. All rights reserved.<br />//<br /><br />#import &lt;Foundation/NSObject.h&gt;<br /><br />@class NSArray, NSMutableArray, NSIndexPath;<br />@interface NSTreeNode : NSObject {<br /><br />&nbsp; &nbsp; NSTreeNode		*_parentNode;<br />	id				_observer;<br />&nbsp; &nbsp; id				_representedObject;<br />&nbsp; &nbsp; NSMutableArray	*_childNodes;<br />	NSString		*childrenKeyPath;<br />}<br />+ (id)treeNodeWithRepresentedObject:(id)modelObject observer:(id)observer;<br />- (id)initWithObject:(id)modelObject parent:(NSTreeNode*)parent children:(NSArray*)children observer:(id)observer;<br /><br />- (void)setRepresentedObject:(id)modelObject;<br />- (id)representedObject;<br /><br />- (void)setParentNode:(NSTreeNode*)parent;<br />- (NSTreeNode*)parentNode;<br /><br />- (void)insertObject:(NSTreeNode*)child inChildNodesAtIndex:(int)index;<br />- (void)insertChildNodes:(NSArray*)children atIndexes:(NSIndexSet*)indexes;<br />- (void)insertChildNodes:(NSArray*)children atIndex:(int)index;<br />- (void)insertRepresentedObjects:(NSArray*)objects atIndexes:(NSIndexSet*)indexes;<br />- (void)insertRepresentedObjects:(NSArray*)objects atIndex:(int)index;<br /><br />- (void)removeAllChilds;<br />- (void)removeObjectFromChildNodesAtIndex:(int)index;<br />- (void)removeChildNodesAtIndexes:(NSIndexSet*)indexes;<br />- (void)removeChild:(NSTreeNode*)child;<br />- (void)removeFromParent;<br /><br />- (NSArray *)childNodes;<br />- (NSMutableArray *)mutableChildNodes;<br />- (int)numberOfChildren ;<br />- (int)countOfChildNodes;<br />- (int)indexOfChild:(NSTreeNode*)child;<br />- (int)indexOfChildIdenticalTo:(NSTreeNode*)child;<br />- (int)indexOfNode:(id)object start:(int)index;<br /><br />- (NSTreeNode*)childAtIndex:(int)index;<br />- (NSTreeNode*)ObjectInChildNodesAtIndex:(int)index;<br />- (NSTreeNode*)firstChild; <br />- (NSTreeNode*)lastChild;<br /><br />- (BOOL)isLeaf;<br />- (BOOL)isDescendantOfNode:(NSTreeNode*)node; <br />- (BOOL)isDescendantOfNodeInArray:(NSArray*)nodes;<br /><br />@end<br /><br />
    
  • Philippe49Philippe49 Membre
    01:51 modifié #7
    Merci laurris, je vais analyser cela
  • Philippe49Philippe49 Membre
    01:51 modifié #8
    dans 1177187121:

    Merci laurris, je vais analyser cela


    Une remarque :
    La méthode isLeaf écrite ainsi ne risque-t-elle pas l'incompatibilité avec une gestion par NSTreeController. En effet pour ajouter un noeud par l'interface graphique, NSTreeController demande d'abord si il à  faire à  une feuille. Si on crée un NSTreeNode (suffixe NS réservé à  Apple) sans fils, on ne pourra jamais ajouter de feuille par l'interface. 

    Une question que je me pose :
    Tu as mis beaucoup de méthodes du style accessor ou KVO. Je me demande toujours dans quelle mesure il faut préciser ces méthodes si leur comportement est standard, du style
    -(void) setChamp:(int) aChamp { champ=aChamp;}
    Je suis prêt à  changer mon fusil d'épaule, mais pour moi, il me semble qu'il faut laisser les mécanismes Cocoa agir, sauf si on a une bonne raison d'intervenir.
  • laurrislaurris Membre
    01:51 modifié #9
    dans 1177231591:

    Une remarque :
    La méthode isLeaf écrite ainsi ne risque-t-elle pas l'incompatibilité avec une gestion par NSTreeController. En effet pour ajouter un noeud par l'interface graphique, NSTreeController demande d'abord si il à  faire à  une feuille. Si on crée un NSTreeNode (suffixe NS réservé à  Apple) sans fils, on ne pourra jamais ajouter de feuille par l'interface. 

    J'ai l'impression que tu supposes que ma classe doit être utilisée avec NSTreeController comme controller et NSOutlineView comme View (en reprenant le paradigme Model-View-Controller).
    En fait, c'est pas tout à  fait ça. Cette classe POURRAIT être utilisée avec NSTreeController (pas testé néanmoins) en spécifiant setObjectClass:[NSTreeNode class]. Mais on est pas obligé, tout comme on est pas obligé d'utiliser NSArrayController pour bénéficier des avantages de la KVO-compliance de NSMutableArray.
    Je ne crois pas que j'ai répondu à  ta question mais c'est parce qu'on ne parle pas de la même utilisation.


    Une question que je me pose :
    Tu as mis beaucoup de méthodes du style accessor ou KVO. Je me demande toujours dans quelle mesure il faut préciser ces méthodes si leur comportement est standard, du style
    -(void) setChamp:(int) aChamp { champ=aChamp;}
    Je suis prêt à  changer mon fusil d'épaule, mais pour moi, il me semble qu'il faut laisser les mécanismes Cocoa agir, sauf si on a une bonne raison d'intervenir.


    Si on veut insérer ou supprimer des éléments à  la main dans la collection observée, on est bien obligé d'appeler ces accesseurs, donc de les avoir dans le code.
    C'est si on passe par un NSArrayController qu'on en a pas besoin.

    Je suis bien conscient que mon code peut paraitre mélanger plusieurs choses. En fait c'est parce que dans le cas d'un arbre, il faut que chaque noeud "controlle" ses enfants. Donc chaque noeud est à  la fois le "model" de son parent et le "controller" de ses enfants. D'où la rupture de la séparation MVC. Je vois pas comment faire autrement.


  • Philippe49Philippe49 Membre
    01:51 modifié #10
    dans 1177248172:

    on ne parle pas de la même utilisation.

    Oui, j'ai bien compris que tu as fait une classe pour gérer une arborescence profitant du KVO, mais sans pour cela que ce soit forcément lié avec des bidings.
    Cela fait déjà  quelques temps que je réfléchis sur les structures arborescentes et j'ai écrit des versions sans bindings et là  j'essaie avec. Ce qui me semble ressortir de ton expérience et de la mienne c'est qu'il est difficile de faire une classe qui réponde aux deux modèles de fonctionnement sans quelques aménagements. C'est un peu comme si il y avait là  deux modèles de structures, difficiles à  rendre compatibles. Mais ce n'est peut-être pas impossible ...

    Une preuve, c'est que toi comme moi, on met "naturellement" return [children count]; pour la méthode isLeaf. En binding cela coince, en version naturelle c'est logique.
    Pour cette question, il me semble que cela vient de la surcharge de cette notion. isLeaf peut signifier l'état d'être sans descendance mais également le fait de ne pas en vouloir. La différence entre stérile et sans enfant.

    dans 1177248172:

    Si on veut insérer ou supprimer des éléments à  la main dans la collection observée, on est bien obligé d'appeler ces accesseurs, donc de les avoir dans le code.

    Cocoa prévoit d'autres mécanismes, comme valueForKey: par exemple.
    Ce que je n'ai pas compris dans la doc d'Apple c'est le KVO compliant. Si on lit, on a l'impression qu'il faut mettre ces méthodes, et si on essaie sans, cela marche quand même.
    Les erreurs du type "this class is not KVO compliant" que j'ai rencontrées sont en général des erreurs de code, ou des non-définitions de variables, et non des erreurs dues à  l'absence de ces méthodes. J'en ai déduit peut-être trop rapidement qu'il y avait deux stratégies :
    + mettre systématiquement toutes ces méthodes (j'ai d'ailleurs fait un programme C pour cela)
    + soit ne les mettre que si leur réaction différait d'une réaction automatique.

    Mon expérience est trop courte, je n'ai pas de réponse. Peut-être me trompjes ?



    dans 1177248172:

    En fait c'est parce que dans le cas d'un arbre, il faut que chaque noeud "controlle" ses enfants. Donc chaque noeud est à  la fois le "model" de son parent et le "controller" de ses enfants.

    Si tu as du temps, tu peux détailler cela ?


    Merci pour ton code, cela m'a permis de confronter mes idées avec les tiennes.
    @+
  • laurrislaurris Membre
    avril 2007 modifié #11

    Une preuve, c'est que toi comme moi, on met "naturellement" return [children count]; pour la méthode isLeaf. En binding cela coince, en version naturelle c'est logique.
    Pour cette question, il me semble que cela vient de la surcharge de cette notion. isLeaf peut signifier l'état d'être sans descendance mais également le fait de ne pas en vouloir. La différence entre stérile et sans enfant.

    Je vois ce que tu veux dire. Mais tout dépend de ce qu'on veut faire: utiliser normalement NSTreeController avec une classe perso. Ou bien essayer de refaire le mécanisme interne de NSTreeController ( ou ce que je suppose qu'il est) comme dans mon cas.

    Je voudrais pas embrouiller encore plus l'affaire mais si on essaie de savoir comment NSTreeController et NSOutlineView discutent ensemble, on peut regarder ceci:
    http://www.blueboxmoon.com/wiki/?page=Binding Tree


    + soit ne les mettre que si leur réaction différait d'une réaction automatique.

    Mon expérience est trop courte, je n'ai pas de réponse. Peut-être me trompjes ?

    Effectivement, les accesseurs KVO ne sont en général utiles que s'ils modifient le comportement habituel. Sauf que: si l'on veut modifier des éléments d'une collection à  la main (donc pas automatique) et que l'on veut que l'observer de la collection soit prévenu, il faut appeller ces méthodes explicitement. Donc les implémenter dans l'observer.
    En fait le problème n'est pas vraiment la "réaction" au changement mais plutôt de savoir si ce changement sera observé ou pas.


    En fait c'est parce que dans le cas d'un arbre, il faut que chaque noeud "controlle" ses enfants. Donc chaque noeud est à  la fois le "model" de son parent et le "controller" de ses enfants.

    Si tu as du temps, tu peux détailler cela ?

    Heu ... détailler pourquoi il est plus logique que le parent controlle ses enfants ? Parce que dans  une collection "plate", les éléments sont organisés de façon absolue donc on peut les contrôler depuis l'extérieur. Alors que dans une collection en arbre, les enfants sont organisés avec des indexes relatifs à  leur parent, donc il parait plus logique que ce soit leur parent qui les contrôle. On peut peut-être faire autrement mais moi j'ai pas trouvé :)


    Merci pour ton code, cela m'a permis de confronter mes idées avec les tiennes.
    @+


    Tout le plaisir est pour moi. D'ailleurs je n'ai toujours pas trouvé LA solution définitive. Il existe des exemples de classe NSBranche en circulation (assez peu j'ai l'impression) et chacune aborde le problème d'une façon différente.
  • Philippe49Philippe49 Membre
    avril 2007 modifié #12
    dans 1177321435:

    si on essaie de savoir comment NSTreeController et NSOutlineView discutent ensemble, on peut regarder ceci:
    http://www.blueboxmoon.com/wiki/?page=Binding Tree


    Voici une autre source qui montre des en-têtes Apple (sur un site perso néammoins)
    http://edeltraut.muellner.de/9Pad/trunk/NSTreeController_Extensions.m
Connectez-vous ou Inscrivez-vous pour répondre.