Add proxy objects to perform segue

This commit is contained in:
Gregory John Casamento 2020-07-02 13:24:48 -04:00
parent 435d688e98
commit 11b5b4d2d6
12 changed files with 529 additions and 61 deletions

View file

@ -34,7 +34,7 @@
extern "C" {
#endif
@protocol NSSequePerforming
@protocol NSSeguePerforming
- (void)performSegueWithIdentifier: (NSStoryboardSegueIdentifier)identifier
sender: (id)sender;

View file

@ -42,6 +42,9 @@ DEFINE_BLOCK_TYPE_NO_ARGS(GSStoryboardSeguePerformHandler, void);
id _sourceController;
id _destinationController;
NSStoryboardSegueIdentifier _identifier;
NSString *_kind;
NSString *_relationship;
GSStoryboardSeguePerformHandler _handler;
}
- (id) sourceController;

View file

@ -28,12 +28,13 @@ Boston, MA 02110-1301, USA.
#import <AppKit/NSNibDeclarations.h>
#import <AppKit/NSResponder.h>
#import <AppKit/NSSeguePerforming.h>
#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST)
@class NSArray, NSBundle, NSPointerArray, NSView;
@class NSArray, NSBundle, NSPointerArray, NSView, NSMapTable;
@interface NSViewController : NSResponder
@interface NSViewController : NSResponder <NSSeguePerforming>
{
@private
NSString *_nibName;
@ -45,6 +46,7 @@ Boston, MA 02110-1301, USA.
NSPointerArray *_editors;
id _autounbinder;
NSString *_designNibBundleIdentifier;
NSMapTable *_segueMap;
struct ___vcFlags
{
unsigned int nib_is_loaded:1;

View file

@ -62,6 +62,7 @@
@class NSView;
@class NSWindowController;
@class NSCachedImageRep;
@class NSViewController;
@class GSWindowDecorationView;
@ -336,6 +337,14 @@ PACKAGE_SCOPE
* Computing frame and content rectangles
*/
/**
* Returns a window with the view of the specified viewController as it's
* content view. The window is resizable, titled, closable, and miniaturizable.
*/
#if OS_API_VERSION(MAC_OS_X_VERSION_10_10, GS_API_LATEST)
+ (instancetype) windowWithContentViewController: (NSViewController *)viewController;
#endif
/**
* Returns the rectangle which would be used for the content view of
* a window whose on-screen size and position is specified by aRect

View file

@ -29,13 +29,15 @@
#import <AppKit/NSNibDeclarations.h>
#import <AppKit/NSResponder.h>
#import <AppKit/NSSeguePerforming.h>
@class NSString;
@class NSArray;
@class NSWindow;
@class NSDocument;
@class NSMapTable;
@interface NSWindowController : NSResponder <NSCoding>
@interface NSWindowController : NSResponder <NSCoding, NSSeguePerforming>
{
@private
NSWindow *_window;
@ -45,6 +47,7 @@
NSDocument *_document;
NSArray *_top_level_objects;
id _owner;
NSMapTable *_segueMap;
struct ___wcFlags
{
unsigned int should_close_document:1;

View file

@ -51,6 +51,7 @@ char **NSArgv = NULL;
@interface NSStoryboard (Private)
+ (void) setMainStoryboard: (NSStoryboard *)storyboard;
- (void) _instantiateApplicationScene;
@end
@ -107,6 +108,7 @@ NSApplicationMain(int argc, const char **argv)
else
{
[NSStoryboard setMainStoryboard: storyboard];
[storyboard _instantiateApplicationScene];
[storyboard instantiateInitialController];
}
}

View file

@ -225,7 +225,7 @@ static NSArray *XmlBoolDefaultYes = nil;
XmlTagsToSkip = [NSArray arrayWithObject: @"dependencies"];
RETAIN(XmlTagsToSkip);
ClassNamePrefixes = [NSArray arrayWithObjects: @"NS", @"IB", nil];
ClassNamePrefixes = [NSArray arrayWithObjects: @"NS", @"IB", @"GS", nil];
RETAIN(ClassNamePrefixes);
XmlReferenceAttributes = [NSArray arrayWithObjects: @"headerView", @"initialItem",

View file

@ -30,6 +30,7 @@
#import <Foundation/NSXMLNode.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSUUID.h>
#import "AppKit/NSApplication.h"
#import "AppKit/NSNib.h"
@ -42,6 +43,189 @@
static NSStoryboard *mainStoryboard = nil;
// The storyboard needs to set this information on controllers...
@interface NSWindowController (__StoryboardPrivate__)
- (void) _setOwner: (id)owner;
- (void) _setTopLevelObjects: (NSArray *)array;
- (void) _setSegueMap: (NSMapTable *)map;
@end
@interface NSViewController (__StoryboardPrivate__)
- (void) _setTopLevelObjects: (NSArray *)array;
- (void) _setSegueMap: (NSMapTable *)map;
@end
@interface NSStoryboardSegue (__StoryboardPrivate__)
- (void) _setKind: (NSString *)k;
- (void) _setRelationship: (NSString *)r;
@end
// this needs to be set on segues
@implementation NSStoryboardSegue (__StoryboardPrivate__)
- (void) _setKind: (NSString *)k
{
ASSIGN(_kind, k);
}
- (void) _setRelationship: (NSString *)r
{
ASSIGN(_relationship, r);
}
@end
@implementation NSWindowController (__StoryboardPrivate__)
- (void) _setOwner: (id)owner
{
_owner = owner; // weak
}
- (void) _setTopLevelObjects: (NSArray *)array
{
_top_level_objects = array;
}
- (void) _setSegueMap: (NSMapTable *)map
{
ASSIGN(_segueMap, map);
}
@end
@implementation NSViewController (__StoryboardPrivate__)
- (void) _setTopLevelObjects: (NSArray *)array
{
_topLevelObjects = array;
}
- (void) _setSegueMap: (NSMapTable *)map
{
ASSIGN(_segueMap, map);
}
@end
// end private methods...
@interface NSStoryboardSeguePerformAction : NSObject <NSCoding, NSCopying>
{
id _target;
SEL _action;
id _sender;
NSString *_identifier;
}
- (id) target;
- (void) setTarget: (id)target;
- (NSString *) selector;
- (void) setSelector: (NSString *)s;
- (SEL) action;
- (void) setAction: (SEL)action;
- (id) sender;
- (void) setSender: (id)sender;
- (NSString *) identifier;
- (void) setIdentifier: (NSString *)identifier;
@end
@implementation NSStoryboardSeguePerformAction
- (id) target
{
return _target;
}
- (void) setTarget: (id)target
{
ASSIGN(_target, target);
}
- (SEL) action
{
return _action;
}
- (void) setAction: (SEL)action
{
_action = action;
}
- (NSString *) selector
{
return NSStringFromSelector(_action);
}
- (void) setSelector: (NSString *)s
{
_action = NSSelectorFromString(s);
}
- (id) sender
{
return _sender;
}
- (void) setSender: (id)sender
{
ASSIGN(_sender, sender);
}
- (NSString *) identifier
{
return _identifier;
}
- (void) setIdentifier: (NSString *)identifier
{
ASSIGN(_identifier, identifier);
}
- (IBAction) doAction: (id)sender
{
[_target performSegueWithIdentifier: _identifier
sender: _sender];
}
- (id) copyWithZone: (NSZone *)z
{
NSStoryboardSeguePerformAction *pa = [[NSStoryboardSeguePerformAction allocWithZone: z] init];
[pa setTarget: _target];
[pa setSelector: [self selector]];
[pa setSender: _sender];
[pa setIdentifier: _identifier];
return pa;
}
- (instancetype) initWithCoder: (NSCoder *)coder
{
self = [super init];
if ([coder allowsKeyedCoding])
{
if ([coder containsValueForKey: @"NSTarget"])
{
[self setTarget: [coder decodeObjectForKey: @"NSTarget"]];
}
if ([coder containsValueForKey: @"NSSelector"])
{
[self setSelector: [coder decodeObjectForKey: @"NSSelector"]];
}
if ([coder containsValueForKey: @"NSSender"])
{
[self setSender: [coder decodeObjectForKey: @"NSSender"]];
}
if ([coder containsValueForKey: @"NSIdentifier"])
{
[self setIdentifier: [coder decodeObjectForKey: @"NSIdentifier"]];
}
}
return self;
}
- (void) encodeWithCoder: (NSCoder *)coder
{
// this is never encoded directly...
}
@end
@implementation NSStoryboard
- (NSXMLElement *) _createCustomObjectWithId: (NSString *)ident
@ -296,6 +480,120 @@ static NSStoryboard *mainStoryboard = nil;
}
}
- (NSMapTable *) _processSegues: (NSXMLDocument *)xmlIn
{
NSMapTable *mapTable = [NSMapTable strongToWeakObjectsMapTable];
NSArray *connectionsArray = [xmlIn nodesForXPath: @"//connections"
error: NULL];
NSArray *array = [xmlIn nodesForXPath: @"//objects"
error: NULL];
NSXMLElement *objects = [array objectAtIndex: 0]; // get the "objects" section
NSArray *controllers = [objects nodesForXPath: @"windowController"
error: NULL];
NSString *src = nil;
if ([controllers count] > 0)
{
NSXMLElement *controller = (NSXMLElement *)[controllers objectAtIndex: 0];
NSXMLNode *idAttr = [controller attributeForName: @"id"];
src = [idAttr stringValue];
}
else
{
controllers = [objects nodesForXPath: @"viewController"
error: NULL];
if ([controllers count] > 0)
{
NSXMLElement *controller = (NSXMLElement *)[controllers objectAtIndex: 0];
NSXMLNode *idAttr = [controller attributeForName: @"id"];
src = [idAttr stringValue];
}
}
if ([connectionsArray count] > 0)
{
NSXMLElement *connections = (NSXMLElement *)[connectionsArray objectAtIndex: 0];
NSArray *children = [connections children]; // there should be only one per set.
NSEnumerator *en = [children objectEnumerator];
id obj = nil;
while ((obj = [en nextObject]) != nil)
{
if ([[obj name] isEqualToString: @"segue"])
{
// get the information from the segue.
NSXMLNode *attr = [obj attributeForName: @"destination"];
NSString *dst = [attr stringValue];
attr = [obj attributeForName: @"kind"];
NSString *kind = [attr stringValue];
attr = [obj attributeForName: @"relationship"];
NSString *rel = [attr stringValue];
[obj detach]; // segue can't be in the archive since it doesn't conform to NSCoding
attr = [obj attributeForName: @"id"];
NSString *uid = [attr stringValue];
attr = [obj attributeForName: @"identifier"];
NSString *identifier = [attr stringValue];
if (identifier == nil)
{
identifier = [[NSUUID UUID] UUIDString];
}
// Create proxy object to invoke methods on the window controller
NSXMLElement *sbproxy = [NSXMLElement elementWithName: @"storyboardSeguePerformAction"];
NSXMLNode *pselector
= [NSXMLNode attributeWithName: @"selector"
stringValue: @"doAction:"];
NSXMLNode *ptarget
= [NSXMLNode attributeWithName: @"target"
stringValue: dst];
NSXMLNode *pident
= [NSXMLNode attributeWithName: @"identifier"
stringValue: identifier];
NSXMLNode *psender
= [NSXMLNode attributeWithName: @"sender"
stringValue: dst];
[sbproxy addAttribute: pselector];
[sbproxy addAttribute: ptarget];
[sbproxy addAttribute: pident];
[sbproxy addAttribute: psender];
NSUInteger count = [[objects children] count];
[objects insertChild: sbproxy
atIndex: count - 1];
// Create action...
NSXMLElement *conns = [NSXMLElement elementWithName: @"connections"];
NSXMLElement *action = [NSXMLElement elementWithName: @"action"];
NSXMLNode *selector
= [NSXMLNode attributeWithName: @"selector"
stringValue: @"doAction:"];
NSXMLNode *target
= [NSXMLNode attributeWithName: @"target"
stringValue: dst];
NSXMLNode *ident
= [NSXMLNode attributeWithName: @"id"
stringValue: uid];
[action addAttribute: selector];
[action addAttribute: target];
[action addAttribute: ident];
[conns addChild: action]; // add to parent..
[sbproxy addChild: conns];
// Create the segue...
NSStoryboardSegue *ss = [[NSStoryboardSegue alloc] initWithIdentifier: identifier
source: src
destination: dst];
[ss _setKind: kind];
[ss _setRelationship: rel];
// Add to maptable...
[mapTable setObject: ss
forKey: identifier];
}
}
}
return mapTable;
}
// Private instance methods...
- (id) initWithName: (NSStoryboardName)name
bundle: (NSBundle *)bundle
@ -344,9 +642,8 @@ static NSStoryboard *mainStoryboard = nil;
[super dealloc];
}
- (id) instantiateInitialController
- (void) _instantiateApplicationScene
{
id controller = nil;
NSDictionary *table;
table = [NSDictionary dictionaryWithObject: NSApp
@ -358,72 +655,83 @@ static NSStoryboard *mainStoryboard = nil;
BOOL success = [loader loadModelData: xmlData
externalNameTable: table
withZone: [self zone]];
if (!success)
{
NSLog(@"Unabled to load Application scene");
}
}
- (id) instantiateInitialController
{
return [self instantiateControllerWithIdentifier: _initialViewControllerId];
}
- (id) instantiateInitialControllerWithCreator: (NSStoryboardControllerCreator)block // 10.15
{
id controller = [self instantiateInitialController];
CALL_BLOCK(block, self);
return controller;
}
- (id) instantiateControllerWithIdentifier: (NSStoryboardSceneIdentifier)identifier
{
id controller = nil;
NSMutableArray *topLevelObjects = [NSMutableArray arrayWithCapacity: 5];
NSDictionary *table = [NSDictionary dictionaryWithObjectsAndKeys: topLevelObjects,
NSNibTopLevelObjects,
NSApp,
NSNibOwner,
nil];
NSString *sceneId = [_controllerMap objectForKey: identifier];
NSXMLDocument *xml = [_scenesMap objectForKey: sceneId];
NSMapTable *segueMap = [self _processSegues: xml];
NSData *xmlData = [xml XMLData];
GSModelLoader *loader = [GSModelLoaderFactory modelLoaderForFileType: @"xib"];
BOOL success = [loader loadModelData: xmlData
externalNameTable: table
withZone: [self zone]];
if (success)
{
NSMutableArray *topLevelObjects = [NSMutableArray arrayWithCapacity: 5];
NSDictionary *table = [NSDictionary dictionaryWithObjectsAndKeys: topLevelObjects,
NSNibTopLevelObjects,
NSApp,
NSNibOwner,
nil];
NSString *initialSceneId = [_controllerMap objectForKey: _initialViewControllerId];
xml = [_scenesMap objectForKey: initialSceneId];
xmlData = [xml XMLData];
loader = [GSModelLoaderFactory modelLoaderForFileType: @"xib"];
success = [loader loadModelData: xmlData
externalNameTable: table
withZone: [self zone]];
NSLog(@"table = %@", table);
if (success)
NSEnumerator *en = [topLevelObjects objectEnumerator];
id o = nil;
while ((o = [en nextObject]) != nil)
{
NSEnumerator *en = [topLevelObjects objectEnumerator];
id o = nil;
while ((o = [en nextObject]) != nil)
if ([o isKindOfClass: [NSWindowController class]])
{
if ([o isKindOfClass: [NSWindowController class]])
{
controller = o;
}
if ([o isKindOfClass: [NSWindow class]] &&
[controller isKindOfClass: [NSWindowController class]])
{
[controller setWindow: o];
[controller showWindow: self];
}
else if ([o isKindOfClass: [NSViewController class]])
{
}
controller = o;
[controller _setSegueMap: segueMap];
}
if ([o isKindOfClass: [NSWindow class]] &&
[controller isKindOfClass: [NSWindowController class]])
{
[controller _setOwner: NSApp];
[controller _setTopLevelObjects: topLevelObjects];
[controller setWindow: o];
[controller showWindow: self];
}
else if ([o isKindOfClass: [NSViewController class]])
{
NSWindow *w = [NSWindow windowWithContentViewController: o];
[w orderFrontRegardless];
}
}
else
{
NSLog(@"Couldn't load initial controller scene");
}
}
else
{
NSLog(@"Couldn't load application scene.");
NSLog(@"Couldn't load initial controller scene");
}
return controller;
}
- (id) instantiateInitialControllerWithCreator: (NSStoryboardControllerCreator)block // 10.15
{
return nil;
}
- (id) instantiateControllerWithIdentifier: (NSStoryboardSceneIdentifier)identifier
{
return nil;
}
- (id) instantiateControllerWithIdentifier: (NSStoryboardSceneIdentifier)identifier
creator: (NSStoryboardControllerCreator)block // 10.15
{
return nil;
id controller = [self instantiateControllerWithIdentifier: identifier];
CALL_BLOCK(block, self);
return controller;
}
@end

View file

@ -22,6 +22,7 @@
Boston, MA 02110 USA.
*/
#import <Foundation/NSString.h>
#import "AppKit/NSStoryboardSegue.h"
@implementation NSStoryboardSegue
@ -41,6 +42,11 @@
return _identifier;
}
- (void) _setHandler: (GSStoryboardSeguePerformHandler)handler
{
ASSIGN(_handler, handler);
}
+ (instancetype)segueWithIdentifier: (NSStoryboardSegueIdentifier)identifier
source: (id)sourceController
destination: (id)destinationController
@ -50,7 +56,8 @@
source: sourceController
destination: destinationController];
AUTORELEASE(segue);
CALL_BLOCK_NO_ARGS(performHandler);
[segue _setHandler: performHandler];
return segue;
}
@ -68,9 +75,31 @@
return self;
}
- (void) dealloc
{
RELEASE(_sourceController);
RELEASE(_destinationController);
RELEASE(_identifier);
RELEASE(_kind);
RELEASE(_relationship);
RELEASE(_handler);
[super dealloc];
}
- (void)perform
{
// TBD...
if ([_kind isEqualToString: @"relationship"])
{
}
else if ([_kind isEqualToString: @"modal"])
{
}
else if ([_kind isEqualToString: @"show"])
{
}
// Perform segue based on it's kind...
CALL_BLOCK_NO_ARGS(_handler);
}
@end

View file

@ -29,11 +29,13 @@
#import <Foundation/NSBundle.h>
#import <Foundation/NSKeyedArchiver.h>
#import <Foundation/NSString.h>
#import <Foundation/NSMapTable.h>
#import "AppKit/NSKeyValueBinding.h"
#import "AppKit/NSNib.h"
#import "AppKit/NSView.h"
#import "AppKit/NSViewController.h"
#import "AppKit/NSStoryboardSegue.h"
@implementation NSViewController
@ -46,7 +48,7 @@
ASSIGN(_nibName, nibNameOrNil);
ASSIGN(_nibBundle, nibBundleOrNil);
return self;
}
@ -63,7 +65,8 @@
DESTROY(_autounbinder);
DESTROY(_designNibBundleIdentifier);
DESTROY(view);
DESTROY(_segueMap);
[super dealloc];
}
@ -151,6 +154,7 @@
return nil;
}
_segueMap = RETAIN([NSMapTable strongToWeakObjectsMapTable]);
if ([aDecoder allowsKeyedCoding])
{
NSView *aView = [aDecoder decodeObjectForKey: @"NSView"];
@ -179,6 +183,34 @@
[aCoder encodeObject: [self view]];
}
}
// NSSeguePerforming methods...
- (void)performSegueWithIdentifier: (NSStoryboardSegueIdentifier)identifier
sender: (id)sender
{
BOOL should = [self shouldPerformSegueWithIdentifier: identifier
sender: sender];
if (should)
{
NSStoryboardSegue *segue = [_segueMap objectForKey: identifier];
[self prepareForSegue: segue
sender: sender];
[segue perform];
}
}
- (void)prepareForSegue: (NSStoryboardSegue *)segue
sender: (id)sender
{
// do nothing in base class method...
}
- (BOOL)shouldPerformSegueWithIdentifier: (NSStoryboardSegueIdentifier)identifier
sender: (id)sender
{
return YES;
}
@end
@implementation NSViewController (NSEditorRegistration)
@ -227,5 +259,5 @@
// Loop over all elements of _editors
[self notImplemented: _cmd];
}
@end

View file

@ -76,6 +76,7 @@
#import "AppKit/NSTextField.h"
#import "AppKit/NSTextFieldCell.h"
#import "AppKit/NSView.h"
#import "AppKit/NSViewController.h"
#import "AppKit/NSWindow.h"
#import "AppKit/NSWindowController.h"
#import "AppKit/PSOperators.h"
@ -723,6 +724,24 @@ static NSNotificationCenter *nc = nil;
}
}
+ (instancetype) windowWithContentViewController: (NSViewController *)viewController
{
NSView *view = [viewController view];
NSRect frame = [view frame];
NSString *title = [viewController title];
NSUInteger style = NSTitledWindowMask |
NSClosableWindowMask |
NSMiniaturizableWindowMask |
NSResizableWindowMask;
NSWindow *window = [[self alloc] initWithContentRect: frame
styleMask: style
backing: NSBackingStoreBuffered
defer: NO];
[window setTitle: title];
[window setContentView: view];
return window;
}
+ (void) removeFrameUsingName: (NSString*)name
{
if (name != nil)

View file

@ -33,13 +33,29 @@
#import <Foundation/NSException.h>
#import <Foundation/NSNotification.h>
#import <Foundation/NSString.h>
#import <Foundation/NSMapTable.h>
#import "AppKit/NSNib.h"
#import "AppKit/NSNibLoading.h"
#import "AppKit/NSPanel.h"
#import "AppKit/NSWindowController.h"
#import "AppKit/NSStoryboardSegue.h"
#import "AppKit/NSStoryboard.h"
#import "NSDocumentFrameworkPrivate.h"
@interface NSStoryboardSegue (__WindowControllerPrivate__)
- (void) _setDestinationController: (id)controller;
@end
@implementation NSStoryboardSegue (__WindowControllerPrivate__)
- (void) _setDestinationController: (id)controller
{
_destinationController = controller;
}
@end
@implementation NSWindowController
+ (void) initialize
@ -137,6 +153,7 @@
RELEASE(_window_nib_path);
RELEASE(_window_frame_autosave_name);
RELEASE(_top_level_objects);
RELEASE(_segueMap);
[super dealloc];
}
@ -527,6 +544,7 @@
if (!self)
return nil;
_segueMap = nil;
ASSIGN(_window_frame_autosave_name, @"");
_wcFlags.should_cascade = YES;
//_wcFlags.should_close_document = NO;
@ -550,4 +568,47 @@
[super encodeWithCoder: coder];
}
// NSSeguePerforming methods...
- (void)performSegueWithIdentifier: (NSStoryboardSegueIdentifier)identifier
sender: (id)sender
{
BOOL should = [self shouldPerformSegueWithIdentifier: identifier
sender: sender];
if (should)
{
NSStoryboardSegue *segue = [_segueMap objectForKey: identifier];
NSStoryboard *ms = [NSStoryboard mainStoryboard];
NSString *destId = [segue destinationController];
id destCon = [ms instantiateControllerWithIdentifier: destId];
[segue _setDestinationController: destCon]; // replace with actual controller...
[self prepareForSegue: segue
sender: sender];
[segue perform];
}
}
- (void)prepareForSegue: (NSStoryboardSegue *)segue
sender: (id)sender
{
// do nothing in base class method...
}
- (BOOL)shouldPerformSegueWithIdentifier: (NSStoryboardSegueIdentifier)identifier
sender: (id)sender
{
return YES;
}
- (IBAction) _invokeSegue: (id)object
{
NSMapTable *table = (NSMapTable *)object;
id sender = [table objectForKey: @"sender"];
NSString *identifier = (NSString *)[table objectForKey: @"identifier"];
[self performSegueWithIdentifier: identifier
sender: sender];
}
@end