Skip to content

NSBrowser bindings changes and general binding improvements #284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
691bf43
NSTableView experimental changes to improve bindings
gcasa Aug 23, 2024
f6231b8
Revert temporary experimental change (for now)
gcasa Aug 25, 2024
35fb894
Update bindings handlng in NSBrowser, NSTextFieldCell
gcasa Aug 28, 2024
fbad487
Merge branch 'master' of github.com:gnustep/libs-gui into NSBrowser_b…
gcasa Sep 28, 2024
d04a1b8
Add code to handle bindings in NSBrowser, skeletal implementation, av…
gcasa Oct 7, 2024
2916a92
Add code to get node data from NSTreeController
gcasa Oct 7, 2024
417a33d
Merge branch 'master' into NSBrowser_bindings_branch
gcasa Oct 8, 2024
245ec26
Fixed issue with getting value and initial item
gcasa Oct 10, 2024
427b9af
Update code to pull the column and selected row properly
gcasa Oct 11, 2024
c3146ba
Clean up white space
gcasa Oct 11, 2024
664fdad
Cleanup setValue:forKey: and valueForKey:
gcasa Oct 11, 2024
5a16976
Simplify NSContentBinding/NSContentValuesBindings in setValue:forKey:…
gcasa Oct 14, 2024
5fd4ff0
Merge branch 'master' into NSBrowser_bindings_branch
gcasa Oct 14, 2024
56d6fa0
Use better variable name for value key
gcasa Oct 19, 2024
10256c9
Merge branch 'master' of github.com:gnustep/libs-gui into NSBrowser_b…
gcasa Oct 30, 2024
59f91c2
Merge branch 'master' of github.com:gnustep/libs-gui into NSBrowser_b…
gcasa Oct 31, 2024
70cdc3c
Merge branch 'master' of github.com:gnustep/libs-gui into NSBrowser_b…
gcasa Nov 3, 2024
557796a
Merge branch 'master' of github.com:gnustep/libs-gui into NSBrowser_b…
gcasa Nov 6, 2024
5580e46
Update ChangeLog to show NSBrowser changes
gcasa Nov 9, 2024
041b8fd
Fix call to rootItemForBrowser as discussed with @fredkiefer
gcasa Nov 11, 2024
aac04a1
Comment unneeded code as discussed with @fredkiefer
gcasa Nov 11, 2024
2bcc354
Merge branch 'master' into NSBrowser_bindings_branch
gcasa Dec 10, 2024
1a74cb6
Cleanup code whitespace
gcasa Dec 12, 2024
a3ebaf2
Merge branch 'master' of github.com:gnustep/libs-gui into NSBrowser_b…
gcasa Jan 1, 2025
08163b4
Merge branch 'master' into NSBrowser_bindings_branch
gcasa Jan 5, 2025
fbd539a
Merge branch 'master' into NSBrowser_bindings_branch
gcasa Jan 10, 2025
4bb57d4
Merge branch 'master' into NSBrowser_bindings_branch
gcasa Jan 18, 2025
fbb22c0
Fix handling of leaf nodes
gcasa Jan 19, 2025
86f97ac
Clean up comments
gcasa Jan 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
2024-11-08 Gregory John Casamento <[email protected]>

* Headers/AppKit/NSBrowser.h: Add dictionary to hold relationships.
* Source/NSBrowser.m: Add logic in methods to support binfings,
expose bindings in +initialize.
* Source/NSTextFieldCell.m: Minor fix to NSTextField for displaying
binding values.

2024-10-31 Richard Frith-Macdonald <[email protected]>

* Source/GSServicesManager.m: fix -laterDate: conditionals to be true
Expand Down
2 changes: 2 additions & 0 deletions Headers/AppKit/NSBrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
@class NSArray;
@class NSIndexPath;
@class NSIndexSet;
@class NSMutableDictionary;

@class NSCell;
@class NSEvent;
Expand Down Expand Up @@ -100,6 +101,7 @@ APPKIT_EXPORT_CLASS
NSBrowserColumnResizingType _columnResizing;

BOOL _itemBasedDelegate;
NSMutableDictionary *_columnDictionary;
}

//
Expand Down
246 changes: 221 additions & 25 deletions Source/NSBrowser.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
Date: September 2002
Author: Gregory Casamento <[email protected]>
Date: July 2024
Note: Added support for 10.6+ delegate methods.
Note: Added support for 10.6+ delegate methods. Added bindings support

This file is part of the GNUstep GUI Library.

Expand Down Expand Up @@ -56,10 +56,13 @@
#import "AppKit/NSColor.h"
#import "AppKit/NSFont.h"
#import "AppKit/NSGraphics.h"
#import "AppKit/NSKeyValueBinding.h"
#import "AppKit/NSMatrix.h"
#import "AppKit/NSScroller.h"
#import "AppKit/NSScrollView.h"
#import "AppKit/NSTableHeaderCell.h"
#import "AppKit/NSTreeController.h"
#import "AppKit/NSTreeNode.h"
#import "AppKit/NSEvent.h"
#import "AppKit/NSViewController.h"
#import "AppKit/NSWindow.h"
Expand Down Expand Up @@ -260,6 +263,10 @@ - (NSBorderType) _resolvedBorderType;
- (void) _themeDidActivate: (NSNotification*)notification;
@end

// Category to handle bindings
@interface NSBrowser (GSBindingsPrivate)
@end

//
// NSBrowser implementation
//
Expand Down Expand Up @@ -2268,7 +2275,7 @@ - (void) setDelegate: (id)anObject
_itemBasedDelegate = NO;

if ([anObject respondsToSelector:
@selector(browser:numberOfChildrenOfItem:)]
@selector(browser:numberOfChildrenOfItem:)]
&& [anObject respondsToSelector:
@selector(browser:child:ofItem:)]
&& [anObject respondsToSelector:
Expand Down Expand Up @@ -2486,6 +2493,10 @@ + (void) initialize
}

[self _themeDidActivate: nil];

// Bindings...
[self exposeBinding: NSContentBinding];
[self exposeBinding: NSContentValuesBinding];
}
}

Expand Down Expand Up @@ -2558,6 +2569,7 @@ - (id) initWithFrame: (NSRect)rect

// Item based delegate, 10.6+
_itemBasedDelegate = NO;
_columnDictionary = [[NSMutableDictionary alloc] init];

[[NSNotificationCenter defaultCenter]
addObserver: self
Expand All @@ -2583,6 +2595,7 @@ - (void) dealloc
RELEASE(_pathSeparator);
RELEASE(_horizontalScroller);
RELEASE(_browserColumns);
RELEASE(_columnDictionary);
TEST_RELEASE(_charBuffer);

[super dealloc];
Expand Down Expand Up @@ -3009,6 +3022,7 @@ - (id) initWithCoder: (NSCoder*)aDecoder

// Item based delegate, 10.6+
_itemBasedDelegate = NO;
_columnDictionary = [[NSMutableDictionary alloc] init];

// Horizontal scroller
_scrollerRect.origin.x = bs.width;
Expand Down Expand Up @@ -3296,10 +3310,19 @@ - (void) _remapColumnSubviews: (BOOL)fromFirst
- (id) _itemForColumn: (NSInteger)column
{
id item = nil;

GSKeyValueBinding *theBinding;
theBinding = [GSKeyValueBinding getBinding: NSContentBinding
forObject: self];
if (column == 0)
{
item = [_browserDelegate rootItemForBrowser: self];
if (theBinding == nil)
{
item = [_browserDelegate rootItemForBrowser: self];
}
else
{
item = nil; // [NSNull null];
}
}
else
{
Expand All @@ -3319,7 +3342,20 @@ - (id) _itemForColumn: (NSInteger)column
{
id cell = [selectedCells objectAtIndex: 0];

item = [cell objectValue];
if (theBinding != nil)
{
NSNumber *colNum = [NSNumber numberWithInteger: col];
NSArray *array = [_columnDictionary objectForKey: colNum];
if ([array count] > 0)
{
NSInteger row = [self selectedRowInColumn: col];
item = [array objectAtIndex: row];
}
}
else
{
item = [cell objectValue];
}
}
}
}
Expand All @@ -3328,22 +3364,104 @@ - (id) _itemForColumn: (NSInteger)column
return item;
}

- (NSString *) _keyPathForValueBinding
{
NSString *keyPath = nil;
NSDictionary *info = [GSKeyValueBinding infoForBinding: NSContentValuesBinding
forObject: self];
if (info != nil)
{
NSString *ikp = [info objectForKey: NSObservedKeyPathKey];
NSUInteger location = [ikp rangeOfString: @"."].location;

keyPath = (location == NSNotFound ? ikp : [ikp substringFromIndex: location + 1]);
}

return keyPath;
}

/* Loads column 'column' (asking the delegate). */
- (void) _performLoadOfColumn: (NSInteger)column
{
NSBrowserColumn *bc;
NSScrollView *sc;
NSMatrix *matrix;
NSInteger i, rows, cols;
NSBrowserColumn *bc = nil;
NSScrollView *sc = nil;
NSMatrix *matrix = nil;
NSInteger i = 0, rows = 0, cols = 0;
id child = nil;
id item = nil;
NSNumber *colNum = nil;
NSTreeController *tc = nil;
NSArray *children = nil;

if (_itemBasedDelegate)
{
GSKeyValueBinding *theBinding;

theBinding = [GSKeyValueBinding getBinding: NSContentBinding
forObject: self];

item = [self _itemForColumn: column];
if (theBinding != nil)
{
id observedObject = [theBinding observedObject];

rows = 0;
colNum = [NSNumber numberWithInteger: column];
if ([observedObject isKindOfClass: [NSTreeController class]])
{
tc = (NSTreeController *)observedObject;

if (item == nil)
{
NSTreeNode *node = (NSTreeNode *)[theBinding destinationValue];

if (node != nil)
{
/* Per the documentation 10.4/5+ uses NSTreeNode as the return value for
* the contents of this tree node consists of a dictionary with a single
* key of "children". This is per the tests for this at
* https://github.com/gcasa/NSTreeController_test. Specifically it returns
* _NSControllerTreeProxy. The equivalent of that class in GNUstep is
* GSControllerTreeProxy.
*/
children = [node mutableChildNodes];
rows = [children count];
item = node;
}
}
else
{
NSString *childrenKeyPath = [tc childrenKeyPathForNode: item];

// Ask the delegate for the number of rows for a given item...
rows = [_browserDelegate browser: self numberOfChildrenOfItem: item];
if (childrenKeyPath != nil)
{
NSString *countKeyPath = [tc countKeyPathForNode: item];

children = [item valueForKeyPath: childrenKeyPath];
if (countKeyPath == nil)
{
rows = [children count]; // get the count directly...
}
else
{
NSNumber *countValue = [item valueForKeyPath: countKeyPath];
rows = [countValue integerValue];
}
}
}

// If the node has children, add them to the column...
if (children != nil)
{
[_columnDictionary setObject: children forKey: colNum];
}
}
}
else
{
// Ask the delegate for the number of rows for a given item...
rows = [_browserDelegate browser: self numberOfChildrenOfItem: item];
}
cols = 1;
}
else if (_passiveDelegate)
Expand Down Expand Up @@ -3410,23 +3528,64 @@ - (void) _performLoadOfColumn: (NSInteger)column
[sc setDocumentView: matrix];

// Loading is different based upon item/passive/active delegate
if (_itemBasedDelegate)
if (_itemBasedDelegate == YES) // && item != nil && tc != nil)
{
// Iterate over the children for the item....
for (i = 0; i < rows; i++)
NSString *childrenKeyPath = [tc childrenKeyPathForNode: item];

if (childrenKeyPath != nil)
{
id aCell = [matrix cellAtRow: i column: 0];
if (![aCell isLoaded])
NSString *leafKeyPath = [tc leafKeyPathForNode: item];
NSString *valueKeyPath = [self _keyPathForValueBinding];

// Iterate over the children for the item....
for (i = 0; i < rows; i++)
{
BOOL leaf = YES;
id val = nil;

child = [_browserDelegate browser: self child: i ofItem: item];
leaf = [_browserDelegate browser: self isLeafItem: child];
val = [_browserDelegate browser: self objectValueForItem: child];
[aCell setLeaf: leaf];
[aCell setObjectValue: val];
[aCell setLoaded: YES];
id aCell = [matrix cellAtRow: i column: 0];
if (![aCell isLoaded])
{
BOOL leaf = YES;
id val = nil;
NSNumber *leafBool = nil;

child = [children objectAtIndex: i];
leafBool = [child valueForKeyPath: leafKeyPath];
leaf = [leafBool boolValue];

// If a content values binding is present, it uses that key path,
// but if one isn't it uses the description... per documentation.
if (valueKeyPath != nil)
{
val = [child valueForKeyPath: valueKeyPath];
}
else
{
val = [child description]; // per documentation.
}

[aCell setLeaf: leaf];
[aCell setObjectValue: val];
[aCell setLoaded: YES];
}
}
}
else
{
// Iterate over the children for the item....
for (i = 0; i < rows; i++)
{
id aCell = [matrix cellAtRow: i column: 0];
if (![aCell isLoaded])
{
BOOL leaf = YES;
id val = nil;

child = [_browserDelegate browser: self child: i ofItem: item];
leaf = [_browserDelegate browser: self isLeafItem: child];
val = [_browserDelegate browser: self objectValueForItem: child];
[aCell setLeaf: leaf];
[aCell setObjectValue: val];
[aCell setLoaded: YES];
}
}
}
}
Expand Down Expand Up @@ -3592,3 +3751,40 @@ - (void) _themeDidActivate: (NSNotification*)notification
}

@end

@implementation NSBrowser (GSBindingsPrivate)

/* Private methods to handle bindings */

- (void) setValue: (id)anObject forKey: (NSString*)aKey
{
if ([aKey isEqual: NSContentBinding]
|| [aKey isEqual: NSContentValuesBinding])
{
// Reload data
_passiveDelegate = NO;
_itemBasedDelegate = YES;

[self loadColumnZero];
NSDebugLLog(@"NSBinding", @"Setting browser view content/values to %@", anObject);
}
else
{
[super setValue: anObject forKey: aKey];
}
}

- (id) valueForKey: (NSString*)aKey
{
if ([aKey isEqual: NSContentBinding]
|| [aKey isEqual: NSContentValuesBinding])
{
return nil;
}
else
{
return [super valueForKey: aKey];
}
}

@end
Loading
Loading