mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-11-11 01:18:45 +00:00
AppKit, Foundation: Fix view archiving and add regression tests
Keyed archiving preserves view hierarchies created in code. Decoded views retain their content, subviews and state, and each view class is verified against its own description before archiving. * CPAccordionView: Add CPCoding to CPAccordionView and CPAccordionViewItem. * CPBox: * Encode the content, title and box views unconditionally. * Clear the superviews of the box and title views during decoding. * CPButton: Initialize the value to CPOffState. * CPMapTable: Add CPCoding. * CPPopUpButton: Clear the inherited button value. * CPSearchField: Update the cancel button when the object value changes. * CPSplitView: Return nil from dividerColor when the theme defines no divider image. * CPStepper: Discard the decoded buttons before recreating them. * CPTabView: * Encode the current tab view items. * Exclude the tabs from the encoded subviews. * CPTokenField: * Discard the decoded scroll view before recreating it. * Add the decoded tokens to the new document view. * CPVisualEffectView: Resolve the backdrop filter property only when a DOM is available. * CPBoxTest: Verify preservation of the content view, hierarchy and title, and decoding of an untitled box without warnings. * CPButtonTest: Verify the initial value of a button. * CPMapTableTest: Verify preservation of entries through archiving. * DropInArchiveFixtures: Add populated view fixtures, tree comparison, and round trip and saved archive helpers. * DropInArchiveGenerator: Save cib descriptions and view archives, and write no archive data if any view fails its round trip. * DropInArchiveTest: Verify new archives, saved archives and reference cibs, and treat decoding warnings as failures. * DropInSnapshot: * Describe the view hierarchy through private views, with state and content. * Add DropInWithFixedScrollerStyle. * DropInArchives, DropInCibs: Add saved view archives, cib descriptions and reference cibs.
This commit is contained in:
1 parent
5db5adac04
commit
f468d483ff
22 files changed
+3749
-33
No files matched your search
@@ -414,3 +414,85 @@ var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPAccordionViewItemIdentifierKey = @"CPAccordionViewItemIdentifierKey",
|
||||
CPAccordionViewItemViewKey = @"CPAccordionViewItemViewKey",
|
||||
CPAccordionViewItemLabelKey = @"CPAccordionViewItemLabelKey";
|
||||
|
||||
@implementation CPAccordionViewItem (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_identifier = [aCoder decodeObjectForKey:CPAccordionViewItemIdentifierKey];
|
||||
_view = [aCoder decodeObjectForKey:CPAccordionViewItemViewKey];
|
||||
_label = [aCoder decodeObjectForKey:CPAccordionViewItemLabelKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPAccordionViewItemIdentifierKey];
|
||||
[aCoder encodeObject:_view forKey:CPAccordionViewItemViewKey];
|
||||
[aCoder encodeObject:_label forKey:CPAccordionViewItemLabelKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPAccordionViewItemsKey = @"CPAccordionViewItemsKey",
|
||||
CPAccordionViewItemHeaderPrototypeKey = @"CPAccordionViewItemHeaderPrototypeKey",
|
||||
CPAccordionViewExpandedItemIndexesKey = @"CPAccordionViewExpandedItemIndexesKey";
|
||||
|
||||
@implementation CPAccordionView (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_items = [];
|
||||
_itemViews = [];
|
||||
_expandedItemIndexes = [CPIndexSet indexSet];
|
||||
|
||||
[self setItemHeaderPrototype:[aCoder decodeObjectForKey:CPAccordionViewItemHeaderPrototypeKey]];
|
||||
|
||||
var items = [aCoder decodeObjectForKey:CPAccordionViewItemsKey],
|
||||
expandedItemIndexes = [aCoder decodeObjectForKey:CPAccordionViewExpandedItemIndexesKey];
|
||||
|
||||
items.forEach(function(anItem)
|
||||
{
|
||||
[self addItem:anItem];
|
||||
});
|
||||
|
||||
items.forEach(function(anItem, anIndex)
|
||||
{
|
||||
if (![expandedItemIndexes containsIndex:anIndex])
|
||||
[self collapseItemAtIndex:anIndex];
|
||||
});
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
// The item views are recreated on decode from the items, so they are not encoded.
|
||||
var subviews = [self subviews];
|
||||
|
||||
[_itemViews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
[super encodeWithCoder:aCoder];
|
||||
[self setSubviews:subviews];
|
||||
|
||||
[aCoder encodeObject:_items forKey:CPAccordionViewItemsKey];
|
||||
[aCoder encodeObject:_itemHeaderPrototype forKey:CPAccordionViewItemHeaderPrototypeKey];
|
||||
[aCoder encodeObject:_expandedItemIndexes forKey:CPAccordionViewExpandedItemIndexesKey];
|
||||
}
|
||||
|
||||
@end
|
||||
+12
-5
@@ -869,6 +869,11 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
// We're coming from elsewhere
|
||||
|
||||
_titleView = [aCoder decodeObjectForKey:CPBoxTitleViewKey];
|
||||
|
||||
// The archiver encodes these views after encodeWithCoder: returns, when they
|
||||
// are subviews again. They are added back below.
|
||||
_boxView._superview = nil;
|
||||
_titleView._superview = nil;
|
||||
}
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPBoxContentViewKey];
|
||||
@@ -877,7 +882,9 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
// FIXME: (see encodeWithCoder to understand why this is not possible)
|
||||
// FIXME: we fix this by hand. This is horrible so please find a structural solution !
|
||||
|
||||
if (_contentView)
|
||||
var contentViewNeedsBoxView = _contentView && [_contentView superview] !== _boxView;
|
||||
|
||||
if (contentViewNeedsBoxView)
|
||||
_contentView._superview = nil;
|
||||
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
@@ -885,7 +892,7 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
[_boxView setAutoresizesSubviews:YES];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
|
||||
if (_contentView)
|
||||
if (contentViewNeedsBoxView)
|
||||
[_boxView setSubviews:@[_contentView]];
|
||||
|
||||
[self addSubview:_boxView];
|
||||
@@ -945,9 +952,9 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
[aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
|
||||
[aCoder encodeObject:_title forKey:CPBoxTitleKey];
|
||||
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePositionKey];
|
||||
[aCoder encodeConditionalObject:_contentView forKey:CPBoxContentViewKey];
|
||||
[aCoder encodeConditionalObject:_titleView forKey:CPBoxTitleViewKey];
|
||||
[aCoder encodeConditionalObject:_boxView forKey:CPBoxBoxViewKey];
|
||||
[aCoder encodeObject:_contentView forKey:CPBoxContentViewKey];
|
||||
[aCoder encodeObject:_titleView forKey:CPBoxTitleViewKey];
|
||||
[aCoder encodeObject:_boxView forKey:CPBoxBoxViewKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -202,6 +202,8 @@ CPButtonImageOffset = 3.0;
|
||||
[self setBezelStyle:CPRoundRectBezelStyle];
|
||||
[self setBordered:YES];
|
||||
|
||||
_value = CPOffState;
|
||||
|
||||
[self _init];
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,9 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
if (self)
|
||||
{
|
||||
// The value of a pop up button is its selected index, and not a button state.
|
||||
_value = nil;
|
||||
|
||||
[self selectItemAtIndex:CPNotFound];
|
||||
|
||||
_preferredEdge = CPMaxYEdge;
|
||||
|
||||
@@ -464,6 +464,12 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
return [super isOpaque] && [_cancelButton isOpaque] && [_searchButton isOpaque];
|
||||
}
|
||||
|
||||
- (void)setObjectValue:(id)anObject
|
||||
{
|
||||
[super setObjectValue:anObject];
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_updateCancelButtonVisibility
|
||||
{
|
||||
[_cancelButton setHidden:([[self stringValue] length] === 0)];
|
||||
|
||||
@@ -221,7 +221,10 @@ var CPThemeStatesForSplitViewDivider = @[@"dummy one as CPSplitViewDividerStyle
|
||||
if (_dividerStyle === CPSplitViewDividerStyleThin)
|
||||
return [self currentValueForThemeAttribute:@"pane-divider-color"];
|
||||
|
||||
return [CPColor colorWithPatternImage:[self currentValueForThemeAttribute:(_isVertical ? @"vertical-divider-color" : @"horizontal-divider-color")]];
|
||||
// A theme may define no divider image, and a pattern needs one.
|
||||
var image = [self currentValueForThemeAttribute:(_isVertical ? @"vertical-divider-color" : @"horizontal-divider-color")];
|
||||
|
||||
return image ? [CPColor colorWithPatternImage:image] : nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -322,6 +322,8 @@ var CPStepperMinValue = @"CPStepperMinValue",
|
||||
_valueWraps = [aCoder decodeBoolForKey:CPStepperValueWraps];
|
||||
_autorepeat = [aCoder decodeBoolForKey:CPStepperAutorepeat];
|
||||
|
||||
// The archive holds the buttons as subviews, and _init creates them again.
|
||||
[self setSubviews:@[]];
|
||||
[self _init];
|
||||
[self setEnabled:[self isEnabled]];
|
||||
}
|
||||
|
||||
+4
-2
@@ -866,15 +866,17 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
// Don't bother to encode the CPBox. We will recreate it on decode and its content view is already
|
||||
// Don't bother to encode the CPBox or the tabs. We will recreate them on decode and its content view is already
|
||||
// stored by the tab view item. Not encoding _box makes the resulting archive smaller and reduces
|
||||
// the surface for decoding bugs (of which we've had many in tab view).
|
||||
var subviews = [self subviews];
|
||||
[_box removeFromSuperview];
|
||||
[_tabs removeFromSuperview];
|
||||
[super encodeWithCoder:aCoder];
|
||||
[self setSubviews:subviews];
|
||||
|
||||
[aCoder encodeObject:_items forKey:CPTabViewItemsKey];
|
||||
// nib2cib stores its items in _items. Otherwise the items are the segments of _tabs.
|
||||
[aCoder encodeObject:_items || [self items] forKey:CPTabViewItemsKey];
|
||||
|
||||
[aCoder encodeConditionalObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
|
||||
|
||||
|
||||
@@ -1741,8 +1741,17 @@ var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSe
|
||||
_completionDelay = [aCoder decodeDoubleForKey:CPTokenFieldCompletionDelayKey] || [[self class] defaultCompletionDelay];
|
||||
_buttonType = [aCoder decodeIntForKey:CPTokenFieldButtonTypeKey] || CPTokenFieldDisclosureButtonType;
|
||||
|
||||
// The archive holds the token scroll view as a subview, and _init creates it again.
|
||||
[self setSubviews:@[]];
|
||||
[self _init];
|
||||
|
||||
// The tokens were created by setObjectValue: before the scroll view existed.
|
||||
var contentView = [_tokenScrollView documentView],
|
||||
tokens = [self _tokens];
|
||||
|
||||
for (var i = 0, count = [tokens count]; i < count; i++)
|
||||
[contentView addSubview:tokens[i]];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@@ -121,7 +121,6 @@ CPVisualEffectStateInactive = 2;
|
||||
- (void)_setEffectEnabled:(BOOL)shouldEnable
|
||||
{
|
||||
var dark = [[self appearance] isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]],
|
||||
prop = CPBrowserStyleProperty("backdrop-filter"),
|
||||
color = (dark ? [CPColor colorWithHexString:@"1e1e1e"] : [CPColor whiteColor]),
|
||||
finalColor = shouldEnable ? [color colorWithAlphaComponent:0.6] : color;
|
||||
|
||||
@@ -129,7 +128,7 @@ CPVisualEffectStateInactive = 2;
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
self._DOMElement.style[prop] = shouldEnable ? "blur(30px)" : nil;
|
||||
self._DOMElement.style[CPBrowserStyleProperty("backdrop-filter")] = shouldEnable ? "blur(30px)" : nil;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -137,3 +137,34 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPMapTableKeysKey = @"CPMapTableKeysKey",
|
||||
CPMapTableObjectsKey = @"CPMapTableObjectsKey";
|
||||
|
||||
@implementation CPMapTable (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
var keys = [aCoder decodeObjectForKey:CPMapTableKeysKey],
|
||||
objects = [aCoder decodeObjectForKey:CPMapTableObjectsKey];
|
||||
|
||||
keys.forEach(function(aKey, anIndex)
|
||||
{
|
||||
[self setObject:objects[anIndex] forKey:aKey];
|
||||
});
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:Array.from(_map.keys()) forKey:CPMapTableKeysKey];
|
||||
[aCoder encodeObject:Array.from(_map.values()) forKey:CPMapTableObjectsKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* CPBoxTest.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by David Richardson on 31/10/2026.
|
||||
* Copyright David Richardson 2026.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
/*
|
||||
A box that is archived and read back must keep its content view, its title and
|
||||
its internal views, and the content view must remain inside the box.
|
||||
*/
|
||||
@implementation CPBoxTest : OJTestCase
|
||||
{
|
||||
CPBox box;
|
||||
CPView contentView;
|
||||
Function logWarning;
|
||||
CPArray warnings;
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
// This will init the global var CPApp which are used internally in the AppKit
|
||||
[[CPApplication alloc] init];
|
||||
|
||||
warnings = [];
|
||||
logWarning = CPLog.warn;
|
||||
|
||||
CPLog.warn = function(aMessage)
|
||||
{
|
||||
warnings.push(String(aMessage));
|
||||
};
|
||||
|
||||
box = [[CPBox alloc] initWithFrame:CGRectMake(0.0, 0.0, 200.0, 100.0)];
|
||||
contentView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 40.0)];
|
||||
|
||||
[contentView addSubview:[[CPView alloc] initWithFrame:CGRectMake(3.0, 5.0, 20.0, 10.0)]];
|
||||
[box setContentView:contentView];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
CPLog.warn = logWarning;
|
||||
}
|
||||
|
||||
- (CPBox)archivedBox
|
||||
{
|
||||
return [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:box]];
|
||||
}
|
||||
|
||||
- (void)testArchivingKeepsTheContentView
|
||||
{
|
||||
var decodedContentView = [[self archivedBox] contentView];
|
||||
|
||||
[self assertNotNull:decodedContentView];
|
||||
[self assert:1 equals:[[decodedContentView subviews] count]];
|
||||
[self assertTrue:CGRectEqualToRect([contentView frame], [decodedContentView frame])];
|
||||
}
|
||||
|
||||
- (void)testArchivingKeepsTheContentViewInsideTheBox
|
||||
{
|
||||
var decodedBox = [self archivedBox],
|
||||
boxView = [[decodedBox contentView] superview];
|
||||
|
||||
[self assertNotNull:boxView];
|
||||
[self assert:decodedBox equals:[boxView superview]];
|
||||
[self assertTrue:[[decodedBox subviews] containsObject:boxView]];
|
||||
}
|
||||
|
||||
- (void)testArchivingLeavesTheBoxUnchanged
|
||||
{
|
||||
var subviews = [box subviews];
|
||||
|
||||
[self archivedBox];
|
||||
|
||||
[self assert:subviews equals:[box subviews]];
|
||||
[self assert:contentView equals:[box contentView]];
|
||||
}
|
||||
|
||||
- (void)testArchivingKeepsTheTitle
|
||||
{
|
||||
[box setTitle:@"Title"];
|
||||
[box setTitlePosition:CPAtTop];
|
||||
|
||||
var decodedBox = [self archivedBox];
|
||||
|
||||
[self assert:@"Title" equals:[decodedBox title]];
|
||||
[self assert:CPAtTop equals:[decodedBox titlePosition]];
|
||||
}
|
||||
|
||||
- (void)testUntitledBoxDecodesWithoutAWarning
|
||||
{
|
||||
[self archivedBox];
|
||||
|
||||
[self assert:[] equals:warnings];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -104,6 +104,12 @@
|
||||
[self assertTrue:wasClicked];
|
||||
}
|
||||
|
||||
- (void)testNewButtonValueIsOff
|
||||
{
|
||||
[self assert:CPOffState equals:[button objectValue] message:@"a new button should be off"];
|
||||
[self assert:CPOffState equals:[button state] message:@"state should be CPOffState"];
|
||||
}
|
||||
|
||||
- (void)testSetObjectValue
|
||||
{
|
||||
[button setObjectValue:CPOnState];
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* DropInArchiveFixtures.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by David Richardson on 31/10/2026.
|
||||
* Copyright David Richardson 2026.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "DropInSnapshot.j"
|
||||
|
||||
/*
|
||||
Creates the views that the archive tests write and read back. An empty view has
|
||||
little to lose, so each view holds real content. The reference for a view is its
|
||||
own description before it is archived, never what a build restores.
|
||||
*/
|
||||
|
||||
/*
|
||||
The only view classes without a fixture. Each entry states the reason. Any other
|
||||
class that cannot be archived is a failure.
|
||||
*/
|
||||
var DropInArchiveExclusions = {
|
||||
@"CPText": @"it is abstract and its text methods raise an exception",
|
||||
@"CPWebView": @"creating one needs a DOM"
|
||||
};
|
||||
|
||||
function DropInArchiveExclusion(aClassName)
|
||||
{
|
||||
return DropInArchiveExclusions[aClassName];
|
||||
}
|
||||
|
||||
function DropInArchiveChild(aWidth, aHeight)
|
||||
{
|
||||
return [[CPView alloc] initWithFrame:CGRectMake(3.0, 5.0, aWidth, aHeight)];
|
||||
}
|
||||
|
||||
function DropInArchiveTableColumn(anIdentifier)
|
||||
{
|
||||
var column = [[CPTableColumn alloc] initWithIdentifier:anIdentifier];
|
||||
|
||||
[column setWidth:60.0];
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/*
|
||||
The content of each class. A class without an entry uses the entry of its
|
||||
nearest superclass.
|
||||
*/
|
||||
var DropInArchivePopulators = {
|
||||
@"CPView": function(aView)
|
||||
{
|
||||
[aView addSubview:DropInArchiveChild(20.0, 10.0)];
|
||||
},
|
||||
@"CPControl": function(aControl)
|
||||
{
|
||||
[aControl setEnabled:NO];
|
||||
[aControl setTag:7];
|
||||
},
|
||||
@"CPAccordionView": function(anAccordionView)
|
||||
{
|
||||
var item = [[CPAccordionViewItem alloc] initWithIdentifier:@"item"];
|
||||
|
||||
[item setLabel:@"Item"];
|
||||
[item setView:DropInArchiveChild(40.0, 30.0)];
|
||||
[anAccordionView addItem:item];
|
||||
},
|
||||
@"CPBox": function(aBox)
|
||||
{
|
||||
var content = DropInArchiveChild(60.0, 40.0);
|
||||
|
||||
[content addSubview:DropInArchiveChild(20.0, 10.0)];
|
||||
[aBox setTitle:@"Box"];
|
||||
[aBox setTitlePosition:CPAtTop];
|
||||
[aBox setContentView:content];
|
||||
},
|
||||
@"CPButton": function(aButton)
|
||||
{
|
||||
[aButton setTitle:@"Button"];
|
||||
[aButton setState:CPOnState];
|
||||
[aButton setTag:7];
|
||||
},
|
||||
@"CPButtonBar": function(aButtonBar)
|
||||
{
|
||||
[aButtonBar setButtons:[[CPButtonBar plusButton], [CPButtonBar minusButton]]];
|
||||
},
|
||||
@"CPColorWell": function(aColorWell)
|
||||
{
|
||||
[aColorWell setColor:[CPColor redColor]];
|
||||
},
|
||||
@"CPComboBox": function(aComboBox)
|
||||
{
|
||||
[aComboBox addItemsWithObjectValues:[@"One", @"Two"]];
|
||||
[aComboBox setStringValue:@"One"];
|
||||
},
|
||||
@"CPDatePicker": function(aDatePicker)
|
||||
{
|
||||
[aDatePicker setDateValue:[[CPDate alloc] initWithString:@"2026-10-31 12:00:00 +0000"]];
|
||||
},
|
||||
@"CPLevelIndicator": function(aLevelIndicator)
|
||||
{
|
||||
[aLevelIndicator setMinValue:1.0];
|
||||
[aLevelIndicator setMaxValue:9.0];
|
||||
[aLevelIndicator setDoubleValue:4.0];
|
||||
},
|
||||
@"CPOutlineView": function(anOutlineView)
|
||||
{
|
||||
var column = DropInArchiveTableColumn(@"one");
|
||||
|
||||
[anOutlineView addTableColumn:column];
|
||||
[anOutlineView setOutlineTableColumn:column];
|
||||
},
|
||||
@"CPPopUpButton": function(aPopUpButton)
|
||||
{
|
||||
[aPopUpButton addItemsWithTitles:[@"One", @"Two"]];
|
||||
[aPopUpButton selectItemAtIndex:1];
|
||||
},
|
||||
@"CPProgressIndicator": function(aProgressIndicator)
|
||||
{
|
||||
[aProgressIndicator setMinValue:1.0];
|
||||
[aProgressIndicator setMaxValue:9.0];
|
||||
[aProgressIndicator setDoubleValue:4.0];
|
||||
},
|
||||
@"CPScrollView": function(aScrollView)
|
||||
{
|
||||
[aScrollView setDocumentView:DropInArchiveChild(300.0, 400.0)];
|
||||
},
|
||||
@"CPSegmentedControl": function(aSegmentedControl)
|
||||
{
|
||||
[aSegmentedControl setSegmentCount:2];
|
||||
[aSegmentedControl setLabel:@"One" forSegment:0];
|
||||
[aSegmentedControl setLabel:@"Two" forSegment:1];
|
||||
[aSegmentedControl setWidth:40.0 forSegment:0];
|
||||
[aSegmentedControl setWidth:40.0 forSegment:1];
|
||||
},
|
||||
@"CPSlider": function(aSlider)
|
||||
{
|
||||
[aSlider setMinValue:1.0];
|
||||
[aSlider setMaxValue:9.0];
|
||||
[aSlider setDoubleValue:4.0];
|
||||
},
|
||||
@"CPSplitView": function(aSplitView)
|
||||
{
|
||||
[aSplitView addSubview:DropInArchiveChild(40.0, 90.0)];
|
||||
[aSplitView addSubview:DropInArchiveChild(40.0, 90.0)];
|
||||
},
|
||||
@"CPStackView": function(aStackView)
|
||||
{
|
||||
[aStackView addArrangedSubview:DropInArchiveChild(40.0, 30.0)];
|
||||
},
|
||||
@"CPStepper": function(aStepper)
|
||||
{
|
||||
[aStepper setMinValue:1.0];
|
||||
[aStepper setMaxValue:9.0];
|
||||
[aStepper setDoubleValue:4.0];
|
||||
},
|
||||
@"CPTableView": function(aTableView)
|
||||
{
|
||||
[aTableView addTableColumn:DropInArchiveTableColumn(@"one")];
|
||||
[aTableView addTableColumn:DropInArchiveTableColumn(@"two")];
|
||||
},
|
||||
@"CPTabView": function(aTabView)
|
||||
{
|
||||
["one", "two"].forEach(function(anIdentifier)
|
||||
{
|
||||
var item = [[CPTabViewItem alloc] initWithIdentifier:anIdentifier];
|
||||
|
||||
[item setLabel:anIdentifier];
|
||||
[item setView:DropInArchiveChild(40.0, 30.0)];
|
||||
[aTabView addTabViewItem:item];
|
||||
});
|
||||
|
||||
[aTabView selectTabViewItemAtIndex:1];
|
||||
},
|
||||
@"CPTextView": function(aTextView)
|
||||
{
|
||||
[aTextView setString:@"Text"];
|
||||
},
|
||||
@"CPTextField": function(aTextField)
|
||||
{
|
||||
[aTextField setStringValue:@"Text"];
|
||||
[aTextField setEditable:YES];
|
||||
[aTextField setBezeled:YES];
|
||||
},
|
||||
@"CPTokenField": function(aTokenField)
|
||||
{
|
||||
[aTokenField setObjectValue:[@"One", @"Two"]];
|
||||
}
|
||||
};
|
||||
|
||||
function DropInArchivePopulator(aClass)
|
||||
{
|
||||
for (; aClass; aClass = class_getSuperclass(aClass))
|
||||
if (DropInArchivePopulators[aClass.name])
|
||||
return DropInArchivePopulators[aClass.name];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
function DropInArchiveFixture(aClass)
|
||||
{
|
||||
var view = [[aClass alloc] initWithFrame:CGRectMake(0.0, 0.0, 200.0, 100.0)];
|
||||
|
||||
DropInArchivePopulator(aClass)(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/*
|
||||
Lists each place where two view descriptions differ.
|
||||
*/
|
||||
function DropInTreeDifferences(expected, current, aPath, differences)
|
||||
{
|
||||
if (expected && current && typeof expected === "object" && typeof current === "object")
|
||||
{
|
||||
Object.keys(expected).concat(Object.keys(current)).filter(function(aKey, anIndex, aList)
|
||||
{
|
||||
return aList.indexOf(aKey) === anIndex;
|
||||
}).forEach(function(aKey)
|
||||
{
|
||||
DropInTreeDifferences(expected[aKey], current[aKey], aPath + "." + aKey, differences);
|
||||
});
|
||||
}
|
||||
else if (expected !== current)
|
||||
differences.push(aPath + ": " + JSON.stringify(expected) + " is now " + JSON.stringify(current));
|
||||
|
||||
return differences;
|
||||
}
|
||||
|
||||
/*
|
||||
Layout changes frames and adds subviews, and a new view has not asked for it. Every
|
||||
view is laid out before it is described, so that two descriptions are comparable.
|
||||
*/
|
||||
function DropInArchiveLayOut(aView)
|
||||
{
|
||||
[aView setNeedsLayout];
|
||||
[_CPDisplayServer run];
|
||||
}
|
||||
|
||||
/*
|
||||
Archives a fixture and reads it back.
|
||||
*/
|
||||
function DropInArchiveRoundTrip(aClass)
|
||||
{
|
||||
return DropInWithFixedScrollerStyle(function()
|
||||
{
|
||||
var view = DropInArchiveFixture(aClass);
|
||||
|
||||
DropInArchiveLayOut(view);
|
||||
|
||||
var original = DropInViewTree(view),
|
||||
data = [CPKeyedArchiver archivedDataWithRootObject:view],
|
||||
decoded = [CPKeyedUnarchiver unarchiveObjectWithData:data];
|
||||
|
||||
DropInArchiveLayOut(decoded);
|
||||
|
||||
return { @"archive": [data rawString], @"original": original, @"decoded": DropInViewTree(decoded) };
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Reads a saved archive back and describes the view.
|
||||
*/
|
||||
function DropInSavedArchiveTree(aRawString)
|
||||
{
|
||||
return DropInWithFixedScrollerStyle(function()
|
||||
{
|
||||
var decoded = [CPKeyedUnarchiver unarchiveObjectWithData:[CPData dataWithRawString:aRawString]];
|
||||
|
||||
DropInArchiveLayOut(decoded);
|
||||
|
||||
return DropInViewTree(decoded);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* DropInArchiveGenerator.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by David Richardson on 31/10/2026.
|
||||
* Copyright David Richardson 2026.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "DropInArchiveFixtures.j"
|
||||
|
||||
/*
|
||||
Saves the archive test data. Run this only against a build of main:
|
||||
|
||||
objj Tests/AppKit/DropInArchiveGenerator.j
|
||||
|
||||
It saves a description of what each reference cib produces. It then saves an
|
||||
archive of each view fixture, with the fixture's description before archiving.
|
||||
An archive is saved only if it restores that description, so a failure stops
|
||||
the generator and no archive data is written.
|
||||
*/
|
||||
var fs = require("fs"),
|
||||
path = require("path");
|
||||
|
||||
function main(args, namedArgs)
|
||||
{
|
||||
[[CPApplication alloc] init];
|
||||
|
||||
var resources = path.join(process.cwd(), "Tests", "AppKit", "Resources"),
|
||||
cibFolder = path.join(resources, "DropInCibs"),
|
||||
archives = {},
|
||||
cibs = {},
|
||||
failures = [];
|
||||
|
||||
fs.readdirSync(cibFolder).sort().forEach(function(aName)
|
||||
{
|
||||
cibs[aName] = DropInCibTree(aName, fs.readFileSync(path.join(cibFolder, aName), {encoding: 'utf-8'}));
|
||||
print(aName + ": " + cibs[aName].length + " top level objects");
|
||||
});
|
||||
|
||||
fs.writeFileSync(path.join(resources, "DropInCibs.json"), JSON.stringify(cibs, null, 1) + "\n");
|
||||
|
||||
DropInSortedClasses().forEach(function(aClass)
|
||||
{
|
||||
var name = aClass.name;
|
||||
|
||||
if (!DropInIsPublicName(name) || !DropInClassIsView(aClass))
|
||||
return;
|
||||
|
||||
if (DropInArchiveExclusion(name))
|
||||
return print("excluded " + name + ": " + DropInArchiveExclusion(name));
|
||||
|
||||
try
|
||||
{
|
||||
var result = DropInArchiveRoundTrip(aClass),
|
||||
differences = DropInTreeDifferences(result.original, result.decoded, name, []);
|
||||
|
||||
if (differences.length)
|
||||
failures = failures.concat(differences);
|
||||
else
|
||||
archives[name] = { @"archive": result.archive, @"state": result.original };
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
failures.push(name + ": " + String(anException));
|
||||
}
|
||||
});
|
||||
|
||||
if (failures.length)
|
||||
{
|
||||
failures.forEach(function(aLine) { print("failed " + aLine); });
|
||||
print(failures.length + " failures, no archive data written");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(resources, "DropInArchives.json"), JSON.stringify(archives, null, 1) + "\n");
|
||||
|
||||
print(Object.keys(archives).length + " archived view classes");
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* DropInArchiveTest.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by David Richardson on 31/10/2026.
|
||||
* Copyright David Richardson 2026.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "DropInArchiveFixtures.j"
|
||||
|
||||
/*
|
||||
Every application with an interface built in Xcode depends on cibs, and those
|
||||
cibs were written before the application's frameworks were last updated. An
|
||||
application that updates its frameworks does not rebuild them first, so the
|
||||
frameworks must keep reading what was already written. A new key may be added to
|
||||
an archive. Its absence from an older archive must then mean the old behavior,
|
||||
and not a new default.
|
||||
|
||||
The reference for a view is the view before it was archived, so a build cannot
|
||||
pass by restoring something else. A warning during decoding is a failure. The
|
||||
classes under test are those in the drop-in baseline, so a class cannot leave
|
||||
these tests unnoticed.
|
||||
|
||||
DropInArchiveGenerator.j saves the data these tests read. Each test reports every
|
||||
difference it finds.
|
||||
*/
|
||||
var fs = require("fs"),
|
||||
path = require("path");
|
||||
|
||||
@implementation DropInArchiveTest : OJTestCase
|
||||
{
|
||||
CPString resources;
|
||||
CPArray warnings;
|
||||
Function logWarning;
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
// This will init the global var CPApp which are used internally in the AppKit
|
||||
[[CPApplication alloc] init];
|
||||
|
||||
resources = path.join(path.dirname(__filename), "Resources");
|
||||
warnings = [];
|
||||
logWarning = CPLog.warn;
|
||||
|
||||
CPLog.warn = function(aMessage)
|
||||
{
|
||||
warnings.push(String(aMessage));
|
||||
};
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
CPLog.warn = logWarning;
|
||||
}
|
||||
|
||||
- (id)jsonNamed:(CPString)aName
|
||||
{
|
||||
return JSON.parse(fs.readFileSync(path.join(resources, aName), {encoding: 'utf-8'}));
|
||||
}
|
||||
|
||||
- (CPArray)viewClassNames
|
||||
{
|
||||
return Object.keys([self jsonNamed:@"DropInBaseline.json"].construction).filter(function(aClassName)
|
||||
{
|
||||
return !DropInArchiveExclusion(aClassName);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)assertNoDifferences:(CPArray)differences
|
||||
{
|
||||
[self assertTrue:differences.length === 0 message:"\n" + differences.join("\n")];
|
||||
}
|
||||
|
||||
- (void)moveWarningsNamed:(CPString)aName into:(CPArray)differences
|
||||
{
|
||||
warnings.forEach(function(aWarning)
|
||||
{
|
||||
differences.push(aName + " warned: " + aWarning);
|
||||
});
|
||||
|
||||
warnings = [];
|
||||
}
|
||||
|
||||
/*
|
||||
A view that is archived and read back must be the same view, with the same
|
||||
content.
|
||||
*/
|
||||
- (void)testNewArchivesRestoreTheOriginalView
|
||||
{
|
||||
var differences = [];
|
||||
|
||||
[self viewClassNames].forEach(function(aClassName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = DropInArchiveRoundTrip(objj_getClass(aClassName));
|
||||
|
||||
DropInTreeDifferences(result.original, result.decoded, aClassName, differences);
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
differences.push(aClassName + ": " + String(anException));
|
||||
}
|
||||
|
||||
[self moveWarningsNamed:aClassName into:differences];
|
||||
});
|
||||
|
||||
[self assertNoDifferences:differences];
|
||||
}
|
||||
|
||||
/*
|
||||
An archive written by main must restore the view it was written from.
|
||||
*/
|
||||
- (void)testExistingArchivesRestoreTheOriginalView
|
||||
{
|
||||
var archivePath = path.join(resources, @"DropInArchives.json");
|
||||
|
||||
[self assertTrue:fs.existsSync(archivePath) message:@"DropInArchives.json has not been generated"];
|
||||
|
||||
var archives = [self jsonNamed:@"DropInArchives.json"],
|
||||
differences = [];
|
||||
|
||||
[self viewClassNames].forEach(function(aClassName)
|
||||
{
|
||||
var saved = archives[aClassName];
|
||||
|
||||
if (!saved)
|
||||
return differences.push(aClassName + ": no saved archive");
|
||||
|
||||
try
|
||||
{
|
||||
DropInTreeDifferences(saved.state, DropInSavedArchiveTree(saved.archive), aClassName, differences);
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
differences.push(aClassName + ": " + String(anException));
|
||||
}
|
||||
|
||||
[self moveWarningsNamed:aClassName into:differences];
|
||||
});
|
||||
|
||||
[self assertNoDifferences:differences];
|
||||
}
|
||||
|
||||
/*
|
||||
These are fixed copies of real cibs, and they do not follow their sources. They are
|
||||
Tests/Manual/SmartFoldersTest/Resources/PredicateEditor.cib and
|
||||
Tests/Manual/TextEditor/Resources/DocumentWindow.cib, as of commit 5db5adac0.
|
||||
Loading one must produce the same windows, views and frames as main. The MainMenu
|
||||
cibs of those applications are not here, because they need application classes
|
||||
to load.
|
||||
*/
|
||||
- (void)testExistingCibsProduceTheSameObjects
|
||||
{
|
||||
var expected = [self jsonNamed:@"DropInCibs.json"],
|
||||
differences = [];
|
||||
|
||||
for (var cibName in expected)
|
||||
{
|
||||
var rawString = fs.readFileSync(path.join(resources, "DropInCibs", cibName), {encoding: 'utf-8'}),
|
||||
current = DropInCibTree(cibName, rawString),
|
||||
expectedTrees = expected[cibName];
|
||||
|
||||
if (current.length !== expectedTrees.length)
|
||||
{
|
||||
differences.push(cibName + ": " + expectedTrees.length + " top level objects are now " + current.length);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var index = 0; index < current.length; index++)
|
||||
DropInTreeDifferences(JSON.parse(expectedTrees[index]), JSON.parse(current[index]), cibName, differences);
|
||||
|
||||
[self moveWarningsNamed:cibName into:differences];
|
||||
}
|
||||
|
||||
[self assertNoDifferences:differences];
|
||||
}
|
||||
|
||||
@end
|
||||
+100
-23
@@ -61,6 +61,37 @@ DropInWindowStyles = {
|
||||
@"hudClosable": CPHUDBackgroundWindowMask | CPClosableWindowMask
|
||||
};
|
||||
|
||||
/*
|
||||
The getters that report a BOOL, and the getters that report a view's content.
|
||||
*/
|
||||
var DropInBooleanGetters = [
|
||||
@"acceptsFirstResponder",
|
||||
@"drawsBackground",
|
||||
@"isBordered",
|
||||
@"isEditable",
|
||||
@"isEnabled",
|
||||
@"isHidden",
|
||||
@"isSelectable"
|
||||
],
|
||||
|
||||
DropInContentGetters = [
|
||||
@"color",
|
||||
@"doubleValue",
|
||||
@"indexOfSelectedItem",
|
||||
@"maxValue",
|
||||
@"minValue",
|
||||
@"numberOfColumns",
|
||||
@"numberOfItems",
|
||||
@"numberOfTabViewItems",
|
||||
@"objectValue",
|
||||
@"segmentCount",
|
||||
@"state",
|
||||
@"string",
|
||||
@"stringValue",
|
||||
@"tag",
|
||||
@"title"
|
||||
];
|
||||
|
||||
function DropInIsPublicName(aName)
|
||||
{
|
||||
return aName.charAt(0) !== "_" && aName.charAt(0) !== "$";
|
||||
@@ -226,18 +257,54 @@ function DropInDefaultConstruction(aClass, throwIfError)
|
||||
}
|
||||
|
||||
/*
|
||||
Describes a view and its public subviews, in order. Private views are left out,
|
||||
because they are the class's own concern.
|
||||
Lists the public views below a view, in order. A private view is the class's own
|
||||
concern and is left out, but the public views inside it are the view's content.
|
||||
An observed view has a generated isa, so its name comes from its class method.
|
||||
*/
|
||||
function DropInPublicSubviews(aView)
|
||||
{
|
||||
return [aView subviews].reduce(function(aList, aSubview)
|
||||
{
|
||||
return aList.concat(DropInIsPublicName([aSubview className]) ? [aSubview] : DropInPublicSubviews(aSubview));
|
||||
}, []);
|
||||
}
|
||||
|
||||
/*
|
||||
Describes the state of one view. An unset BOOL is nil and means NO, so a BOOL is
|
||||
recorded by its truth value.
|
||||
*/
|
||||
function DropInViewTreeState(aView)
|
||||
{
|
||||
var state = {};
|
||||
|
||||
DropInConstructionGetters.concat(DropInContentGetters).forEach(function(aGetter)
|
||||
{
|
||||
var selector = CPSelectorFromString(aGetter);
|
||||
|
||||
if (![aView respondsToSelector:selector])
|
||||
return;
|
||||
|
||||
var value = [aView performSelector:selector];
|
||||
|
||||
state[aGetter] = DropInDescribeValue(DropInBooleanGetters.indexOf(aGetter) !== -1 ? !!value : value);
|
||||
});
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
Describes a view and everything public below it.
|
||||
*/
|
||||
function DropInViewTree(aView)
|
||||
{
|
||||
return {
|
||||
@"class": aView.isa.name,
|
||||
@"class": [aView className],
|
||||
@"frame": DropInDescribeValue([aView frame]),
|
||||
@"subviews": [aView subviews].filter(function(aSubview)
|
||||
{
|
||||
return DropInIsPublicName(aSubview.isa.name);
|
||||
}).map(DropInViewTree)
|
||||
@"state": DropInViewTreeState(aView),
|
||||
@"subviews": DropInPublicSubviews(aView).map(function(aSubview)
|
||||
{
|
||||
return DropInViewTree(aSubview);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -291,23 +358,37 @@ function DropInObjectTree(anObject)
|
||||
@end
|
||||
|
||||
/*
|
||||
Loads a cib and describes its top level objects, sorted so that the order in
|
||||
which the cib lists them does not matter.
|
||||
|
||||
A scroll view takes its scroller style from a global setting when it is decoded,
|
||||
and that style changes the frames inside it. Applications and tests change the
|
||||
setting, so each cib loads with the same style. Layout also changes frames and
|
||||
adds subviews, so it is complete before anything is described.
|
||||
A scroll view takes its scroller style from a global setting, and that style changes
|
||||
the frames and views inside it. Applications and tests change the setting, so a
|
||||
description that is saved or compared is always made with the same style.
|
||||
*/
|
||||
function DropInCibTree(aCibName, aRawString)
|
||||
function DropInWithFixedScrollerStyle(aFunction)
|
||||
{
|
||||
var topLevelObjects = [],
|
||||
cib = [[CPCib alloc] _initWithData:[CPData dataWithRawString:aRawString] bundle:nil cibName:aCibName],
|
||||
scrollerStyle = [CPScrollView globalScrollerStyle];
|
||||
var scrollerStyle = [CPScrollView globalScrollerStyle];
|
||||
|
||||
[CPScrollView setGlobalScrollerStyle:CPScrollerStyleLegacy];
|
||||
|
||||
try
|
||||
{
|
||||
return aFunction();
|
||||
}
|
||||
finally
|
||||
{
|
||||
[CPScrollView setGlobalScrollerStyle:scrollerStyle];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Loads a cib and describes its top level objects, sorted so that the order in
|
||||
which the cib lists them does not matter. Layout changes frames and adds subviews,
|
||||
so it is complete before anything is described.
|
||||
*/
|
||||
function DropInCibTree(aCibName, aRawString)
|
||||
{
|
||||
var topLevelObjects = [],
|
||||
cib = [[CPCib alloc] _initWithData:[CPData dataWithRawString:aRawString] bundle:nil cibName:aCibName];
|
||||
|
||||
return DropInWithFixedScrollerStyle(function()
|
||||
{
|
||||
[cib instantiateCibWithExternalNameTable:@{ CPCibOwner: [[_DropInCibOwner alloc] init], CPCibTopLevelObjects: topLevelObjects }];
|
||||
[_CPDisplayServer run];
|
||||
@@ -316,11 +397,7 @@ function DropInCibTree(aCibName, aRawString)
|
||||
{
|
||||
return JSON.stringify(aTree);
|
||||
}).sort();
|
||||
}
|
||||
finally
|
||||
{
|
||||
[CPScrollView setGlobalScrollerStyle:scrollerStyle];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function DropInClassIsView(aClass)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"SmartFoldersPredicateEditor.cib": [
|
||||
"{\"class\":\"CPApplication\"}",
|
||||
"{\"class\":\"CPWindow\",\"title\":\"Predicate Editor\",\"contentView\":{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":1,\\\"y\\\":1},\\\"size\\\":{\\\"height\\\":290,\\\"width\\\":551}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"18\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPTextField\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":63,\\\"y\\\":12},\\\"size\\\":{\\\"height\\\":29,\\\"width\\\":474}}\",\"state\":{\"acceptsFirstResponder\":\"true\",\"alignment\":\"4\",\"autoresizingMask\":\"34\",\"backgroundColor\":\"CPColor:rgba(255, 255, 255, 1)\",\"bezelStyle\":\"0\",\"controlSize\":\"0\",\"drawsBackground\":\"true\",\"isBordered\":\"false\",\"isEditable\":\"true\",\"isEnabled\":\"true\",\"isHidden\":\"false\",\"isSelectable\":\"true\",\"lineBreakMode\":\"2\",\"verticalAlignment\":\"1\",\"doubleValue\":\"0\",\"objectValue\":\"\",\"stringValue\":\"\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":29,\\\"width\\\":474}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]}]},{\"class\":\"CPTextField\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":19,\\\"y\\\":18},\\\"size\\\":{\\\"height\\\":17,\\\"width\\\":41}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"alignment\":\"4\",\"autoresizingMask\":\"36\",\"backgroundColor\":\"CPColor:rgba(175, 175, 175, 1)\",\"bezelStyle\":\"0\",\"controlSize\":\"0\",\"drawsBackground\":\"false\",\"isBordered\":\"false\",\"isEditable\":\"false\",\"isEnabled\":\"true\",\"isHidden\":\"false\",\"isSelectable\":\"false\",\"lineBreakMode\":\"2\",\"verticalAlignment\":\"1\",\"doubleValue\":\"0\",\"objectValue\":\"Name:\",\"stringValue\":\"Name:\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":17,\\\"width\\\":41}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]}]},{\"class\":\"CPScrollView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":20,\\\"y\\\":55},\\\"size\\\":{\\\"height\\\":184,\\\"width\\\":512}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"18\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPClipView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":1,\\\"y\\\":1},\\\"size\\\":{\\\"height\\\":182,\\\"width\\\":510}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"8\",\"backgroundColor\":\"CPColor:rgba(232, 232, 232, 1)\",\"isHidden\":\"false\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPPredicateEditor\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":182,\\\"width\\\":510}}\",\"state\":{\"acceptsFirstResponder\":\"true\",\"alignment\":\"0\",\"autoresizingMask\":\"18\",\"backgroundColor\":\"nil\",\"controlSize\":\"0\",\"isEditable\":\"true\",\"isEnabled\":\"true\",\"isHidden\":\"false\",\"lineBreakMode\":\"2\",\"verticalAlignment\":\"nil\",\"doubleValue\":\"0\",\"objectValue\":\"nil\",\"stringValue\":\"\",\"tag\":\"0\"},\"subviews\":[]}]},{\"class\":\"CPScroller\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":-100,\\\"y\\\":269},\\\"size\\\":{\\\"height\\\":7,\\\"width\\\":360}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"alignment\":\"0\",\"autoresizingMask\":\"8\",\"backgroundColor\":\"nil\",\"controlSize\":\"0\",\"isEnabled\":\"false\",\"isHidden\":\"true\",\"lineBreakMode\":\"2\",\"verticalAlignment\":\"1\",\"doubleValue\":\"0\",\"objectValue\":\"0\",\"stringValue\":\"0\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":7,\\\"width\\\":360}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]}]},{\"class\":\"CPScroller\",\"frame\":\"{\\\"origin\\\":{\\\"Line truncated
|
||||
],
|
||||
"TextEditorDocumentWindow.cib": [
|
||||
"{\"class\":\"CPApplication\"}",
|
||||
"{\"class\":\"CPWindow\",\"title\":\"Window\",\"contentView\":{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":1,\\\"y\\\":92},\\\"size\\\":{\\\"height\\\":168,\\\"width\\\":474}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"18\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPScrollView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":202,\\\"width\\\":476}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"18\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPClipView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":202,\\\"width\\\":469}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"8\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPTextView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":270,\\\"width\\\":469}}\",\"state\":{\"acceptsFirstResponder\":\"true\",\"autoresizingMask\":\"18\",\"backgroundColor\":\"CPColor:rgba(255, 255, 255, 1)\",\"isEditable\":\"true\",\"isHidden\":\"false\",\"isSelectable\":\"true\",\"objectValue\":\"CPTextStorage\",\"string\":\"\",\"stringValue\":\"\",\"tag\":\"0\"},\"subviews\":[]}]},{\"class\":\"CPScroller\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":-100,\\\"y\\\":355},\\\"size\\\":{\\\"height\\\":7,\\\"width\\\":240}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"alignment\":\"0\",\"autoresizingMask\":\"8\",\"backgroundColor\":\"nil\",\"controlSize\":\"0\",\"isEnabled\":\"false\",\"isHidden\":\"true\",\"lineBreakMode\":\"2\",\"verticalAlignment\":\"1\",\"doubleValue\":\"0\",\"objectValue\":\"0\",\"stringValue\":\"0\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":7,\\\"width\\\":240}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]}]},{\"class\":\"CPScroller\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":469,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":202,\\\"width\\\":7}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"alignment\":\"0\",\"autoresizingMask\":\"8\",\"backgroundColor\":\"nil\",\"controlSize\":\"0\",\"isEnabled\":\"true\",\"isHidden\":\"false\",\"lineBreakMode\":\"2\",\"verticalAlignment\":\"1\",\"doubleValue\":\"0\",\"objectValue\":\"0\",\"stringValue\":\"0\",\"tag\":\"0\"},\"subviews\":[{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":202,\\\"width\\\":7}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":202},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":151.1259259259259,\\\"width\\\":7}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"nil\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]}]},{\"class\":\"CPView\",\"frame\":\"{\\\"origin\\\":{\\\"x\\\":0,\\\"y\\\":0},\\\"size\\\":{\\\"height\\\":0,\\\"width\\\":0}}\",\"state\":{\"acceptsFirstResponder\":\"false\",\"autoresizingMask\":\"0\",\"backgroundColor\":\"CPColor:rgba(255, 255, 255, 1)\",\"isHidden\":\"false\",\"tag\":\"-1\"},\"subviews\":[]},{\"class\":\"CPClipView\",\"frame\":\"{\\\"origin\\\":{\\\"xLine truncated
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
280NPLIST;1.0;D;K;4;$topD;K;18;CPCibObjectDataKeyD;K;6;CP$UIDd;1;2E;E;K;8;$objectsA;S;5;$nullD;K;10;$classnameS;16;_CPCibObjectDataK;8;$classesA;S;16;_CPCibObjectDataS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;1E;K;28;_CPCibObjectDataNamesKeysKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataNamesValuesKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataClassesKeysKeyD;K;6;CP$UIDd;1;0E;K;32;_CPCibObjectDataClassesValuesKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataConnectionsKeyD;K;6;CP$UIDd;1;4E;K;28;_CPCibObjectDataFrameworkKeyD;K;6;CP$UIDd;1;0E;K;26;_CPCibObjectDataNextOidKeyD;K;6;CP$UIDd;1;5E;K;30;_CPCibObjectDataObjectsKeysKeyD;K;6;CP$UIDd;1;6E;K;32;_CPCibObjectDataObjectsValuesKeyD;K;6;CP$UIDd;1;7E;K;26;_CPCibObjectDataOidKeysKeyD;K;6;CP$UIDd;1;8E;K;28;_CPCibObjectDataOidValuesKeyD;K;6;CP$UIDd;1;9E;K;28;_CPCibObjectDataFileOwnerKeyD;K;6;CP$UIDd;2;11E;K;33;_CPCibObjectDataVisibleWindowsKeyD;K;6;CP$UIDd;2;13E;E;D;K;10;$classnameS;7;CPArrayK;8;$classesA;S;7;CPArrayS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;15E;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;17E;D;K;6;CP$UIDd;2;18E;D;K;6;CP$UIDd;2;20E;D;K;6;CP$UIDd;2;21E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;22E;D;K;6;CP$UIDd;2;24E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;28E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;2;33E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;37E;D;K;6;CP$UIDd;2;39E;D;K;6;CP$UIDd;2;41E;D;K;6;CP$UIDd;2;42E;D;K;6;CP$UIDd;2;43E;D;K;6;CP$UIDd;2;44E;D;K;6;CP$UIDd;2;45E;D;K;6;CP$UIDd;2;46E;D;K;6;CP$UIDd;2;47E;D;K;6;CP$UIDd;2;48E;D;K;6;CP$UIDd;2;49E;D;K;6;CP$UIDd;2;50E;D;K;6;CP$UIDd;2;51E;D;K;6;CP$UIDd;2;52E;D;K;6;CP$UIDd;2;53E;D;K;6;CP$UIDd;2;54E;D;K;6;CP$UIDd;2;55E;D;K;6;CP$UIDd;2;56E;D;K;6;CP$UIDd;2;57E;D;K;6;CP$UIDd;2;58E;D;K;6;CP$UIDd;2;59E;D;K;6;CP$UIDd;2;61E;D;K;6;CP$UIDd;2;62E;D;K;6;CP$UIDd;2;64E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;65E;D;K;6;CP$UIDd;1;0E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;11E;D;K;6;CP$UIDd;2;11E;D;K;6;CP$UIDd;2;24E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;28E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;2;33E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;37E;D;K;6;CP$UIDd;2;39E;D;K;6;CP$UIDd;2;39E;D;K;6;CP$UIDd;2;37E;D;K;6;CP$UIDd;2;43E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;45E;D;K;6;CP$UIDd;2;46E;D;K;6;CP$UIDd;2;46E;D;K;6;CP$UIDd;2;45E;D;K;6;CP$UIDd;2;49E;D;K;6;CP$UIDd;2;49E;D;K;6;CP$UIDd;2;49E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;53E;D;K;6;CP$UIDd;2;54E;D;K;6;CP$UIDd;2;53E;D;K;6;CP$UIDd;2;56E;D;K;6;CP$UIDd;2;56E;D;K;6;CP$UIDd;2;56E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;64E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;65E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;10;$classnameS;18;_CPCibCustomObjectK;8;$classesA;S;18;_CPCibCustomObjectS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;10E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;66E;E;D;K;10;$classnameS;5;CPSetK;8;$classesA;S;5;CPSetS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;12E;K;15;CPSetObjectsKeyD;K;6;CP$UIDd;2;67E;E;D;K;10;$classnameS;20;CPCibOutletConnectorK;8;$classesA;S;20;CPCibOutletConnectorS;14;CPCibConnectorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;22E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;11E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;68E;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;11E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;28E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;69E;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;11E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;35E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;70E;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;11E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;24E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;71E;E;D;K;10;$classnameS;21;CPCibControlConnectorK;8;$classesA;S;21;CPCibControlConnectorS;14;CPCibConnectorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;19E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;64E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;11E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;72E;E;D;K;6;$classD;K;6;CP$UIDd;2;19E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;65E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;11E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;72E;E;D;K;6;$classD;K;6;CP$UIDd;2;10E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;73E;E;D;K;10;$classnameS;20;_CPCibWindowTemplateK;8;$classesA;S;20;_CPCibWindowTemplateS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;23E;K;30;_CPCibWindowTemplateMinSizeKeyD;K;6;CP$UIDd;2;74E;K;30;_CPCibWindowTemplateMaxSizeKeyD;K;6;CP$UIDd;2;75E;K;32;_CPCibWindowTemplateViewClassKeyD;K;6;CP$UIDd;1;0E;K;34;_CPCibWindowTemplateWindowClassKeyD;K;6;CP$UIDd;2;76E;K;30;_CPCiLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
280NPLIST;1.0;D;K;4;$topD;K;18;CPCibObjectDataKeyD;K;6;CP$UIDd;1;2E;E;K;8;$objectsA;S;5;$nullD;K;10;$classnameS;16;_CPCibObjectDataK;8;$classesA;S;16;_CPCibObjectDataS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;1E;K;28;_CPCibObjectDataNamesKeysKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataNamesValuesKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataClassesKeysKeyD;K;6;CP$UIDd;1;0E;K;32;_CPCibObjectDataClassesValuesKeyD;K;6;CP$UIDd;1;0E;K;30;_CPCibObjectDataConnectionsKeyD;K;6;CP$UIDd;1;4E;K;28;_CPCibObjectDataFrameworkKeyD;K;6;CP$UIDd;1;0E;K;26;_CPCibObjectDataNextOidKeyD;K;6;CP$UIDd;1;5E;K;30;_CPCibObjectDataObjectsKeysKeyD;K;6;CP$UIDd;1;6E;K;32;_CPCibObjectDataObjectsValuesKeyD;K;6;CP$UIDd;1;7E;K;26;_CPCibObjectDataOidKeysKeyD;K;6;CP$UIDd;1;8E;K;28;_CPCibObjectDataOidValuesKeyD;K;6;CP$UIDd;1;9E;K;28;_CPCibObjectDataFileOwnerKeyD;K;6;CP$UIDd;2;11E;K;33;_CPCibObjectDataVisibleWindowsKeyD;K;6;CP$UIDd;2;13E;E;D;K;10;$classnameS;7;CPArrayK;8;$classesA;S;7;CPArrayS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;15E;D;K;6;CP$UIDd;2;16E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;17E;D;K;6;CP$UIDd;2;19E;D;K;6;CP$UIDd;2;21E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;25E;D;K;6;CP$UIDd;2;27E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;30E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;34E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;38E;D;K;6;CP$UIDd;2;40E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;11E;D;K;6;CP$UIDd;2;11E;D;K;6;CP$UIDd;2;19E;D;K;6;CP$UIDd;2;21E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;25E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;19E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;32E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;10;$classnameS;18;_CPCibCustomObjectK;8;$classesA;S;18;_CPCibCustomObjectS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;10E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;41E;E;D;K;10;$classnameS;5;CPSetK;8;$classesA;S;5;CPSetS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;12E;K;15;CPSetObjectsKeyD;K;6;CP$UIDd;2;42E;E;D;K;10;$classnameS;20;CPCibOutletConnectorK;8;$classesA;S;20;CPCibOutletConnectorS;14;CPCibConnectorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;11E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;27E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;43E;E;D;K;6;$classD;K;6;CP$UIDd;2;14E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;11E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;19E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;2;44E;E;D;K;6;$classD;K;6;CP$UIDd;2;10E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;45E;E;D;K;10;$classnameS;20;_CPCibWindowTemplateK;8;$classesA;S;20;_CPCibWindowTemplateS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;18E;K;32;_CPCibWindowTemplateViewClassKeyD;K;6;CP$UIDd;2;32E;K;34;_CPCibWindowTemplateWindowClassKeyD;K;6;CP$UIDd;2;46E;K;30;_CPCibWindowTemplateWTFlagsKeyD;K;6;CP$UIDd;2;47E;K;33;_CPCibWindowTemplateWindowRectKeyD;K;6;CP$UIDd;2;48E;K;33;_CPCibWindowTemplateScreenRectKeyD;K;6;CP$UIDd;2;49E;K;30;_CPCibWindowTempatStyleMaskKeyD;K;6;CP$UIDd;2;50E;K;34;_CPCibWindowTemplateWindowTitleKeyD;K;6;CP$UIDd;2;51E;K;33;_CPCibWindowTemplateWindowViewKeyD;K;6;CP$UIDd;2;21E;E;D;K;10;$classnameS;6;CPViewK;8;$classesA;S;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;20E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;52E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;53E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;53E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;2;54E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;55E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;2;56E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;57E;K;14;CPViewScaleKeyD;K;6;CP$UIDd;2;58E;K;18;CPViewSizeScaleKeyD;K;6;CP$UIDd;2;58E;K;17;CPViewIsScaledKeyD;K;6;CP$UIDd;2;59E;K;19;CPViewAppearanceKeyD;K;6;CP$UIDd;1;0E;K;22;CPViewTrackingAreasKeyD;K;6;CP$UIDd;2;60E;E;D;K;10;$classnameS;12;CPScrollViewK;8;$classesA;S;12;CPScrollViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;22E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;21E;K;18;CPResponderMenuKeyD;K;6;CP$UIDd;1;0E;K;12;CPViewTagKeyD;K;6;CP$UIDd;2;52E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;53E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;53E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;2;61E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;21E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;55E;K;19;CPViewThemeClassKeyD;K;6;CP$UIDd;2;62E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;57E;K;14;CPViewScaleKeyD;K;6;CP$UIDd;2;58E;K;18;CPViewSizeScaleKeyD;K;6;CP$UIDd;2;58E;K;17;CPViewIsScaledKeyD;K;6;CP$UIDd;2;59E;K;19;CPViewAppearanceKeyD;K;6;CP$UIDd;2;64E;K;22;CPViewTrackingAreasKeyD;K;6;CP$UIDd;2;65E;K;23;CPScrollViewContentViewD;K;6;CP$UIDd;2;25E;K;29;CPScrollViewHeaderClipViewKeyD;K;6;CP$UIDd;1;0E;K;21;CPScrollViewVScrollerD;K;6;CP$UIDd;2;30E;K;21;CPScrollViewHScrollerD;K;6Line truncated
|
||||
@@ -1,6 +1,8 @@
|
||||
@import <Foundation/CPMapTable.j>
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPEnumerator.j>
|
||||
@import <Foundation/CPKeyedArchiver.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
@import <Foundation/CPMutableArray.j>
|
||||
@import <OJUnit/OJTestCase.j>
|
||||
|
||||
@@ -139,6 +141,18 @@
|
||||
[self assertTrue:[foundValues containsObject:@"value3"]];
|
||||
}
|
||||
|
||||
- (void)testArchivingPreservesEntries
|
||||
{
|
||||
[map_table setObject:@"first" forKey:@"one"];
|
||||
[map_table setObject:@"second" forKey:@"two"];
|
||||
|
||||
var decoded = [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:map_table]];
|
||||
|
||||
[self assert:2 equals:[decoded count]];
|
||||
[self assert:@"first" equals:[decoded objectForKey:@"one"]];
|
||||
[self assert:@"second" equals:[decoded objectForKey:@"two"]];
|
||||
}
|
||||
|
||||
- (void)testDictionaryRepresentation
|
||||
{
|
||||
[map_table setObject:@"value1" forKey:@"key1"];
|
||||
|
||||
Reference in new issue
Block a user