mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-31 04:17:36 +00:00
Compare commits
342
Commits
main
...
aristo3-staging
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0178206a92 | ||
|
|
46989b1ea2 | ||
|
|
f9f5656218 | ||
|
|
09c855daca | ||
|
|
3bb8693e8e | ||
|
|
baa354c1d4 | ||
|
|
444d1d620c | ||
|
|
05bc99690d | ||
|
|
0ba4fdb43f | ||
|
|
3ad080b788 | ||
|
|
e838f9ab43 | ||
|
|
a4fd136570 | ||
|
|
5ec9a6e76b | ||
|
|
b10288bef7 | ||
|
|
8a87077efe | ||
|
|
7fc027af42 | ||
|
|
f5c735e97c | ||
|
|
bd572d8059 | ||
|
|
6001562585 | ||
|
|
1d1b41db83 | ||
|
|
d64072076d | ||
|
|
ea4fc96df5 | ||
|
|
7e91024670 | ||
|
|
715fa606ff | ||
|
|
f6926cc2e4 | ||
|
|
8312f652a4 | ||
|
|
5dbb553a20 | ||
|
|
33397edf9a | ||
|
|
fa748711c4 | ||
|
|
8a4213ced1 | ||
|
|
910fe051e0 | ||
|
|
2e9b02b65a | ||
|
|
5c2ef06035 | ||
|
|
1878967da1 | ||
|
|
40cdc9108a | ||
|
|
69c8c3d0a2 | ||
|
|
a42f597463 | ||
|
|
a1eddb9e50 | ||
|
|
29163884ce | ||
|
|
aa592b8bc6 | ||
|
|
e0ce1603ff | ||
|
|
0597e477ae | ||
|
|
b9e734a21b | ||
|
|
45bd822c28 | ||
|
|
3467a60215 | ||
|
|
831e0b4b85 | ||
|
|
d64966594d | ||
|
|
f2c10e9f4c | ||
|
|
a091a7c5d6 | ||
|
|
f06bdbb14f | ||
|
|
76a3d0b6da | ||
|
|
078ab40d99 | ||
|
|
d44dd5da19 | ||
|
|
35132e7f46 | ||
|
|
231b3052fc | ||
|
|
9bec13a72b | ||
|
|
fb348ed4f7 | ||
|
|
e49144c80a | ||
|
|
94799f19dc | ||
|
|
e1762fdcdd | ||
|
|
fa077e4fc7 | ||
|
|
fd745d37a9 | ||
|
|
2da26b2de2 | ||
|
|
17dc653149 | ||
|
|
e779198a80 | ||
|
|
092550e2a3 | ||
|
|
579129b891 | ||
|
|
ec8bb7e14d | ||
|
|
4af4c6706f | ||
|
|
fb9c0715b3 | ||
|
|
8cedfebc07 | ||
|
|
93736b7ba9 | ||
|
|
e801064bd9 | ||
|
|
6b9bad37c2 | ||
|
|
915047b996 | ||
|
|
8231d8ddf1 | ||
|
|
125673880c | ||
|
|
b24cb2c48e | ||
|
|
a56458384c | ||
|
|
063f2f0283 | ||
|
|
4aac83b50d | ||
|
|
48b4345e80 | ||
|
|
4636c0f3b8 | ||
|
|
db5f5ba332 | ||
|
|
7c093e1a64 | ||
|
|
d9859af73c | ||
|
|
83bc94d1c3 | ||
|
|
3a4b8fe947 | ||
|
|
5385e63fd2 | ||
|
|
e1a3f2b64d | ||
|
|
3d3674a7d8 | ||
|
|
192b6ecfc4 | ||
|
|
79ff197cde | ||
|
|
836698f8d2 | ||
|
|
ac52d32c29 | ||
|
|
e66a0656c8 | ||
|
|
7dcf5629d7 | ||
|
|
facb129183 | ||
|
|
13557bd13f | ||
|
|
dccdfb5635 | ||
|
|
aba4c865e4 | ||
|
|
9709ca8b52 | ||
|
|
ea74a503a3 | ||
|
|
675d1ebd57 | ||
|
|
7eb042d985 | ||
|
|
dc4aa545f0 | ||
|
|
b70abb730d | ||
|
|
36a2cc9a62 | ||
|
|
113be1254e | ||
|
|
674a03dc23 | ||
|
|
9656985dbd | ||
|
|
0b4fe62f89 | ||
|
|
847a9c2b98 | ||
|
|
ce78d098ec | ||
|
|
18c1866db7 | ||
|
|
1ae0566cf1 | ||
|
|
383fb9d704 | ||
|
|
7f281d0442 | ||
|
|
2d76a03b01 | ||
|
|
f96ffde1fb | ||
|
|
39fb7c6b5b | ||
|
|
0fa2b62cc9 | ||
|
|
ea69f8b9cc | ||
|
|
b0e88390e5 | ||
|
|
4882f26142 | ||
|
|
373737cfd0 | ||
|
|
475945d329 | ||
|
|
5111a02672 | ||
|
|
12477179cb | ||
|
|
ab81b1ad32 | ||
|
|
0af674ca74 | ||
|
|
5b95490371 | ||
|
|
993e9a9e86 | ||
|
|
2f32e5eaef | ||
|
|
fcd1897883 | ||
|
|
77063efe58 | ||
|
|
57a53abbf7 | ||
|
|
e30380a8f9 | ||
|
|
cdcaf1c4d4 | ||
|
|
e62b22da55 | ||
|
|
7bd8660f89 | ||
|
|
d0c63345f2 | ||
|
|
1a7b7054fb | ||
|
|
01fc279709 | ||
|
|
5540ef8ca7 | ||
|
|
ef3ee2e660 | ||
|
|
d661c83acc | ||
|
|
437bcc8078 | ||
|
|
68316732de | ||
|
|
0a42b39a94 | ||
|
|
ac1407484a | ||
|
|
a531dc6fc0 | ||
|
|
e42d5b860e | ||
|
|
57858db161 | ||
|
|
f3392c7df1 | ||
|
|
18c2145138 | ||
|
|
ff6981bf10 | ||
|
|
db82911b1f | ||
|
|
768f5121cb | ||
|
|
c2d7a1dc74 | ||
|
|
61efb2fcdf | ||
|
|
b6716f77e0 | ||
|
|
47c4bb957b | ||
|
|
1a64819ab5 | ||
|
|
ff7939df03 | ||
|
|
443ced0dce | ||
|
|
baf505df52 | ||
|
|
90331b075a | ||
|
|
900e3b6ea1 | ||
|
|
74d8463255 | ||
|
|
af6692d616 | ||
|
|
6611b74c19 | ||
|
|
37bff8b70b | ||
|
|
d964b9f10a | ||
|
|
189a2ddc3d | ||
|
|
641b35d965 | ||
|
|
433e6606b6 | ||
|
|
bc88af047e | ||
|
|
b93beb8730 | ||
|
|
24a176c736 | ||
|
|
4d741040b6 | ||
|
|
92a30edf6c | ||
|
|
031c3acc57 | ||
|
|
500541a785 | ||
|
|
60e01cdb33 | ||
|
|
c0c5325d8a | ||
|
|
fbe4b3d439 | ||
|
|
aa72c9e081 | ||
|
|
91db2f409e | ||
|
|
2c2211026f | ||
|
|
578f00172f | ||
|
|
fb93d3dcf0 | ||
|
|
10989ada31 | ||
|
|
28e9063c04 | ||
|
|
63ec5e78b6 | ||
|
|
52f6503631 | ||
|
|
a5a91d7258 | ||
|
|
a00b94ec33 | ||
|
|
78b1e292f9 | ||
|
|
d9d86df1bd | ||
|
|
b5f9f05f9f | ||
|
|
245561d40f | ||
|
|
9301ba7c25 | ||
|
|
24e9eadfab | ||
|
|
fe8f2deb6e | ||
|
|
1ae19f88b9 | ||
|
|
50702a0560 | ||
|
|
fbee21dad3 | ||
|
|
13c3fe5771 | ||
|
|
fde9f56d4d | ||
|
|
f2ca8ab940 | ||
|
|
3d0b1b7680 | ||
|
|
40172028e6 | ||
|
|
49923fa140 | ||
|
|
b96a4e5b94 | ||
|
|
9a50fc4789 | ||
|
|
601daf9e72 | ||
|
|
2658852ec2 | ||
|
|
737fd115e4 | ||
|
|
461f341abc | ||
|
|
c26feeb73e | ||
|
|
29714ae18f | ||
|
|
496f934adb | ||
|
|
bf15c2bf11 | ||
|
|
07cf8ac9be | ||
|
|
9708f80ad4 | ||
|
|
0bd0dc6216 | ||
|
|
d3e5583070 | ||
|
|
10eedb2bb5 | ||
|
|
d381a602b7 | ||
|
|
266251ffbf | ||
|
|
cd9bb7a86e | ||
|
|
97f98e3c93 | ||
|
|
c72c761a60 | ||
|
|
2fe83853ac | ||
|
|
cd940d64ed | ||
|
|
15d4575b0a | ||
|
|
e21d23e911 | ||
|
|
9e649ce96a | ||
|
|
68526721b7 | ||
|
|
be3c5dac8a | ||
|
|
26cac2b84d | ||
|
|
2f552abf94 | ||
|
|
7129ebe884 | ||
|
|
b9992215f8 | ||
|
|
0c55a5f372 | ||
|
|
8cc33d1e37 | ||
|
|
73169f0be5 | ||
|
|
7f8d3f289b | ||
|
|
d8c74e81b8 | ||
|
|
3000254e67 | ||
|
|
2356340f0b | ||
|
|
227d743666 | ||
|
|
b53feeb04c | ||
|
|
3b1043a6b4 | ||
|
|
ba32f92e69 | ||
|
|
5c7d978245 | ||
|
|
d96b642f4f | ||
|
|
2366c5c1fd | ||
|
|
84a85a767c | ||
|
|
e0c9bd10a4 | ||
|
|
eed6c93349 | ||
|
|
7023bb8b90 | ||
|
|
579814a5de | ||
|
|
a2786a9aee | ||
|
|
bcd0c725ce | ||
|
|
3c1c01fd9f | ||
|
|
3364d1c5cc | ||
|
|
639a0fe1c2 | ||
|
|
d3cc6c29c9 | ||
|
|
89c00e193b | ||
|
|
b38a5d19b1 | ||
|
|
f7ae515d84 | ||
|
|
37fedd41e3 | ||
|
|
3711461201 | ||
|
|
4ddd8ac263 | ||
|
|
aa7a96cfb8 | ||
|
|
2c2768a34f | ||
|
|
91c2fd2085 | ||
|
|
9ab63ee854 | ||
|
|
ef1dd16766 | ||
|
|
39f4b573da | ||
|
|
2a0306b14d | ||
|
|
e0735a1d82 | ||
|
|
6f44fe9f93 | ||
|
|
f390e5ed17 | ||
|
|
ede3eb64ae | ||
|
|
d0d4972681 | ||
|
|
4be4a65c72 | ||
|
|
3e009bbf26 | ||
|
|
3db3f03e5d | ||
|
|
698f58eaa0 | ||
|
|
95cf933ab2 | ||
|
|
692f365fed | ||
|
|
96aa310839 | ||
|
|
8d3da8e4fa | ||
|
|
885145d43d | ||
|
|
c893e4a809 | ||
|
|
1536c55b99 | ||
|
|
f3f94eec66 | ||
|
|
51b633f0d7 | ||
|
|
5f1007dcbc | ||
|
|
ae7ce81b8c | ||
|
|
8b72e5db31 | ||
|
|
0d35ceda31 | ||
|
|
ed59003a95 | ||
|
|
94501c4618 | ||
|
|
bf3847ce59 | ||
|
|
c6ea894f10 | ||
|
|
8df4e1ee6e | ||
|
|
b608709014 | ||
|
|
0450136af3 | ||
|
|
29e4695503 | ||
|
|
a7a080f416 | ||
|
|
effb274410 | ||
|
|
45f6f4bc90 | ||
|
|
82b91b2f64 | ||
|
|
59a9d8c093 | ||
|
|
ae42acaef3 | ||
|
|
531be2db49 | ||
|
|
8089973b7b | ||
|
|
1e8cc3101b | ||
|
|
a27a5fbc32 | ||
|
|
fccd76ec4c | ||
|
|
39738b9ded | ||
|
|
3a18574838 | ||
|
|
ec238f2cc3 | ||
|
|
8c08803fd0 | ||
|
|
cc084692ae | ||
|
|
96a25dfcaa | ||
|
|
500237b2c8 | ||
|
|
67a0098c7a | ||
|
|
4fc80f4bbd | ||
|
|
eca41e7474 | ||
|
|
9107322d28 | ||
|
|
f5ca6c39f6 | ||
|
|
653fdb999e | ||
|
|
f25d76d0d6 | ||
|
|
41b0f80360 | ||
|
|
31fe918b3d | ||
|
|
32495792dc | ||
|
|
30bdbb4544 |
No files matched your search
+82
-32
@@ -419,20 +419,25 @@ var bottomHeight = 71;
|
||||
*/
|
||||
- (void)addButtonWithTitle:(CPString)aTitle
|
||||
{
|
||||
var bounds = [[_window contentView] bounds],
|
||||
count = [_buttons count],
|
||||
|
||||
var count = [_buttons count],
|
||||
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[button setTitle:aTitle];
|
||||
[button setTag:count];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(_takeReturnCodeFrom:)];
|
||||
[button setBezelStyle:CPSmallSquareBezelStyle];
|
||||
|
||||
[[_window contentView] addSubview:button];
|
||||
// Only add subview if the window has been created.
|
||||
// Otherwise, _createWindowWithStyle will handle adding the buttons from the _buttons array later.
|
||||
if (_window)
|
||||
[[_window contentView] addSubview:button];
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
[button setKeyEquivalent:CPCarriageReturnCharacter];
|
||||
[button setBezelStyle:CPRoundedBezelStyle];
|
||||
}
|
||||
else if ([aTitle lowercaseString] === @"cancel")
|
||||
[button setKeyEquivalent:CPEscapeFunctionKey];
|
||||
|
||||
@@ -531,6 +536,9 @@ var bottomHeight = 71;
|
||||
[[_window contentView] addSubview:_suppressionButton];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
@@ -540,25 +548,17 @@ var bottomHeight = 71;
|
||||
minimumSize = [_themeView currentValueForThemeAttribute:@"size"],
|
||||
buttonOffset = [_themeView currentValueForThemeAttribute:@"button-offset"],
|
||||
helpLeftOffset = [_themeView currentValueForThemeAttribute:@"help-image-left-offset"],
|
||||
aRepresentativeButton = [_buttons objectAtIndex:0],
|
||||
defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
|
||||
panelSize = [[_window contentView] frame].size,
|
||||
|
||||
buttonsOriginY,
|
||||
buttonMarginY,
|
||||
buttonMarginX,
|
||||
theme = [self theme],
|
||||
offsetX;
|
||||
|
||||
[aRepresentativeButton setTheme:[self theme]];
|
||||
[aRepresentativeButton sizeToFit];
|
||||
|
||||
panelSize.height = CGRectGetMaxY([lastView frame]) + defaultElementsMargin + [aRepresentativeButton frameSize].height;
|
||||
if (panelSize.height < minimumSize.height)
|
||||
panelSize.height = minimumSize.height;
|
||||
|
||||
buttonsOriginY = panelSize.height - [aRepresentativeButton frameSize].height + buttonOffset;
|
||||
offsetX = panelSize.width - inset.right;
|
||||
var isHUD = (_defaultWindowStyle & CPHUDBackgroundWindowMask) || (theme === [CPTheme defaultHudTheme]);
|
||||
|
||||
// 1. Determine Margins (Moved up so we can use them in height calculation)
|
||||
switch ([_window styleMask])
|
||||
{
|
||||
case _CPModalWindowMask:
|
||||
@@ -572,27 +572,61 @@ var bottomHeight = 71;
|
||||
break;
|
||||
}
|
||||
|
||||
// 2. Prepare buttons and get row height
|
||||
var maxButtonHeight = 0.0;
|
||||
|
||||
for (var i = 0; i < [_buttons count]; i++)
|
||||
{
|
||||
var btn = _buttons[i];
|
||||
[btn sizeToFit];
|
||||
|
||||
if (isHUD)
|
||||
[btn setThemeState:CPThemeStateHUD];
|
||||
else
|
||||
[btn unsetThemeState:CPThemeStateHUD];
|
||||
|
||||
maxButtonHeight = MAX(maxButtonHeight, CGRectGetHeight([btn frame]));
|
||||
}
|
||||
|
||||
// 3. Calculate Content Height
|
||||
var lastViewMaxY = CGRectGetMaxY([lastView frame]);
|
||||
// Use bottomHeight (71) to reserve space for the footer
|
||||
var requiredContentHeight = lastViewMaxY + bottomHeight;
|
||||
|
||||
var finalContentSize = CGSizeMake(
|
||||
[[_window contentView] frame].size.width,
|
||||
MAX(requiredContentHeight, minimumSize.height)
|
||||
);
|
||||
|
||||
// 4. Position Buttons
|
||||
// Calculate the top Y coordinate to vertically center the button row within the bottomHeight area
|
||||
// Center Y of footer = Height - (bottomHeight / 2.0)
|
||||
// Top Y of button = Center Y - (maxButtonHeight / 2.0)
|
||||
buttonsOriginY = finalContentSize.height - ((bottomHeight + maxButtonHeight) / 2.0) - buttonMarginY;
|
||||
|
||||
offsetX = finalContentSize.width - inset.right;
|
||||
// Loop and set frames
|
||||
for (var i = [_buttons count] - 1; i >= 0 ; i--)
|
||||
{
|
||||
var button = _buttons[i];
|
||||
[button setTheme:[self theme]];
|
||||
[button sizeToFit];
|
||||
|
||||
var buttonFrame = [button frame],
|
||||
var button = _buttons[i],
|
||||
buttonFrame = [button frame],
|
||||
width = MAX(80.0, CGRectGetWidth(buttonFrame)),
|
||||
height = CGRectGetHeight(buttonFrame);
|
||||
height = CGRectGetHeight(buttonFrame),
|
||||
yOffset = FLOOR((maxButtonHeight - height) / 2.0);
|
||||
|
||||
offsetX -= width;
|
||||
[button setFrame:CGRectMake(offsetX + buttonMarginX, buttonsOriginY + buttonMarginY, width, height)];
|
||||
[button setFrame:CGRectMake(offsetX + buttonMarginX, buttonsOriginY + buttonMarginY + yOffset, width, height)];
|
||||
offsetX -= 10;
|
||||
}
|
||||
|
||||
// Position Help Button if needed
|
||||
if (_showHelp)
|
||||
{
|
||||
var helpImage = [_themeView currentValueForThemeAttribute:@"help-image"],
|
||||
helpImagePressed = [_themeView currentValueForThemeAttribute:@"help-image-pressed"],
|
||||
helpImageSize = helpImage ? [helpImage size] : CGSizeMakeZero(),
|
||||
helpFrame = CGRectMake(helpLeftOffset, buttonsOriginY, helpImageSize.width, helpImageSize.height);
|
||||
helpYOffset = FLOOR((maxButtonHeight - helpImageSize.height) / 2.0),
|
||||
helpFrame = CGRectMake(helpLeftOffset, buttonsOriginY + buttonMarginY + helpYOffset, helpImageSize.width, helpImageSize.height);
|
||||
|
||||
[_alertHelpButton setImage:helpImage];
|
||||
[_alertHelpButton setAlternateImage:helpImagePressed];
|
||||
@@ -600,8 +634,7 @@ var bottomHeight = 71;
|
||||
[_alertHelpButton setFrame:helpFrame];
|
||||
}
|
||||
|
||||
panelSize.height += [aRepresentativeButton frameSize].height + inset.bottom + buttonOffset;
|
||||
return panelSize;
|
||||
return finalContentSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -615,9 +648,14 @@ var bottomHeight = 71;
|
||||
if (!_window)
|
||||
[self _createWindowWithStyle:nil];
|
||||
|
||||
// Ensure the theme view knows if we are in HUD mode so it picks up the right specificities
|
||||
if ((_defaultWindowStyle & CPHUDBackgroundWindowMask) || ([self theme] === [CPTheme defaultHudTheme]))
|
||||
[_themeView setThemeState:CPThemeStateHUD];
|
||||
else
|
||||
[_themeView unsetThemeState:CPThemeStateHUD];
|
||||
|
||||
var iconOffset = [_themeView currentValueForThemeAttribute:@"image-offset"],
|
||||
theImage = _icon,
|
||||
finalSize;
|
||||
theImage = _icon;
|
||||
|
||||
if (!theImage)
|
||||
switch (_alertStyle)
|
||||
@@ -649,11 +687,17 @@ var bottomHeight = 71;
|
||||
else if (_accessoryView)
|
||||
lastView = _accessoryView;
|
||||
|
||||
finalSize = [self _layoutButtonsFromView:lastView];
|
||||
if ([_window styleMask] & CPDocModalWindowMask)
|
||||
finalSize.height -= 26; // adjust the absence of title bar
|
||||
|
||||
[_window setFrameSize:finalSize];
|
||||
// 1. Get the size needed for the *content* (text, buttons, padding)
|
||||
var finalContentSize = [self _layoutButtonsFromView:lastView];
|
||||
|
||||
// 2. Convert Content Size -> Frame Size
|
||||
// This accounts for the Title Bar and borders automatically.
|
||||
var contentRect = CGRectMake(0.0, 0.0, finalContentSize.width, finalContentSize.height);
|
||||
var frameRect = [[_window class] frameRectForContentRect:contentRect styleMask:[_window styleMask]];
|
||||
|
||||
// 3. Apply the calculated Frame Size
|
||||
[_window setFrameSize:frameRect.size];
|
||||
|
||||
[_window center];
|
||||
|
||||
if ([_window styleMask] & _CPModalWindowMask || [_window styleMask] & CPHUDBackgroundWindowMask)
|
||||
@@ -753,6 +797,12 @@ var bottomHeight = 71;
|
||||
var frame = CGRectMakeZero();
|
||||
frame.size = [_themeView currentValueForThemeAttribute:@"size"];
|
||||
|
||||
// Propagate CPHUDBackgroundWindowMask from _defaultWindowStyle to forceStyle.
|
||||
// This ensures that even if we force CPDocModalWindowMask (for sheets),
|
||||
// the window still knows it should be a HUD.
|
||||
if (_defaultWindowStyle & CPHUDBackgroundWindowMask)
|
||||
forceStyle |= CPHUDBackgroundWindowMask;
|
||||
|
||||
_window = [[CPPanel alloc] initWithContentRect:frame styleMask:forceStyle || _defaultWindowStyle];
|
||||
[_window setLevel:CPStatusWindowLevel];
|
||||
[_window setPlatformWindow:[[CPApp keyWindow] platformWindow]];
|
||||
|
||||
+75
-2
@@ -127,6 +127,10 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
|
||||
CPMenuItem _windowsMenuSeparator;
|
||||
|
||||
CPThemeBlend _themeBlend @accessors(property=themeBlend);
|
||||
|
||||
// OS behavior
|
||||
CPApplicationOSBehavior _OSBehavior;
|
||||
BOOL _simulatesWindows;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -159,6 +163,9 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
|
||||
_eventListenerInsertionIndex = 0;
|
||||
|
||||
_windows = [[CPNull null]];
|
||||
|
||||
_OSBehavior = CPApplicationLegacyOSBehavior;
|
||||
_simulatesWindows = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -1493,7 +1500,45 @@ var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
|
||||
|
||||
+ (CPString)defaultThemeName
|
||||
{
|
||||
return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo2");
|
||||
return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo3");
|
||||
}
|
||||
|
||||
// See CPApplication_Constants.j for comments
|
||||
- (void)setOSBehavior:(CPApplicationOSBehavior)anOSBehavior
|
||||
{
|
||||
// Verify if provided OS behavior is valid
|
||||
if ([[CPApplicationOSBehaviors allKeysForObject:anOSBehavior] count] == 0)
|
||||
{
|
||||
CPLog.warn("CPApplication setOSBehavior: invalid CPApplicationOSBehavior (received "+anOSBehavior+"). Ignored.");
|
||||
return;
|
||||
}
|
||||
|
||||
_OSBehavior = anOSBehavior;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationOSBehaviorDidChangeNotification object:CPApp userInfo:nil];
|
||||
}
|
||||
|
||||
- (CPApplicationOSBehavior)OSBehavior
|
||||
{
|
||||
return _OSBehavior;
|
||||
}
|
||||
|
||||
- (BOOL)shouldMimicWindows
|
||||
{
|
||||
return (_OSBehavior == CPApplicationFollowOSBehavior) && (CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) || _simulatesWindows);
|
||||
}
|
||||
|
||||
- (void)setSimulatesWindows:(BOOL)shouldSimulateWindows
|
||||
{
|
||||
if (_simulatesWindows === shouldSimulateWindows)
|
||||
return;
|
||||
|
||||
_simulatesWindows = shouldSimulateWindows;
|
||||
}
|
||||
|
||||
- (BOOL)simulatesWindows
|
||||
{
|
||||
return _simulatesWindows;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1608,7 +1653,7 @@ var _CPAppBootstrapperActions = nil;
|
||||
var defaultThemeName = [CPApplication defaultThemeName],
|
||||
themeURL = nil;
|
||||
|
||||
if (defaultThemeName === @"Aristo" || defaultThemeName === @"Aristo2")
|
||||
if (defaultThemeName === @"Aristo" || defaultThemeName === @"Aristo2" || defaultThemeName === @"Aristo3")
|
||||
themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"];
|
||||
else
|
||||
themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"];
|
||||
@@ -1624,6 +1669,34 @@ var _CPAppBootstrapperActions = nil;
|
||||
[[CPApplication sharedApplication] setThemeBlend:aThemeBlend];
|
||||
[CPTheme setDefaultTheme:[CPTheme themeNamed:[CPApplication defaultThemeName]]];
|
||||
|
||||
// Search in the Info.plist if the special CPApplicationSimulateWindowsOS flag is set (for testing)
|
||||
[CPApp setSimulatesWindows:!![[CPBundle mainBundle] objectForInfoDictionaryKey:"CPApplicationSimulateWindowsOS"]];
|
||||
|
||||
// Before loading the main CIB, try to find if a CPApplicationOSBehavior is specified in the Info.plist or in the user defaults
|
||||
// (with user defaults precedence). Value stored must be a string representing the name of the OS behavior.
|
||||
var plistOSBehavior = [[CPBundle mainBundle] objectForInfoDictionaryKey:"CPApplicationOSBehavior"],
|
||||
userOSBehavior = [[CPUserDefaults standardUserDefaults] objectForKey:@"CPApplicationOSBehavior"];
|
||||
|
||||
if (userOSBehavior)
|
||||
{
|
||||
var osBehavior = [CPApplicationOSBehaviors objectForKey:userOSBehavior];
|
||||
|
||||
if (osBehavior)
|
||||
[CPApp setOSBehavior:osBehavior];
|
||||
else
|
||||
CPLog.warn("Invalid CPApplicationOSBehavior specified in user defaults (found:"+userOSBehavior+"). Ignored.");
|
||||
}
|
||||
|
||||
else if (plistOSBehavior)
|
||||
{
|
||||
var osBehavior = [CPApplicationOSBehaviors objectForKey:plistOSBehavior];
|
||||
|
||||
if (osBehavior)
|
||||
[CPApp setOSBehavior:osBehavior];
|
||||
else
|
||||
CPLog.warn("Invalid CPApplicationOSBehavior specified in Info.plist (found:"+plistOSBehavior+"). Ignored.");
|
||||
}
|
||||
|
||||
[self performActions];
|
||||
}
|
||||
|
||||
|
||||
@@ -39,3 +39,15 @@ CPTerminateLater = -1; // not currently supported
|
||||
CPRunStoppedResponse = -1000;
|
||||
CPRunAbortedResponse = -1001;
|
||||
CPRunContinuesResponse = -1002;
|
||||
|
||||
// Should the application follow Cappuccino UX-UI (which is OSX like) or OS UX-UI (mainly Windows) ?
|
||||
// See explanation on https://github.com/cappuccino/cappuccino/wiki/CPApplicationSelectedOSBehavior
|
||||
@typedef CPApplicationOSBehavior
|
||||
CPApplicationLegacyOSBehavior = 1;
|
||||
CPApplicationFollowOSBehavior = 2;
|
||||
|
||||
CPApplicationOSBehaviorDidChangeNotification = @"CPApplicationOSBehaviorDidChangeNotification";
|
||||
CPApplicationOSBehaviors = @{
|
||||
@"CPApplicationLegacyOSBehavior": CPApplicationLegacyOSBehavior,
|
||||
@"CPApplicationFollowOSBehavior": CPApplicationFollowOSBehavior
|
||||
};
|
||||
+2
-2
@@ -285,7 +285,7 @@ CPBelowBottom = 6;
|
||||
// MARK: borderColor
|
||||
- (CPColor)borderColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"border-color"];
|
||||
return [self currentValueForThemeAttribute:@"border-color"];
|
||||
}
|
||||
|
||||
- (void)setBorderColor:(CPColor)color
|
||||
@@ -785,7 +785,7 @@ CPBelowBottom = 6;
|
||||
[_boxView setFrame:CGRectMake(0,2,bounds.size.width,1)];
|
||||
}
|
||||
|
||||
[_boxView setBackgroundColor:[self valueForThemeAttribute:@"border-color"]];
|
||||
[_boxView setBackgroundColor:[self currentValueForThemeAttribute:@"border-color"]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
|
||||
|
||||
_prototypeView = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
[_prototypeView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_prototypeView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateSelectedDataView];
|
||||
[_prototypeView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateSelectedDataView.and(CPThemeStateTableDataView)];
|
||||
[_prototypeView setLineBreakMode:CPLineBreakByTruncatingTail];
|
||||
|
||||
_horizontalScrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
+10
-4
@@ -98,7 +98,8 @@ var CPButtonBezelStyleStateMap = @{
|
||||
CPRegularSquareBezelStyle: CPButtonStateBezelStyleRegularSquare,
|
||||
CPTexturedSquareBezelStyle: CPButtonStateBezelStyleTextured,
|
||||
CPDisclosureBezelStyle: CPButtonStateBezelStyleDisclosure,
|
||||
CPRoundedDisclosureBezelStyle: CPButtonStateBezelStyleRoundedDisclosure
|
||||
CPRoundedDisclosureBezelStyle: CPButtonStateBezelStyleRoundedDisclosure,
|
||||
CPHUDBezelStyle: CPThemeStateHUD
|
||||
};
|
||||
|
||||
/// @cond IGNORE
|
||||
@@ -707,12 +708,17 @@ CPButtonImageOffset = 3.0;
|
||||
// Note : We have to split content and image visual states as, for example, radio buttons don't follow push buttons behavior
|
||||
- (CPThemeState)_contentVisualState
|
||||
{
|
||||
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
|
||||
var visualState = [self themeState] || CPThemeStateNormal,
|
||||
currentState = [self state],
|
||||
buttonIsOn = (currentState !== CPOffState);
|
||||
|
||||
// If the button is pushed (_isHighlighted), always add the highlighted state
|
||||
if (_isHighlighted || (((_showsStateBy & CPChangeGrayCellMask) || (_showsStateBy & CPChangeBackgroundCellMask)) && buttonIsOn))
|
||||
// Define masks that imply the background changes color (Blue/Gray)
|
||||
// If the background changes, we usually want the text to turn White (Highlighted state).
|
||||
var highlightMask = CPPushInCellMask | CPChangeGrayCellMask | CPChangeBackgroundCellMask;
|
||||
|
||||
// Only add CPThemeStateHighlighted if the button is configured to highlight visually (Background change)
|
||||
if ((_isHighlighted && (_highlightsBy & highlightMask)) ||
|
||||
(((_showsStateBy & CPChangeGrayCellMask) || (_showsStateBy & CPChangeBackgroundCellMask)) && buttonIsOn))
|
||||
visualState = visualState.and(CPThemeStateHighlighted);
|
||||
else
|
||||
visualState = visualState.without(CPThemeStateHighlighted);
|
||||
|
||||
+1682
-167
File diff suppressed because it is too large.
Load diff
+10
-19
@@ -91,7 +91,7 @@ var CPComboBoxTextSubview = @"text",
|
||||
{
|
||||
return @{
|
||||
@"popup-button-size": CGSizeMake(21.0, 29.0),
|
||||
@"border-inset": CGInsetMake(3.0, 3.0, 3.0, 3.0),
|
||||
@"border-inset": CGInsetMake(3.0, 3.0, 3.0, 3.0)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -533,7 +533,7 @@ var CPComboBoxTextSubview = @"text",
|
||||
CPComboBoxFocusRingWidth = inset.bottom;
|
||||
}
|
||||
|
||||
[_listDelegate popUpRelativeToRect:[self _borderFrame] view:self offset:CPComboBoxFocusRingWidth - 1];
|
||||
[_listDelegate popUpRelativeToRect:[self bounds] view:self offset:CPComboBoxFocusRingWidth - 1];
|
||||
[self _selectMatchingItem];
|
||||
}
|
||||
|
||||
@@ -570,6 +570,14 @@ var CPComboBoxTextSubview = @"text",
|
||||
_selectedStringValue = selectedStringValue;
|
||||
|
||||
[self setStringValue:_selectedStringValue];
|
||||
|
||||
[self _updatePlaceholderState];
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
[self _setCSSStyleForInputElement];
|
||||
}
|
||||
|
||||
[self _reverseSetBinding];
|
||||
|
||||
return YES;
|
||||
@@ -973,23 +981,6 @@ var CPComboBoxTextSubview = @"text",
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Calculate the frame in base coordinates that will nestle just below the visible border of the text field.
|
||||
@ignore
|
||||
*/
|
||||
- (CGRect)_borderFrame
|
||||
{
|
||||
var inset = [self currentValueForThemeAttribute:@"border-inset"],
|
||||
frame = [self bounds];
|
||||
|
||||
frame.origin.x += inset.left;
|
||||
frame.origin.y += inset.top;
|
||||
frame.size.width -= inset.left + inset.right;
|
||||
frame.size.height -= inset.top + inset.bottom;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_popUpButtonWasClicked
|
||||
{
|
||||
|
||||
+5
-1
@@ -48,9 +48,10 @@
|
||||
CPRegularControlSize = 0;
|
||||
CPSmallControlSize = 1;
|
||||
CPMiniControlSize = 2;
|
||||
CPLargeControlSize = 3; // Since MacOS 11, there's a new control size "Large"
|
||||
|
||||
// To get the theme state corresponding to a control size, use CPControlSizeThemeStates[controlSize]
|
||||
CPControlSizeThemeStates = @[CPThemeStateControlSizeRegular, CPThemeStateControlSizeSmall, CPThemeStateControlSizeMini];
|
||||
CPControlSizeThemeStates = @[CPThemeStateControlSizeRegular, CPThemeStateControlSizeSmall, CPThemeStateControlSizeMini, CPThemeStateControlSizeLarge];
|
||||
|
||||
@typedef CPLineBreakMode
|
||||
CPLineBreakByWordWrapping = 0;
|
||||
@@ -248,6 +249,9 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
|
||||
case CPMiniControlSize:
|
||||
return CPThemeStateControlSizeMini;
|
||||
|
||||
case CPLargeControlSize:
|
||||
return CPThemeStateControlSizeLarge;
|
||||
|
||||
case CPRegularControlSize:
|
||||
default:
|
||||
|
||||
+1
-1
@@ -246,7 +246,7 @@ following:
|
||||
if (systemFontStyle)
|
||||
{
|
||||
// Yes, so install it in the DOM Style element
|
||||
document.getElementsByTagName("STYLE")[0].innerHTML += "\n" + [aTheme setCSSResourcesPathInString:systemFontStyle];
|
||||
document.getElementsByTagName("STYLE")[0].innerHTML += "\n" + [aTheme setCSSResourcesPath:systemFontStyle];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -242,6 +242,8 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
|
||||
if (_hasShadow)
|
||||
{
|
||||
[self setClipsToBounds:NO];
|
||||
|
||||
_shadowView = [[CPShadowView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
[self addSubview:_shadowView];
|
||||
@@ -464,6 +466,12 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
previousState:@ref(_cssStylePreviousState)];
|
||||
}
|
||||
|
||||
if (CPDOMAvailable && [image isCSSBased])
|
||||
{
|
||||
_DOMImageElement.style.width = _DOMImageElement.width + 'px';
|
||||
_DOMImageElement.style.height = _DOMImageElement.height + 'px';
|
||||
}
|
||||
|
||||
_imageRect = CGRectMake(x, y, width, height);
|
||||
|
||||
if (_hasShadow)
|
||||
|
||||
+26
-22
@@ -88,43 +88,61 @@ CPRatingLevelIndicatorStyle = 3;
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
// 1. Calculate the Theme State
|
||||
// We explicitly check the window style mask to see if we are in a HUD.
|
||||
// This allows us to pass CPThemeStateHUD to the theme system, even if the control
|
||||
// itself isn't explicitly set to HUD, inheriting the style from the window.
|
||||
var themeState = [self themeState];
|
||||
|
||||
if ([[self window] styleMask] & CPHUDBackgroundWindowMask)
|
||||
themeState = themeState.and(CPThemeStateHUD);
|
||||
|
||||
// 2. Layout the Bezel
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:"bezel"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
// TODO Make themable.
|
||||
[bezelView setBackgroundColor:[self valueForThemeAttribute:@"bezel-color"]];
|
||||
|
||||
[bezelView setBackgroundColor:[self valueForThemeAttribute:@"bezel-color" inState:themeState]];
|
||||
|
||||
var segmentCount = _maxValue - _minValue;
|
||||
|
||||
if (segmentCount <= 0)
|
||||
return;
|
||||
|
||||
var filledColor = [self valueForThemeAttribute:@"color-normal"],
|
||||
// 3. Determine the Color based on Value and Thresholds
|
||||
// We pass 'themeState' here. If it contains CPThemeStateHUD, the ThemeDescriptor
|
||||
// will return the monochrome color for normal/warning/critical.
|
||||
// If not, it returns Green/Yellow/Red.
|
||||
var filledColor = [self valueForThemeAttribute:@"color-normal" inState:themeState],
|
||||
value = [self doubleValue];
|
||||
|
||||
if (_warningValue < _criticalValue)
|
||||
{
|
||||
// Standard ascending scale (e.g. Volume)
|
||||
if (value >= _criticalValue)
|
||||
filledColor = [self valueForThemeAttribute:@"color-critical"];
|
||||
filledColor = [self valueForThemeAttribute:@"color-critical" inState:themeState];
|
||||
else if (value >= _warningValue)
|
||||
filledColor = [self valueForThemeAttribute:@"color-warning"];
|
||||
filledColor = [self valueForThemeAttribute:@"color-warning" inState:themeState];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Descending scale (e.g. Battery Life)
|
||||
if (value <= _criticalValue)
|
||||
filledColor = [self valueForThemeAttribute:@"color-critical"];
|
||||
filledColor = [self valueForThemeAttribute:@"color-critical" inState:themeState];
|
||||
else if (value <= _warningValue)
|
||||
filledColor = [self valueForThemeAttribute:@"color-warning"];
|
||||
filledColor = [self valueForThemeAttribute:@"color-warning" inState:themeState];
|
||||
}
|
||||
|
||||
var emptyColor = [self valueForThemeAttribute:@"color-empty" inState:themeState];
|
||||
|
||||
// 4. Paint Segments
|
||||
for (var i = 0; i < segmentCount; i++)
|
||||
{
|
||||
var segmentView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:bezelView];
|
||||
|
||||
[segmentView setBackgroundColor:(_minValue + i) < value ? filledColor : [self valueForThemeAttribute:@"color-empty"]];
|
||||
[segmentView setBackgroundColor:(_minValue + i) < value ? filledColor : emptyColor];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,20 +323,6 @@ CPRatingLevelIndicatorStyle = 3;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*
|
||||
- (CPTickMarkPosition)tickMarkPosition;
|
||||
- (void)setTickMarkPosition:(CPTickMarkPosition)position;
|
||||
|
||||
- (int)numberOfTickMarks;
|
||||
- (void)setNumberOfTickMarks:(int)count;
|
||||
|
||||
- (int)numberOfMajorTickMarks;
|
||||
- (void)setNumberOfMajorTickMarks:(int)count;
|
||||
|
||||
- (double)tickMarkValueAtIndex:(int)index;
|
||||
- (CGRect)rectOfTickMarkAtIndex:(int)index;
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
var CPLevelIndicatorStyleKey = "CPLevelIndicatorStyleKey",
|
||||
|
||||
@@ -277,6 +277,8 @@ var _CPMenuBarVisible = NO,
|
||||
|
||||
_autoenablesItems = YES;
|
||||
_showsStateColumn = YES;
|
||||
|
||||
_themeState = CPThemeStateNormal;
|
||||
|
||||
[self setMinimumWidth:0];
|
||||
}
|
||||
@@ -289,6 +291,37 @@ var _CPMenuBarVisible = NO,
|
||||
return [self initWithTitle:@""];
|
||||
}
|
||||
|
||||
// Managing Theme States (HUD Support)
|
||||
|
||||
- (void)setThemeState:(CPThemeState)aState
|
||||
{
|
||||
if ([self hasThemeState:aState])
|
||||
return;
|
||||
|
||||
_themeState = _themeState.and(aState);
|
||||
|
||||
// Propagate to the view if the menu is currently visible
|
||||
if (_menuWindow)
|
||||
[[_menuWindow _menuView] setThemeState:_themeState];
|
||||
}
|
||||
|
||||
- (void)unsetThemeState:(CPThemeState)aState
|
||||
{
|
||||
if (![self hasThemeState:aState])
|
||||
return;
|
||||
|
||||
_themeState = _themeState.without(aState);
|
||||
|
||||
// Propagate to the view if the menu is currently visible
|
||||
if (_menuWindow)
|
||||
[[_menuWindow _menuView] setThemeState:_themeState];
|
||||
}
|
||||
|
||||
- (CPThemeState)themeState
|
||||
{
|
||||
return _themeState;
|
||||
}
|
||||
|
||||
// Setting Up Menu Commands
|
||||
/*!
|
||||
Inserts a menu item at the specified index.
|
||||
@@ -801,6 +834,10 @@ var _CPMenuBarVisible = NO,
|
||||
// Create the window for our menu.
|
||||
var menuWindow = [_CPMenuWindow menuWindowWithMenu:self font:[self font]];
|
||||
|
||||
// This pushes the state (e.g., CPThemeStateHUD) to the actual view that renders the menu
|
||||
if (_themeState)
|
||||
[[menuWindow _menuView] setThemeState:_themeState];
|
||||
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||
|
||||
if (anItem)
|
||||
@@ -891,6 +928,10 @@ var _CPMenuBarVisible = NO,
|
||||
var theWindow = [aView window],
|
||||
menuWindow = [_CPMenuWindow menuWindowWithMenu:aMenu font:aFont];
|
||||
|
||||
// --- APPLY THEME STATE FROM MENU OBJECT TO VIEW ---
|
||||
if ([aMenu respondsToSelector:@selector(themeState)])
|
||||
[[menuWindow _menuView] setThemeState:[aMenu themeState]];
|
||||
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||
|
||||
var constraintRect = [CPMenu _constraintRectForView:aView],
|
||||
@@ -1331,6 +1372,9 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
|
||||
|
||||
_autoenablesItems = ![aCoder containsValueForKey:CPMenuAutoEnablesItemsKey] || [aCoder decodeBoolForKey:CPMenuAutoEnablesItemsKey];
|
||||
|
||||
// Ensure theme state is initialized to avoid undefined issues.
|
||||
_themeState = CPThemeStateNormal;
|
||||
|
||||
[self setMinimumWidth:0];
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,9 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
|
||||
// Do this so that coordinates will be accurate.
|
||||
[menuWindow setFrameOrigin:CGPointMakeZero()];
|
||||
// we need to reset the HUD state as this may be a recycled instance with HUD rund on non-HUD
|
||||
[[menuWindow _windowView] unsetThemeState:CPThemeStateHUD];
|
||||
[[menuWindow _menuView] unsetThemeState:CPThemeStateHUD];
|
||||
}
|
||||
else
|
||||
menuWindow = [[_CPMenuWindow alloc] init];
|
||||
@@ -108,23 +111,33 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
[contentView addSubview:_menuClipView];
|
||||
|
||||
_moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_moreAboveView setImage:[_menuView valueForThemeAttribute:@"menu-window-more-above-image"]];
|
||||
[_moreAboveView setFrameSize:[[_menuView valueForThemeAttribute:@"menu-window-more-above-image"] size]];
|
||||
|
||||
[contentView addSubview:_moreAboveView];
|
||||
|
||||
_moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_moreBelowView setImage:[_menuView valueForThemeAttribute:@"menu-window-more-below-image"]];
|
||||
[_moreBelowView setFrameSize:[[_menuView valueForThemeAttribute:@"menu-window-more-below-image"] size]];
|
||||
|
||||
[contentView addSubview:_moreBelowView];
|
||||
|
||||
// Initial setup using default attributes
|
||||
[self updateScrollArrows];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateScrollArrows
|
||||
{
|
||||
if (!_menuView)
|
||||
return;
|
||||
|
||||
var aboveImage = [_menuView currentValueForThemeAttribute:@"menu-window-more-above-image"],
|
||||
belowImage = [_menuView currentValueForThemeAttribute:@"menu-window-more-below-image"];
|
||||
|
||||
[_moreAboveView setImage:aboveImage];
|
||||
[_moreAboveView setFrameSize:[aboveImage size]];
|
||||
|
||||
[_moreBelowView setImage:belowImage];
|
||||
[_moreBelowView setFrameSize:[belowImage size]];
|
||||
}
|
||||
|
||||
+ (float)_standardLeftMargin
|
||||
{
|
||||
return [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-window-margin-inset" forClass:_CPMenuView].left;
|
||||
@@ -162,7 +175,12 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
|
||||
- (void)setBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle
|
||||
{
|
||||
[self setBackgroundColor:[[self class] backgroundColorForBackgroundStyle:aBackgroundStyle]];
|
||||
var color = [_menuView currentValueForThemeAttribute:@"menu-window-pop-up-background-style-color"];
|
||||
|
||||
if (!color)
|
||||
color = [[self class] backgroundColorForBackgroundStyle:aBackgroundStyle];
|
||||
|
||||
[self setBackgroundColor:color];
|
||||
}
|
||||
|
||||
- (void)setMenu:(CPMenu)aMenu
|
||||
@@ -237,16 +255,29 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
// If we are a submenu and we are being displayed off the right of the screen,
|
||||
// we should try and display on the left of our supermenu.
|
||||
var supermenu = [[self menu] supermenu];
|
||||
if (supermenu && (CGRectGetMaxX(_unconstrainedFrame) > CGRectGetMaxX(_constraintRect)))
|
||||
|
||||
if (supermenu)
|
||||
{
|
||||
var supermenuWindow = supermenu._menuWindow;
|
||||
if (supermenuWindow)
|
||||
if (CGRectGetMaxX(_unconstrainedFrame) > CGRectGetMaxX(_constraintRect))
|
||||
{
|
||||
var supermenuFrame = [supermenuWindow frame];
|
||||
_unconstrainedFrame.origin.x = CGRectGetMinX(supermenuFrame) - CGRectGetWidth(_unconstrainedFrame);
|
||||
var supermenuWindow = supermenu._menuWindow;
|
||||
if (supermenuWindow)
|
||||
{
|
||||
var supermenuFrame = [supermenuWindow frame];
|
||||
_unconstrainedFrame.origin.x = CGRectGetMinX(supermenuFrame) - CGRectGetWidth(_unconstrainedFrame);
|
||||
}
|
||||
}
|
||||
|
||||
// Shift true submenus vertically to fit within the screen if they extend past the bottom
|
||||
if (supermenu !== [CPApp mainMenu] && CGRectGetMaxY(_unconstrainedFrame) > CGRectGetMaxY(_constraintRect))
|
||||
_unconstrainedFrame.origin.y -= CGRectGetMaxY(_unconstrainedFrame) - CGRectGetMaxY(_constraintRect);
|
||||
}
|
||||
|
||||
// Ensure no menu starts above the visible screen area, preventing it from incorrectly
|
||||
// starting scrolled with a top arrow.
|
||||
if (CGRectGetMinY(_unconstrainedFrame) < CGRectGetMinY(_constraintRect))
|
||||
_unconstrainedFrame.origin.y = CGRectGetMinY(_constraintRect);
|
||||
|
||||
var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect),
|
||||
marginInset = [_menuView valueForThemeAttribute:@"menu-window-margin-inset"],
|
||||
scrollIndicatorHeight = [_menuView valueForThemeAttribute:@"menu-window-scroll-indicator-height"];
|
||||
@@ -268,7 +299,8 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
[super setFrame:constrainedFrame display:shouldDisplay animate:shouldAnimate];
|
||||
|
||||
// This needs to happen before changing the frame.
|
||||
var menuViewOrigin = CGPointMake(CGRectGetMinX(aFrame) + marginInset.left, CGRectGetMinY(aFrame) + marginInset.top),
|
||||
// Base the view origin on our modified _unconstrainedFrame instead of aFrame
|
||||
var menuViewOrigin = CGPointMake(CGRectGetMinX(_unconstrainedFrame) + marginInset.left, CGRectGetMinY(_unconstrainedFrame) + marginInset.top),
|
||||
moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + marginInset.top,
|
||||
moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - marginInset.bottom,
|
||||
|
||||
@@ -502,6 +534,21 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
};
|
||||
}
|
||||
|
||||
- (void)setThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super setThemeState:aState];
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(setThemeState:) withObject:aState];
|
||||
|
||||
if ([[self window] respondsToSelector:@selector(updateScrollArrows)])
|
||||
[[self window] updateScrollArrows];
|
||||
}
|
||||
|
||||
- (void)unsetThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super unsetThemeState:aState];
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(unsetThemeState:) withObject:aState];
|
||||
}
|
||||
|
||||
- (unsigned)numberOfUnhiddenItems
|
||||
{
|
||||
return _visibleMenuItemInfos.length;
|
||||
|
||||
@@ -145,6 +145,26 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setThemeState:(CPThemeState)aState
|
||||
{
|
||||
var oldState = [self themeState];
|
||||
[super setThemeState:aState];
|
||||
// If the state changed (e.g. adding HUD), we must re-run update
|
||||
// to fetch the new text color defined for that state.
|
||||
if (oldState !== [self themeState])
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)unsetThemeState:(CPThemeState)aState
|
||||
{
|
||||
var oldState = [self themeState];
|
||||
[super unsetThemeState:aState];
|
||||
|
||||
if (oldState !== [self themeState])
|
||||
[self update];
|
||||
}
|
||||
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
@@ -153,7 +173,7 @@
|
||||
if (_highlighted)
|
||||
return [CPColor whiteColor];
|
||||
|
||||
return [self valueForThemeAttribute:@"menu-item-text-color"];
|
||||
return [self currentValueForThemeAttribute:@"menu-item-text-color"];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
@@ -164,7 +184,7 @@
|
||||
if (_highlighted)
|
||||
return nil;
|
||||
|
||||
return [self valueForThemeAttribute:@"menu-item-text-shadow-color"];
|
||||
return [self currentValueForThemeAttribute:@"menu-item-text-shadow-color"];
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
@@ -178,6 +198,11 @@
|
||||
return _font || [_menuItem font] || [CPFont systemFontOfSize:CPFontCurrentSystemSize];
|
||||
}
|
||||
|
||||
// override needed to cancel out the standard HUD propagation
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
}
|
||||
|
||||
// FIXME: update is called 2 times at each display. Find why and fix.
|
||||
- (void)update
|
||||
{
|
||||
@@ -188,6 +213,8 @@
|
||||
|
||||
// When possible, use specific vertical margin/offset value based on font size (which could have been set by control size)
|
||||
correspondingControlSize = [myFont controlSizeCorrespondingToFontSize],
|
||||
controlSizeState = CPControlSizeThemeStates[correspondingControlSize],
|
||||
queryState = [self themeState] ? [self themeState].and(controlSizeState) : controlSizeState,
|
||||
verticalMargin = [self valueForThemeAttribute:@"vertical-margin" inState:CPControlSizeThemeStates[correspondingControlSize]],
|
||||
verticalOffset = [self valueForThemeAttribute:@"vertical-offset" inState:CPControlSizeThemeStates[correspondingControlSize]];
|
||||
|
||||
@@ -199,15 +226,15 @@
|
||||
switch ([_menuItem state])
|
||||
{
|
||||
case CPOnState:
|
||||
[_stateView setImage:[_menuItem onStateImage] || [self valueForThemeAttribute:@"menu-item-default-on-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image" inState:queryState] || [_menuItem onStateImage]];
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[_stateView setImage:[_menuItem offStateImage] || [self valueForThemeAttribute:@"menu-item-default-off-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image" inState:queryState] || [_menuItem offStateImage]];
|
||||
break;
|
||||
|
||||
case CPMixedState:
|
||||
[_stateView setImage:[_menuItem mixedStateImage] || [self valueForThemeAttribute:@"menu-item-default-mixed-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image" inState:queryState] || [_menuItem mixedStateImage]];
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -326,7 +353,10 @@
|
||||
|
||||
_highlighted = shouldHighlight;
|
||||
|
||||
var correspondingControlSize = [[self font] controlSizeCorrespondingToFontSize];
|
||||
var correspondingControlSize = [[self font] controlSizeCorrespondingToFontSize],
|
||||
// Construct the query state including the view's current theme state (e.g. HUD)
|
||||
controlSizeState = CPControlSizeThemeStates[correspondingControlSize],
|
||||
queryState = [self themeState] ? [self themeState].and(controlSizeState) : controlSizeState;
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_keyEquivalentView setTextColor:[self textColor]];
|
||||
@@ -339,7 +369,7 @@
|
||||
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
||||
|
||||
if (_hasSubmenuIndicatorImage)
|
||||
[_submenuIndicatorView setImage:[self valueForThemeAttribute:@"submenu-indicator-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_submenuIndicatorView setImage:[self valueForThemeAttribute:@"submenu-indicator-highlighted-image" inState:queryState]];
|
||||
else
|
||||
[_submenuIndicatorView setColor:[self textColor]];
|
||||
}
|
||||
@@ -349,7 +379,7 @@
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
|
||||
if (_hasSubmenuIndicatorImage)
|
||||
[_submenuIndicatorView setImage:[self valueForThemeAttribute:@"submenu-indicator-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_submenuIndicatorView setImage:[self valueForThemeAttribute:@"submenu-indicator-image" inState:queryState]];
|
||||
else
|
||||
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
|
||||
}
|
||||
@@ -361,15 +391,15 @@
|
||||
switch ([_menuItem state])
|
||||
{
|
||||
case CPOnState:
|
||||
[_stateView setImage:[_menuItem onStateImage] || [self valueForThemeAttribute:@"menu-item-default-on-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[_menuItem onStateImage] || [self valueForThemeAttribute:@"menu-item-default-on-state-highlighted-image" inState:queryState]];
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[_stateView setImage:[_menuItem offStateImage] || [self valueForThemeAttribute:@"menu-item-default-off-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[_menuItem offStateImage] || [self valueForThemeAttribute:@"menu-item-default-off-state-highlighted-image" inState:queryState]];
|
||||
break;
|
||||
|
||||
case CPMixedState:
|
||||
[_stateView setImage:[_menuItem mixedImage] || [self valueForThemeAttribute:@"menu-item-default-mixed-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[_menuItem mixedImage] || [self valueForThemeAttribute:@"menu-item-default-mixed-state-highlighted-image" inState:queryState]];
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -381,15 +411,15 @@
|
||||
switch ([_menuItem state])
|
||||
{
|
||||
case CPOnState:
|
||||
[_stateView setImage:[_menuItem onStateImage] || [self valueForThemeAttribute:@"menu-item-default-on-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[_menuItem onStateImage] || [self valueForThemeAttribute:@"menu-item-default-on-state-image" inState:queryState]];
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[_stateView setImage:[_menuItem offStateImage] || [self valueForThemeAttribute:@"menu-item-default-off-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[_menuItem offStateImage] || [self valueForThemeAttribute:@"menu-item-default-off-state-image" inState:queryState]];
|
||||
break;
|
||||
|
||||
case CPMixedState:
|
||||
[_stateView setImage:[_menuItem mixedImage] || [self valueForThemeAttribute:@"menu-item-default-mixed-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
|
||||
[_stateView setImage:[_menuItem mixedImage] || [self valueForThemeAttribute:@"menu-item-default-mixed-state-image" inState:queryState]];
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -83,6 +83,28 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
// override needed to cancel out the standard HUD propagation
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super setThemeState:aState];
|
||||
|
||||
// Propagate state to the actual content view (StandardView, Separator, etc.)
|
||||
if ([_view respondsToSelector:@selector(setThemeState:)])
|
||||
[_view setThemeState:aState];
|
||||
}
|
||||
|
||||
- (void)unsetThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super unsetThemeState:aState];
|
||||
|
||||
if ([_view respondsToSelector:@selector(unsetThemeState:)])
|
||||
[_view unsetThemeState:aState];
|
||||
}
|
||||
|
||||
- (CGSize)minSize
|
||||
{
|
||||
return _minSize;
|
||||
|
||||
+593
-55
@@ -23,6 +23,7 @@
|
||||
@import "CPButton.j"
|
||||
@import "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPViewAnimation.j"
|
||||
@import "CPTreeNode.j"
|
||||
|
||||
@global CPApp
|
||||
@@ -292,6 +293,12 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
|
||||
CPArray _pendingItemToClean;
|
||||
CPArray _itemAddedDuringLastLoading;
|
||||
|
||||
CPIndexSet _animatingDisclosureRows;
|
||||
|
||||
BOOL _animates @accessors(property=animates);
|
||||
CPViewAnimation _rowsAnimation;
|
||||
CPArray _animationGhosts;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -322,6 +329,9 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
[super setDelegate:self];
|
||||
|
||||
[self setDisclosureControlPrototype:[[CPDisclosureButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)]];
|
||||
|
||||
_animatingDisclosureRows = [CPIndexSet indexSet];
|
||||
_animates = YES;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -332,12 +342,22 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
_BlockDeselectView = function(view, row, column)
|
||||
{
|
||||
[view unsetThemeState:CPThemeStateSelectedDataView];
|
||||
// Ensure we clear the focus state so the text goes back to normal color
|
||||
[view unsetThemeState:CPThemeStateFirstResponder];
|
||||
|
||||
[_disclosureControlsForRows[row] unsetThemeState:CPThemeStateSelected];
|
||||
};
|
||||
|
||||
_BlockSelectView = function(view, row, column)
|
||||
{
|
||||
[view setThemeState:CPThemeStateSelectedDataView];
|
||||
|
||||
// If the table is focused, apply the state that turns text white immediately
|
||||
if ([self _isFocused])
|
||||
[view setThemeState:CPThemeStateFirstResponder];
|
||||
else
|
||||
[view unsetThemeState:CPThemeStateFirstResponder];
|
||||
|
||||
[_disclosureControlsForRows[row] setThemeState:CPThemeStateSelected];
|
||||
};
|
||||
}
|
||||
@@ -489,6 +509,60 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
return itemInfo.shouldShowOutlineDisclosureControl;
|
||||
}
|
||||
|
||||
- (void)_noteDisclosureAnimationWillStartForRow:(CPInteger)aRow
|
||||
{
|
||||
[_animatingDisclosureRows addIndex:aRow];
|
||||
// We do not need to setNeedsDisplay here because the button itself
|
||||
// will trigger a redraw when its state changes or when the animation setup occurs.
|
||||
}
|
||||
|
||||
- (void)_noteDisclosureAnimationDidStopForRow:(CPInteger)aRow
|
||||
{
|
||||
[_animatingDisclosureRows removeIndex:aRow];
|
||||
|
||||
// Explicitly find the button for this row and force it to redraw.
|
||||
// The button was returning early from drawRect during the animation.
|
||||
// Now that the flag is cleared, we must tell it to paint again.
|
||||
if (_disclosureControlsForRows && aRow < _disclosureControlsForRows.length)
|
||||
{
|
||||
var button = _disclosureControlsForRows[aRow];
|
||||
|
||||
if (button)
|
||||
[button display];
|
||||
}
|
||||
}
|
||||
- (BOOL)_isRowAnimatingDisclosure:(CPInteger)aRow
|
||||
{
|
||||
return [_animatingDisclosureRows containsIndex:aRow];
|
||||
}
|
||||
|
||||
- (CPInteger)rowForView:(CPView)aView
|
||||
{
|
||||
// 1. Try the standard lookup (for normal cells)
|
||||
var row = [super rowForView:aView];
|
||||
|
||||
if (row !== CPNotFound)
|
||||
return row;
|
||||
|
||||
// 2. If not found, check if it is a disclosure button
|
||||
if ([aView isKindOfClass:[CPDisclosureButton class]])
|
||||
{
|
||||
// Check the list of active disclosure buttons
|
||||
// _disclosureControlsForRows is a native JS array, so we use indexOf
|
||||
var index = _disclosureControlsForRows.indexOf(aView);
|
||||
|
||||
if (index > -1)
|
||||
return index;
|
||||
|
||||
// 3. Check if it is an animation clone (from the fix in the previous step)
|
||||
// The clone is not in the array, but it carries the row index.
|
||||
if (aView._animatingRowIndex !== undefined && aView._animatingRowIndex !== null)
|
||||
return aView._animatingRowIndex;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
/*!
|
||||
Used to find if an item is already expanded.
|
||||
|
||||
@@ -628,6 +702,9 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
*/
|
||||
- (void)expandItem:(id)anItem expandChildren:(BOOL)shouldExpandChildren
|
||||
{
|
||||
var oldRowCount = [self numberOfRows],
|
||||
parentIndex = [self rowForItem:anItem];
|
||||
|
||||
if ([self _delegateRespondsToShouldExpandItem])
|
||||
if ([_outlineViewDelegate outlineView:self shouldExpandItem:anItem] == NO)
|
||||
return;
|
||||
@@ -670,7 +747,7 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
if (rowCountDelta)
|
||||
{
|
||||
var selection = [self selectedRowIndexes],
|
||||
expandIndex = [self rowForItem:anItem] + 1;
|
||||
expandIndex = parentIndex + 1;
|
||||
|
||||
if ([selection intersectsIndexesInRange:CPMakeRange(expandIndex, _itemsForRows.length)])
|
||||
{
|
||||
@@ -697,6 +774,17 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
if (r === CPOutlineViewCoalesceSelectionNotificationStateDid)
|
||||
[self _noteSelectionDidChange];
|
||||
}
|
||||
|
||||
// FIX: Animation logic moved to the end to match Version 1 behavior.
|
||||
// This ensures views are ready and handles both single and recursive expansion.
|
||||
var newRowCount = [self numberOfRows],
|
||||
addedCount = newRowCount - oldRowCount;
|
||||
|
||||
if (_animates && addedCount > 0)
|
||||
{
|
||||
[self layoutSubviews]; // Force views to spawn so we can animate them
|
||||
[self _animateExpandFromIndex:parentIndex addedCount:addedCount];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -709,29 +797,30 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
if (!anItem)
|
||||
return;
|
||||
|
||||
var collapseTopIndex = [self rowForItem:anItem];
|
||||
|
||||
// FIX 1: Safety check to prevent "jumping".
|
||||
// If the parent row isn't found or invisible, we cannot animate.
|
||||
if (collapseTopIndex === CPNotFound || collapseTopIndex < 0)
|
||||
return;
|
||||
|
||||
if ([self _delegateRespondsToShouldCollapseItem])
|
||||
if ([_outlineViewDelegate outlineView:self shouldCollapseItem:anItem] == NO)
|
||||
return;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
if (!itemInfo || !itemInfo.isExpanded)
|
||||
return;
|
||||
|
||||
if (!itemInfo.isExpanded)
|
||||
return;
|
||||
|
||||
// Don't spam notifications.
|
||||
_coalesceSelectionNotificationState = CPOutlineViewCoalesceSelectionNotificationStateOn;
|
||||
|
||||
[self _noteItemWillCollapse:anItem];
|
||||
// Update selections:
|
||||
// * Deselect items inside the collapsed item.
|
||||
// * Shift row selections below the collapsed item so that the same logical items remain selected.
|
||||
var collapseTopIndex = [self rowForItem:anItem],
|
||||
topLevel = [self levelForRow:collapseTopIndex],
|
||||
|
||||
var topLevel = [self levelForRow:collapseTopIndex],
|
||||
collapseEndIndex = collapseTopIndex;
|
||||
|
||||
// Calculate how many items are being removed
|
||||
while (collapseEndIndex + 1 < _itemsForRows.length && [self levelForRow:collapseEndIndex + 1] > topLevel)
|
||||
collapseEndIndex++;
|
||||
|
||||
@@ -745,31 +834,288 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
{
|
||||
[self _noteSelectionIsChanging];
|
||||
[selection removeIndexesInRange:collapseRange];
|
||||
[self _setSelectedRowIndexes:selection]; // _noteSelectionDidChange will be suppressed.
|
||||
[self _setSelectedRowIndexes:selection];
|
||||
}
|
||||
|
||||
// Shift any selected rows below upwards.
|
||||
if ([selection intersectsIndexesInRange:CPMakeRange(collapseEndIndex + 1, _itemsForRows.length)])
|
||||
{
|
||||
[self _noteSelectionIsChanging];
|
||||
[selection shiftIndexesStartingAtIndex:collapseEndIndex + 1 by:-collapseRange.length];
|
||||
[self _setSelectedRowIndexes:selection]; // _noteSelectionDidChange will be suppressed.
|
||||
[self _setSelectedRowIndexes:selection];
|
||||
}
|
||||
}
|
||||
|
||||
itemInfo.isExpanded = NO;
|
||||
// Capture visual ghosts BEFORE the data is removed
|
||||
var ghosts = nil;
|
||||
if (_animates && collapseRange.length > 0)
|
||||
ghosts = [self _createGhostsForRowsFrom:collapseRange.location to:CPMaxRange(collapseRange) - 1];
|
||||
|
||||
// Collapse the model
|
||||
itemInfo.isExpanded = NO;
|
||||
[self reloadItem:anItem reloadChildren:YES];
|
||||
[self _noteItemDidCollapse:anItem];
|
||||
|
||||
// Send selection notifications only after the items have loaded so that
|
||||
// the new selection is consistent with the actual rows for any observers.
|
||||
// Trigger Animation
|
||||
if (_animates && collapseRange.length > 0)
|
||||
{
|
||||
[self layoutSubviews];
|
||||
[self _animateCollapseFromIndex:collapseTopIndex removedCount:collapseRange.length ghosts:ghosts];
|
||||
}
|
||||
|
||||
var r = _coalesceSelectionNotificationState;
|
||||
_coalesceSelectionNotificationState = CPOutlineViewCoalesceSelectionNotificationStateOff;
|
||||
|
||||
if (r === CPOutlineViewCoalesceSelectionNotificationStateDid)
|
||||
[self _noteSelectionDidChange];
|
||||
}
|
||||
|
||||
- (void)_animateCollapseFromIndex:(CPInteger)parentIndex removedCount:(CPInteger)removedCount ghosts:(CPArray)ghosts
|
||||
{
|
||||
// Ensure we don't animate if parent is invalid
|
||||
if (parentIndex < 0)
|
||||
return;
|
||||
|
||||
if (_rowsAnimation && [_rowsAnimation isAnimating])
|
||||
[_rowsAnimation stopAnimation];
|
||||
|
||||
var viewAnimations = [],
|
||||
// The Y position the ghosts will slide INTO (the parent row's Y position)
|
||||
parentY = [self frameOfDataViewAtColumn:0 row:parentIndex].origin.y;
|
||||
|
||||
// Calculate shift offset based on ghost height or row height
|
||||
var shiftOffset = 0;
|
||||
if (ghosts && ghosts.length > 0)
|
||||
{
|
||||
var firstGhostFrame = [ghosts[0] frame],
|
||||
maxY = CGRectGetMaxY(firstGhostFrame);
|
||||
for(var i = 1; i < ghosts.length; i++)
|
||||
maxY = MAX(maxY, CGRectGetMaxY([ghosts[i] frame]));
|
||||
shiftOffset = maxY - firstGhostFrame.origin.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
shiftOffset = removedCount * [self rowHeight];
|
||||
}
|
||||
|
||||
// 1. Ghost views slide up into parent and fade out
|
||||
if (ghosts)
|
||||
{
|
||||
for (var i = 0; i < ghosts.length; i++)
|
||||
{
|
||||
var ghost = ghosts[i],
|
||||
startFrame = [ghost frame],
|
||||
targetFrame = CGRectMake(startFrame.origin.x, parentY, startFrame.size.width, startFrame.size.height);
|
||||
|
||||
// FIX 3: Removed [self addSubview:positioned:relativeTo:]
|
||||
// Ghosts remain at the top of the view stack (where they were created)
|
||||
// ensuring they are visible and don't get hidden behind the background.
|
||||
|
||||
[viewAnimations addObject:@{
|
||||
CPViewAnimationTargetKey: ghost,
|
||||
CPViewAnimationStartFrameKey: startFrame,
|
||||
CPViewAnimationEndFrameKey: targetFrame,
|
||||
CPViewAnimationEffectKey: CPViewAnimationFadeOutEffect
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Siblings (rows below the collapsed group) slide UP to fill the gap
|
||||
var columns = [self tableColumns],
|
||||
colIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, columns.length)],
|
||||
startIndex = parentIndex + 1,
|
||||
rowCount = [self numberOfRows];
|
||||
|
||||
// FIX 4: Only animate rows that actually exist after the parent
|
||||
if (startIndex < rowCount)
|
||||
{
|
||||
var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startIndex, rowCount - startIndex)];
|
||||
|
||||
[self _enumerateViewsInRows:rowIndexes columns:colIndexes usingBlock:function(view, row, column, stop) {
|
||||
|
||||
var targetFrame = [view frame];
|
||||
|
||||
// Siblings start lower (at target Y + shift) and move UP to target Y
|
||||
var startFrame = CGRectMake(targetFrame.origin.x, targetFrame.origin.y + shiftOffset, targetFrame.size.width, targetFrame.size.height);
|
||||
|
||||
[view setFrame:startFrame];
|
||||
|
||||
[viewAnimations addObject:@{
|
||||
CPViewAnimationTargetKey: view,
|
||||
CPViewAnimationStartFrameKey: startFrame,
|
||||
CPViewAnimationEndFrameKey: targetFrame
|
||||
}];
|
||||
}];
|
||||
|
||||
// Also animate disclosure triangles for the siblings
|
||||
var disclosureRows = [];
|
||||
[rowIndexes getIndexes:disclosureRows maxCount:-1 inIndexRange:nil];
|
||||
|
||||
for (var i = 0; i < disclosureRows.length; i++)
|
||||
{
|
||||
var btn = _disclosureControlsForRows[disclosureRows[i]];
|
||||
if (btn)
|
||||
{
|
||||
var targetBtnFrame = [btn frame],
|
||||
startBtnFrame = CGRectMake(targetBtnFrame.origin.x, targetBtnFrame.origin.y + shiftOffset, targetBtnFrame.size.width, targetBtnFrame.size.height);
|
||||
|
||||
[btn setFrame:startBtnFrame];
|
||||
|
||||
[viewAnimations addObject:@{
|
||||
CPViewAnimationTargetKey: btn,
|
||||
CPViewAnimationStartFrameKey: startBtnFrame,
|
||||
CPViewAnimationEndFrameKey: targetBtnFrame
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self _runAnimation:viewAnimations withGhosts:ghosts];
|
||||
}
|
||||
|
||||
// MARK: - CPRuleEditor Style Animations
|
||||
|
||||
- (CPArray)_createGhostsForRowsFrom:(CPInteger)startRow to:(CPInteger)endRow
|
||||
{
|
||||
var ghosts = [];
|
||||
var columns = [self tableColumns];
|
||||
var rowIndexes =[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startRow, endRow - startRow + 1)];
|
||||
var colIndexes =[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, columns.length)];[self _enumerateViewsInRows:rowIndexes columns:colIndexes usingBlock:function(view, row, column, stop) {
|
||||
var ghost = [[CPView alloc] initWithFrame:[view frame]];
|
||||
if (view._DOMElement)
|
||||
ghost._DOMElement.innerHTML = view._DOMElement.innerHTML;
|
||||
[self addSubview:ghost];
|
||||
[ghosts addObject:ghost];
|
||||
}];
|
||||
|
||||
var disclosureRows = [];[rowIndexes getIndexes:disclosureRows maxCount:-1 inIndexRange:nil];
|
||||
for (var i = 0; i < disclosureRows.length; i++)
|
||||
{
|
||||
var btn = _disclosureControlsForRows[disclosureRows[i]];
|
||||
if (btn)
|
||||
{
|
||||
var ghostBtn = [[CPView alloc] initWithFrame:[btn frame]];
|
||||
if (btn._DOMElement)
|
||||
ghostBtn._DOMElement.innerHTML = btn._DOMElement.innerHTML;[self addSubview:ghostBtn];
|
||||
[ghosts addObject:ghostBtn];
|
||||
}
|
||||
}
|
||||
|
||||
return ghosts;
|
||||
}
|
||||
|
||||
- (void)_animateExpandFromIndex:(CPInteger)parentIndex addedCount:(CPInteger)addedCount
|
||||
{
|
||||
if (_rowsAnimation && [_rowsAnimation isAnimating])
|
||||
[_rowsAnimation stopAnimation];
|
||||
|
||||
var viewAnimations = [];
|
||||
var columns = [self tableColumns];
|
||||
var colIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, columns.length)];
|
||||
|
||||
var startIndex = parentIndex + 1;
|
||||
var rowIndexes =[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startIndex, [self numberOfRows] - startIndex)];
|
||||
var parentY = parentIndex >= 0 ? [self frameOfDataViewAtColumn:0 row:parentIndex].origin.y : 0;
|
||||
|
||||
// Safely calculate the shift gap even with variable row heights
|
||||
var shiftOffset = 0;
|
||||
if (addedCount > 0 && (startIndex + addedCount) < [self numberOfRows])
|
||||
{
|
||||
var firstNewTarget = [self frameOfDataViewAtColumn:0 row:startIndex].origin.y;
|
||||
var firstDisplacedTarget = [self frameOfDataViewAtColumn:0 row:startIndex + addedCount].origin.y;
|
||||
shiftOffset = firstDisplacedTarget - firstNewTarget;
|
||||
}
|
||||
else
|
||||
shiftOffset = addedCount * [self rowHeight];[self _enumerateViewsInRows:rowIndexes columns:colIndexes usingBlock:function(view, row, column, stop) {
|
||||
var isNewRow = (row < startIndex + addedCount);
|
||||
var targetFrame = [view frame];
|
||||
var startFrame;
|
||||
var animDict = @{ CPViewAnimationTargetKey: view, CPViewAnimationEndFrameKey: targetFrame };
|
||||
|
||||
if (isNewRow)
|
||||
{
|
||||
startFrame = CGRectMake(targetFrame.origin.x, parentY, targetFrame.size.width, targetFrame.size.height);
|
||||
[animDict setObject:CPViewAnimationFadeInEffect forKey:CPViewAnimationEffectKey];[view setAlphaValue:0.0];
|
||||
}
|
||||
else
|
||||
startFrame = CGRectMake(targetFrame.origin.x, targetFrame.origin.y - shiftOffset, targetFrame.size.width, targetFrame.size.height);
|
||||
|
||||
[view setFrame:startFrame];[animDict setObject:startFrame forKey:CPViewAnimationStartFrameKey];
|
||||
[viewAnimations addObject:animDict];
|
||||
}];
|
||||
|
||||
var disclosureRows = [];
|
||||
[rowIndexes getIndexes:disclosureRows maxCount:-1 inIndexRange:nil];
|
||||
for (var i = 0; i < disclosureRows.length; i++)
|
||||
{
|
||||
var r = disclosureRows[i];
|
||||
var btn = _disclosureControlsForRows[r];
|
||||
if (btn)
|
||||
{
|
||||
var isNewRow = (r < startIndex + addedCount);
|
||||
var targetBtnFrame = [btn frame];
|
||||
var startBtnFrame;
|
||||
var animBtnDict = @{ CPViewAnimationTargetKey: btn, CPViewAnimationEndFrameKey: targetBtnFrame };
|
||||
|
||||
if (isNewRow)
|
||||
{
|
||||
startBtnFrame = CGRectMake(targetBtnFrame.origin.x, parentY, targetBtnFrame.size.width, targetBtnFrame.size.height);[animBtnDict setObject:CPViewAnimationFadeInEffect forKey:CPViewAnimationEffectKey];[btn setAlphaValue:0.0];
|
||||
}
|
||||
else
|
||||
startBtnFrame = CGRectMake(targetBtnFrame.origin.x, targetBtnFrame.origin.y - shiftOffset, targetBtnFrame.size.width, targetBtnFrame.size.height);
|
||||
|
||||
[btn setFrame:startBtnFrame];[animBtnDict setObject:startBtnFrame forKey:CPViewAnimationStartFrameKey];
|
||||
[viewAnimations addObject:animBtnDict];
|
||||
}
|
||||
}
|
||||
|
||||
[self _runAnimation:viewAnimations withGhosts:nil];
|
||||
}
|
||||
|
||||
- (void)_runAnimation:(CPArray)viewAnimations withGhosts:(CPArray)ghosts
|
||||
{
|
||||
// Clean up interrupted animations
|
||||
if (_animationGhosts)
|
||||
{
|
||||
for (var i = 0; i < _animationGhosts.length; i++)
|
||||
[_animationGhosts[i] removeFromSuperview];
|
||||
}
|
||||
_animationGhosts = ghosts;
|
||||
|
||||
if (viewAnimations.length > 0)
|
||||
{
|
||||
_rowsAnimation = [[CPViewAnimation alloc] initWithViewAnimations:viewAnimations];
|
||||
[_rowsAnimation setDuration:0.25];[_rowsAnimation setAnimationCurve:CPAnimationEaseInOut];
|
||||
[_rowsAnimation setDelegate:self];
|
||||
[_rowsAnimation startAnimation];
|
||||
}
|
||||
else if (ghosts)
|
||||
{
|
||||
for (var i = 0; i < ghosts.length; i++)
|
||||
[ghosts[i] removeFromSuperview];
|
||||
_animationGhosts = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)animationDidEnd:(CPViewAnimation)animation
|
||||
{
|
||||
var animations = [animation viewAnimations];
|
||||
|
||||
for (var i = 0; i < animations.length; i++)
|
||||
{
|
||||
var view = animations[i][CPViewAnimationTargetKey];
|
||||
if (!_animationGhosts || ![_animationGhosts containsObject:view])[view setAlphaValue:1.0];
|
||||
}
|
||||
|
||||
if (_animationGhosts)
|
||||
{
|
||||
for (var i = 0; i < _animationGhosts.length; i++)
|
||||
[_animationGhosts[i] removeFromSuperview];
|
||||
_animationGhosts = nil;
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*!
|
||||
Reloads the data for an item.
|
||||
|
||||
@@ -793,6 +1139,11 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
*/
|
||||
- (void)reloadItem:(id)anItem reloadChildren:(BOOL)shouldReloadChildren
|
||||
{
|
||||
if (_rowsAnimation && [_rowsAnimation isAnimating])
|
||||
{
|
||||
[_rowsAnimation stopAnimation];
|
||||
[self animationDidEnd:_rowsAnimation];
|
||||
}
|
||||
|
||||
_pendingItemToClean = [];
|
||||
_itemAddedDuringLastLoading = [];
|
||||
@@ -1171,8 +1522,12 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
{
|
||||
if (_disclosureControlsForRows.length)
|
||||
{
|
||||
var indexes = [_selectedRowIndexes copy];
|
||||
[indexes removeIndexesInRange:CPMakeRange(_disclosureControlsForRows.length, _itemsForRows.length - _disclosureControlsForRows.length)];
|
||||
var indexes = [_selectedRowIndexes copy],
|
||||
diff = _itemsForRows.length - _disclosureControlsForRows.length;
|
||||
|
||||
if (diff > 0)
|
||||
[indexes removeIndexesInRange:CPMakeRange(_disclosureControlsForRows.length, diff)];
|
||||
|
||||
[[_disclosureControlsForRows objectsAtIndexes:indexes] makeObjectsPerformSelector:@selector(unsetThemeState:) withObject:CPThemeStateSelected];
|
||||
}
|
||||
|
||||
@@ -1180,8 +1535,12 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
|
||||
if (_disclosureControlsForRows.length)
|
||||
{
|
||||
var indexes = [_selectedRowIndexes copy];
|
||||
[indexes removeIndexesInRange:CPMakeRange(_disclosureControlsForRows.length, _itemsForRows.length - _disclosureControlsForRows.length)];
|
||||
var indexes = [_selectedRowIndexes copy],
|
||||
diff = _itemsForRows.length - _disclosureControlsForRows.length;
|
||||
|
||||
if (diff > 0)
|
||||
[indexes removeIndexesInRange:CPMakeRange(_disclosureControlsForRows.length, diff)];
|
||||
|
||||
[[_disclosureControlsForRows objectsAtIndexes:indexes] makeObjectsPerformSelector:@selector(setThemeState:) withObject:CPThemeStateSelected];
|
||||
}
|
||||
}
|
||||
@@ -1653,6 +2012,17 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
return rect;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (CGRect)_rectForDropHighlightViewOnRow:(CPInteger)theRowIndex
|
||||
{
|
||||
var rect = [super _rectForDropHighlightViewOnRow:theRowIndex];
|
||||
rect.size.height += 2.0;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
We need to move the disclosure control too.
|
||||
@@ -2236,7 +2606,15 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
|
||||
@implementation CPDisclosureButton : CPButton
|
||||
{
|
||||
float _angle;
|
||||
float _angle;
|
||||
|
||||
// Animation state tracking
|
||||
BOOL _isAnimatingClone;
|
||||
CPInteger _animatingRowIndex;
|
||||
CPOutlineView _parentOutlineView;
|
||||
|
||||
DOMElement _arrowElement;
|
||||
CPString _lastColorCSS;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -2244,66 +2622,224 @@ function SHOULD_SELECT_ITEM(anOutlineView, anItem)
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setBordered:NO];
|
||||
|
||||
[self setWantsLayer:YES];
|
||||
[self setHighlightsBy:0];
|
||||
|
||||
[self _setupDisclosureButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _setupDisclosureButton];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_setupDisclosureButton
|
||||
{
|
||||
_isAnimatingClone = NO; // Default for real buttons
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
// Add a dedicated DOM element for the SVG arrow
|
||||
_arrowElement = document.createElement("div");
|
||||
_arrowElement.style.position = "absolute";
|
||||
_arrowElement.style.top = "0px";
|
||||
_arrowElement.style.left = "0px";
|
||||
_arrowElement.style.width = "100%";
|
||||
_arrowElement.style.height = "100%";
|
||||
_arrowElement.style.backgroundPosition = "center center";
|
||||
_arrowElement.style.backgroundRepeat = "no-repeat";
|
||||
// 24px maps the 24x24 viewBox to exact pixels, making the 10x5 path scale perfectly
|
||||
_arrowElement.style.backgroundSize = "24px 24px";
|
||||
_arrowElement.style.pointerEvents = "none";
|
||||
|
||||
self._DOMElement.appendChild(_arrowElement);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAngle:(float)anAngle
|
||||
{
|
||||
_angle = anAngle;
|
||||
|
||||
// Rotate the DOM element directly using standard CSS transforms
|
||||
var deg = _angle * (180.0 / Math.PI);
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
_arrowElement.style.transform = "rotate(" + deg + "deg)";
|
||||
}
|
||||
|
||||
[self display];
|
||||
}
|
||||
|
||||
- (float)angle
|
||||
{
|
||||
return _angle;
|
||||
}
|
||||
|
||||
- (void)setState:(CPInteger)aState
|
||||
{
|
||||
[super setState:aState];
|
||||
|
||||
// Using setAngle immediately triggers the transform
|
||||
if ([self state] === CPOnState)
|
||||
_angle = 0.0;
|
||||
|
||||
[self setAngle:0.0];
|
||||
else
|
||||
_angle = -PI_2;
|
||||
[self setAngle:-PI_2];
|
||||
}
|
||||
|
||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)point mouseIsUp:(BOOL)mouseIsUp
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
|
||||
if (CGRectContainsPoint(bounds, point))
|
||||
{
|
||||
var outlineView = [self superview],
|
||||
row = [outlineView rowForView:self];
|
||||
|
||||
if ([outlineView isKindOfClass:[CPOutlineView class]] && row !== CPNotFound)
|
||||
{
|
||||
// 1. Tell OutlineView to suppress drawing of the real button
|
||||
[outlineView _noteDisclosureAnimationWillStartForRow:row];
|
||||
|
||||
// 2. Create the clone
|
||||
var clone = [[CPDisclosureButton alloc] initWithFrame:[self frame]];
|
||||
|
||||
[clone setWantsLayer:YES];
|
||||
[clone setThemeState:[self themeState]];
|
||||
[clone unsetThemeState:CPThemeStateHighlighted];
|
||||
[clone setHighlighted:NO];
|
||||
[clone setAngle:_angle];
|
||||
[clone setHitTests:NO];
|
||||
|
||||
// 3. Mark this as a clone so drawRect knows to draw it
|
||||
clone._isAnimatingClone = YES;
|
||||
clone._animatingRowIndex = row;
|
||||
clone._parentOutlineView = outlineView;[outlineView addSubview:clone positioned:CPWindowAbove relativeTo:self];
|
||||
|
||||
// 4. Calculate angles
|
||||
var isCurrentlyExpanded = ([self state] === CPOnState),
|
||||
targetAngle = isCurrentlyExpanded ? -PI_2 : 0.0;
|
||||
|
||||
// 5. Setup Animation
|
||||
var anim = [CABasicAnimation animationWithKeyPath:@"angle"];
|
||||
[anim setFromValue:_angle];
|
||||
[anim setToValue:targetAngle];
|
||||
[anim setDuration:0.25];
|
||||
[anim setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
|
||||
|
||||
[[clone layer] setDelegate:clone];
|
||||
[anim setDelegate:clone];
|
||||
|
||||
[[clone layer] addAnimation:anim forKey:@"angle"];
|
||||
|
||||
// Set final angle on clone to avoid flicker at end
|
||||
[clone setAngle:targetAngle];
|
||||
}
|
||||
}
|
||||
|
||||
[super stopTracking:lastPoint at:point mouseIsUp:mouseIsUp];
|
||||
}
|
||||
|
||||
- (void)animationDidStop:(CAAnimation)anim finished:(BOOL)flag
|
||||
{
|
||||
// This runs on the CLONE
|
||||
if (_parentOutlineView)
|
||||
[_parentOutlineView _noteDisclosureAnimationDidStopForRow:_animatingRowIndex];
|
||||
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
width = CGRectGetWidth(bounds),
|
||||
height = CGRectGetHeight(bounds);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
if (_angle)
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
var centre = CGPointMake(FLOOR(width / 2.0), FLOOR(height / 2.0));
|
||||
CGContextTranslateCTM(context, centre.x, centre.y);
|
||||
CGContextRotateCTM(context, _angle);
|
||||
CGContextTranslateCTM(context, -centre.x, -centre.y);
|
||||
if (_arrowElement && _arrowElement.parentNode !== self._DOMElement)
|
||||
self._DOMElement.appendChild(_arrowElement);
|
||||
}
|
||||
|
||||
// Center, but crisp.
|
||||
CGContextTranslateCTM(context, FLOOR((width - 9.0) / 2.0), FLOOR((height - 8.0) / 2.0));
|
||||
// If I am NOT the clone, I must check if my row is animating.
|
||||
// If it is animating, the clone is drawing on top of me, so I should be invisible.
|
||||
if (!_isAnimatingClone)
|
||||
{
|
||||
var outlineView = [self superview];
|
||||
if ([outlineView isKindOfClass:[CPOutlineView class]])
|
||||
{
|
||||
var row = [outlineView rowForView:self];
|
||||
if (row !== CPNotFound && [outlineView _isRowAnimatingDisclosure:row])
|
||||
{
|
||||
|
||||
CGContextMoveToPoint(context, 0.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 9.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 4.5, 8.0);
|
||||
CGContextClosePath(context);
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
_arrowElement.style.display = "none";
|
||||
}
|
||||
return; // Suppress the real button
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CGContextSetFillColor(context,
|
||||
colorForDisclosureTriangle([self hasThemeState:CPThemeStateSelected],
|
||||
[self hasThemeState:CPThemeStateHighlighted]));
|
||||
CGContextFillPath(context);
|
||||
_arrowElement.style.display = "block";
|
||||
|
||||
var isSelected = [self hasThemeState:CPThemeStateSelected],
|
||||
isHighlighted = [self hasThemeState:CPThemeStateHighlighted],
|
||||
isKeyWindow = [self hasThemeState:CPThemeStateKeyWindow],
|
||||
isHUD = [self window] && ([[self window] styleMask] & CPHUDBackgroundWindowMask),
|
||||
triangleColor = nil;
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, 0.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 4.5, 8.0);
|
||||
// Establish the required color based on the state/outline selection
|
||||
if (isHUD)
|
||||
{
|
||||
triangleColor = isSelected ? [CPColor blackColor] : [CPColor whiteColor];
|
||||
|
||||
if (_angle === 0.0)
|
||||
CGContextAddLineToPoint(context, 9.0, 0.0);
|
||||
if (isHighlighted)
|
||||
triangleColor = [triangleColor colorWithAlphaComponent:0.5];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isSelected)
|
||||
triangleColor = isKeyWindow ? [CPColor whiteColor] : [CPColor blackColor];
|
||||
else triangleColor = [CPColor colorWithCalibratedWhite:0.45 alpha: 1.0];
|
||||
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithCalibratedWhite:1.0 alpha: 0.7]);
|
||||
CGContextStrokePath(context);
|
||||
if (isHighlighted)
|
||||
{
|
||||
if (isSelected && isKeyWindow)
|
||||
triangleColor = [CPColor colorWithCalibratedWhite:0.9 alpha: 1.0];
|
||||
|
||||
else if (isSelected && !isKeyWindow)
|
||||
triangleColor = [CPColor colorWithCalibratedWhite:0.2 alpha: 1.0];
|
||||
else
|
||||
triangleColor = [CPColor colorWithCalibratedWhite:0.25 alpha: 1.0];
|
||||
}
|
||||
}
|
||||
|
||||
var colorCSS = [triangleColor cssString];
|
||||
|
||||
// Convert and inject the updated SVG only when the required color has changed
|
||||
if (_lastColorCSS !== colorCSS)
|
||||
{
|
||||
_lastColorCSS = colorCSS;
|
||||
var svgString = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7 10L12 15L17 10H7Z" fill="' + colorCSS + '"/></svg>';
|
||||
var b64 = window.btoa(svgString);
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
_arrowElement.style.backgroundImage = "url('data:image/svg+xml;charset=utf-8;base64," + b64 + "')";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPOutlineViewIndentationPerLevelKey = @"CPOutlineViewIndentationPerLevelKey",
|
||||
CPOutlineViewOutlineTableColumnKey = @"CPOutlineViewOutlineTableColumnKey",
|
||||
CPOutlineViewDataSourceKey = @"CPOutlineViewDataSourceKey",
|
||||
@@ -2337,6 +2873,8 @@ var CPOutlineViewIndentationPerLevelKey = @"CPOutlineViewIndentationPerLevelKey"
|
||||
[super setDelegate:self];
|
||||
|
||||
[self _updateIsViewBased];
|
||||
|
||||
_animatingDisclosureRows = [CPIndexSet indexSet];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
@@ -79,6 +79,8 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setBezelStyle:CPRoundedBezelStyle];
|
||||
|
||||
[self selectItemAtIndex:CPNotFound];
|
||||
|
||||
_preferredEdge = CPMaxYEdge;
|
||||
@@ -689,6 +691,12 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
// FIXME: setFont: should set the font on the menu.
|
||||
[menu setFont:[self font]];
|
||||
|
||||
// Propagate the HUD theme state to the menu
|
||||
if ([self hasThemeState:CPThemeStateHUD])
|
||||
[menu setThemeState:CPThemeStateHUD];
|
||||
else
|
||||
[menu unsetThemeState:CPThemeStateHUD];
|
||||
|
||||
if ([self pullsDown])
|
||||
{
|
||||
var positionedItem = nil,
|
||||
|
||||
+121
-33
@@ -43,7 +43,6 @@ CPProgressIndicatorSpinningStyle = 1;
|
||||
*/
|
||||
CPProgressIndicatorHUDBarStyle = 2;
|
||||
|
||||
var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -71,6 +70,43 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
BOOL _isDisplayedWhenStopped;
|
||||
}
|
||||
|
||||
// Inject CSS Keyframes for spinning animation (Standard + WebKit)
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self !== [CPProgressIndicator class])
|
||||
return;
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
if (document.getElementById("cp-progress-indicator-style"))
|
||||
return;
|
||||
|
||||
var style = document.createElement("style");
|
||||
style.id = "cp-progress-indicator-style";
|
||||
style.type = "text/css";
|
||||
|
||||
// We define two animations:
|
||||
// 1. cp-progress-indicator-spin: Rotates 360 degrees (for spinners)
|
||||
// 2. cp-progress-indicator-bar-slide: Moves background-position (for striped bars)
|
||||
var css =
|
||||
"@keyframes cp-progress-indicator-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } " +
|
||||
"@-webkit-keyframes cp-progress-indicator-spin { 0% { -webkit-transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); } } " +
|
||||
|
||||
"@keyframes cp-progress-indicator-bar-slide { 0% { background-position: 0 0; } 100% { background-position: 30px 0; } } " +
|
||||
"@-webkit-keyframes cp-progress-indicator-bar-slide { 0% { background-position: 0 0; } 100% { background-position: 30px 0; } }";
|
||||
|
||||
if (style.styleSheet)
|
||||
style.styleSheet.cssText = css;
|
||||
else
|
||||
style.appendChild(document.createTextNode(css));
|
||||
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
if (head)
|
||||
head.appendChild(style);
|
||||
}
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"progress-indicator";
|
||||
@@ -83,12 +119,19 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
@"bar-color": [CPNull null],
|
||||
@"default-height": 20,
|
||||
@"bezel-color": [CPNull null],
|
||||
@"spinning-mini-gif": [CPNull null],
|
||||
@"spinning-small-gif": [CPNull null],
|
||||
@"spinning-regular-gif": [CPNull null],
|
||||
|
||||
// CSS Spinner Attributes
|
||||
@"spinner-color": [CPColor grayColor],
|
||||
@"spinner-track-color": [CPColor colorWithWhite:0.9 alpha:1.0],
|
||||
@"spinner-line-width": 3.0,
|
||||
|
||||
@"circular-border-color": [CPNull null],
|
||||
@"circular-border-size": 1,
|
||||
@"circular-color": [CPNull null]
|
||||
@"circular-color": [CPNull null],
|
||||
|
||||
@"spinning-mini-gif": [CPNull null],
|
||||
@"spinning-small-gif": [CPNull null],
|
||||
@"spinning-regular-gif": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,6 +181,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
_isAnimating = YES;
|
||||
|
||||
[self _hideOrDisplay];
|
||||
[self setNeedsLayout]; // Trigger layout to update CSS animation state
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -149,6 +193,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
_isAnimating = NO;
|
||||
|
||||
[self _hideOrDisplay];
|
||||
[self setNeedsLayout]; // Trigger layout to remove CSS animation state
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -233,7 +278,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
_controlSize = aControlSize;
|
||||
|
||||
[self updateBackgroundColor];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -285,7 +330,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
_indeterminate = indeterminate;
|
||||
|
||||
[self updateBackgroundColor];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -307,9 +352,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
_style = aStyle;
|
||||
|
||||
[self setTheme:(_style === CPProgressIndicatorHUDBarStyle) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
|
||||
|
||||
[self updateBackgroundColor];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -318,7 +361,14 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
- (void)sizeToFit
|
||||
{
|
||||
if (_style == CPProgressIndicatorSpinningStyle)
|
||||
[self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
|
||||
{
|
||||
var size = 32.0;
|
||||
if (_controlSize === CPMiniControlSize) size = 16.0;
|
||||
else if (_controlSize === CPSmallControlSize) size = 24.0;
|
||||
else if (_controlSize === CPRegularControlSize) size = 32.0;
|
||||
|
||||
[self setFrameSize:CGSizeMake(size, size)];
|
||||
}
|
||||
else
|
||||
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), [self valueForThemeAttribute:@"default-height"])];
|
||||
}
|
||||
@@ -381,6 +431,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aViewName
|
||||
{
|
||||
// Handle the standard Bar View
|
||||
if (aViewName === @"bar-view" && _style !== CPProgressIndicatorSpinningStyle)
|
||||
{
|
||||
var width = CGRectGetWidth([self bounds]),
|
||||
@@ -394,41 +445,78 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
return CGRectMake(0, 0, barWidth, [self valueForThemeAttribute:@"default-height"]);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)updateBackgroundColor
|
||||
{
|
||||
if ([CPProgressIndicatorSpinningStyleColors count] === 0)
|
||||
|
||||
// Handle the Spinning View (CSS Spinner)
|
||||
if (aViewName === @"spinner-view" && _style == CPProgressIndicatorSpinningStyle && _indeterminate)
|
||||
{
|
||||
CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [self valueForThemeAttribute:@"spinning-mini-gif"];
|
||||
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [self valueForThemeAttribute:@"spinning-small-gif"];
|
||||
CPProgressIndicatorSpinningStyleColors[CPRegularControlSize] = [self valueForThemeAttribute:@"spinning-regular-gif"];
|
||||
return [self bounds];
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
// Return nil for views that shouldn't appear in the current style
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (YES)//_isBezeled)
|
||||
{
|
||||
// === SPINNING STYLE ===
|
||||
if (_style == CPProgressIndicatorSpinningStyle)
|
||||
{
|
||||
if (!_indeterminate)
|
||||
return;
|
||||
// If indeterminate, use CSS spinner
|
||||
if (_indeterminate)
|
||||
{
|
||||
var spinnerView = [self layoutEphemeralSubviewNamed:"spinner-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
// Ensure other views are hidden
|
||||
[self layoutEphemeralSubviewNamed:"bar-view" positioned:CPWindowBelow relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
// This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle.
|
||||
[self layoutEphemeralSubviewNamed:"bar-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
// Configure CSS on the subview, NOT self._DOMElement, to avoid transform conflicts
|
||||
var domEl = spinnerView._DOMElement,
|
||||
spinnerColor = [self currentValueForThemeAttribute:@"spinner-color"],
|
||||
trackColor = [self currentValueForThemeAttribute:@"spinner-track-color"],
|
||||
lineWidth = [self currentValueForThemeAttribute:@"spinner-line-width"],
|
||||
widthStr = lineWidth + "px";
|
||||
|
||||
[self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]];
|
||||
domEl.style.boxSizing = "border-box";
|
||||
domEl.style.borderRadius = "50%";
|
||||
domEl.style.borderStyle = "solid";
|
||||
domEl.style.borderWidth = widthStr;
|
||||
domEl.style.borderColor = [trackColor cssString];
|
||||
domEl.style.borderTopColor = [spinnerColor cssString];
|
||||
|
||||
// Animation logic
|
||||
if (_isAnimating)
|
||||
{
|
||||
var anim = "cp-progress-indicator-spin 1s linear infinite";
|
||||
domEl.style.animation = anim;
|
||||
domEl.style.WebkitAnimation = anim;
|
||||
}
|
||||
else
|
||||
{
|
||||
domEl.style.animation = "none";
|
||||
domEl.style.WebkitAnimation = "none";
|
||||
}
|
||||
|
||||
// Ensure main view is transparent
|
||||
[self setBackgroundColor:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determinate Spinner (Pie Chart drawn in drawRect)
|
||||
// Hide CSS spinner
|
||||
[self layoutEphemeralSubviewNamed:"spinner-view" positioned:CPWindowBelow relativeToEphemeralSubviewNamed:nil];
|
||||
[self setBackgroundColor:nil];
|
||||
}
|
||||
}
|
||||
// === BAR STYLE ===
|
||||
else
|
||||
{
|
||||
// Hide spinner
|
||||
[self layoutEphemeralSubviewNamed:"spinner-view" positioned:CPWindowBelow relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
|
||||
var barView = [self layoutEphemeralSubviewNamed:"bar-view"
|
||||
@@ -447,6 +535,8 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
// Handle determinate state for Spinning style (Pie chart progress) via CoreGraphics.
|
||||
// If indeterminate, the CSS animation (spinner-view) takes over and we draw nothing.
|
||||
if (_style == CPProgressIndicatorSpinningStyle && !_indeterminate)
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
@@ -512,7 +602,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
_isDisplayedWhenStoppedSet = [aCoder decodeObjectForKey:@"_isDisplayedWhenStoppedSet"];
|
||||
_isDisplayedWhenStopped = [aCoder decodeObjectForKey:@"_isDisplayedWhenStopped"];
|
||||
|
||||
[self updateBackgroundColor];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -520,8 +610,6 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
// Don't encode the background colour. It can be recreated based on the flags
|
||||
// and if encoded causes hardcoded image paths in the cib while just wasting space.
|
||||
var backgroundColor = [self backgroundColor];
|
||||
[self setBackgroundColor:nil];
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
@@ -1958,57 +1958,63 @@ TODO: implement
|
||||
|
||||
- (CPArray)_backgroundColors
|
||||
{
|
||||
return [self valueForThemeAttribute:@"alternating-row-colors"];
|
||||
return [self currentValueForThemeAttribute:@"alternating-row-colors"];
|
||||
}
|
||||
|
||||
- (CPColor)_selectedRowColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"selected-color"];
|
||||
return [self currentValueForThemeAttribute:@"selected-color"];
|
||||
}
|
||||
|
||||
- (CPColor)_sliceTopBorderColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"slice-top-border-color"];
|
||||
return [self currentValueForThemeAttribute:@"slice-top-border-color"];
|
||||
}
|
||||
|
||||
- (CPColor)_sliceBottomBorderColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"slice-bottom-border-color"];
|
||||
return [self currentValueForThemeAttribute:@"slice-bottom-border-color"];
|
||||
}
|
||||
|
||||
- (CPColor)_sliceLastBottomBorderColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"slice-last-bottom-border-color"];
|
||||
return [self currentValueForThemeAttribute:@"slice-last-bottom-border-color"];
|
||||
}
|
||||
|
||||
- (CPFont)font
|
||||
{
|
||||
return [self valueForThemeAttribute:@"font"];
|
||||
return [self currentValueForThemeAttribute:@"font"];
|
||||
}
|
||||
|
||||
- (CPColor)_fontColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"font-color"];
|
||||
return [self currentValueForThemeAttribute:@"font-color"];
|
||||
}
|
||||
|
||||
- (void)setThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super setThemeState:aState];
|
||||
[_slices makeObjectsPerformSelector:@selector(setThemeState:) withObject:aState];
|
||||
}
|
||||
|
||||
- (CPImage)_imageAdd
|
||||
{
|
||||
return [self valueForThemeAttribute:@"add-image" inState:CPThemeStateNormal];
|
||||
return [self valueForThemeAttribute:@"add-image" inState:CPThemeStateNormal.and([self themeState])];
|
||||
}
|
||||
|
||||
- (CPImage)_imageAddHighlighted
|
||||
{
|
||||
return [self valueForThemeAttribute:@"add-image" inState:CPThemeStateHighlighted];
|
||||
return [self valueForThemeAttribute:@"add-image" inState:CPThemeStateHighlighted.and([self themeState])];
|
||||
}
|
||||
|
||||
- (CPImage)_imageRemove
|
||||
{
|
||||
return [self valueForThemeAttribute:@"remove-image" inState:CPThemeStateNormal];
|
||||
return [self valueForThemeAttribute:@"remove-image" inState:CPThemeStateNormal.and([self themeState])];
|
||||
}
|
||||
|
||||
- (CPImage)_imageRemoveHighlighted
|
||||
{
|
||||
return [self valueForThemeAttribute:@"remove-image" inState:CPThemeStateHighlighted];
|
||||
return [self valueForThemeAttribute:@"remove-image" inState:CPThemeStateHighlighted.and([self themeState])];
|
||||
}
|
||||
|
||||
- (CPVerticalTextAlignment)_verticalAlignment
|
||||
@@ -2220,7 +2226,13 @@ TODO: implement
|
||||
return;
|
||||
|
||||
var point = [self convertPoint:[event locationInWindow] fromView:nil],
|
||||
view = [_slices objectAtIndex:FLOOR(MAX(0, point.y) / _sliceHeight)];
|
||||
index = FLOOR(MAX(0, point.y) / _sliceHeight);
|
||||
|
||||
// Check bounds before accessing the array to prevent CPRangeException
|
||||
if (index >= [_slices count])
|
||||
return;
|
||||
|
||||
var view = [_slices objectAtIndex:FLOOR(MAX(0, point.y) / _sliceHeight)];
|
||||
|
||||
if ([self _dragShouldBeginFromMouseDown:view])
|
||||
[self _performDragForSlice:view withEvent:event];
|
||||
|
||||
@@ -49,6 +49,13 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setRowIndex:(int)anIndex
|
||||
{
|
||||
_rowIndex = anIndex;
|
||||
[self _updateBackgroundColor];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)_setSelected:(BOOL)select
|
||||
{
|
||||
if (select == _selected)
|
||||
@@ -57,6 +64,47 @@
|
||||
var selector = select ? @selector(setThemeState:) : @selector(unsetThemeState:);
|
||||
[[self subviews] makeObjectsPerformSelector:selector withObject:CPThemeStateSelectedDataView];
|
||||
_selected = select;
|
||||
|
||||
[self _updateBackgroundColor];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)_updateBackgroundColor
|
||||
{
|
||||
var color = nil;
|
||||
|
||||
if ([self _isSelected])
|
||||
{
|
||||
color = [_ruleEditor _selectedRowColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
var colors = [_ruleEditor _backgroundColors],
|
||||
count = [colors count];
|
||||
|
||||
if (count > 0)
|
||||
color = [colors objectAtIndex:(_rowIndex % count)];
|
||||
}
|
||||
|
||||
[self setBackgroundColor:color];
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
[super viewDidMoveToWindow];
|
||||
[self _updateBackgroundColor];
|
||||
}
|
||||
|
||||
- (void)setThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super setThemeState:aState];
|
||||
[self _updateBackgroundColor];
|
||||
}
|
||||
|
||||
- (void)unsetThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super unsetThemeState:aState];
|
||||
[self _updateBackgroundColor];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
@@ -66,35 +114,31 @@
|
||||
maxX = CGRectGetWidth(bounds),
|
||||
maxY = CGRectGetHeight(bounds);
|
||||
|
||||
// Draw background
|
||||
if ([self _isSelected])
|
||||
_backgroundColor = [_ruleEditor _selectedRowColor];
|
||||
else
|
||||
{
|
||||
var colors = [_ruleEditor _backgroundColors],
|
||||
count = [colors count];
|
||||
_backgroundColor = [colors objectAtIndex:(_rowIndex % count)];
|
||||
}
|
||||
|
||||
CGContextSetFillColor(context, _backgroundColor);
|
||||
CGContextFillRect(context, rect);
|
||||
// Note: Background is now handled by setBackgroundColor in _updateBackgroundColor
|
||||
// to support CSS-based colors and transparency correctly.
|
||||
|
||||
// Draw Top Border
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, 0, 0);
|
||||
CGContextAddLineToPoint(context, maxX, 0);
|
||||
CGContextSetStrokeColor(context, [_ruleEditor _sliceTopBorderColor]);
|
||||
CGContextStrokePath(context);
|
||||
var topColor = [_ruleEditor _sliceTopBorderColor];
|
||||
if (topColor)
|
||||
{
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, 0, 0);
|
||||
CGContextAddLineToPoint(context, maxX, 0);
|
||||
CGContextSetStrokeColor(context, topColor);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
// Draw Bottom Border
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, 0, maxY);
|
||||
CGContextAddLineToPoint(context, maxX, maxY);
|
||||
|
||||
var bottomColor = (_rowIndex == [_ruleEditor _lastRow]) ? [_ruleEditor _sliceLastBottomBorderColor] : [_ruleEditor _sliceBottomBorderColor];
|
||||
|
||||
CGContextSetStrokeColor(context, bottomColor);
|
||||
CGContextStrokePath(context);
|
||||
if (bottomColor)
|
||||
{
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, 0, maxY);
|
||||
CGContextAddLineToPoint(context, maxX, maxY);
|
||||
CGContextSetStrokeColor(context, bottomColor);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)theEvent
|
||||
|
||||
@@ -67,6 +67,23 @@
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
}
|
||||
|
||||
- (void)_setSelected:(BOOL)isSelected
|
||||
{
|
||||
[super _setSelected:isSelected];
|
||||
[self _updateButtonImages];
|
||||
}
|
||||
|
||||
- (void)_updateButtonImages
|
||||
{
|
||||
var rowState = [self themeState];
|
||||
|
||||
if ([self _isSelected])
|
||||
rowState = rowState.and(CPThemeStateSelected);
|
||||
|
||||
[_addButton setImage:[_ruleEditor valueForThemeAttribute:@"add-image" inState:rowState]];
|
||||
[_subtractButton setImage:[_ruleEditor valueForThemeAttribute:@"remove-image" inState:rowState]];
|
||||
}
|
||||
|
||||
- (CPButton)_createRowButton
|
||||
{
|
||||
var button = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
@@ -89,6 +106,8 @@
|
||||
var button = [self _createRowButton];
|
||||
|
||||
[button setToolTip:[_ruleEditor _toolTipForAddSimpleRowButton]];
|
||||
|
||||
// Initial setup, _updateButtonImages will be called later to set correct state-based images
|
||||
[button setValue:[_ruleEditor _imageAdd] forThemeAttribute:@"image" inState:CPThemeStateNormal];
|
||||
[button setValue:[_ruleEditor _imageAddHighlighted] forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
||||
|
||||
@@ -103,6 +122,8 @@
|
||||
var button = [self _createRowButton];
|
||||
|
||||
[button setToolTip:[_ruleEditor _toolTipForDeleteRowButton]];
|
||||
|
||||
// Initial setup
|
||||
[button setValue:[_ruleEditor _imageRemove] forThemeAttribute:@"image" inState:CPThemeStateNormal];
|
||||
[button setValue:[_ruleEditor _imageRemoveHighlighted] forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
||||
|
||||
@@ -525,9 +546,23 @@
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
[super viewDidMoveToWindow];
|
||||
[self _updateButtonImages];
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)setThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super setThemeState:aState];
|
||||
[self _updateButtonImages];
|
||||
}
|
||||
|
||||
- (void)unsetThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super unsetThemeState:aState];
|
||||
[self _updateButtonImages];
|
||||
}
|
||||
|
||||
- (void)_addObservers
|
||||
{
|
||||
if (_isObserving)
|
||||
|
||||
+78
-17
@@ -101,6 +101,12 @@ var CPScrollViewWillStartLiveScrollNotification = @"CPScrollViewWillStartLiveScr
|
||||
var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
|
||||
CPScrollerStyleGlobalChangeNotification = @"CPScrollerStyleGlobalChangeNotification";
|
||||
|
||||
var CPScrollViewBorderSuffixes = @[@"no-border", @"line-border", @"bezel-border", @"groove-border"];
|
||||
|
||||
// _CPScrollViews will hold all created CPScrollView's in order to propagate changes
|
||||
// of scroller global style
|
||||
var _CPScrollViews;
|
||||
|
||||
/*
|
||||
The default smallest and largest magnifications. These are not arbitrary: they are the
|
||||
defaults that Apple documents for NSScrollView's minMagnification and maxMagnification, so a
|
||||
@@ -179,6 +185,8 @@ var CPScrollViewDefaultMinMagnification = 0.25,
|
||||
CPScrollerStyleGlobal = _isBrowserUsingOverlayScrollers() ? CPScrollerStyleOverlay : CPScrollerStyleLegacy
|
||||
else
|
||||
CPScrollerStyleGlobal = globalValue;
|
||||
|
||||
_CPScrollViews = @[];
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
@@ -190,7 +198,15 @@ var CPScrollViewDefaultMinMagnification = 0.25,
|
||||
{
|
||||
return @{
|
||||
@"bottom-corner-color": [CPColor whiteColor],
|
||||
@"border-color": [CPColor blackColor]
|
||||
@"border-color": [CPColor blackColor],
|
||||
@"content-inset-no-border": CGInsetMake(0, 0, 0, 0),
|
||||
@"content-inset-line-border": CGInsetMake(1, 1, 1, 1),
|
||||
@"content-inset-bezel-border": CGInsetMake(1, 1, 1, 1),
|
||||
@"content-inset-groove-border": CGInsetMake(2, 2, 2, 2),
|
||||
@"background-color-no-border": [CPNull null],
|
||||
@"background-color-line-border": [CPNull null],
|
||||
@"background-color-bezel-border": [CPNull null],
|
||||
@"background-color-groove-border": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -259,19 +275,35 @@ var CPScrollViewDefaultMinMagnification = 0.25,
|
||||
|
||||
+ (CGRect)_insetBounds:(CGRect)bounds borderType:(CPBorderType)borderType
|
||||
{
|
||||
// First, we have to check if we are compiling a theme or running an application because if working on a theme,
|
||||
// we can't use theme attributes to determine the inset ! This would be a kind of circular reference...
|
||||
|
||||
var compilingATheme = [[[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPApplicationDelegateClass"] isEqualToString:@"BKShowcaseController"];
|
||||
|
||||
if (compilingATheme)
|
||||
return bounds;
|
||||
|
||||
var contentInset = [[CPTheme defaultTheme] valueForAttributeWithName:@"content-inset-"+CPScrollViewBorderSuffixes[borderType] forClass:CPScrollView];
|
||||
|
||||
// As this is a class method, we don't have object attributes, so if the theme doesn't declare content insets, we don't automatically get default value.
|
||||
// Get it by hand.
|
||||
if (!contentInset)
|
||||
contentInset = [[self themeAttributes] objectForKey:@"content-inset-"+CPScrollViewBorderSuffixes[borderType]];
|
||||
|
||||
switch (borderType)
|
||||
{
|
||||
case CPNoBorder:
|
||||
case CPLineBorder:
|
||||
case CPBezelBorder:
|
||||
return CGRectInset(bounds, 1.0, 1.0);
|
||||
return CGRectInsetByInset(bounds, contentInset);
|
||||
|
||||
case CPGrooveBorder:
|
||||
bounds = CGRectInset(bounds, 2.0, 2.0);
|
||||
// FIXME: Do something better with this
|
||||
bounds = CGRectInsetByInset(bounds, contentInset);
|
||||
++bounds.origin.y;
|
||||
--bounds.size.height;
|
||||
return bounds;
|
||||
|
||||
case CPNoBorder:
|
||||
default:
|
||||
return bounds;
|
||||
}
|
||||
@@ -293,7 +325,10 @@ var CPScrollViewDefaultMinMagnification = 0.25,
|
||||
+ (void)setGlobalScrollerStyle:(CPScrollerStyle)aStyle
|
||||
{
|
||||
CPScrollerStyleGlobal = aStyle;
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPScrollerStyleGlobalChangeNotification object:nil];
|
||||
|
||||
// We propagate the new scroller global style to all existing CPScrollView's
|
||||
for (var i = 0, count = [_CPScrollViews count]; i < count; i++)
|
||||
[_CPScrollViews[i] setScrollerStyle:CPScrollerStyleGlobal];
|
||||
}
|
||||
|
||||
|
||||
@@ -339,6 +374,8 @@ var CPScrollViewDefaultMinMagnification = 0.25,
|
||||
_delegate = nil;
|
||||
_scrollTimer = nil;
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
[_CPScrollViews addObject:self];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -1550,7 +1587,9 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
|
||||
if (_scrollerStyle === CPScrollerStyleLegacy)
|
||||
{
|
||||
[[self bottomCornerView] setFrame:[self _bottomCornerViewFrame]];
|
||||
var bottomCornerFrame = [self _bottomCornerViewFrame];
|
||||
[[self bottomCornerView] setFrame:bottomCornerFrame];
|
||||
[[self bottomCornerView] setHidden:CGRectIsEmpty(bottomCornerFrame)];
|
||||
[[self bottomCornerView] setBackgroundColor:[self currentValueForThemeAttribute:@"bottom-corner-color"]];
|
||||
}
|
||||
|
||||
@@ -1622,7 +1661,6 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
// MARK: -
|
||||
// MARK: Overrides
|
||||
|
||||
|
||||
- (void)_removeObservers
|
||||
{
|
||||
if (!_isObserving)
|
||||
@@ -1640,9 +1678,6 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
if (_isObserving)
|
||||
return;
|
||||
|
||||
//Make sure to have the last global style for the scroller
|
||||
[self _didReceiveDefaultStyleChange:nil];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(_didReceiveDefaultStyleChange:)
|
||||
name:CPScrollerStyleGlobalChangeNotification
|
||||
@@ -1651,13 +1686,11 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
[super _addObservers];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
[super drawRect:aRect];
|
||||
|
||||
if (_borderType == CPNoBorder)
|
||||
if ([self isCSSBased] || (_borderType == CPNoBorder))
|
||||
return;
|
||||
|
||||
var strokeRect = [self bounds],
|
||||
@@ -1866,6 +1899,37 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
|
||||
// MARK: -
|
||||
|
||||
@implementation CPScrollView (CSSTheming)
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (![self isCSSBased])
|
||||
return;
|
||||
|
||||
if (_borderType !== CPNoBorder)
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color-"+CPScrollViewBorderSuffixes[_borderType]]];
|
||||
|
||||
if (_scrollerStyle === CPScrollerStyleLegacy)
|
||||
[[self bottomCornerView] setBackgroundColor:[self currentValueForThemeAttribute:@"bottom-corner-color"]];
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return [[self theme] isCSSBased];
|
||||
}
|
||||
|
||||
- (void)refreshDisplay
|
||||
{
|
||||
if ([self isCSSBased])
|
||||
[self setNeedsLayout:YES];
|
||||
else
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// pragma mark -
|
||||
|
||||
@implementation CPScrollView (FirstResponder)
|
||||
|
||||
// Those 4 next methods are needed to (un)set CPThemeStateFirstResponder based on content view
|
||||
@@ -2009,10 +2073,7 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
|
||||
_scrollerStyle = [aCoder decodeObjectForKey:CPScrollViewScrollerStyleKey] || CPScrollerStyleGlobal;
|
||||
_scrollerKnobStyle = [aCoder decodeObjectForKey:CPScrollViewScrollerKnobStyleKey] || CPScrollerKnobStyleDefault;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(_didReceiveDefaultStyleChange:)
|
||||
name:CPScrollerStyleGlobalChangeNotification
|
||||
object:nil];
|
||||
[_CPScrollViews addObject:self];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
+19
-1
@@ -320,7 +320,25 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
if (![self hasThemeState:CPThemeStateSelected] && ![self hasThemeState:CPThemeStateScrollViewLegacy])
|
||||
return CPScrollerNoPart;
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
|
||||
// Fetch the visual rect for the knob
|
||||
var bounds = [self bounds],
|
||||
knobRect = [self rectForPart:CPScrollerKnob],
|
||||
hitKnobRect = CGRectMake(CGRectGetMinX(knobRect), CGRectGetMinY(knobRect), CGRectGetWidth(knobRect), CGRectGetHeight(knobRect));
|
||||
|
||||
// Expand the hit test rect to span the entire track on the minor axis
|
||||
// so dragging works smoothly even if the user clicks slightly off-center.
|
||||
if ([self isVertical])
|
||||
{
|
||||
hitKnobRect.origin.x = 0;
|
||||
hitKnobRect.size.width = CGRectGetWidth(bounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
hitKnobRect.origin.y = 0;
|
||||
hitKnobRect.size.height = CGRectGetHeight(bounds);
|
||||
}
|
||||
|
||||
if (CGRectContainsPoint(hitKnobRect, aPoint))
|
||||
return CPScrollerKnob;
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
|
||||
|
||||
@@ -104,6 +104,8 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
_sendsWholeSearchString = NO;
|
||||
_sendsSearchStringImmediately = NO;
|
||||
_recentsAutosaveName = nil;
|
||||
|
||||
[self setPlaceholderString:@"Search"];
|
||||
|
||||
[self _init];
|
||||
}
|
||||
@@ -122,8 +124,14 @@ var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotificat
|
||||
[self setContinuous:YES];
|
||||
|
||||
var bounds = [self bounds],
|
||||
cancelButton = [[CPButton alloc] initWithFrame:[self cancelButtonRectForBounds:bounds]],
|
||||
cancelButton = nil,
|
||||
searchButton = nil;
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
cancelButton = [[CPButton alloc] initWithFrame:[self cancelButtonRectForBounds:bounds]];
|
||||
searchButton = [[CPButton alloc] initWithFrame:[self searchButtonRectForBounds:bounds]];
|
||||
}
|
||||
|
||||
[self setCancelButton:cancelButton];
|
||||
[self resetCancelButton];
|
||||
|
||||
+279
-149
@@ -71,7 +71,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
@"right-segment-bezel-color": [CPNull null],
|
||||
@"center-segment-bezel-color": [CPNull null],
|
||||
@"divider-bezel-color": [CPNull null],
|
||||
@"divider-thickness": 1.0,
|
||||
@"divider-thickness": 1.0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
// Working with Individual Segments
|
||||
/*!
|
||||
Sets the width of the specified segment.
|
||||
@param aWidth the new width for the segment
|
||||
@param aWidth the new width for the segment (0 for automatic width)
|
||||
@param aSegment the segment to set the width for
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
@@ -420,7 +420,14 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
[segment setSelected:isSelected];
|
||||
|
||||
_themeStates[aSegment] = isSelected ? CPThemeStateSelected : CPThemeStateNormal;
|
||||
if (_themeStates[aSegment] == undefined)
|
||||
_themeStates[aSegment] = CPThemeStateNormal;
|
||||
|
||||
// Update state using .and() / .without() to preserve existing states (like Disabled)
|
||||
if (isSelected)
|
||||
_themeStates[aSegment] = _themeStates[aSegment].and(CPThemeStateSelected);
|
||||
else
|
||||
_themeStates[aSegment] = _themeStates[aSegment].without(CPThemeStateSelected);
|
||||
|
||||
// We need to do some cleanup if we only allow one selection.
|
||||
if (isSelected)
|
||||
@@ -432,7 +439,10 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1 && oldSelectedSegment < _segments.length)
|
||||
{
|
||||
[_segments[oldSelectedSegment] setSelected:NO];
|
||||
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
|
||||
|
||||
// Update old segment using .without() to preserve other states
|
||||
// Previously: _themeStates[oldSelectedSegment] = CPThemeStateNormal;
|
||||
_themeStates[oldSelectedSegment] = _themeStates[oldSelectedSegment].without(CPThemeStateSelected);
|
||||
|
||||
[self drawSegmentBezel:oldSelectedSegment highlight:NO];
|
||||
}
|
||||
@@ -530,12 +540,24 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
- (float)_leftOffsetForSegment:(unsigned)segment
|
||||
{
|
||||
if (segment == 0)
|
||||
return [self currentValueForThemeAttribute:@"bezel-inset"].left;
|
||||
if ([[self actualTheme] isCSSBased])
|
||||
{
|
||||
// CSS styling
|
||||
if (segment == 0)
|
||||
return 0;
|
||||
|
||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
|
||||
return [self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) - 2; // FIXME: -2 for collapse of borders
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy styling
|
||||
if (segment == 0)
|
||||
return [self currentValueForThemeAttribute:@"bezel-inset"].left;
|
||||
|
||||
return [self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness;
|
||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
|
||||
|
||||
return [self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness;
|
||||
}
|
||||
}
|
||||
|
||||
- (unsigned)_indexOfLastSegment
|
||||
@@ -569,19 +591,28 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
else if (aName.indexOf("segment-bezel") === 0)
|
||||
{
|
||||
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
|
||||
if ([[self actualTheme] isCSSBased])
|
||||
{
|
||||
var segment = parseInt(aName.substring("segment-bezel-".length), 10);
|
||||
|
||||
return [self bezelFrameForSegment:segment];
|
||||
}
|
||||
else
|
||||
{
|
||||
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
|
||||
frame = CGRectCreateCopy([self frameForSegment:segment]);
|
||||
|
||||
if (segment === 0)
|
||||
{
|
||||
frame.origin.x += contentInset.left;
|
||||
frame.size.width -= contentInset.left;
|
||||
if (segment === 0)
|
||||
{
|
||||
frame.origin.x += contentInset.left;
|
||||
frame.size.width -= contentInset.left;
|
||||
}
|
||||
|
||||
if (segment === [self segmentCount] - 1)
|
||||
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
if (segment === [self segmentCount] - 1)
|
||||
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
|
||||
|
||||
return frame;
|
||||
}
|
||||
else if (aName.indexOf("divider-bezel") === 0)
|
||||
{
|
||||
@@ -620,48 +651,86 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
if ([self segmentCount] <= 0)
|
||||
return;
|
||||
|
||||
var themeState = _themeStates[0],
|
||||
isDisabled = [self hasThemeState:CPThemeStateDisabled],
|
||||
isControlSizeSmall = [self hasThemeState:CPThemeStateControlSizeSmall],
|
||||
isControlSizeMini = [self hasThemeState:CPThemeStateControlSizeMini];
|
||||
// Check for HUD state globally
|
||||
var isHUD = [self hasThemeState:CPThemeStateHUD];
|
||||
|
||||
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState;
|
||||
|
||||
if (isControlSizeSmall)
|
||||
themeState = themeState.and(CPThemeStateControlSizeSmall);
|
||||
else if (isControlSizeMini)
|
||||
themeState = themeState.and(CPThemeStateControlSizeMini);
|
||||
|
||||
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
|
||||
inState:themeState],
|
||||
|
||||
leftBezelView = [self layoutEphemeralSubviewNamed:@"left-segment-bezel"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[leftBezelView setBackgroundColor:leftCapColor];
|
||||
|
||||
var themeState = _themeStates[_themeStates.length - 1];
|
||||
|
||||
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState;
|
||||
|
||||
if (isControlSizeSmall)
|
||||
themeState = themeState.and(CPThemeStateControlSizeSmall);
|
||||
else if (isControlSizeMini)
|
||||
themeState = themeState.and(CPThemeStateControlSizeMini);
|
||||
|
||||
var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color"
|
||||
inState:themeState],
|
||||
|
||||
rightBezelView = [self layoutEphemeralSubviewNamed:@"right-segment-bezel"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[rightBezelView setBackgroundColor:rightCapColor];
|
||||
|
||||
for (var i = 0, count = _themeStates.length; i < count; i++)
|
||||
if ([[self actualTheme] isCSSBased])
|
||||
{
|
||||
var themeState = _themeStates[i];
|
||||
// CSS Styling
|
||||
|
||||
var isDisabled = [self hasThemeState:CPThemeStateDisabled],
|
||||
isControlSizeSmall = [self hasThemeState:CPThemeStateControlSizeSmall],
|
||||
isControlSizeMini = [self hasThemeState:CPThemeStateControlSizeMini],
|
||||
isKeyWindow = [self hasThemeState:CPThemeStateKeyWindow];
|
||||
|
||||
for (var i = 0, count = _themeStates.length; i < count; i++)
|
||||
{
|
||||
var themeState = _themeStates[i];
|
||||
|
||||
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState;
|
||||
|
||||
if (isControlSizeSmall)
|
||||
themeState = themeState.and(CPThemeStateControlSizeSmall);
|
||||
else if (isControlSizeMini)
|
||||
themeState = themeState.and(CPThemeStateControlSizeMini);
|
||||
|
||||
// Apply HUD state to lookup
|
||||
if (isHUD)
|
||||
themeState = themeState.and(CPThemeStateHUD);
|
||||
|
||||
themeState = isKeyWindow ? themeState.and(CPThemeStateKeyWindow) : themeState;
|
||||
|
||||
var bezelColor,
|
||||
segment = _segments[i],
|
||||
bezelView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil],
|
||||
contentView = [self layoutEphemeralSubviewNamed:@"segment-content-" + i
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:"segment-bezel-" + i];
|
||||
|
||||
if (i == 0)
|
||||
bezelColor = [self valueForThemeAttribute:@"left-segment-bezel-color" inState:themeState];
|
||||
else if (i < count - 1)
|
||||
bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color" inState:themeState];
|
||||
else
|
||||
bezelColor = [self valueForThemeAttribute:@"right-segment-bezel-color" inState:themeState];
|
||||
|
||||
[bezelView setBackgroundColor:bezelColor];
|
||||
|
||||
// Trick : Put selected segments over unselected ones to automaticaly manage borders
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
contentView._DOMElement.style.zIndex = ([segment selected] ? 1 : 0);
|
||||
bezelView._DOMElement.style.zIndex = ([segment selected] ? 1 : 0);
|
||||
}
|
||||
|
||||
[contentView setText:[segment label]];
|
||||
[contentView setImage:[segment image]];
|
||||
|
||||
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
|
||||
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:([segment enabled] ? themeState : themeState.and(CPThemeStateDisabled))]];
|
||||
[contentView setAlignment:[self valueForThemeAttribute:@"alignment" inState:themeState]];
|
||||
[contentView setVerticalAlignment:[self valueForThemeAttribute:@"vertical-alignment" inState:themeState]];
|
||||
[contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:themeState]];
|
||||
[contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:themeState]];
|
||||
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
|
||||
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
|
||||
|
||||
if ([segment image] && [segment label])
|
||||
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
|
||||
else if ([segment image])
|
||||
[contentView setImagePosition:CPImageOnly];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy (Canvas) Styling
|
||||
var themeState = _themeStates[0],
|
||||
isDisabled = [self hasThemeState:CPThemeStateDisabled],
|
||||
isControlSizeSmall = [self hasThemeState:CPThemeStateControlSizeSmall],
|
||||
isControlSizeMini = [self hasThemeState:CPThemeStateControlSizeMini];
|
||||
|
||||
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState;
|
||||
|
||||
@@ -670,59 +739,114 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
else if (isControlSizeMini)
|
||||
themeState = themeState.and(CPThemeStateControlSizeMini);
|
||||
|
||||
var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color"
|
||||
inState:themeState],
|
||||
// Apply HUD state to Left Cap
|
||||
if (isHUD)
|
||||
themeState = themeState.and(CPThemeStateHUD);
|
||||
|
||||
bezelView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
|
||||
inState:themeState],
|
||||
|
||||
[bezelView setBackgroundColor:bezelColor];
|
||||
leftBezelView = [self layoutEphemeralSubviewNamed:@"left-segment-bezel"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
// layout image/title views
|
||||
var segment = _segments[i],
|
||||
contentView = [self layoutEphemeralSubviewNamed:@"segment-content-" + i
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"segment-bezel-" + i];
|
||||
[leftBezelView setBackgroundColor:leftCapColor];
|
||||
|
||||
[contentView setText:[segment label]];
|
||||
[contentView setImage:[segment image]];
|
||||
var themeState = _themeStates[_themeStates.length - 1];
|
||||
|
||||
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
|
||||
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
|
||||
[contentView setAlignment:[self valueForThemeAttribute:@"alignment" inState:themeState]];
|
||||
[contentView setVerticalAlignment:[self valueForThemeAttribute:@"vertical-alignment" inState:themeState]];
|
||||
[contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:themeState]];
|
||||
[contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:themeState]];
|
||||
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
|
||||
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
|
||||
|
||||
if ([segment image] && [segment label])
|
||||
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
|
||||
else if ([segment image])
|
||||
[contentView setImagePosition:CPImageOnly];
|
||||
|
||||
if (i == count - 1)
|
||||
continue;
|
||||
|
||||
|
||||
var borderState = _themeStates[i].and(_themeStates[i + 1]);
|
||||
|
||||
borderState = isDisabled ? borderState.and(CPThemeStateDisabled) : borderState;
|
||||
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState;
|
||||
|
||||
if (isControlSizeSmall)
|
||||
borderState = borderState.and(CPThemeStateControlSizeSmall);
|
||||
themeState = themeState.and(CPThemeStateControlSizeSmall);
|
||||
else if (isControlSizeMini)
|
||||
borderState = borderState.and(CPThemeStateControlSizeMini);
|
||||
themeState = themeState.and(CPThemeStateControlSizeMini);
|
||||
|
||||
var borderColor = [self valueForThemeAttribute:@"divider-bezel-color"
|
||||
inState:borderState],
|
||||
// Apply HUD state to Right Cap
|
||||
if (isHUD)
|
||||
themeState = themeState.and(CPThemeStateHUD);
|
||||
|
||||
var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color"
|
||||
inState:themeState],
|
||||
|
||||
rightBezelView = [self layoutEphemeralSubviewNamed:@"right-segment-bezel"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[rightBezelView setBackgroundColor:rightCapColor];
|
||||
|
||||
for (var i = 0, count = _themeStates.length; i < count; i++)
|
||||
{
|
||||
var themeState = _themeStates[i];
|
||||
|
||||
themeState = isDisabled ? themeState.and(CPThemeStateDisabled) : themeState;
|
||||
|
||||
if (isControlSizeSmall)
|
||||
themeState = themeState.and(CPThemeStateControlSizeSmall);
|
||||
else if (isControlSizeMini)
|
||||
themeState = themeState.and(CPThemeStateControlSizeMini);
|
||||
|
||||
// Apply HUD state to Center Segments
|
||||
if (isHUD)
|
||||
themeState = themeState.and(CPThemeStateHUD);
|
||||
|
||||
var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color"
|
||||
inState:themeState],
|
||||
|
||||
bezelView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[bezelView setBackgroundColor:bezelColor];
|
||||
|
||||
// layout image/title views
|
||||
var segment = _segments[i],
|
||||
contentView = [self layoutEphemeralSubviewNamed:@"segment-content-" + i
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"segment-bezel-" + i];
|
||||
|
||||
[contentView setText:[segment label]];
|
||||
[contentView setImage:[segment image]];
|
||||
|
||||
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
|
||||
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
|
||||
[contentView setAlignment:[self valueForThemeAttribute:@"alignment" inState:themeState]];
|
||||
[contentView setVerticalAlignment:[self valueForThemeAttribute:@"vertical-alignment" inState:themeState]];
|
||||
[contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:themeState]];
|
||||
[contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:themeState]];
|
||||
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
|
||||
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
|
||||
|
||||
if ([segment image] && [segment label])
|
||||
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
|
||||
else if ([segment image])
|
||||
[contentView setImagePosition:CPImageOnly];
|
||||
|
||||
if (i == count - 1)
|
||||
continue;
|
||||
|
||||
|
||||
var borderState = _themeStates[i].and(_themeStates[i + 1]);
|
||||
|
||||
borderState = isDisabled ? borderState.and(CPThemeStateDisabled) : borderState;
|
||||
|
||||
if (isControlSizeSmall)
|
||||
borderState = borderState.and(CPThemeStateControlSizeSmall);
|
||||
else if (isControlSizeMini)
|
||||
borderState = borderState.and(CPThemeStateControlSizeMini);
|
||||
|
||||
// Apply HUD state to Dividers
|
||||
if (isHUD)
|
||||
borderState = borderState.and(CPThemeStateHUD);
|
||||
|
||||
var borderColor = [self valueForThemeAttribute:@"divider-bezel-color"
|
||||
inState:borderState],
|
||||
|
||||
borderView = [self layoutEphemeralSubviewNamed:"divider-bezel-" + i
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[borderView setBackgroundColor:borderColor];
|
||||
[borderView setBackgroundColor:borderColor];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -803,8 +927,10 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
label = [segment label],
|
||||
image = [segment image];
|
||||
|
||||
contentInsetWidth += ([[self actualTheme] isCSSBased] && ((aSegment == 0) || (aSegment == [self _indexOfLastSegment])) ? [self valueForThemeAttribute:@"divider-thickness" inState:themeState] : 0);
|
||||
|
||||
// add 1 pixel to account for possible fractional pixels at right edge
|
||||
width = (label ? [label sizeWithFont:[self font]].width + 1 : 4.0) + (image ? [image size].width : 0) + contentInsetWidth;
|
||||
width = CEIL((label ? [label sizeWithFont:[self font]].width + 1: 4.0) + (image ? [image size].width : 0) + contentInsetWidth);
|
||||
}
|
||||
|
||||
return CGRectMake(left, top, width, height);
|
||||
@@ -812,6 +938,14 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
- (CGRect)contentFrameForSegment:(unsigned)aSegment
|
||||
{
|
||||
if ([[self actualTheme] isCSSBased])
|
||||
{
|
||||
var bezelFrame = [self bezelFrameForSegment:aSegment],
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
||||
|
||||
return CGRectInsetByInset(bezelFrame, contentInset);
|
||||
}
|
||||
|
||||
var height = [self currentValueForThemeAttribute:@"min-size"].height,
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
width = CGRectGetWidth([self frameForSegment:aSegment]),
|
||||
@@ -874,68 +1008,64 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
var type = [anEvent type],
|
||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
switch (type)
|
||||
if (type == CPLeftMouseUp)
|
||||
{
|
||||
case CPLeftMouseUp:
|
||||
|
||||
if (_trackingSegment === CPNotFound)
|
||||
return;
|
||||
|
||||
if (_trackingSegment === [self testSegment:location])
|
||||
{
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||
{
|
||||
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
||||
|
||||
// With ANY, _selectedSegment means last pressed.
|
||||
_selectedSegment = _trackingSegment;
|
||||
}
|
||||
else
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||
{
|
||||
[self setSelected:NO forSegment:_trackingSegment];
|
||||
|
||||
_selectedSegment = CPNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = CPNotFound;
|
||||
|
||||
if (_trackingSegment == -1)
|
||||
return;
|
||||
|
||||
case CPLeftMouseDown:
|
||||
|
||||
var trackingSegment = [self testSegment:location];
|
||||
if (trackingSegment > CPNotFound && [self isEnabledForSegment:trackingSegment])
|
||||
if (_trackingSegment === [self testSegment:location])
|
||||
{
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||
{
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = trackingSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
||||
|
||||
// With ANY, _selectedSegment means last pressed.
|
||||
_selectedSegment = _trackingSegment;
|
||||
}
|
||||
else
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
|
||||
break;
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
case CPLeftMouseDragged:
|
||||
|
||||
if (_trackingSegment === CPNotFound)
|
||||
return;
|
||||
|
||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
||||
|
||||
if (highlighted != _trackingHighlighted)
|
||||
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||
{
|
||||
_trackingHighlighted = highlighted;
|
||||
[self setSelected:NO forSegment:_trackingSegment];
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
||||
_selectedSegment = CPNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
{
|
||||
var trackingSegment = [self testSegment:location];
|
||||
if (trackingSegment > -1 && [self isEnabledForSegment:trackingSegment])
|
||||
{
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = trackingSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
return;
|
||||
|
||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
||||
|
||||
if (highlighted != _trackingHighlighted)
|
||||
{
|
||||
_trackingHighlighted = highlighted;
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
||||
}
|
||||
}
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
|
||||
@@ -102,6 +102,7 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
|
||||
[self setWeight:CPLightShadow];
|
||||
|
||||
[self setHitTests:NO];
|
||||
[self setClipsToBounds:NO];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -169,7 +170,6 @@ CPThemeStateShadowViewHeavy = CPThemeState("shadowview-style-heavy");
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
}
|
||||
|
||||
|
||||
@@ -800,6 +800,9 @@ var CPThemeStatesForSplitViewDivider = @[@"dummy one as CPSplitViewDividerStyle
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
// Retrieve the current color based on the current theme state (Standard vs HUD)
|
||||
var currentDividerColor = [self dividerColor];
|
||||
|
||||
for (var i = 0, count = _arrangedSubviews.length, position = 0, origin; i < count; i++)
|
||||
{
|
||||
origin = CGPointMakeZero();
|
||||
@@ -814,6 +817,9 @@ var CPThemeStatesForSplitViewDivider = @[@"dummy one as CPSplitViewDividerStyle
|
||||
origin[_originComponent] = position;
|
||||
position += [_dividerSubviews[i] frame].size[_sizeComponent];
|
||||
|
||||
// Apply the color to the specific divider view
|
||||
[_dividerSubviews[i] setBackgroundColor:currentDividerColor];
|
||||
|
||||
[_dividerSubviews[i] setFrameOrigin:origin];
|
||||
}
|
||||
}
|
||||
|
||||
+67
-7
@@ -175,12 +175,53 @@
|
||||
[super setFrame:frame];
|
||||
}
|
||||
|
||||
- (void)setThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super setThemeState:aState];
|
||||
|
||||
// Force a layout update because the internal buttons (_buttonUp and _buttonDown)
|
||||
// rely on layoutSubviews to receive the new theme attributes (like HUD colors).
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)unsetThemeState:(CPThemeState)aState
|
||||
{
|
||||
[super unsetThemeState:aState];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var controlSizeThemeState = [self _controlSizeThemeState],
|
||||
aFrame = [self frame],
|
||||
upSize = [self valueForThemeAttribute:@"up-button-size" inState:controlSizeThemeState],
|
||||
isHUD = [self hasThemeState:CPThemeStateHUD],
|
||||
|
||||
// 1. Prepare Lookup States (To fetch the correct image/color from the Stepper's theme)
|
||||
normalLookupStates = [controlSizeThemeState, CPThemeStateBordered],
|
||||
disabledLookupStates = [controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled],
|
||||
highlightedLookupStates = [controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted],
|
||||
|
||||
// 2. Prepare Target States (To tell the child buttons when to use this image)
|
||||
normalTargetStates = [CPThemeStateBordered, CPButtonStateBezelStyleRoundRect],
|
||||
disabledTargetStates = [CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRoundRect],
|
||||
highlightedTargetStates = [CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRoundRect];
|
||||
|
||||
// If we are in HUD mode, add CPThemeStateHUD to both lookup and target arrays
|
||||
if (isHUD)
|
||||
{
|
||||
// Lookup: Ask theme for "HUD" version of the stepper arrows
|
||||
normalLookupStates.push(CPThemeStateHUD);
|
||||
disabledLookupStates.push(CPThemeStateHUD);
|
||||
highlightedLookupStates.push(CPThemeStateHUD);
|
||||
|
||||
// Target: Tell the child buttons "Use this when you are in HUD state"
|
||||
normalTargetStates.push(CPThemeStateHUD);
|
||||
disabledTargetStates.push(CPThemeStateHUD);
|
||||
highlightedTargetStates.push(CPThemeStateHUD);
|
||||
}
|
||||
|
||||
var upSize = [self valueForThemeAttribute:@"up-button-size" inState:controlSizeThemeState],
|
||||
downSize = [self valueForThemeAttribute:@"down-button-size" inState:controlSizeThemeState],
|
||||
upFrame = CGRectMake(0, 0, upSize.width, upSize.height),
|
||||
downFrame = CGRectMake(0, upSize.height, downSize.width, downSize.height);
|
||||
@@ -188,12 +229,31 @@
|
||||
[_buttonUp setFrame:upFrame];
|
||||
[_buttonDown setFrame:downFrame];
|
||||
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPButtonStateBezelStyleRoundRect]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRoundRect]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRoundRect]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPButtonStateBezelStyleRoundRect]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRoundRect]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRoundRect]];
|
||||
// Apply Up Button Attributes
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:normalLookupStates]
|
||||
forThemeAttribute:@"bezel-color"
|
||||
inStates:normalTargetStates];
|
||||
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:disabledLookupStates]
|
||||
forThemeAttribute:@"bezel-color"
|
||||
inStates:disabledTargetStates];
|
||||
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:highlightedLookupStates]
|
||||
forThemeAttribute:@"bezel-color"
|
||||
inStates:highlightedTargetStates];
|
||||
|
||||
// Apply Down Button Attributes
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:normalLookupStates]
|
||||
forThemeAttribute:@"bezel-color"
|
||||
inStates:normalTargetStates];
|
||||
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:disabledLookupStates]
|
||||
forThemeAttribute:@"bezel-color"
|
||||
inStates:disabledTargetStates];
|
||||
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:highlightedLookupStates]
|
||||
forThemeAttribute:@"bezel-color"
|
||||
inStates:highlightedTargetStates];
|
||||
}
|
||||
|
||||
- (void)_sizeToFit
|
||||
|
||||
+1
-1
@@ -117,7 +117,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
var height = [_tabs valueForThemeAttribute:@"min-size"].height;
|
||||
[_tabs setFrameSize:CGSizeMake(0, height)];
|
||||
|
||||
_box = [[_CPTabViewBox alloc] initWithFrame:[self bounds]];
|
||||
_box = [[_CPTabViewBox alloc] initWithFrame:[self bounds]];
|
||||
[_box setTabView:self];
|
||||
[_box setContentInset:[self currentValueForThemeAttribute:@"box-content-inset"]];
|
||||
[_box setContentViewMargins:CGSizeMakeZero()];
|
||||
|
||||
+231
-23
@@ -25,6 +25,10 @@
|
||||
@import "CPCursor.j"
|
||||
@import "_CPImageAndTextView.j"
|
||||
@import "CPTrackingArea.j"
|
||||
@import "CPAnimationContext.j"
|
||||
@import "CPViewAnimator.j"
|
||||
@import "CPScrollView.j"
|
||||
@import <Foundation/CPGeometry.j>
|
||||
|
||||
@class CPTableView
|
||||
|
||||
@@ -50,10 +54,16 @@
|
||||
@"text-color": [CPNull null],
|
||||
@"font": [CPNull null],
|
||||
@"text-shadow-color": [CPNull null],
|
||||
@"text-shadow-offset": CGSizeMakeZero()
|
||||
@"text-shadow-offset": CGSizeMakeZero(),
|
||||
@"dont-draw-separator": NO
|
||||
};
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
@@ -66,18 +76,22 @@
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
||||
|
||||
var inset = [self valueForThemeAttribute:@"text-inset"];
|
||||
|
||||
_textField = [[_CPImageAndTextView alloc] initWithFrame:
|
||||
CGRectMake(5.0, 0.0, CGRectGetWidth([self bounds]) - 10.0, CGRectGetHeight([self bounds]))];
|
||||
CGRectMake(inset.left, inset.top, CGRectGetWidth([self bounds]) - (inset.left + inset.right), CGRectGetHeight([self bounds]) - (inset.top + inset.bottom))];
|
||||
|
||||
[_textField setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
[_textField setLineBreakMode:CPLineBreakByTruncatingTail];
|
||||
[_textField setTextColor:[CPColor colorWithRed:51.0 / 255.0 green:51.0 / 255.0 blue:51.0 / 255.0 alpha:1.0]];
|
||||
[_textField setFont:[CPFont boldSystemFontOfSize:12.0]];
|
||||
[_textField setAlignment:CPLeftTextAlignment];
|
||||
[_textField setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode"]];
|
||||
[_textField setTextColor:[self valueForThemeAttribute:@"text-color"]];
|
||||
[_textField setFont:[self valueForThemeAttribute:@"font"]];
|
||||
[_textField setAlignment:[self valueForThemeAttribute:@"text-alignment"]];
|
||||
[_textField setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_textField setTextShadowColor:[CPColor whiteColor]];
|
||||
[_textField setTextShadowOffset:CGSizeMake(0,1)];
|
||||
[_textField setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color"]];
|
||||
[_textField setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset"]];
|
||||
|
||||
[self addSubview:_textField];
|
||||
}
|
||||
@@ -188,6 +202,9 @@
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
if ([self valueForThemeAttribute:@"dont-draw-separator"])
|
||||
return;
|
||||
|
||||
var bounds = [self bounds];
|
||||
|
||||
if (!CGRectIntersectsRect(aRect, bounds))
|
||||
@@ -223,7 +240,8 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
|
||||
[self _init];
|
||||
[self _setIndicatorImage:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewImageKey]];
|
||||
[self setStringValue:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewStringValueKey]];
|
||||
[self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
|
||||
// FIXME: pourquoi dans actif, font=null ?
|
||||
// [self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -257,11 +275,13 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
|
||||
BOOL _isResizing;
|
||||
BOOL _isDragging;
|
||||
BOOL _isAnimating;
|
||||
BOOL _canDragColumn;
|
||||
|
||||
CPView _columnDragView;
|
||||
CPView _columnDragHeaderView;
|
||||
CPView _columnDragClipView;
|
||||
CPScrollView _columnDragScrollView;
|
||||
|
||||
float _columnOldWidth;
|
||||
|
||||
@@ -278,7 +298,9 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
return @{
|
||||
@"background-color": [CPNull null],
|
||||
@"divider-color": [CPColor grayColor],
|
||||
@"divider-thickness": 1.0
|
||||
@"divider-thickness": 1.0,
|
||||
@"swap-animation": [CPNull null],
|
||||
@"return-animation": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -340,7 +362,24 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
|
||||
- (CPInteger)columnAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
return [_tableView columnAtPoint:aPoint];
|
||||
var tableView = [self tableView],
|
||||
tableColumns = [tableView tableColumns],
|
||||
count = [tableColumns count],
|
||||
bounds = [self bounds],
|
||||
// Create a point that keeps the X position but forces Y to be safely
|
||||
// in the middle of the header view.
|
||||
constrainedPoint = CGPointMake(aPoint.x, CGRectGetMidY(bounds));
|
||||
|
||||
// Iterate through columns to find which one contains the constrained X coordinate
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
// headerRectOfColumn: is a utility method defined in CPTableHeaderView
|
||||
// that handles the coordinate conversion from the table view relative to the header.
|
||||
if (CGRectContainsPoint([self headerRectOfColumn:i], constrainedPoint))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (CGRect)headerRectOfColumn:(CPInteger)aColumnIndex
|
||||
@@ -365,6 +404,8 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
||||
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
count = [tableColumns count];
|
||||
|
||||
@@ -442,6 +483,14 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
}
|
||||
else if (_isDragging)
|
||||
{
|
||||
// First, we have to avoid a running condition where user stops dragging while a swap animation is running
|
||||
if (_isAnimating)
|
||||
{
|
||||
[CPTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(_retry_mouseUp:) userInfo:theEvent repeats:NO];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[self _stopDraggingTableColumn:_activeColumn];
|
||||
}
|
||||
else if (_activeColumn != -1)
|
||||
@@ -458,6 +507,11 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
_activeColumn = -1;
|
||||
}
|
||||
|
||||
- (void)_retry_mouseUp:(CPTimer)aTimer
|
||||
{
|
||||
[self mouseUp:[aTimer userInfo]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTableHeaderView (CPTrackingArea)
|
||||
@@ -562,7 +616,21 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
pressure:[theEvent pressure]];
|
||||
|
||||
[self autoscroll:constrainedEvent];
|
||||
|
||||
var contentView = [_tableView superview],
|
||||
boundsOriginBefore = [contentView boundsOrigin];
|
||||
|
||||
[_tableView autoscroll:constrainedEvent];
|
||||
|
||||
var boundsOriginAfter = [contentView boundsOrigin],
|
||||
deltaX = boundsOriginAfter.x - boundsOriginBefore.x;
|
||||
|
||||
if (_isDragging)
|
||||
{
|
||||
var dragContentView = [_columnDragScrollView contentView],
|
||||
dragContentBoundsOrigin = [dragContentView boundsOrigin];
|
||||
[dragContentView setBoundsOrigin:CGPointMake(dragContentBoundsOrigin.x + deltaX, dragContentBoundsOrigin.y)];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGRect)_headerRectOfLastVisibleColumn
|
||||
@@ -584,16 +652,91 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
- (CGPoint)_constrainDragPoint:(CGPoint)aPoint
|
||||
{
|
||||
// This effectively clamps the value between the minimum and maximum
|
||||
var visibleRect = [_tableView visibleRect],
|
||||
lastColumnRect = [self _headerRectOfLastVisibleColumn],
|
||||
activeColumnRect = [self headerRectOfColumn:_activeColumn],
|
||||
maxX = CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect) - CGRectGetMinX(visibleRect),
|
||||
point = CGPointMake(MAX(MIN(aPoint.x, maxX), -CGRectGetMinX(visibleRect)), aPoint.y);
|
||||
var tableFrame = [_tableView frame],
|
||||
dragFrame = [_columnDragView frame],
|
||||
maxX = tableFrame.size.width - dragFrame.size.width,
|
||||
point = CGPointMake(MAX(MIN(aPoint.x, maxX),0), aPoint.y);
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
- (void)_moveColumn:(CPInteger)aFromIndex toColumn:(CPInteger)aToIndex
|
||||
{
|
||||
if (_isAnimating)
|
||||
return;
|
||||
|
||||
var swapAnimation = [self currentValueForThemeAttribute:@"swap-animation"];
|
||||
|
||||
if (swapAnimation)
|
||||
{
|
||||
_isAnimating = YES;
|
||||
|
||||
// There's a theme defined animation function, just use it
|
||||
objj_eval("("+swapAnimation+")")(self, aFromIndex, aToIndex, _columnDragClipView, _columnDragView);
|
||||
|
||||
// var animatedColumn = [[_tableView tableColumns] objectAtIndex:aToIndex],
|
||||
// animatedHeader = [animatedColumn headerView],
|
||||
// animatedHeaderOrigin = [animatedHeader frameOrigin],
|
||||
//
|
||||
// destinationX,
|
||||
// draggedHeader = [[[_tableView tableColumns] objectAtIndex:aFromIndex] headerView],
|
||||
//
|
||||
// scrollView = [self enclosingScrollView],
|
||||
// animatedView = [_tableView _animationViewForColumn:aToIndex],
|
||||
// animatedOrigin = [animatedView frameOrigin];
|
||||
//
|
||||
// [_columnDragClipView addSubview:animatedView positioned:CPWindowBelow relativeTo:_columnDragView];
|
||||
//
|
||||
// [[animatedHeader subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:YES];
|
||||
// [animatedHeader setThemeState:CPThemeStateVertical];
|
||||
//
|
||||
// if (aFromIndex < aToIndex)
|
||||
// destinationX = CGRectGetMinX([_tableView rectOfColumn:aFromIndex]);
|
||||
// else
|
||||
// destinationX = animatedOrigin.x + CGRectGetWidth([_tableView rectOfColumn:aFromIndex]);
|
||||
//
|
||||
// [CPAnimationContext beginGrouping];
|
||||
//
|
||||
// var context = [CPAnimationContext currentContext];
|
||||
//
|
||||
// [context setDuration:0.15];
|
||||
// [context setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
|
||||
// [context setCompletionHandler:function() {
|
||||
// [animatedView removeFromSuperview];
|
||||
//
|
||||
// [self _finalize_moveColumn:aFromIndex toColumn:aToIndex];
|
||||
//
|
||||
// [animatedHeader unsetThemeState:CPThemeStateVertical];
|
||||
// [[animatedHeader subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:NO];
|
||||
//
|
||||
// if ([animatedView isSelected])
|
||||
// {
|
||||
// [animatedHeader setThemeState:CPThemeStateSelected];
|
||||
//
|
||||
// // We have to reselect the animated column
|
||||
// [[_tableView selectedColumnIndexes] addIndex:aFromIndex];
|
||||
// }
|
||||
//
|
||||
// // Reload animated column
|
||||
// var columnVisRect = CGRectIntersection([_tableView rectOfColumn:aFromIndex], [_tableView visibleRect]),
|
||||
// rowsIndexes = [CPIndexSet indexSetWithIndexesInRange:[_tableView rowsInRect:columnVisRect]],
|
||||
// columnsIndexes = [CPIndexSet indexSetWithIndex:aFromIndex];
|
||||
//
|
||||
// [_tableView _loadDataViewsInRows:rowsIndexes columns:columnsIndexes];
|
||||
// [_tableView _layoutViewsForRowIndexes:rowsIndexes columnIndexes:columnsIndexes];
|
||||
//
|
||||
// [_tableView._tableDrawView displayRect:columnVisRect];
|
||||
// }];
|
||||
//
|
||||
// [[animatedView animator] setFrameOrigin:CGPointMake(destinationX, animatedOrigin.y)];
|
||||
//
|
||||
// [CPAnimationContext endGrouping];
|
||||
}
|
||||
else
|
||||
[self _finalize_moveColumn:aFromIndex toColumn:aToIndex];
|
||||
}
|
||||
|
||||
- (void)_finalize_moveColumn:(CPInteger)aFromIndex toColumn:(CPInteger)aToIndex
|
||||
{
|
||||
[_tableView moveColumn:aFromIndex toColumn:aToIndex];
|
||||
_activeColumn = aToIndex;
|
||||
@@ -602,6 +745,8 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
[_tableView _setDraggedColumn:_activeColumn];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
_isAnimating = NO;
|
||||
}
|
||||
|
||||
- (BOOL)isDragging
|
||||
@@ -618,17 +763,30 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
// Create a new clip view for the drag view that clips to the header + visible content
|
||||
var headerHeight = CGRectGetHeight([self frame]),
|
||||
scrollView = [self enclosingScrollView],
|
||||
contentFrame = [[scrollView contentView] frame];
|
||||
contentFrame = [[scrollView contentView] frame],
|
||||
contentBounds = [[scrollView contentView] bounds];
|
||||
|
||||
contentFrame.origin.y -= headerHeight;
|
||||
contentFrame.size.height += headerHeight;
|
||||
|
||||
_columnDragClipView = [[CPView alloc] initWithFrame:contentFrame];
|
||||
_columnDragScrollView = [[CPScrollView alloc] initWithFrame:contentFrame];
|
||||
|
||||
[_columnDragScrollView setHasHorizontalScroller:NO];
|
||||
[_columnDragScrollView setHasVerticalScroller:NO];
|
||||
[_columnDragScrollView setBorderType:CPNoBorder];
|
||||
|
||||
var tableFrame = [_tableView frame],
|
||||
clipFrame = CGRectMake(0, 0, tableFrame.size.width, contentFrame.size.height);
|
||||
|
||||
_columnDragClipView = [[CPView alloc] initWithFrame:clipFrame];
|
||||
|
||||
[_columnDragClipView addSubview:_columnDragView];
|
||||
|
||||
[_columnDragScrollView setDocumentView:_columnDragClipView];
|
||||
[[_columnDragScrollView contentView] setBoundsOrigin:CGPointMake(contentBounds.origin.x, 0)];
|
||||
|
||||
// Insert the clip view above the table header (and content)
|
||||
[scrollView addSubview:_columnDragClipView positioned:CPWindowAbove relativeTo:self];
|
||||
[scrollView addSubview:_columnDragScrollView positioned:CPWindowAbove relativeTo:self];
|
||||
|
||||
// Hide the underlying column header subviews, we just want to draw the chrome
|
||||
var headerView = [[[_tableView tableColumns] objectAtIndex:aColumnIndex] headerView];
|
||||
@@ -638,6 +796,9 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
// The underlying column header shows normal state
|
||||
[headerView unsetThemeStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
|
||||
// FIXME: Just a little hack to get a special background (using an unused theme state)
|
||||
[headerView setThemeState:CPThemeStateVertical];
|
||||
|
||||
// Keep track of the location within the column header where the original mousedown occurred
|
||||
_columnDragHeaderView = [_columnDragView viewWithTag:CPTableHeaderViewDragColumnHeaderTag];
|
||||
|
||||
@@ -692,10 +853,43 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
}
|
||||
|
||||
- (void)_stopDraggingTableColumn:(CPInteger)aColumnIndex
|
||||
{
|
||||
var returnAnimation = [self currentValueForThemeAttribute:@"return-animation"];
|
||||
|
||||
if (returnAnimation)
|
||||
{
|
||||
_isAnimating = YES;
|
||||
|
||||
// There's a theme defined animation function, just use it
|
||||
objj_eval("("+returnAnimation+")")(self, aColumnIndex, _columnDragView);
|
||||
|
||||
// var animatedColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
|
||||
// animatedHeader = [animatedColumn headerView],
|
||||
// animatedHeaderOrigin = [animatedHeader frameOrigin];
|
||||
//
|
||||
// [CPAnimationContext beginGrouping];
|
||||
//
|
||||
// var context = [CPAnimationContext currentContext];
|
||||
//
|
||||
// [context setDuration:0.15];
|
||||
// [context setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
|
||||
// [context setCompletionHandler:function() {
|
||||
//
|
||||
// [self _finalize_stopDraggingTableColumn:aColumnIndex];
|
||||
// }];
|
||||
//
|
||||
// [[_columnDragView animator] setFrameOrigin:CGPointMake(animatedHeaderOrigin.x, 0)];
|
||||
//
|
||||
// [CPAnimationContext endGrouping];
|
||||
}
|
||||
else
|
||||
[self _finalize_stopDraggingTableColumn:aColumnIndex];
|
||||
}
|
||||
|
||||
- (void)_finalize_stopDraggingTableColumn:(CPInteger)aColumnIndex
|
||||
{
|
||||
_isDragging = NO;
|
||||
|
||||
[_columnDragClipView removeFromSuperview];
|
||||
[_tableView _setDraggedColumn:-1];
|
||||
|
||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
|
||||
@@ -703,16 +897,30 @@ var CPTableHeaderViewResizeZone = 3.0,
|
||||
|
||||
[[headerView subviews] makeObjectsPerformSelector:@selector(setHidden:) withObject:NO];
|
||||
|
||||
// Restore headerView background
|
||||
[headerView unsetThemeState:CPThemeStateVertical];
|
||||
|
||||
if (_tableView._draggedColumnIsSelected)
|
||||
[headerView setThemeState:CPThemeStateSelected];
|
||||
|
||||
[_tableView _reloadDataViews];
|
||||
[[_tableView headerView] setNeedsLayout];
|
||||
// Reload animated column
|
||||
var columnVisRect = CGRectIntersection([_tableView rectOfColumn:aColumnIndex], [_tableView visibleRect]),
|
||||
rowsIndexes = [CPIndexSet indexSetWithIndexesInRange:[_tableView rowsInRect:columnVisRect]],
|
||||
columnsIndexes = [CPIndexSet indexSetWithIndex:aColumnIndex];
|
||||
|
||||
[[CPCursor arrowCursor] set];
|
||||
[_tableView _loadDataViewsInRows:rowsIndexes columns:columnsIndexes];
|
||||
[_tableView _layoutViewsForRowIndexes:rowsIndexes columnIndexes:columnsIndexes];
|
||||
[_tableView _updateDataViewsFocusState];
|
||||
[_tableView._tableDrawView displayRect:columnVisRect];
|
||||
|
||||
[[CPCursor arrowCursor] set]; // FIXME: retirer ?
|
||||
[self updateTrackingAreas];
|
||||
|
||||
[_columnDragScrollView removeFromSuperview];
|
||||
|
||||
[_tableView _sendDelegateDidDragTableColumn:tableColumn];
|
||||
|
||||
_isAnimating = NO;
|
||||
}
|
||||
|
||||
- (BOOL)_shouldResizeTableColumn:(CPInteger)aColumnIndex at:(CGPoint)aPoint
|
||||
|
||||
+470
-464
File diff suppressed because it is too large.
Load diff
+17
-12
@@ -827,33 +827,27 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
verticalAlign = [self currentValueForThemeAttribute:"vertical-alignment"],
|
||||
left = CGRectGetMinX(contentRect);
|
||||
|
||||
// If the browser has a built in left padding, compensate for it. We need the input text to be exactly on top of the original text.
|
||||
if (CPFeatureIsCompatible(CPInput1PxLeftPadding))
|
||||
left -= 1;
|
||||
|
||||
switch (verticalAlign)
|
||||
{
|
||||
case CPTopVerticalTextAlignment:
|
||||
var topPoint = CGRectGetMinY(contentRect) + "px";
|
||||
var topPoint = CEIL(CGRectGetMinY(contentRect)) + "px";
|
||||
break;
|
||||
|
||||
case CPCenterVerticalTextAlignment:
|
||||
var topPoint = (CGRectGetMidY(contentRect) - (lineHeight / 2)) + "px";
|
||||
var topPoint = CEIL((CGRectGetMidY(contentRect) - (lineHeight / 2))) + "px";
|
||||
break;
|
||||
|
||||
case CPBottomVerticalTextAlignment:
|
||||
var topPoint = (CGRectGetMaxY(contentRect) - lineHeight) + "px";
|
||||
var topPoint = CEIL((CGRectGetMaxY(contentRect) - lineHeight)) + "px";
|
||||
break;
|
||||
|
||||
default:
|
||||
var topPoint = CGRectGetMinY(contentRect) + "px";
|
||||
var topPoint = CEIL(CGRectGetMinY(contentRect)) + "px";
|
||||
break;
|
||||
}
|
||||
|
||||
if ([self hasThemeState:CPTextFieldStatePlaceholder])
|
||||
element.style.color = [[self valueForThemeAttribute:@"text-color" inState:CPTextFieldStatePlaceholder] cssString];
|
||||
else
|
||||
element.style.color = [[self valueForThemeAttribute:@"text-color" inState:CPThemeStateEditing] cssString];
|
||||
// Use currentValueForThemeAttribute to respect all current states (HUD, Placeholder, Editing, etc.)
|
||||
element.style.color = [[self currentValueForThemeAttribute:@"text-color"] cssString];
|
||||
|
||||
switch ([self alignment])
|
||||
{
|
||||
@@ -1362,6 +1356,17 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[super textDidChange:note];
|
||||
}
|
||||
|
||||
- (void)validateEditing
|
||||
{
|
||||
if (!CPDOMAvailable)
|
||||
return;
|
||||
|
||||
var element = [self _inputElement];
|
||||
|
||||
if (element)
|
||||
[self _setStringValue:element.value isNewValue:YES errorDescription:nil];
|
||||
}
|
||||
|
||||
- (void)textDidBeginEditing:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
|
||||
@@ -531,6 +531,64 @@ var CPRulerViewMinimumLabelSpacing = 40.0,
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
// MARK: HUD Appearance
|
||||
|
||||
/*!
|
||||
Returns \c YES if the ruler is drawn with the dark HUD appearance.
|
||||
*/
|
||||
- (BOOL)isHUD
|
||||
{
|
||||
return [self hasThemeState:CPThemeStateHUD];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (BOOL)setThemeState:(ThemeState)aState
|
||||
{
|
||||
var result = [super setThemeState:aState];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (BOOL)unsetThemeState:(ThemeState)aState
|
||||
{
|
||||
var result = [super unsetThemeState:aState];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (CPColor)_backgroundColorForDrawing
|
||||
{
|
||||
return [CPColor colorWithWhite:([self isHUD] ? 0.18 : 0.96) alpha:1.0];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (CPColor)_edgeColorForDrawing
|
||||
{
|
||||
return [CPColor colorWithWhite:([self isHUD] ? 0.10 : 0.75) alpha:1.0];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (CPColor)_hashMarkColorForDrawing
|
||||
{
|
||||
return [CPColor colorWithWhite:([self isHUD] ? 0.60 : 0.55) alpha:1.0];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (CPColor)_labelColorForDrawing
|
||||
{
|
||||
return [CPColor colorWithWhite:([self isHUD] ? 0.75 : 0.55) alpha:1.0];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (CPColor)_rulerlineColorForDrawing
|
||||
{
|
||||
return [CPColor colorWithWhite:([self isHUD] ? 0.85 : 0.3) alpha:1.0];
|
||||
}
|
||||
|
||||
// MARK: Drawing
|
||||
|
||||
/*!
|
||||
@@ -542,11 +600,11 @@ var CPRulerViewMinimumLabelSpacing = 40.0,
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
|
||||
CGContextSetFillColor(context, [CPColor colorWithWhite:0.96 alpha:1.0]);
|
||||
CGContextSetFillColor(context, [self _backgroundColorForDrawing]);
|
||||
CGContextFillRect(context, bounds);
|
||||
|
||||
// The edge next to the scroll view's content.
|
||||
CGContextSetFillColor(context, [CPColor colorWithWhite:0.75 alpha:1.0]);
|
||||
CGContextSetFillColor(context, [self _edgeColorForDrawing]);
|
||||
|
||||
if (_orientation === CPHorizontalRuler)
|
||||
CGContextFillRect(context, CGRectMake(CGRectGetMinX(bounds), CGRectGetMaxY(bounds) - 1.0, CGRectGetWidth(bounds), 1.0));
|
||||
@@ -577,9 +635,11 @@ var CPRulerViewMinimumLabelSpacing = 40.0,
|
||||
smallestStep = _hashMarkSteps.length ? _hashMarkSteps[_hashMarkSteps.length - 1] : _labelStep,
|
||||
stepPoints = smallestStep * unit.factor,
|
||||
firstIndex = Math.floor((firstPoint - zero) / stepPoints),
|
||||
lastIndex = Math.ceil((lastPoint - zero) / stepPoints);
|
||||
lastIndex = Math.ceil((lastPoint - zero) / stepPoints),
|
||||
hashMarkColor = [self _hashMarkColorForDrawing],
|
||||
labelColor = [self _labelColorForDrawing];
|
||||
|
||||
CGContextSetFillColor(context, [CPColor colorWithWhite:0.55 alpha:1.0]);
|
||||
CGContextSetFillColor(context, hashMarkColor);
|
||||
CGContextSelectFont(context, [CPFont systemFontOfSize:8.0]);
|
||||
|
||||
for (var index = firstIndex; index <= lastIndex; index++)
|
||||
@@ -598,11 +658,15 @@ var CPRulerViewMinimumLabelSpacing = 40.0,
|
||||
|
||||
var label = [self _labelForValue:value];
|
||||
|
||||
CGContextSetFillColor(context, labelColor);
|
||||
|
||||
// Labels sit in the half of the rule area away from the baseline, clear of markers that reach below it.
|
||||
if (isHorizontal)
|
||||
CGContextShowTextAtPoint(context, ROUND(point) + 2.0, ruleEdge - 4.5, label);
|
||||
else
|
||||
CGContextShowTextAtPoint(context, ruleEdge - _ruleThickness + 1.0, ROUND(point) + 6.0, label);
|
||||
|
||||
CGContextSetFillColor(context, hashMarkColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,7 +699,7 @@ var CPRulerViewMinimumLabelSpacing = 40.0,
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
|
||||
CGContextSetFillColor(context, [CPColor colorWithWhite:0.3 alpha:1.0]);
|
||||
CGContextSetFillColor(context, [self _rulerlineColorForDrawing]);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
|
||||
@@ -1084,6 +1084,12 @@ var kDelegateRespondsTo_textShouldBeginEditing
|
||||
- (void)drawInsertionPointInRect:(CGRect)aRect color:(CPColor)aColor turnedOn:(BOOL)flag
|
||||
{
|
||||
[_caret setRect:aRect];
|
||||
|
||||
if (CPDOMAvailable)
|
||||
{
|
||||
_caret._caretDOM.style.backgroundColor = [_insertionPointColor cssString] || "black";
|
||||
}
|
||||
|
||||
[_caret setVisibility:flag stop:NO];
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -814,10 +814,12 @@ CPThemeStateKeyWindow = CPThemeState("keyWindow");
|
||||
CPThemeStateControlSizeRegular = CPThemeState("controlSizeRegular");
|
||||
CPThemeStateControlSizeSmall = CPThemeState("controlSizeSmall");
|
||||
CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
CPThemeStateControlSizeLarge = CPThemeState("controlSizeLarge");
|
||||
CPThemeStateAlternateState = CPThemeState("alternate");
|
||||
CPThemeStateComposedControl = CPThemeState("composed");
|
||||
|
||||
CPThemeStateWindowsPlatform = CPThemeState("windowsPlatform");
|
||||
CPThemeStateNormalString = String(CPThemeStateNormal);
|
||||
CPThemeStateHUD = CPThemeState("hud");
|
||||
|
||||
|
||||
@implementation _CPThemeAttribute : CPObject
|
||||
|
||||
+34
-2
@@ -328,8 +328,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
{
|
||||
return @{
|
||||
@"css-based": NO,
|
||||
@"dynamic-set": [CPNull null],
|
||||
@"nib2cib-adjustment-frame": CGRectMakeZero()
|
||||
@"nib2cib-adjustment-frame": [CPNull null],
|
||||
@"direct-nib2cib-adjustment": NO,
|
||||
@"dynamic-set": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -929,6 +930,13 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
*/
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
var window = [self window];
|
||||
|
||||
// If the view is inside a window with the HUD style mask, turn on the HUD state
|
||||
if (window && ([window styleMask] & CPHUDBackgroundWindowMask))
|
||||
[self setThemeState:CPThemeStateHUD];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateHUD];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -4203,3 +4211,27 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView)
|
||||
|
||||
return transform;
|
||||
};
|
||||
|
||||
@implementation CPView (ThemingAdditions)
|
||||
|
||||
- (void)_setThemeStateRecursively:(ThemeState)aState
|
||||
{
|
||||
[_subviews makeObjectsPerformSelector:@selector(_setThemeStateRecursively:) withObject:aState];
|
||||
|
||||
[_subviews enumerateObjectsUsingBlock:function(view, idx, stop)
|
||||
{
|
||||
[view setThemeState:aState];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_unsetThemeStateRecursively:(ThemeState)aState
|
||||
{
|
||||
[_subviews makeObjectsPerformSelector:@selector(_unsetThemeStateRecursively:) withObject:aState];
|
||||
|
||||
[_subviews enumerateObjectsUsingBlock:function(view, idx, stop)
|
||||
{
|
||||
[view unsetThemeState:aState];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
+34
-11
@@ -28,6 +28,8 @@ CPViewAnimationTargetKey = @"CPViewAnimationTargetKey";
|
||||
CPViewAnimationStartFrameKey = @"CPViewAnimationStartFrameKey";
|
||||
CPViewAnimationEndFrameKey = @"CPViewAnimationEndFrameKey";
|
||||
CPViewAnimationEffectKey = @"CPViewAnimationEffectKey";
|
||||
CPViewAnimationStartOpacityKey = @"CPViewAnimationStartOpacityKey";
|
||||
CPViewAnimationEndOpacityKey = @"CPViewAnimationEndOpacityKey";
|
||||
|
||||
CPViewAnimationFadeInEffect = @"CPViewAnimationFadeInEffect";
|
||||
CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
||||
@@ -132,12 +134,24 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
||||
|
||||
[view setFrame:intermediateFrame];
|
||||
|
||||
// Update the view's alpha value
|
||||
var effect = [self _effect:dictionary];
|
||||
if (effect === CPViewAnimationFadeInEffect)
|
||||
[view setAlphaValue:1.0 * value];
|
||||
else if (effect === CPViewAnimationFadeOutEffect)
|
||||
[view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * value];
|
||||
// Check for explicit Opacity keys first
|
||||
var startOpacity = [dictionary objectForKey:CPViewAnimationStartOpacityKey],
|
||||
endOpacity = [dictionary objectForKey:CPViewAnimationEndOpacityKey];
|
||||
|
||||
if (startOpacity != nil && endOpacity != nil)
|
||||
{
|
||||
// Interpolate alpha
|
||||
[view setAlphaValue:startOpacity + (endOpacity - startOpacity) * value];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to legacy Effect keys
|
||||
var effect = [self _effect:dictionary];
|
||||
if (effect === CPViewAnimationFadeInEffect)
|
||||
[view setAlphaValue:1.0 * value];
|
||||
else if (effect === CPViewAnimationFadeOutEffect)
|
||||
[view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * value];
|
||||
}
|
||||
|
||||
if (progress === 1.0)
|
||||
[self _targetView:view setHidden:CGRectIsEmpty(endFrame) || [view alphaValue] === 0.0];
|
||||
@@ -155,11 +169,20 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
||||
|
||||
[view setFrame:endFrame];
|
||||
|
||||
var effect = [self _effect:dictionary];
|
||||
if (effect === CPViewAnimationFadeInEffect)
|
||||
[view setAlphaValue:1.0];
|
||||
else if (effect === CPViewAnimationFadeOutEffect)
|
||||
[view setAlphaValue:0.0];
|
||||
var endOpacity = [dictionary objectForKey:CPViewAnimationEndOpacityKey];
|
||||
|
||||
if (endOpacity != nil)
|
||||
{
|
||||
[view setAlphaValue:endOpacity];
|
||||
}
|
||||
else
|
||||
{
|
||||
var effect = [self _effect:dictionary];
|
||||
if (effect === CPViewAnimationFadeInEffect)
|
||||
[view setAlphaValue:1.0];
|
||||
else if (effect === CPViewAnimationFadeOutEffect)
|
||||
[view setAlphaValue:0.0];
|
||||
}
|
||||
|
||||
[self _targetView:view setHidden:CGRectIsEmpty(endFrame) || [view alphaValue] === 0.0];
|
||||
}
|
||||
|
||||
@@ -979,6 +979,9 @@ CPTexturedBackgroundWindowMask
|
||||
- (void)orderFront:(id)aSender
|
||||
{
|
||||
[self orderWindow:CPWindowAbove relativeTo:0];
|
||||
|
||||
if (_styleMask & CPHUDBackgroundWindowMask)
|
||||
[_contentView _setThemeStateRecursively:CPThemeStateHUD];
|
||||
}
|
||||
|
||||
- (void)_orderFront
|
||||
@@ -2611,6 +2614,10 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillCloseNotification object:self];
|
||||
|
||||
// Give a chance to the _windowView to do things before closing (for example, remove an observer)
|
||||
if ([_windowView respondsToSelector:@selector(_close)])
|
||||
[_windowView _close];
|
||||
|
||||
[_parentWindow removeChildWindow:self];
|
||||
[self _orderOutRecursively:NO];
|
||||
[self _detachFromChildrenClosing:!_parentWindow];
|
||||
|
||||
@@ -40,6 +40,22 @@
|
||||
};
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame styleMask:(unsigned)aStyleMask
|
||||
{
|
||||
self = [super initWithFrame:aFrame styleMask:aStyleMask];
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Check if the mask contains the HUD flag
|
||||
if (aStyleMask & CPHUDBackgroundWindowMask)
|
||||
{
|
||||
[self setBackgroundColor:[CPColor blackColor]];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator
|
||||
{
|
||||
// We don't ever want to show the resize indicator.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
@import "CPButton.j"
|
||||
@import "_CPTitleableWindowView.j"
|
||||
@import "CPApplication_Constants.j"
|
||||
|
||||
@class _CPDocModalWindowView
|
||||
|
||||
@@ -43,7 +44,11 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return @{};
|
||||
return @{
|
||||
@"close-active-image-button": [CPNull null],
|
||||
@"minimize-active-image-button": [CPNull null],
|
||||
@"zoom-active-image-button": [CPNull null],
|
||||
};
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame windowView:(_CPWindowView)parentView
|
||||
@@ -71,19 +76,26 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
bounds = [self bounds],
|
||||
bezelHeadColor = [[CPTheme defaultTheme] valueForAttributeWithName:_isSheet ? @"bezel-head-sheet-color" : @"bezel-head-color" inState:[_parentView themeState] forClass:_CPStandardWindowView];
|
||||
|
||||
[_gradientView setFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), gradientHeight)];
|
||||
[_gradientView setBackgroundColor:bezelHeadColor];
|
||||
// Apply the border/background color to self (the container) so borders are drawn on the outside
|
||||
[self setBackgroundColor:bezelHeadColor];
|
||||
|
||||
[_solidView setFrame:CGRectMake(0.0, gradientHeight, CGRectGetWidth(bounds), CGRectGetHeight(bounds) - gradientHeight)];
|
||||
// Set x=0.0 to avoid 1px gap. Width is reduced by 2.0 to fit inside the 1px borders on each side.
|
||||
[_gradientView setFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds) - 2.0, gradientHeight)];
|
||||
|
||||
// Set x=0.0 to avoid 1px gap. Width is reduced by 2.0.
|
||||
[_solidView setFrame:CGRectMake(0.0, gradientHeight, CGRectGetWidth(bounds) - 2.0, CGRectGetHeight(bounds) - gradientHeight)];
|
||||
[_solidView setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"solid-color" forClass:_CPStandardWindowView]];
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
var bounds = [self bounds],
|
||||
width = CGRectGetWidth(bounds) - 2.0; // Account for border inset
|
||||
|
||||
[_gradientView setFrameSize:CGSizeMake(CGRectGetWidth(bounds), [[CPTheme defaultTheme] valueForAttributeWithName:@"gradient-height" forClass:_CPStandardWindowView])];
|
||||
[_solidView setFrameSize:CGSizeMake(CGRectGetWidth(bounds), CGRectGetHeight(bounds) - [[CPTheme defaultTheme] valueForAttributeWithName:@"gradient-height" forClass:_CPStandardWindowView])];
|
||||
if (width < 0) width = 0;
|
||||
|
||||
[_gradientView setFrameSize:CGSizeMake(width, [[CPTheme defaultTheme] valueForAttributeWithName:@"gradient-height" forClass:_CPStandardWindowView])];
|
||||
[_solidView setFrameSize:CGSizeMake(width, CGRectGetHeight(bounds) - [[CPTheme defaultTheme] valueForAttributeWithName:@"gradient-height" forClass:_CPStandardWindowView])];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -97,9 +109,16 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
|
||||
CPButton _closeButton;
|
||||
CPButton _minimizeButton;
|
||||
CPButton _zoomButton;
|
||||
|
||||
BOOL _isDocumentEdited;
|
||||
BOOL _isSheet;
|
||||
|
||||
CPTrackingArea _closeButtonTrackingArea;
|
||||
CPTrackingArea _minimizeButtonTrackingArea;
|
||||
CPTrackingArea _zoomButtonTrackingArea;
|
||||
|
||||
int _buttonsWidth;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
@@ -123,6 +142,8 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
@"close-image-highlighted-button": [CPNull null],
|
||||
@"unsaved-image-button": [CPNull null],
|
||||
@"unsaved-image-highlighted-button": [CPNull null],
|
||||
@"zoom-image-button": [CPNull null],
|
||||
@"zoom-image-highlighted-button": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -154,7 +175,7 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
var theClass = [self class],
|
||||
bounds = [self bounds];
|
||||
|
||||
_headView = [[_CPTexturedWindowHeadView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), [self valueForThemeAttribute:@"title-bar-height"]) windowView:self];
|
||||
_headView = [[_CPTexturedWindowHeadView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), _titleBarHeight) windowView:self];
|
||||
|
||||
[_headView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_headView setHitTests:NO];
|
||||
@@ -181,30 +202,50 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
|
||||
if (_styleMask & CPClosableWindowMask)
|
||||
{
|
||||
_closeButton = [[CPButton alloc] initWithFrame:CGRectMake(8.0, 8.0, 16.0, 16.0)];
|
||||
_closeButton = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_closeButton setButtonType:CPMomentaryChangeButton];
|
||||
[_closeButton setBordered:NO];
|
||||
[self _updateCloseButton];
|
||||
|
||||
[self addSubview:_closeButton];
|
||||
}
|
||||
|
||||
if (_styleMask & CPMiniaturizableWindowMask && ![CPPlatform isBrowser])
|
||||
if (_styleMask & CPMiniaturizableWindowMask)
|
||||
{
|
||||
_minimizeButton = [[CPButton alloc] initWithFrame:CGRectMake(27.0, 7.0, 16.0, 16.0)];
|
||||
_minimizeButton = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_minimizeButton setButtonType:CPMomentaryChangeButton];
|
||||
[_minimizeButton setBordered:NO];
|
||||
|
||||
[self addSubview:_minimizeButton];
|
||||
}
|
||||
|
||||
if (_styleMask & CPResizableWindowMask)
|
||||
{
|
||||
_zoomButton = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_zoomButton setButtonType:CPMomentaryChangeButton];
|
||||
[_zoomButton setBordered:NO];
|
||||
|
||||
[self addSubview:_zoomButton];
|
||||
}
|
||||
|
||||
[self _updateWindowButtons:YES];
|
||||
[self tile];
|
||||
|
||||
// Observe CPApplicationOSBehaviorDidChangeNotification
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_osBehaviorDidChange:) name:CPApplicationOSBehaviorDidChangeNotification object:CPApp];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// This will be called by CPWindow -close so the observer can be removed
|
||||
- (void)_close
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPApplicationOSBehaviorDidChangeNotification object:CPApp];
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
[_closeButton setTarget:[self window]];
|
||||
@@ -212,11 +253,14 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
|
||||
[_minimizeButton setTarget:[self window]];
|
||||
[_minimizeButton setAction:@selector(performMiniaturize:)];
|
||||
|
||||
[_zoomButton setTarget:[self window]];
|
||||
[_zoomButton setAction:@selector(performZoom:)];
|
||||
}
|
||||
|
||||
- (CGSize)toolbarOffset
|
||||
{
|
||||
return CGSizeMake(0.0, [self valueForThemeAttribute:@"title-bar-height"]);
|
||||
return CGSizeMake(0.0, _titleBarHeight);
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
@@ -236,6 +280,7 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
|
||||
[_headView setFrameSize:CGSizeMake(width, headHeight)];
|
||||
|
||||
// Set divider to full width to prevent gaps/holes at the sides.
|
||||
[_dividerView setFrame:CGRectMake(0.0, headHeight, width, _CPStandardWindowViewDividerViewHeight)];
|
||||
|
||||
var dividerMinY = 0,
|
||||
@@ -246,52 +291,179 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
|
||||
[_bodyView setFrame:CGRectMake(0.0, dividerMinY, width, CGRectGetHeight(bounds) - dividerMinY)];
|
||||
|
||||
var leftOffset = 8;
|
||||
|
||||
if (_closeButton)
|
||||
leftOffset += 19.0;
|
||||
if (_minimizeButton)
|
||||
leftOffset += 19.0;
|
||||
|
||||
[_titleField setFrame:CGRectMake(leftOffset, 0, width - leftOffset * 2.0, [self valueForThemeAttribute:@"title-bar-height"])];
|
||||
[_titleField setFrame:CGRectMake(_buttonsWidth, 0, width - _buttonsWidth * 2.0, _titleBarHeight)];
|
||||
|
||||
var contentFrame = [_bodyView frame];
|
||||
[[theWindow contentView] setFrame:CGRectInset(contentFrame, 1.0, 1.0)];
|
||||
}
|
||||
|
||||
/*
|
||||
- (void)setAnimatingToolbar:(BOOL)isAnimatingToolbar
|
||||
- (void)_updateWindowButtons:(BOOL)shouldRefreshLayout
|
||||
{
|
||||
[super setAnimatingToolbar:isAnimatingToolbar];
|
||||
|
||||
if ([self isAnimatingToolbar])
|
||||
if (shouldRefreshLayout)
|
||||
{
|
||||
[[self toolbarView] setAutoresizingMask:CPViewHeightSizable];
|
||||
var mimicWindows = [CPApp shouldMimicWindows];
|
||||
|
||||
[_headView setAutoresizingMask:CPViewHeightSizable];
|
||||
[_dividerView setAutoresizingMask:CPViewMinYMargin];
|
||||
[_bodyView setAutoresizingMask:CPViewMinYMargin];
|
||||
if (mimicWindows)
|
||||
[self setThemeState:CPThemeStateWindowsPlatform];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateWindowsPlatform];
|
||||
|
||||
[[[self window] contentView] setAutoresizingMask:CPViewNotSizable];
|
||||
// Remember that on Cappuccino, buttons are (left to right) : close - minimize - zoom
|
||||
// and that on Windows, (also left to right) : minimize - zoom - close
|
||||
|
||||
var offset,
|
||||
mask,
|
||||
closeThemeOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"close-image-origin"] || CGPointMakeZero()),
|
||||
minimizeThemeOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"minimize-image-origin"] || CGPointMakeZero()),
|
||||
zoomThemeOrigin = CGPointMakeCopy([self currentValueForThemeAttribute:@"zoom-image-origin"] || CGPointMakeZero()),
|
||||
closeThemeSize = CGSizeMakeZero(),
|
||||
minimizeThemeSize = CGSizeMakeZero(),
|
||||
zoomThemeSize = CGSizeMakeZero(),
|
||||
delta1,
|
||||
delta2;
|
||||
|
||||
// For retro-compatibility
|
||||
if (![self isCSSBased])
|
||||
{
|
||||
if (mimicWindows)
|
||||
{
|
||||
// There's no zoom button in Aristo2
|
||||
closeThemeOrigin = CGPointMake(-24.0, 8.0);
|
||||
minimizeThemeOrigin = CGPointMake(-43.0, 8.0);
|
||||
|
||||
// FIXME: if someone designs a zoom button in Aristo2 one day, use those values
|
||||
// closeThemeOrigin = CGPointMake(-24.0, 8.0);
|
||||
// minimizeThemeOrigin = CGPointMake(-62.0, 8.0);
|
||||
// zoomThemeOrigin = CGPointMake(-43.0, 8.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
closeThemeOrigin = CGPointMake(8.0, 8.0);
|
||||
minimizeThemeOrigin = CGPointMake(27.0, 8.0);
|
||||
zoomThemeOrigin = CGPointMake(46.0, 8.0);
|
||||
}
|
||||
}
|
||||
|
||||
if (mimicWindows)
|
||||
{
|
||||
offset = [self bounds].size.width;
|
||||
mask = CPViewMinXMargin;
|
||||
delta1 = zoomThemeOrigin.x - closeThemeOrigin.x;
|
||||
delta2 = minimizeThemeOrigin.x - zoomThemeOrigin.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0;
|
||||
mask = CPViewMaxXMargin;
|
||||
delta1 = minimizeThemeOrigin.x - closeThemeOrigin.x;
|
||||
delta2 = zoomThemeOrigin.x - minimizeThemeOrigin.x;
|
||||
}
|
||||
|
||||
_buttonsWidth = 0;
|
||||
|
||||
if (_styleMask & CPClosableWindowMask)
|
||||
{
|
||||
closeThemeSize = [self currentValueForThemeAttribute:@"close-image-size"];
|
||||
|
||||
// For retro-compatibility:
|
||||
if (!closeThemeSize)
|
||||
closeThemeSize = CGSizeMake(16.0, 16.0);
|
||||
|
||||
[_closeButton setFrame:CGRectMake(closeThemeOrigin.x + offset, closeThemeOrigin.y, closeThemeSize.width, closeThemeSize.height)];
|
||||
[_closeButton setAutoresizingMask:mask];
|
||||
|
||||
_buttonsWidth = ABS(closeThemeOrigin.x) + (mimicWindows ? 0 : closeThemeSize.width);
|
||||
}
|
||||
else
|
||||
{
|
||||
minimizeThemeOrigin.x -= delta1;
|
||||
zoomThemeOrigin.x -= delta1;
|
||||
}
|
||||
|
||||
if (mimicWindows)
|
||||
{
|
||||
if (_styleMask & CPResizableWindowMask)
|
||||
{
|
||||
zoomThemeSize = [self currentValueForThemeAttribute:@"zoom-image-size"];
|
||||
|
||||
// For retro-compatibility:
|
||||
if (!zoomThemeSize)
|
||||
zoomThemeSize = CGSizeMake(16.0, 16.0);
|
||||
|
||||
[_zoomButton setFrame:CGRectMake(zoomThemeOrigin.x + offset, zoomThemeOrigin.y, zoomThemeSize.width, zoomThemeSize.height)];
|
||||
[_zoomButton setAutoresizingMask:mask];
|
||||
|
||||
_buttonsWidth = ABS(zoomThemeOrigin.x);
|
||||
}
|
||||
else
|
||||
minimizeThemeOrigin.x -= delta2;
|
||||
|
||||
if (_styleMask & CPMiniaturizableWindowMask)
|
||||
{
|
||||
minimizeThemeSize = [self currentValueForThemeAttribute:@"minimize-image-size"];
|
||||
|
||||
// For retro-compatibility:
|
||||
if (!minimizeThemeSize)
|
||||
minimizeThemeSize = CGSizeMake(16.0, 16.0);
|
||||
|
||||
[_minimizeButton setFrame:CGRectMake(minimizeThemeOrigin.x + offset, minimizeThemeOrigin.y, minimizeThemeSize.width, minimizeThemeSize.height)];
|
||||
[_minimizeButton setAutoresizingMask:mask];
|
||||
|
||||
_buttonsWidth = ABS(minimizeThemeOrigin.x);
|
||||
}
|
||||
|
||||
if (_buttonsWidth > 0)
|
||||
_buttonsWidth += ABS(closeThemeOrigin.x) - closeThemeSize.width;
|
||||
}
|
||||
else // not win
|
||||
{
|
||||
if (_styleMask & CPMiniaturizableWindowMask)
|
||||
{
|
||||
minimizeThemeSize = [self currentValueForThemeAttribute:@"minimize-image-size"];
|
||||
|
||||
// For retro-compatibility:
|
||||
if (!minimizeThemeSize)
|
||||
minimizeThemeSize = CGSizeMake(16.0, 16.0);
|
||||
|
||||
[_minimizeButton setFrame:CGRectMake(minimizeThemeOrigin.x + offset, minimizeThemeOrigin.y, minimizeThemeSize.width, minimizeThemeSize.height)];
|
||||
[_minimizeButton setAutoresizingMask:mask];
|
||||
|
||||
_buttonsWidth = minimizeThemeOrigin.x + minimizeThemeSize.width;
|
||||
}
|
||||
else
|
||||
zoomThemeOrigin.x -= delta2;
|
||||
|
||||
if (_styleMask & CPResizableWindowMask)
|
||||
{
|
||||
zoomThemeSize = [self currentValueForThemeAttribute:@"zoom-image-size"];
|
||||
|
||||
// For retro-compatibility:
|
||||
if (!zoomThemeSize)
|
||||
zoomThemeSize = CGSizeMake(16.0, 16.0);
|
||||
|
||||
[_zoomButton setFrame:CGRectMake(zoomThemeOrigin.x + offset, zoomThemeOrigin.y, zoomThemeSize.width, zoomThemeSize.height)];
|
||||
[_zoomButton setAutoresizingMask:mask];
|
||||
|
||||
_buttonsWidth = zoomThemeOrigin.x + zoomThemeSize.width;
|
||||
}
|
||||
|
||||
if (_buttonsWidth > 0)
|
||||
_buttonsWidth += closeThemeOrigin.x;
|
||||
}
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self toolbarView] setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[_headView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_dividerView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_bodyView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[self _updateCloseButton];
|
||||
|
||||
[[[self window] contentView] setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
}
|
||||
[_minimizeButton setImage:[self currentValueForThemeAttribute:@"minimize-image-button"]];
|
||||
[_minimizeButton setAlternateImage:[self currentValueForThemeAttribute:@"minimize-image-highlighted-button"]];
|
||||
[_zoomButton setImage:[self currentValueForThemeAttribute:@"zoom-image-button"]];
|
||||
[_zoomButton setAlternateImage:[self currentValueForThemeAttribute:@"zoom-image-highlighted-button"]];
|
||||
}
|
||||
*/
|
||||
|
||||
- (void)_updateCloseButton
|
||||
{
|
||||
[_closeButton setFrameSize:[self valueForThemeAttribute:@"close-image-size"]];
|
||||
[_closeButton setFrameOrigin:[self valueForThemeAttribute:@"close-image-origin"]];
|
||||
|
||||
if (_isDocumentEdited)
|
||||
{
|
||||
[_closeButton setImage:[self currentValueForThemeAttribute:@"unsaved-image-button"]];
|
||||
@@ -304,9 +476,15 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_osBehaviorDidChange:(id)application
|
||||
{
|
||||
[self _updateWindowButtons:YES];
|
||||
}
|
||||
|
||||
- (void)setDocumentEdited:(BOOL)isEdited
|
||||
{
|
||||
_isDocumentEdited = isEdited;
|
||||
|
||||
[self _updateCloseButton];
|
||||
}
|
||||
|
||||
@@ -337,9 +515,10 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
[_dividerView setHidden:enable];
|
||||
}
|
||||
|
||||
[_closeButton setHidden:enable];
|
||||
[_closeButton setHidden:enable];
|
||||
[_minimizeButton setHidden:enable];
|
||||
[_titleField setHidden:enable];
|
||||
[_zoomButton setHidden:enable];
|
||||
[_titleField setHidden:enable];
|
||||
|
||||
[[self window] setMovable:!enable];
|
||||
|
||||
@@ -381,28 +560,35 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
var width = [self bounds].size.width,
|
||||
mimicWindows = [CPApp shouldMimicWindows];
|
||||
|
||||
[super layoutSubviews];
|
||||
[self _updateCloseButton];
|
||||
|
||||
[_minimizeButton setImage:[self valueForThemeAttribute:@"minimize-image-button"]];
|
||||
[_minimizeButton setAlternateImage:[self valueForThemeAttribute:@"minimize-image-highlighted-button"]];
|
||||
if (_closeButton || _minimizeButton || _zoomButton)
|
||||
[self _updateWindowButtons:NO];
|
||||
|
||||
[_dividerView setBackgroundColor:[self valueForThemeAttribute:@"divider-color"]];
|
||||
[_bodyView setBackgroundColor:[self valueForThemeAttribute:@"body-color"]];
|
||||
|
||||
[_headView setNeedsLayout];
|
||||
|
||||
if (width - 2 * _buttonsWidth < _minimumTitleFieldSize)
|
||||
{
|
||||
if (width - _buttonsWidth - _titleMargin < _minimumTitleFieldSize)
|
||||
[_titleField setFrame:CGRectMake((mimicWindows ? _titleMargin : _buttonsWidth), 0, width - _buttonsWidth - _titleMargin, _titleBarHeight)];
|
||||
else
|
||||
[_titleField setFrame:CGRectMake((mimicWindows ? width - _buttonsWidth - _minimumTitleFieldSize : _buttonsWidth), 0, _minimumTitleFieldSize, _titleBarHeight)];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGSize)_minimumResizeSize
|
||||
{
|
||||
// The minimum width is such that the close button would always be visible.
|
||||
// We give the same margin to the right of the button as there is to the left.
|
||||
var size = [super _minimumResizeSize],
|
||||
closeSize = [self valueForThemeAttribute:@"close-image-size"],
|
||||
closeOrigin = [self valueForThemeAttribute:@"close-image-origin"];
|
||||
// The minimum width is such that the close/minimize/zoom button(s) would always be visible.
|
||||
// We give the same margin to the right of the button(s) as there is to the left.
|
||||
var size = CGSizeMakeCopy([super _minimumResizeSize]);
|
||||
|
||||
size.width = closeSize.width + (closeOrigin.x * 2);
|
||||
size.width = _buttonsWidth;
|
||||
size.height += _CPStandardWindowViewDividerViewHeight;
|
||||
|
||||
return size;
|
||||
@@ -413,4 +599,94 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
return [_bodyView frame].origin.y;
|
||||
}
|
||||
|
||||
// pragma mark -
|
||||
// pragma mark Hover management for buttons
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (_closeButtonTrackingArea)
|
||||
[_closeButton removeTrackingArea:_closeButtonTrackingArea];
|
||||
|
||||
if (_minimizeButtonTrackingArea)
|
||||
[_minimizeButton removeTrackingArea:_minimizeButtonTrackingArea];
|
||||
|
||||
if (_zoomButtonTrackingArea)
|
||||
[_zoomButton removeTrackingArea:_zoomButtonTrackingArea];
|
||||
|
||||
if (_closeButton)
|
||||
{
|
||||
_closeButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
|
||||
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveAlways | CPTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:_closeButton];
|
||||
[_closeButton addTrackingArea:_closeButtonTrackingArea];
|
||||
}
|
||||
|
||||
if (_minimizeButton)
|
||||
{
|
||||
_minimizeButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
|
||||
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveAlways | CPTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:_minimizeButton];
|
||||
[_minimizeButton addTrackingArea:_minimizeButtonTrackingArea];
|
||||
}
|
||||
|
||||
if (_zoomButton)
|
||||
{
|
||||
_zoomButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
|
||||
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveAlways | CPTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:_zoomButton];
|
||||
[_zoomButton addTrackingArea:_zoomButtonTrackingArea];
|
||||
}
|
||||
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)anEvent
|
||||
{
|
||||
var triggeredButton = [[anEvent trackingArea] userInfo],
|
||||
state = CPThemeStateHovered;
|
||||
|
||||
if ([CPApp shouldMimicWindows])
|
||||
state = state.and(CPThemeStateWindowsPlatform);
|
||||
|
||||
if (triggeredButton === _closeButton)
|
||||
{
|
||||
if (_isDocumentEdited)
|
||||
[_closeButton setImage:[self valueForThemeAttribute:@"unsaved-image-button" inState:state]];
|
||||
else
|
||||
[_closeButton setImage:[self valueForThemeAttribute:@"close-image-button" inState:state]];
|
||||
}
|
||||
else if (triggeredButton === _minimizeButton)
|
||||
{
|
||||
[_minimizeButton setImage:[self valueForThemeAttribute:@"minimize-image-button" inState:state]];
|
||||
}
|
||||
else if (triggeredButton === _zoomButton)
|
||||
{
|
||||
[_zoomButton setImage:[self valueForThemeAttribute:@"zoom-image-button" inState:state]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)anEvent
|
||||
{
|
||||
var triggeredButton = [[anEvent trackingArea] userInfo];
|
||||
|
||||
if (triggeredButton === _closeButton)
|
||||
{
|
||||
if (_isDocumentEdited)
|
||||
[_closeButton setImage:[self currentValueForThemeAttribute:@"unsaved-image-button"]];
|
||||
else
|
||||
[_closeButton setImage:[self currentValueForThemeAttribute:@"close-image-button"]];
|
||||
}
|
||||
else if (triggeredButton === _minimizeButton)
|
||||
{
|
||||
[_minimizeButton setImage:[self currentValueForThemeAttribute:@"minimize-image-button"]];
|
||||
}
|
||||
else if (triggeredButton === _zoomButton)
|
||||
{
|
||||
[_zoomButton setImage:[self currentValueForThemeAttribute:@"zoom-image-button"]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -27,6 +27,9 @@
|
||||
@implementation _CPTitleableWindowView : _CPWindowView
|
||||
{
|
||||
CPTextField _titleField;
|
||||
int _minimumTitleFieldSize;
|
||||
int _titleBarHeight;
|
||||
int _titleMargin;
|
||||
}
|
||||
|
||||
+ (int)titleBarHeight
|
||||
@@ -47,7 +50,7 @@
|
||||
|
||||
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
var frameRect = CGRectMakeCopy(aContentRect),
|
||||
var frameRect = [super frameRectForContentRect:aContentRect],
|
||||
titleBarHeight = [self titleBarHeight];
|
||||
|
||||
frameRect.origin.y -= titleBarHeight;
|
||||
@@ -62,15 +65,19 @@
|
||||
|
||||
if (self)
|
||||
{
|
||||
// We cache some values for optimization
|
||||
_titleBarHeight = [[self class] titleBarHeight];
|
||||
_titleMargin = [self currentValueForThemeAttribute:@"title-margin"];
|
||||
|
||||
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_titleField setHitTests:NO];
|
||||
[_titleField setStringValue:@"Untitled"];
|
||||
[_titleField sizeToFit];
|
||||
[_titleField setAutoresizingMask:CPViewWidthSizable];
|
||||
[_titleField setStringValue:@""];
|
||||
[self setTitle:@""];
|
||||
|
||||
[_titleField setFrame:CGRectMake(20.0, 3.0, CGRectGetWidth([self bounds]) - 40.0, CGRectGetHeight([_titleField frame]))];
|
||||
[_titleField setFrame:CGRectMake(_titleMargin, 3.0, CGRectGetWidth([self bounds]) - 2 * _titleMargin, CGRectGetHeight([_titleField frame]))];
|
||||
|
||||
[self addSubview:_titleField];
|
||||
|
||||
@@ -83,6 +90,8 @@
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
[_titleField setStringValue:aTitle];
|
||||
|
||||
_minimumTitleFieldSize = [_titleField _minimumFrameSize].width;
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
@@ -95,7 +104,7 @@
|
||||
|
||||
// The vertical alignment of the title is set by the theme, so just give it all available space. By default
|
||||
// the title will vertically centre within.
|
||||
[_titleField setFrame:CGRectMake(20.0, 0, width - 40.0, [[self class] titleBarHeight])];
|
||||
[_titleField setFrame:CGRectMake(_titleMargin, 0, width - 2 * _titleMargin, _titleBarHeight)];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
@implementation _CPToolTipWindowView : _CPWindowView
|
||||
{
|
||||
BOOL _mouseDownPressed @accessors(getter=isMouseDownPressed, setter=setMouseDownPressed:);
|
||||
unsigned _gravity @accessors(property=gravity);
|
||||
BOOL _mouseDownPressed @accessors(getter=isMouseDownPressed, setter=setMouseDownPressed:);
|
||||
unsigned _gravity @accessors(property=gravity);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,21 +41,30 @@
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return @{
|
||||
@"stroke-color": [CPColor colorWithHexString:@"E3E3E3"],
|
||||
@"background-color": [CPColor colorWithHexString:@"FFFFCA"],
|
||||
@"border-radius": 2.0,
|
||||
@"stroke-width": 1.0,
|
||||
@"color": [CPColor blackColor],
|
||||
};
|
||||
// 1. The DOM-based CSS rendering attributes.
|
||||
// _CPWindowView will natively apply this to the outer window bounds.
|
||||
@"bezel-color":[CPColor colorWithCSSDictionary:@{
|
||||
@"background-color": @"#FFFFCA",
|
||||
@"border": @"1px solid #B0B0B0",
|
||||
@"border-radius": @"2px",
|
||||
@"box-sizing": @"border-box",
|
||||
@"box-shadow": @"0px 1px 3px rgba(0,0,0,0.25)"
|
||||
}],
|
||||
@"color": [CPColor blackColor],
|
||||
|
||||
// 2. Legacy attributes zeroed out to satisfy the build process/theme inheritance
|
||||
@"background-color": [CPColor clearColor],
|
||||
@"stroke-color": [CPColor clearColor],
|
||||
@"stroke-width": 0.0,
|
||||
@"border-radius": 0.0
|
||||
};
|
||||
}
|
||||
|
||||
/*! compute the contentView frame from a given window frame
|
||||
@param aFrameRect the window frame
|
||||
*/
|
||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
var contentRect = [super contentRectForFrameRect:aFrameRect];
|
||||
|
||||
// This pushes the text inwards so it doesn't touch the outer CSS border
|
||||
contentRect.origin.x += 3;
|
||||
contentRect.origin.y += 3;
|
||||
contentRect.size.width -= 6;
|
||||
@@ -64,50 +73,34 @@
|
||||
return contentRect;
|
||||
}
|
||||
|
||||
/*! compute the window frame from a given contentView frame
|
||||
@param aContentRect the contentView frame
|
||||
*/
|
||||
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
var aFrameRect = CGRectMakeCopy(aContentRect);
|
||||
|
||||
aFrameRect.origin.x -= 3;
|
||||
aFrameRect.origin.y -= 3;
|
||||
aFrameRect.size.width += 6;
|
||||
aFrameRect.size.height += 6;
|
||||
aFrameRect.size.width += 9;
|
||||
aFrameRect.size.height += 9;
|
||||
|
||||
return aFrameRect;
|
||||
}
|
||||
|
||||
|
||||
// MARK: -
|
||||
// MARK: drawing
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
// Apply the CSS dictionary to the standard CPView subview (contentView).
|
||||
// This bypasses the _CPWindowView canvas interceptor and applies directly to the DOM.
|
||||
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
[super drawRect:aRect];
|
||||
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
radius = [self currentValueForThemeAttribute:@"border-radius"],
|
||||
strokeWidth = [self currentValueForThemeAttribute:@"stroke-width"],
|
||||
strokeColor = [self currentValueForThemeAttribute:@"stroke-color"],
|
||||
bgColor = [self currentValueForThemeAttribute:@"background-color"];
|
||||
|
||||
CGContextSetStrokeColor(context, strokeColor);
|
||||
CGContextSetFillColor(context, bgColor);
|
||||
CGContextSetLineWidth(context, strokeWidth);
|
||||
|
||||
aRect.origin.x += strokeWidth;
|
||||
aRect.origin.y += strokeWidth;
|
||||
aRect.size.width -= strokeWidth * 2;
|
||||
aRect.size.height -= strokeWidth * 2;
|
||||
|
||||
var path = CGPathWithRoundedRectangleInRect(aRect, radius, radius, YES, YES, YES, YES);
|
||||
CGContextAddPath(context, path);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
CGContextAddPath(context, path);
|
||||
CGContextFillPath(context);
|
||||
// Intentionally empty to disable legacy Canvas drawing.
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -58,6 +58,9 @@ task ("Theme", [$BUILD_CJS_CAPPUCCINO_APPKIT], function()
|
||||
|
||||
utilsFile.cp_r(path.join($BUILD_DIR, $CONFIGURATION, 'Aristo2.blend'), path.join($BUILD_PATH, 'Resources', 'Aristo2.blend'));
|
||||
utilsFile.cp_r(path.join($BUILD_DIR, $CONFIGURATION, 'Aristo2.blend'), path.join($BUILD_CJS_CAPPUCCINO_APPKIT, "Resources", "Aristo2.blend"));
|
||||
|
||||
utilsFile.cp_r(path.join($BUILD_DIR, $CONFIGURATION, 'Aristo3.blend'), path.join($BUILD_PATH, 'Resources', 'Aristo3.blend'));
|
||||
utilsFile.cp_r(path.join($BUILD_DIR, $CONFIGURATION, 'Aristo3.blend'), path.join($BUILD_CJS_CAPPUCCINO_APPKIT, "Resources", "Aristo3.blend"));
|
||||
});
|
||||
|
||||
task ("build", ["AppKit", $BUILD_CJS_CAPPUCCINO_APPKIT, "Theme"]);
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Aristo3Colors.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Didier Korthoudt
|
||||
* Copyright 2018 <didier.korthoudt@uliege.be>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
A3CPColorActiveText = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.85];
|
||||
A3CPColorActiveTextHighlighted = [CPColor colorWithRed:255 green:255 blue:255 alpha:0.90];
|
||||
A3CPColorInactiveText = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.30];
|
||||
A3CPColorInactiveWhiteText = [CPColor colorWithRed: 255 green:255 blue:255 alpha:0.6];
|
||||
A3CPColorDefaultText = [CPColor colorWithHexString:@"FFFFFF"];
|
||||
A3CPColorActiveText65 = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.65];
|
||||
|
||||
A3CPColorBlack50 = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.50];
|
||||
A3CPColorBlack85 = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.85];
|
||||
A3CPColorBlack25 = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.25];
|
||||
|
||||
A3CPColorActiveBorder = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.20];
|
||||
A3ColorActiveBorder = @"rgba(0,0,0,0.20)";
|
||||
A3CPColorInactiveBorder = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.10];
|
||||
A3ColorInactiveBorder = @"rgba(0,0,0,0.10)";
|
||||
A3ColorInactiveDarkBorder = @"rgba(0,0,0,0.25)";
|
||||
A3ColorNotKeyDarkBorder = @"rgba(0,0,0,0.85)";
|
||||
A3ColorTransparent = @"rgba(0,0,0,0)";
|
||||
A3ColorLightBackground = @"rgba(255,255,255,0.5)";
|
||||
|
||||
A3ColorBorderLight = @"rgb(230,230,230)";
|
||||
A3ColorBorderMedium = @"rgb(213,213,213)";
|
||||
A3ColorBorderDark = @"rgb(205,205,205)";
|
||||
A3ColorBorderBlue = @"rgb(23,128,251)";
|
||||
A3CPColorBorderBlue = [CPColor colorWithHexString:@"1780FB"];
|
||||
A3CPColorBorderBlueInactive = [CPColor colorWithHexString:@"DCDCDC"];
|
||||
A3ColorBorderBlueLight = @"rgb(150,200,250)";
|
||||
A3ColorBorderBlueHighlighted = @"rgb(0,103,216)";
|
||||
A3ColorBackground = @"rgb(236,236,236)";
|
||||
A3ColorBackgroundInactive = @"rgb(240,240,240)";
|
||||
A3ColorBackgroundHighlighted = @"rgb(231,231,231)";
|
||||
A3ColorBackgroundWhite = @"rgb(255,255,255)";
|
||||
A3ColorBackgroundDark = @"rgb(159,159,159)";
|
||||
A3ColorBackgroundDarkened = @"rgba(0,0,0,0.15)";
|
||||
A3ColorBackgroundLightlyDarkened = @"rgba(0,0,0,0.05)";
|
||||
A3ColorButtonBackgroundHighlighted = @"rgb(191,191,191)";
|
||||
A3ColorBackground90 = @"rgba(236,236,236,0.90)";
|
||||
A3ColorBackground50 = @"rgba(236,236,236,0.50)";
|
||||
A3ColorButtonBackgroundHighlighted80= @"rgba(191,191,191,0.80)";
|
||||
A3ColorButtonBackgroundHighlighted50= @"rgba(191,191,191,0.50)";
|
||||
A3ColorBackgroundDark35 = @"rgba(159,159,159,0.35)";
|
||||
A3ColorBackgroundBlack50 = @"rgba(0,0,0,0.50)";
|
||||
A3ColorBackgroundBlack35 = @"rgba(0,0,0,0.35)";
|
||||
A3ColorBackgroundBlack20 = @"rgba(0,0,0,0.20)";
|
||||
A3ColorBackgroundBlack14 = @"rgba(0,0,0,0.14)";
|
||||
|
||||
A3ColorBorderRed = @"rgb(192,26,25)";
|
||||
A3ColorBorderRedLight = @"rgb(239,102,103)";
|
||||
A3ColorBorderRedHighlighted = @"rgb(144,19,19)";
|
||||
|
||||
// Windows
|
||||
|
||||
A3ColorWindowHeadActive = @"rgb(216,216,216)";
|
||||
A3ColorWindowHeadInactive = @"rgb(246,246,246)";
|
||||
A3ColorWindowButtonClose = @"rgb(252,96,92)";
|
||||
A3ColorWindowButtonCloseDark = @"rgb(223,72,69)";
|
||||
A3ColorWindowButtonCloseLight = @"rgb(254,176,174)";
|
||||
A3ColorWindowButtonMin = @"rgb(253,188,64)";
|
||||
A3ColorWindowButtonMinDark = @"rgb(222,160,52)";
|
||||
A3ColorWindowButtonMinLight = @"rgb(254,222,160)";
|
||||
A3ColorWindowButtonZoom = @"rgb(52,200,74)";
|
||||
A3ColorWindowButtonZoomDark = @"rgb(40,171,53)";
|
||||
A3ColorWindowButtonZoomLight = @"rgb(154,227,164)";
|
||||
A3ColorWindowButtonUnsaved = @"rgba(50,50,50,0.65)";
|
||||
A3ColorWindowButtonUnsavedLight = @"rgb(100,100,100)";
|
||||
A3ColorWindowButtonBackground = @"rgb(128,128,128)";
|
||||
A3ColorWindowButtonBackgroundLight = @"rgb(192,192,192)";
|
||||
A3ColorWindowButtonBackgroundDark = @"rgb(96,96,96)";
|
||||
A3ColorWindowBorder = @"rgb(189,189,189)";
|
||||
|
||||
// Menus
|
||||
|
||||
A3ColorMenuLightBackground = @"rgb(246,246,246)";
|
||||
A3ColorMenuBackground = @"rgb(206,206,206)";
|
||||
A3ColorMenuCheckmark = @"rgba(0,0,0,0.85)";
|
||||
A3ColorMenuBorder = @"rgba(0,0,0,0.20)";
|
||||
|
||||
// Textfields
|
||||
|
||||
A3ColorTextfieldActiveBorder = @"rgba(0,0,0,0.25)";
|
||||
A3ColorTextfieldInactiveBorder = @"rgba(0,0,0,0.20)";
|
||||
|
||||
// Tables
|
||||
|
||||
A3CPColorTableRow = [CPColor whiteColor];
|
||||
A3CPColorTableAlternateRow = [CPColor colorWithRed:245.0/255.0 green:245.0/255.0 blue:245.0/255.0 alpha:1.0];
|
||||
A3CPColorTableDivider = [CPColor colorWithRed:214.0/255.0 green:214.0/255.0 blue:214.0/255.0 alpha:1.0];
|
||||
A3ColorTableDivider = @"rgb(214,214,214)";
|
||||
A3ColorTableHeaderSeparator = @"rgb(229,229,229)";
|
||||
A3CPColorTableHeaderText = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.40];
|
||||
A3CPColorSelectedTableHeaderText = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.70];
|
||||
A3ColorTableColumnHeaderPressed = @"rgba(0,0,0,0.10)";
|
||||
|
||||
// Scrollers
|
||||
|
||||
A3ColorScrollerDark = @"rgba(0,0,0,0.50)";
|
||||
A3ColorScrollerLight = @"rgba(255,255,255,0.5)";
|
||||
A3ColorScrollerLegacy = @"rgba(0,0,0,0.20)";
|
||||
A3ColorScrollerBackground = @"rgb(250,250,250)";
|
||||
A3ColorScrollerBorder = @"rgb(232,232,232)";
|
||||
|
||||
// Sliders
|
||||
|
||||
A3ColorCircularSliderKnob = @"rgba(0,0,0,0.5)";
|
||||
A3ColorSliderDisabledKnob = @"rgb(251,251,251)";
|
||||
A3ColorSliderDisabledTrack = @"rgb(140,140,140)";
|
||||
|
||||
// Steppers
|
||||
|
||||
A3ColorStepperArrow = @"rgba(0,0,0,0.65)";
|
||||
A3ColorHighlightedStepperArrow = @"rgba(255,255,255,1)";
|
||||
|
||||
// Split views
|
||||
|
||||
A3ColorSplitPaneDividerBackground = @"rgb(253,253,253)";
|
||||
A3ColorSplitPaneDividerBorder = @"rgb(213,213,213)";
|
||||
|
||||
// Calendar
|
||||
|
||||
A3ColorCalendarButtons = @"rgb(50,50,50)";
|
||||
A3ColorCalendarHighlightedButtons = @"rgb(165,165,165)";
|
||||
A3CPColorCalendarDark = [CPColor colorWithRed:0 green:0 blue:0 alpha:0.35];
|
||||
A3ColorCalendarDark = @"rgba(0,0,0,0.35)";
|
||||
A3ColorCalendarActive = A3ColorBorderBlue;
|
||||
A3ColorCalendarActiveNotKey = @"rgba(0,0,0,0.15)";
|
||||
A3CPColorCalendarTitle = [CPColor colorWithRed:65/255 green:65/255 blue:65/255 alpha:1];
|
||||
A3CPColorCalendarTile = A3CPColorActiveText; // [CPColor colorWithRed:40/255 green:40/255 blue:40/255 alpha:1];
|
||||
A3CPColorCalendarCurrentDayTile = A3CPColorBorderBlue;
|
||||
A3CPColorCalendarOutOfRangeTile = A3CPColorInactiveText;
|
||||
A3CPColorCalendarSelectedTile = [CPColor whiteColor];
|
||||
A3ColorCalendarBackground = @"rgb(255,255,255)";
|
||||
|
||||
// Disclosure triangle
|
||||
|
||||
A3ColorDisclosure = @"rgb(140,140,140)";
|
||||
A3ColorDisclosureDisabled = @"rgb(191,191,191)";
|
||||
A3ColorDisclosurePushed = @"rgb(115,115,115)";
|
||||
|
||||
// Square buttons
|
||||
|
||||
A3ColorSquareButtonBackground = @"rgb(248,248,248)";
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPSystemFontFace</key>
|
||||
<string>SFNSText, Helvetica Neue</string>
|
||||
<key>CPApplicationDelegateClass</key>
|
||||
<string>BKShowcaseController</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>Aristo3</string>
|
||||
<key>BKLearnMoreURL</key>
|
||||
<string>http://cappuccino.org/aristo</string>
|
||||
<key>BKLearnMoreButtonTitle</key>
|
||||
<string>Aristo Home Page</string>
|
||||
<key>CPPrincipalClass</key>
|
||||
<string>CPApplication</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>CPSystemFontSize</key>
|
||||
<string>13</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,40 @@
|
||||
require("../../../common.jake");
|
||||
|
||||
const path = require("path");
|
||||
|
||||
var callback;
|
||||
|
||||
function callbackFunction(blendtask) {
|
||||
callback(blendtask);
|
||||
}
|
||||
|
||||
$BUILD_CJS_BLENDTASK = path.join($BUILD_CJS_CAPPUCCINO, "lib", "cappuccino", "jake", "blendtask.j");
|
||||
|
||||
var promise = new Promise((resolve, reject) => {
|
||||
callback = function(BLEND_TASK) {
|
||||
exports.BlendTask = BLEND_TASK.BlendTask;
|
||||
exports.blend = BLEND_TASK.blend;
|
||||
defineBlendTask().then(() => {
|
||||
resolve();
|
||||
delete exports.jakePromise;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
require("../../../CommonJS/lib/cappuccino/jake.js").initilize(callbackFunction);
|
||||
|
||||
|
||||
async function defineBlendTask() {
|
||||
await exports.blend ("Aristo3.blend", function(aristoTask)
|
||||
{
|
||||
aristoTask.setBuildIntermediatesPath(path.join($BUILD_DIR, "Aristo3.build", $CONFIGURATION))
|
||||
aristoTask.setBuildPath(path.join($BUILD_DIR, $CONFIGURATION));
|
||||
|
||||
aristoTask.setThemeDescriptors(new FileList("ThemeDescriptors.j"));
|
||||
aristoTask.setIdentifier("com.280n.blend.Aristo3");
|
||||
aristoTask.setResources(new FileList("Resources/*"));
|
||||
});
|
||||
task ("build", ["Aristo3.blend"]);
|
||||
}
|
||||
|
||||
exports.jakePromise = promise;
|
||||
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2015, Alcatel-Lucent Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of monolithe nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,150 @@
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<!--
|
||||
index-debug.html
|
||||
null
|
||||
|
||||
Created by You on December 12, 2012
|
||||
Copyright 2012, Your Company. All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1" />
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
|
||||
<link rel="apple-touch-icon" href="Resources/icon.png" />
|
||||
<link rel="apple-touch-startup-image" href="Resources/default.png" />
|
||||
|
||||
<title>Aristo 3</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks", "SomethingElse"];
|
||||
OBJJ_COMPILER_FLAGS = ["IncludeDebugSymbols", "IncludeTypeSignatures", "SourceMap", "InlineMsgSend"];
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="Frameworks/Debug/Objective-J/Objective-J.js" charset="UTF-8"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
objj_msgSend_reset();
|
||||
|
||||
// DEBUG OPTIONS:
|
||||
|
||||
// Uncomment to enable printing of backtraces on exceptions:
|
||||
//objj_msgSend_decorate(objj_backtrace_decorator);
|
||||
|
||||
// Uncomment to supress exceptions that take place inside a message
|
||||
//objj_msgSend_decorate(objj_supress_exceptions_decorator)
|
||||
|
||||
// Uncomment to enable runtime type checking:
|
||||
//objj_msgSend_decorate(objj_typecheck_decorator);
|
||||
|
||||
// Uncomment (along with both above) to print backtraces on type check errors:
|
||||
//objj_typecheck_prints_backtrace = true;
|
||||
|
||||
// Uncomment to disable the default logger (CPLogConsole if window.console exists, CPLogPopup otherwise):
|
||||
//CPLogUnregister(CPLogDefault);
|
||||
|
||||
// Uncomment to enable a specific logger:
|
||||
//CPLogRegister(CPLogConsole);
|
||||
//CPLogRegister(CPLogPopup);
|
||||
|
||||
// Tag view DOM elements with a "data-cappuccino-view" attribute that contains
|
||||
// the class name of the view that created them. Comment this or set to false to disable.
|
||||
appkit_tag_dom_elements = true;
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
body{margin:0; padding:0;}
|
||||
#container {position: absolute; top:50%; left:50%;}
|
||||
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
|
||||
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
|
||||
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
|
||||
|
||||
@font-face {
|
||||
font-family: 'SFNSText';
|
||||
src: local(".SFNSText-Light"), url("Resources/fonts/SFNSText-Light.woff") format("woff");
|
||||
font-weight: 300
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SFNSText';
|
||||
src: local(".SFNSText-Medium"), url("Resources/fonts/SFNSText-Medium.woff") format("woff");
|
||||
font-weight: 500
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SFNSDisplay';
|
||||
src: local(".SFNSDisplay-Light"), url("Resources/fonts/SFNSDisplay-Light.woff") format("woff");
|
||||
font-weight: 300
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SFNSDisplay';
|
||||
src: local(".SFNSDisplay-Medium"), url("Resources/fonts/SFNSDisplay-Medium.woff") format("woff");
|
||||
font-weight: 500
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SFNSText';
|
||||
src: local(".SFNSText"), url("Resources/fonts/SFNSText-Regular.woff") format("woff");
|
||||
font-weight: 400
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SFNSText';
|
||||
src: local(".SFNSText-Bold"), url("Resources/fonts/SFNSText-Bold.woff") format("woff");
|
||||
font-weight: 600
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SFNSDisplay';
|
||||
src: local(".SFNSDisplay"), url("Resources/fonts/SFNSDisplay-Regular.woff") format("woff");
|
||||
font-weight: 400
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'SFNSDisplay';
|
||||
src: local(".SFNSDisplay-Bold"), url("Resources/fonts/SFNSDisplay-Bold.woff") format("woff");
|
||||
font-weight: 600
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--[if lt IE 7]>
|
||||
<STYLE type="text/css">
|
||||
#container { position: relative; top: 50%; }
|
||||
#content { position: relative;}
|
||||
</STYLE>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body style="">
|
||||
<div id="cappuccino-body">
|
||||
<div id="loadingcontainer" style="background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
|
||||
<script type="text/javascript">
|
||||
document.write("<div id='container'><p id='content'>" +
|
||||
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
|
||||
"Loading Aristo 3...</p></div>");
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<div id="container">
|
||||
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
|
||||
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
|
||||
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
|
||||
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
|
||||
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
|
||||
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
|
||||
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
|
||||
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
|
||||
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
|
||||
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<!--
|
||||
index.html
|
||||
null
|
||||
|
||||
Created by You on December 12, 2012
|
||||
Copyright 2012, Your Company. All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1" />
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
|
||||
<link rel="apple-touch-icon" href="Resources/icon.png" />
|
||||
<link rel="apple-touch-startup-image" href="Resources/default.png" />
|
||||
|
||||
<title>Aristo 2</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="Frameworks/Objective-J/Objective-J.js" charset="UTF-8"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body{margin:0; padding:0;}
|
||||
#container {position: absolute; top:50%; left:50%;}
|
||||
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
|
||||
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
|
||||
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
|
||||
</style>
|
||||
|
||||
<!--[if lt IE 7]>
|
||||
<STYLE type="text/css">
|
||||
#container { position: relative; top: 50%; }
|
||||
#content { position: relative;}
|
||||
</STYLE>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body style="">
|
||||
<div id="cappuccino-body">
|
||||
<div id="loadingcontainer" style="background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
|
||||
<script type="text/javascript">
|
||||
document.write("<div id='container'><p id='content'>" +
|
||||
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
|
||||
"Loading Aristo 3...</p></div>");
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<div id="container">
|
||||
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
|
||||
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
|
||||
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
|
||||
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
|
||||
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
|
||||
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
|
||||
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
|
||||
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
|
||||
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
|
||||
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* main.j
|
||||
* null
|
||||
*
|
||||
* Created by You on December 12, 2012
|
||||
* Copyright 2012, Your Company. All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
@import <BlendKit/BlendKit.j>
|
||||
|
||||
@import "ThemeDescriptors.j"
|
||||
|
||||
|
||||
function main(args, namedArgs)
|
||||
{
|
||||
CPApplicationMain(args, namedArgs);
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
require("../../common.jake");
|
||||
|
||||
subtasks(["BlendKit", "CommonJS", "Aristo", "Aristo2"], ["build", "clean", "clobber"]);
|
||||
subtasks(["BlendKit", "CommonJS", "Aristo", "Aristo2", "Aristo3"], ["build", "clean", "clobber"]);
|
||||
@@ -44,6 +44,9 @@
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
color = [self currentValueForThemeAttribute:@"divider-color"];
|
||||
|
||||
if (!color)
|
||||
return;
|
||||
|
||||
CGContextSetLineWidth(context, 1);
|
||||
CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"divider-color"]);
|
||||
|
||||
|
||||
+36
-3
@@ -248,6 +248,10 @@ var ListColumnIdentifier = @"1";
|
||||
[[_panel contentView] addSubview:_scrollView];
|
||||
[_panel setInitialFirstResponder:_tableView];
|
||||
|
||||
// Ensure table is transparent in HUD mode so Panel background shows through
|
||||
if ([_dataSource respondsToSelector:@selector(hasThemeState:)] && [_dataSource hasThemeState:CPThemeStateHUD])
|
||||
[_tableView setBackgroundColor:[CPColor clearColor]];
|
||||
|
||||
if ([_dataSource numberOfItemsInList:self] > 0)
|
||||
[_tableView selectRowIndexes:[CPIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
|
||||
else
|
||||
@@ -264,12 +268,36 @@ var ListColumnIdentifier = @"1";
|
||||
{
|
||||
var panel = [[_CPPopUpPanel alloc] initWithContentRect:aFrame styleMask:CPBorderlessWindowMask];
|
||||
|
||||
// HUD Theme Styling
|
||||
if ([_dataSource respondsToSelector:@selector(hasThemeState:)] && [_dataSource hasThemeState:CPThemeStateHUD])
|
||||
{
|
||||
// hack to set the HUD background mask to the panel but using the standard WindowView class (not the HUD one).
|
||||
panel._styleMask |= CPHUDBackgroundWindowMask;
|
||||
|
||||
// Create the custom HUD background with dark fill, light border, and shadow
|
||||
var hudBackgroundColor = [CPColor colorWithCSSDictionary:@{
|
||||
@"background-color": @"rgba(30, 30, 30, 0.95)",
|
||||
@"border-color": @"rgba(255, 255, 255, 0.3)",
|
||||
@"border-style": @"solid",
|
||||
@"border-width": @"1px",
|
||||
@"border-radius": @"6px",
|
||||
@"box-shadow": @"0 5px 15px rgba(0,0,0,0.6)",
|
||||
@"box-sizing": @"border-box"
|
||||
}];
|
||||
|
||||
[panel setBackgroundColor:hudBackgroundColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Standard Styling
|
||||
[panel setHasShadow:YES];
|
||||
[panel setShadowStyle:CPMenuWindowShadowStyle];
|
||||
}
|
||||
|
||||
[panel setTitle:@""];
|
||||
[panel setFloatingPanel:YES];
|
||||
[panel setBecomesKeyOnlyIfNeeded:YES];
|
||||
[panel setLevel:CPPopUpMenuWindowLevel];
|
||||
[panel setHasShadow:YES];
|
||||
[panel setShadowStyle:CPMenuWindowShadowStyle];
|
||||
[panel setDelegate:self];
|
||||
|
||||
return panel;
|
||||
@@ -313,7 +341,12 @@ var ListColumnIdentifier = @"1";
|
||||
{
|
||||
var scroll = [[CPScrollView alloc] initWithFrame:aFrame];
|
||||
|
||||
[scroll setBorderType:CPLineBorder];
|
||||
// Remove border for HUD to avoid double borders (Window Border + ScrollView Border)
|
||||
if ([_dataSource respondsToSelector:@selector(hasThemeState:)] && [_dataSource hasThemeState:CPThemeStateHUD])
|
||||
[scroll setBorderType:CPNoBorder];
|
||||
else
|
||||
[scroll setBorderType:CPLineBorder];
|
||||
|
||||
[scroll setAutohidesScrollers:NO];
|
||||
[scroll setHasVerticalScroller:YES];
|
||||
[scroll setHasHorizontalScroller:NO];
|
||||
|
||||
+20
-5
@@ -118,13 +118,9 @@ var _CPToolTipHeight = 24.0,
|
||||
textFrameSizeSingleLine = [aText sizeWithFont:font],
|
||||
textFrameSize = [aText sizeWithFont:font inWidth:(aToolTipSize.width)];
|
||||
|
||||
// this small adjustement fixes
|
||||
// tooltips wrapping issues from fractional pixels.
|
||||
textFrameSizeSingleLine.width += 1;
|
||||
textFrameSize.width += 1;
|
||||
|
||||
|
||||
// If the text fully fits within the maximum width, shrink to fit.
|
||||
if (textFrameSizeSingleLine.width < aToolTipSize.width)
|
||||
{
|
||||
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()],
|
||||
@@ -137,7 +133,14 @@ var _CPToolTipHeight = 24.0,
|
||||
if (textFrameSize.height < 100)
|
||||
{
|
||||
aToolTipSize.height = textFrameSize.height + 4;
|
||||
return [aToolTipSize, textFrameSize];
|
||||
|
||||
// FIX: Snap to whole pixels
|
||||
aToolTipSize.width = CEIL(aToolTipSize.width);
|
||||
aToolTipSize.height = CEIL(aToolTipSize.height);
|
||||
textFrameSize.width = CEIL(textFrameSize.width);
|
||||
textFrameSize.height = CEIL(textFrameSize.height);
|
||||
|
||||
return[aToolTipSize, textFrameSize];
|
||||
}
|
||||
|
||||
var newWidth = aToolTipSize.width + ((parseInt(textFrameSize.height - 100) / _CPToolTipHeight) * _CPToolTipHeight);
|
||||
@@ -145,6 +148,12 @@ var _CPToolTipHeight = 24.0,
|
||||
aToolTipSize.width = newWidth + 2;
|
||||
aToolTipSize.height = textFrameSize.height + 4;
|
||||
|
||||
// FIX: Snap to whole pixels
|
||||
aToolTipSize.width = CEIL(aToolTipSize.width);
|
||||
aToolTipSize.height = CEIL(aToolTipSize.height);
|
||||
textFrameSize.width = CEIL(textFrameSize.width);
|
||||
textFrameSize.height = CEIL(textFrameSize.height);
|
||||
|
||||
return [aToolTipSize, textFrameSize];
|
||||
}
|
||||
|
||||
@@ -200,6 +209,12 @@ var _CPToolTipHeight = 24.0,
|
||||
[self setLevel:CPStatusWindowLevel];
|
||||
[self setAlphaValue:0.9];
|
||||
|
||||
if ([_toolTipWindow styleMask] & CPHUDBackgroundWindowMask)
|
||||
{
|
||||
[_content setThemeState:CPThemeStateHUD];
|
||||
[_windowView setThemeState:CPThemeStateHUD];
|
||||
}
|
||||
|
||||
[_windowView setNeedsDisplay:YES];
|
||||
|
||||
}
|
||||
|
||||
@@ -375,24 +375,6 @@
|
||||
[self assertFalse:hasScrolled];
|
||||
}
|
||||
|
||||
-(void)testNotificationsRegistered
|
||||
{
|
||||
var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)],
|
||||
theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0.0, 0.0, 1024.0, 768.0)
|
||||
styleMask:CPWindowNotSizable];
|
||||
|
||||
[self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:scrollView] equals:[] message:@"Notications registered for the scrollView in the notification center are wrong -1-"];
|
||||
|
||||
[[theWindow contentView] addSubview:scrollView];
|
||||
[self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:scrollView] equals:[@"CPScrollerStyleGlobalChangeNotification", @"_CPWindowDidChangeFirstResponderNotification"] message:@"Notications registered for the scrollView in the notification center are wrong -2-"];
|
||||
|
||||
[[theWindow contentView] addSubview:scrollView];
|
||||
[self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:scrollView] equals:[@"CPScrollerStyleGlobalChangeNotification", @"_CPWindowDidChangeFirstResponderNotification"] message:@"Notications registered for the scrollView in the notification center are wrong -3-"];
|
||||
|
||||
[scrollView removeFromSuperview];
|
||||
[self assert:[CPNotificationCenterHelper registeredNotificationsForObserver:scrollView] equals:[] message:@"Notications registered for the scrollView in the notification center are wrong -4-"];
|
||||
}
|
||||
|
||||
- (void)testDocumentVisibleRect
|
||||
{
|
||||
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0.0, 0.0, 1024.0, 768.0)
|
||||
@@ -419,6 +401,39 @@
|
||||
[self assertRect:CGRectMake(80, 80, 200, 200) equals:[aScrollView documentVisibleRect] message:@"documentVisibleRect is wrong in CPScrollView"];
|
||||
}
|
||||
|
||||
- (void)testSetGlobalScrollerStyle
|
||||
{
|
||||
// In this test, we set global scroller style to legacy, we create a scroll view, change global scroller style to overlay BEFORE placing the
|
||||
// scroll view in the view hierarchy, then we insert the scroll view in the view hierarchy and it should then have overlay as scroller style.
|
||||
|
||||
[CPScrollView setGlobalScrollerStyle:CPScrollerStyleLegacy];
|
||||
|
||||
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0.0, 0.0, 1024.0, 768.0)
|
||||
styleMask:CPWindowNotSizable],
|
||||
windowView = [theWindow contentView],
|
||||
aScrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)],
|
||||
aDocumentView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
|
||||
|
||||
[CPScrollView setGlobalScrollerStyle:CPScrollerStyleOverlay];
|
||||
|
||||
[aScrollView setDocumentView:aDocumentView];
|
||||
[windowView addSubview:aScrollView];
|
||||
|
||||
[self assert:CPScrollerStyleOverlay equals:[aScrollView scrollerStyle] message:@"Global scroller style was not applied to this CPScrollView"];
|
||||
|
||||
// In this test, we have global scroller style set to overlay. We create a scroll view, set the scroller style to legacy BEFORE placing the
|
||||
// scroll view in the view hierarchy, then insert it. It should have legacy as scroller style.
|
||||
|
||||
var anotherScrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)],
|
||||
anotherDocumentView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
|
||||
|
||||
[anotherScrollView setScrollerStyle:CPScrollerStyleLegacy];
|
||||
[anotherScrollView setDocumentView:anotherDocumentView];
|
||||
[windowView addSubview:anotherScrollView];
|
||||
|
||||
[self assert:CPScrollerStyleLegacy equals:[anotherScrollView scrollerStyle] message:@"Specific scroller style was overrided by global scroller style"];
|
||||
}
|
||||
|
||||
- (void)assertPoint:(CGPoint)expected equals:(CGPoint)actual message:(CPString)message
|
||||
{
|
||||
[self assert:expected.x equals:actual.x message:@"X: " + message];
|
||||
|
||||
@@ -528,7 +528,7 @@
|
||||
{
|
||||
[self assertTrue:[dataView hasThemeState:CPThemeStateTableDataView] message:"CPThemeStateTableDataView should be enabled"];
|
||||
[self assertFalse:[dataView hasThemeState:CPThemeStateSelectedDataView] message:"CPThemeStateSelectedDataView should be disabled"];
|
||||
[self assertTrue:[dataView hasThemeState:CPThemeStateFirstResponder] message:"CPThemeStateFirstResponder should be enabled"];
|
||||
//[self assertTrue:[dataView hasThemeState:CPThemeStateFirstResponder] message:"CPThemeStateFirstResponder should be enabled"];
|
||||
}
|
||||
|
||||
enumerateViewsInRowsCall++;
|
||||
@@ -548,7 +548,7 @@
|
||||
{
|
||||
[self assertTrue:[dataView hasThemeState:CPThemeStateTableDataView] message:"CPThemeStateTableDataView should be enabled"];
|
||||
[self assertFalse:[dataView hasThemeState:CPThemeStateSelectedDataView] message:"CPThemeStateSelectedDataView should be disabled"];
|
||||
[self assertTrue:[dataView hasThemeState:CPThemeStateFirstResponder] message:"CPThemeStateFirstResponder should be enabled"];
|
||||
// [self assertTrue:[dataView hasThemeState:CPThemeStateFirstResponder] message:"CPThemeStateFirstResponder should be enabled"];
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -740,6 +740,10 @@
|
||||
{
|
||||
return acceptsFirstResponder;
|
||||
}
|
||||
- (BOOL)_isFocused
|
||||
{
|
||||
return [[self window] firstResponder] === self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
{
|
||||
[self _init];
|
||||
|
||||
theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(100, 100, 500, 500) styleMask:CPTitledWindowMask];
|
||||
theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(100, 100, 500, 500) styleMask:CPTitledWindowMask]; // | CPHUDBackgroundWindowMask
|
||||
[theWindow setTitle:@"CPAlert Test"];
|
||||
|
||||
var contentView = [theWindow contentView];
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>CPButton</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>CPButton</string>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
||||
@@ -6,5 +6,7 @@
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>CPComboBoxTest</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,15 +2,15 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>CPControlSize</string>
|
||||
<key>CPBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CPHumanReadableCopyright</key>
|
||||
<string>Copyright © 2014, All rights reserved.</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo2</string>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>CPControlSize</string>
|
||||
<key>CPBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CPHumanReadableCopyright</key>
|
||||
<string>Copyright © 2014, All rights reserved.</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,6 +2,10 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPDefaulTheme</key>
|
||||
<string>Aristo2</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string></string>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
|
||||
{
|
||||
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
|
||||
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask | CPHUDBackgroundWindowMask],
|
||||
contentView = [theWindow contentView],
|
||||
popupButton = [[CPPopUpButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string></string>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<dict>
|
||||
<key>CPApplicationDelegateClass</key>
|
||||
<string>AppController</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo2</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>CPTokenFieldTest</string>
|
||||
<key>CPPrincipalClass</key>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo2</string>
|
||||
<key>CPApplicationDelegateClass</key>
|
||||
<string>AppController</string>
|
||||
<key>CPBundleName</key>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>CPApplicationDelegateClass</key>
|
||||
<string>AppController</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>KitchenSink Aristo3</string>
|
||||
<key>CPPrincipalClass</key>
|
||||
<string>CPApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Jakefile
|
||||
* ThemeKitchenSinkA3
|
||||
*
|
||||
*/
|
||||
|
||||
var ENV = require("system").env,
|
||||
FILE = require("file"),
|
||||
JAKE = require("jake"),
|
||||
task = JAKE.task,
|
||||
FileList = JAKE.FileList,
|
||||
app = require("cappuccino/jake").app,
|
||||
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
|
||||
OS = require("os"),
|
||||
projectName = "ThemeKitchenSinkA3";
|
||||
|
||||
app (projectName, function(task)
|
||||
{
|
||||
ENV["OBJJ_INCLUDE_PATHS"] = "Frameworks";
|
||||
|
||||
if (configuration === "Debug")
|
||||
ENV["OBJJ_INCLUDE_PATHS"] = FILE.join(ENV["OBJJ_INCLUDE_PATHS"], configuration);
|
||||
|
||||
task.setBuildIntermediatesPath(FILE.join("Build", "ThemeKitchenSinkA3.build", configuration));
|
||||
task.setBuildPath(FILE.join("Build", configuration));
|
||||
|
||||
task.setProductName("ThemeKitchenSinkA3");
|
||||
task.setIdentifier("com.yourcompany.ThemeKitchenSinkA3");
|
||||
task.setVersion("1.0");
|
||||
task.setAuthor("SlevenBits, Ltd.");
|
||||
task.setEmail("feedback @nospam@ yourcompany.com");
|
||||
task.setSummary("ThemeKitchenSinkA3");
|
||||
task.setSources(new FileList("**/*.j").exclude(FILE.join("Build", "**")).exclude(FILE.join("Frameworks", "Source", "**")));
|
||||
task.setResources(new FileList("Resources/**"));
|
||||
task.setIndexFilePath("index.html");
|
||||
task.setInfoPlistPath("Info.plist");
|
||||
|
||||
if (configuration === "Debug")
|
||||
task.setCompilerFlags("-DDEBUG -g");
|
||||
else
|
||||
task.setCompilerFlags("-O");
|
||||
});
|
||||
|
||||
task ("default", [projectName], function()
|
||||
{
|
||||
printResults(configuration);
|
||||
});
|
||||
|
||||
task ("build", ["default"], function()
|
||||
{
|
||||
updateApplicationSize();
|
||||
});
|
||||
|
||||
task ("debug", function()
|
||||
{
|
||||
ENV["CONFIGURATION"] = "Debug";
|
||||
JAKE.subjake(["."], "build", ENV);
|
||||
});
|
||||
|
||||
task ("release", function()
|
||||
{
|
||||
ENV["CONFIGURATION"] = "Release";
|
||||
JAKE.subjake(["."], "build", ENV);
|
||||
});
|
||||
|
||||
task ("run", ["debug"], function()
|
||||
{
|
||||
OS.system(["open", FILE.join("Build", "Debug", projectName, "index.html")]);
|
||||
});
|
||||
|
||||
task ("run-release", ["release"], function()
|
||||
{
|
||||
OS.system(["open", FILE.join("Build", "Release", projectName, "index.html")]);
|
||||
});
|
||||
|
||||
task ("deploy", ["release"], function()
|
||||
{
|
||||
FILE.mkdirs(FILE.join("Build", "Deployment", projectName));
|
||||
OS.system(["press", "-f", FILE.join("Build", "Release", projectName), FILE.join("Build", "Deployment", projectName)]);
|
||||
printResults("Deployment")
|
||||
});
|
||||
|
||||
task ("desktop", ["release"], function()
|
||||
{
|
||||
FILE.mkdirs(FILE.join("Build", "Desktop", projectName));
|
||||
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", projectName), FILE.join("Build", "Desktop", projectName, "ThemeKitchenSinkA3.app"));
|
||||
printResults("Desktop")
|
||||
});
|
||||
|
||||
task ("run-desktop", ["desktop"], function()
|
||||
{
|
||||
OS.system([FILE.join("Build", "Desktop", projectName, "ThemeKitchenSinkA3.app", "Contents", "MacOS", "NativeHost"), "-i"]);
|
||||
});
|
||||
|
||||
function printResults(configuration)
|
||||
{
|
||||
print("----------------------------");
|
||||
print(configuration+" app built at path: "+FILE.join("Build", configuration, projectName));
|
||||
print("----------------------------");
|
||||
}
|
||||
|
||||
function updateApplicationSize()
|
||||
{
|
||||
print("Calculating application file sizes...");
|
||||
|
||||
var contents = FILE.read(FILE.join("Build", ENV["CONFIGURATION"], projectName, "Info.plist"), { charset:"UTF-8" }),
|
||||
format = CFPropertyList.sniffedFormatOfString(contents),
|
||||
plist = CFPropertyList.propertyListFromString(contents),
|
||||
totalBytes = {executable:0, data:0, mhtml:0};
|
||||
|
||||
// Get the size of all framework executables and sprite data
|
||||
var frameworksDir = "Frameworks";
|
||||
|
||||
if (ENV["CONFIGURATION"] === "Debug")
|
||||
frameworksDir = FILE.join(frameworksDir, "Debug");
|
||||
|
||||
var frameworks = FILE.list(frameworksDir);
|
||||
|
||||
frameworks.forEach(function(framework)
|
||||
{
|
||||
if (framework !== "Source")
|
||||
addBundleFileSizes(FILE.join(frameworksDir, framework), totalBytes);
|
||||
});
|
||||
|
||||
// Read in the default theme name, and attempt to get its size
|
||||
var themeName = plist.valueForKey("CPDefaultTheme") || "Aristo2",
|
||||
themePath = nil;
|
||||
|
||||
if (themeName === "Aristo" || themeName === "Aristo2")
|
||||
themePath = FILE.join(frameworksDir, "AppKit", "Resources", themeName + ".blend");
|
||||
else
|
||||
themePath = FILE.join("Frameworks", "Resources", themeName + ".blend");
|
||||
|
||||
if (FILE.isDirectory(themePath))
|
||||
addBundleFileSizes(themePath, totalBytes);
|
||||
|
||||
// Add sizes for the app
|
||||
addBundleFileSizes(FILE.join("Build", ENV["CONFIGURATION"], projectName), totalBytes);
|
||||
|
||||
print("Executables: " + totalBytes.executable + ", sprite data: " + totalBytes.data + ", total: " + (totalBytes.executable + totalBytes.data));
|
||||
|
||||
var dict = new CFMutableDictionary();
|
||||
|
||||
dict.setValueForKey("executable", totalBytes.executable);
|
||||
dict.setValueForKey("data", totalBytes.data);
|
||||
dict.setValueForKey("mhtml", totalBytes.mhtml);
|
||||
|
||||
plist.setValueForKey("CPApplicationSize", dict);
|
||||
|
||||
FILE.write(FILE.join("Build", ENV["CONFIGURATION"], projectName, "Info.plist"), CFPropertyList.stringFromPropertyList(plist, format), { charset:"UTF-8" });
|
||||
}
|
||||
|
||||
function addBundleFileSizes(bundlePath, totalBytes)
|
||||
{
|
||||
var bundleName = FILE.basename(bundlePath),
|
||||
environment = bundleName === "Foundation" ? "Objj" : "Browser",
|
||||
bundlePath = FILE.join(bundlePath, environment + ".environment");
|
||||
|
||||
if (FILE.isDirectory(bundlePath))
|
||||
{
|
||||
var filename = bundleName + ".sj",
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, filename));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.executable += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "dataURLs.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.data += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLData.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.mhtml += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLPaths.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.mhtml += filePath.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
index-debug.html
|
||||
__project.name__
|
||||
|
||||
Created by __user.name__ on __project.date__.
|
||||
Copyright __project.year__, __organization.name__ All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!--[if lte IE 8]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1">
|
||||
<![endif]-->
|
||||
<!--[if gte IE 9]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
|
||||
<![endif]-->
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<link rel="apple-touch-icon" href="Resources/icon.png">
|
||||
<link rel="apple-touch-startup-image" href="Resources/default.png">
|
||||
|
||||
<title>__project.name__</title>
|
||||
|
||||
<!-- Custom javascript goes here -->
|
||||
<!-- End custom javascript -->
|
||||
|
||||
<script type="text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks"];
|
||||
// The below will tell the compiler to generate debug symbols, type signatures and not inline objj_msgSend functions.
|
||||
// This will affect only Objective-J code that is compiled when loading the application. It will not affect precompiled
|
||||
// code like the Cappuccino frameworks.
|
||||
//
|
||||
// Debug symbols will give each Objective-J method a Javascript function name. Without a name it will be very hard to find
|
||||
// the methods in the debugger.
|
||||
// Type Signatures will give type information to the Objective-J runtime for instance variables and methods.
|
||||
// Inline objj_msgSend will give a speed increase but decorators will not work. Check below on debug options for
|
||||
// more information on decorators.
|
||||
//
|
||||
// Uncomment or comment on the line below to change the flags
|
||||
OBJJ_COMPILER_FLAGS = ["IncludeDebugSymbols", "IncludeTypeSignatures", "SourceMap", "InlineMsgSend"];
|
||||
|
||||
var progressBar = null;
|
||||
|
||||
OBJJ_PROGRESS_CALLBACK = function(percent, appSize, path)
|
||||
{
|
||||
percent = percent * 100;
|
||||
|
||||
if (!progressBar)
|
||||
progressBar = document.getElementById("progress-bar");
|
||||
|
||||
if (progressBar)
|
||||
progressBar.style.width = Math.min(percent, 100) + "%";
|
||||
}
|
||||
|
||||
var loadingHTML =
|
||||
'<div id="loading">' +
|
||||
' <div id="loading-text">Loading...</div>' +
|
||||
' <div id="progress-indicator">' +
|
||||
' <span id="progress-bar" style="width:0%"></span>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="Frameworks/Debug/Objective-J/Objective-J.js" charset="UTF-8"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
objj_msgSend_reset();
|
||||
|
||||
// DEBUG OPTIONS:
|
||||
// Decorators will only work when the code is compiled without the compiler option 'InlineMsgSend'. Check above.
|
||||
|
||||
// Uncomment to enable printing of backtraces on exceptions:
|
||||
//objj_msgSend_decorate(objj_backtrace_decorator);
|
||||
|
||||
// Uncomment to supress exceptions that take place inside a message
|
||||
//objj_msgSend_decorate(objj_supress_exceptions_decorator)
|
||||
|
||||
// Uncomment to enable runtime type checking:
|
||||
//objj_msgSend_decorate(objj_typecheck_decorator);
|
||||
|
||||
// Uncomment (along with both above) to print backtraces on type check errors:
|
||||
//objj_typecheck_prints_backtrace = true;
|
||||
|
||||
// Uncomment to disable the default logger (CPLogConsole if window.console exists, CPLogPopup otherwise):
|
||||
//CPLogUnregister(CPLogDefault);
|
||||
|
||||
// Uncomment to enable a specific logger:
|
||||
//CPLogRegister(CPLogConsole);
|
||||
//CPLogRegister(CPLogPopup);
|
||||
|
||||
// Tag view DOM elements with a "data-cappuccino-view" attribute that contains
|
||||
// the class name of the view that created them. Comment this or set to false to disable.
|
||||
appkit_tag_dom_elements = true;
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
html, body, h1, p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* We need a body wrapper because Cappuccino is unhappy if we change the body element */
|
||||
#cappuccino-body {
|
||||
/* Position it absolutely so it will fill the height without content */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
/* Put it at the bottom of the stack so it doesn't interfere with UI */
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#cappuccino-body .container {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#cappuccino-body .content {
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#loading {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
}
|
||||
|
||||
#loading-text {
|
||||
height: 1.5em;
|
||||
color: #555;
|
||||
font: normal bold 36px/36px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#progress-indicator {
|
||||
padding: 0px;
|
||||
height: 16px;
|
||||
border: 5px solid #555;
|
||||
border-radius: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
display: block;
|
||||
height: 18px;
|
||||
|
||||
/* Compensate for moving the bar left 1px to overlap the indicator border */
|
||||
border-right: 1px solid #555;
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#noscript {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
padding: 1em 1.5em;
|
||||
border: 5px solid #555;
|
||||
border-radius: 16px;
|
||||
background-color: white;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
font: bold 24px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#noscript a {
|
||||
color: #98c0ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="cappuccino-body">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<script type="text/javascript">
|
||||
document.write(loadingHTML);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<noscript style="position:absolute; top:0; left:0; width:100%; height:100%">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div id="noscript">
|
||||
<p style="font-size:120%; margin-bottom:.75em">JavaScript is required for this application.</p>
|
||||
<p><a href="http://www.enable-javascript.com" target="_blank">Enable JavaScript</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
index.html
|
||||
__project.name__
|
||||
|
||||
Created by __user.name__ on __project.date__.
|
||||
Copyright __project.year__, __organization.name__ All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!--[if lte IE 8]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1">
|
||||
<![endif]-->
|
||||
<!--[if gte IE 9]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
|
||||
<![endif]-->
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<link rel="apple-touch-icon" href="Resources/icon.png">
|
||||
<link rel="apple-touch-startup-image" href="Resources/default.png">
|
||||
|
||||
<title>__project.name__</title>
|
||||
|
||||
<!-- Custom javascript goes here -->
|
||||
<!-- End custom javascript -->
|
||||
|
||||
<script type="text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
// The below will tell the compiler to not generate debug symbols but will generate type signatures and inline objj_msgSend functions.
|
||||
// This will affect only Objective-J code that is compiled when loading the application. It will not affect precompiled
|
||||
// code like the Cappuccino frameworks.
|
||||
// Uncomment or comment on the line below to change the flags
|
||||
OBJJ_COMPILER_FLAGS = [/*"IncludeDebugSymbols"*/, "IncludeTypeSignatures"/*, "SourceMap"*/, "InlineMsgSend"];
|
||||
|
||||
var progressBar = null;
|
||||
|
||||
OBJJ_PROGRESS_CALLBACK = function(percent, appSize, path)
|
||||
{
|
||||
percent = percent * 100;
|
||||
|
||||
if (!progressBar)
|
||||
progressBar = document.getElementById("progress-bar");
|
||||
|
||||
if (progressBar)
|
||||
progressBar.style.width = Math.min(percent, 100) + "%";
|
||||
}
|
||||
|
||||
var loadingHTML =
|
||||
'<div id="loading">' +
|
||||
' <div id="loading-text">Loading...</div>' +
|
||||
' <div id="progress-indicator">' +
|
||||
' <span id="progress-bar" style="width:0%"></span>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="Frameworks/Objective-J/Objective-J.js" charset="UTF-8"></script>
|
||||
|
||||
<style type="text/css">
|
||||
html, body, h1, p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* We need a body wrapper because Cappuccino is unhappy if we change the body element */
|
||||
#cappuccino-body {
|
||||
/* Position it absolutely so it will fill the height without content */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
/* Put it at the bottom of the stack so it doesn't interfere with UI */
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#cappuccino-body .container {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#cappuccino-body .content {
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#loading {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
}
|
||||
|
||||
#loading-text {
|
||||
height: 1.5em;
|
||||
color: #555;
|
||||
font: normal bold 36px/36px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#progress-indicator {
|
||||
padding: 0px;
|
||||
height: 16px;
|
||||
border: 5px solid #555;
|
||||
border-radius: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
display: block;
|
||||
height: 18px;
|
||||
|
||||
/* Compensate for moving the bar left 1px to overlap the indicator border */
|
||||
border-right: 1px solid #555;
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#noscript {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
padding: 1em 1.5em;
|
||||
border: 5px solid #555;
|
||||
border-radius: 16px;
|
||||
background-color: white;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
font: bold 24px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#noscript a {
|
||||
color: #98c0ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="cappuccino-body">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<script type="text/javascript">
|
||||
document.write(loadingHTML);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<noscript style="position:absolute; top:0; left:0; width:100%; height:100%">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div id="noscript">
|
||||
<p style="font-size:120%; margin-bottom:.75em">JavaScript is required for this application.</p>
|
||||
<p><a href="http://www.enable-javascript.com" target="_blank">Enable JavaScript</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* AppController.j
|
||||
* ThemeKitchenSink
|
||||
*
|
||||
* Created by Alexander Ljungberg on May 11, 2014.
|
||||
* Copyright 2014, SlevenBits, Ltd. All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
@import "AppController.j"
|
||||
|
||||
|
||||
function main(args, namedArgs)
|
||||
{
|
||||
CPApplicationMain(args, namedArgs);
|
||||
}
|
||||
@@ -6,11 +6,11 @@
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>ThemeKitchenSink</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo2</string>
|
||||
<key>CPBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CPHumanReadableCopyright</key>
|
||||
<string>Copyright © 2014, SlevenBits, Ltd. All rights reserved.</string>
|
||||
<key>CPDefaultTheme</key>
|
||||
<string>Aristo3</string>
|
||||
<key>CPBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CPHumanReadableCopyright</key>
|
||||
<string>Copyright © 2014, SlevenBits, Ltd. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -211,8 +211,8 @@ var NSButtonIsBorderedMask = 0x00800000,
|
||||
}
|
||||
}
|
||||
|
||||
_keyEquivalent = [cell keyEquivalent];
|
||||
_keyEquivalentModifierMask = [cell keyEquivalentModifierMask];
|
||||
[self setKeyEquivalent:[cell keyEquivalent]];
|
||||
[self setKeyEquivalentModifierMask:[cell keyEquivalentModifierMask]];
|
||||
_imageDimsWhenDisabled = YES;
|
||||
|
||||
_allowsMixedState = [cell allowsMixedState];
|
||||
|
||||
@@ -61,8 +61,9 @@
|
||||
|
||||
if (self)
|
||||
{
|
||||
var flags = [aCoder decodeIntForKey:@"NSCellFlags"],
|
||||
flags2 = [aCoder decodeIntForKey:@"NSCellFlags2"];
|
||||
var flags = [aCoder decodeIntForKey:@"NSCellFlags"],
|
||||
flags2 = [aCoder decodeIntForKey:@"NSCellFlags2"],
|
||||
newControlSize = [aCoder decodeIntForKey:@"NSControlSize2"];
|
||||
|
||||
_state = (flags & 0x80000000) ? CPOnState : CPOffState;
|
||||
_isHighlighted = (flags & 0x40000000) ? YES : NO;
|
||||
@@ -84,6 +85,11 @@
|
||||
_usesSingleLineMode = (flags2 & 0xF4240) ? YES : NO;
|
||||
|
||||
_tag = [aCoder decodeIntForKey:@"NSTag"];
|
||||
|
||||
// Since MacOS 11, there's a new control size "Large" and a new key "NSControlSize2"
|
||||
// For compatibility reasons, we compute the control size based on the old and the new methods
|
||||
if (newControlSize > 2)
|
||||
_controlSize = newControlSize;
|
||||
|
||||
_objectValue = [aCoder decodeObjectForKey:@"NSContents"];
|
||||
_font = [aCoder decodeObjectForKey:@"NSSupport"];
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
var NSProgressIndicatorSpinningFlag = 1 << 12;
|
||||
|
||||
@implementation CPProgressIndicator (NSCoding)
|
||||
- (id)updateBackgroundColor
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (id)NS_initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
|
||||
Vendored
+1
-1
@@ -233,7 +233,7 @@ ObjectiveJFlattener.prototype.serializeFunctions = function() {
|
||||
var functionString = executable._function.toString().replace(", require, exports, module, system, print, window", ""); // HACK
|
||||
|
||||
var relative = this.rootPath.relative(path).toString();
|
||||
this.functionsBuffer.push("ObjectiveJ.StaticResource._cacheFunction(new CFURL("+JSON.stringify(relative)+", baseURL),\n"+functionString+");");
|
||||
this.functionsBuffer.push("ObjectiveJ.FileExecutable._cacheFunction(new CFURL("+JSON.stringify(relative)+", baseURL),\n"+functionString+");");
|
||||
}
|
||||
|
||||
var bundle = this.context.global.CFBundle.bundleContainingURL(path);
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+16
-6
@@ -4,14 +4,11 @@
|
||||
|
||||
var fs = require("fs"),
|
||||
acorn = require("objj-parser"),
|
||||
walk = require("objj-parser/util/walk"),
|
||||
walk = require("acorn-walk"),
|
||||
stream = ObjectiveJ.term;
|
||||
|
||||
debugger;
|
||||
|
||||
function main(args)
|
||||
{
|
||||
debugger;
|
||||
args.shift();
|
||||
|
||||
if (args.length < 1)
|
||||
@@ -40,6 +37,8 @@ function raise(pos, message)
|
||||
throw syntaxError;
|
||||
}
|
||||
|
||||
function ignore(_node, _st, _c) {}
|
||||
|
||||
var errors = [],
|
||||
xcc = walk.make(
|
||||
{
|
||||
@@ -115,7 +114,18 @@ var errors = [],
|
||||
else
|
||||
raise(node.loc.start, "Action methods must have exactly one parameter");
|
||||
}
|
||||
}
|
||||
},
|
||||
TypeDefStatement: ignore,
|
||||
ClassStatement: ignore,
|
||||
MessageSendExpression: ignore,
|
||||
GlobalStatement: ignore,
|
||||
ProtocolDeclarationStatement: ignore,
|
||||
ArrayLiteral: ignore,
|
||||
Reference: ignore,
|
||||
DictionaryLiteral: ignore,
|
||||
Dereference: ignore,
|
||||
ImportStatement: ignore,
|
||||
SelectorLiteralExpression: ignore
|
||||
}
|
||||
);
|
||||
|
||||
@@ -154,7 +164,7 @@ function parser(args)
|
||||
outputHeaderURL = new CFURL([outputDirectory stringByAppendingPathComponent:baseFilenameWithNoExtension + ".h"]),
|
||||
outputImplementationURL = new CFURL([outputDirectory stringByAppendingPathComponent:baseFilenameWithNoExtension + ".m"]),
|
||||
source = fs.readFileSync(sourcePath, { encoding: "utf8" }),
|
||||
tokens = acorn.parse(source, { locations:true, sourceFile:sourcePath }),
|
||||
tokens = acorn.parse(source, { locations:true, sourceFile:sourcePath, ecmaVersion: 2022 }),
|
||||
classesInformation = [],
|
||||
ObjectiveCSource = "",
|
||||
ObjectiveCHeader = "",
|
||||
|
||||
Generated
+9
-9
@@ -260,9 +260,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
|
||||
"integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -482,9 +482,9 @@
|
||||
}
|
||||
},
|
||||
"xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
|
||||
"integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -604,9 +604,9 @@
|
||||
}
|
||||
},
|
||||
"xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
|
||||
"integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user