Compare commits

..
Author SHA1 Message Date
Aparajita Fishman 8f1191733b Fixed race condition when editing table cells
Previously, when double-clicking on a table cell to edit it, there was a possibility of a race condition. A refresh of the display was requested, then the run loop was passed through once. But it was possible for the display refresh to not be queued when the run loop was passed through, in which case the refresh would cancel the editing. Or at least I think that's what was happening.  ;-)

This commit (hopefully) eliminates the race condition by synchronously refreshing the layout and display.

Made some miscellaneous formatting fixes as well.
2016-01-19 23:12:11 -08:00
Antoine Mercadal 346a0b84ae Merge branch 'master' of https://github.com/cappuccino/cappuccino 2016-01-19 13:28:17 -08:00
Antoine Mercadal ad48381744 FIXED: Bug introduced by https://github.com/cappuccino/cappuccino/commit/9fba72cbab42145dc585c01efd82ce645373c04e#diff-227ef108401c2160357ae3fc65018c65R620
The changes in CPBox were messing with the subviews encoding. This patch restores the previous behavior of CPBox.
The original bug might be reintroduced, but this was definitly not the good solution
2016-01-19 13:26:10 -08:00
cacaodev 6496a6643f Merge remote-tracking branch 'upstream/master' 2016-01-16 22:06:42 +01:00
cacaodev e172217427 Merge pull request #2404 from cacaodev/CPArray-mapUsingBlock
New: CPArray -arrayByApplyingBlock:
2016-01-16 19:25:07 +01:00
cacaodev 8c67eee4c8 (CPArray)arrayByApplyingBlock: documentation 2016-01-16 18:28:04 +01:00
Antoine Mercadal e260632a45 Merge pull request #2408 from slevenbits/xcodecapp-settings-tweaks
Fix some typos/grammar in XcodeCapp settings, plus improve layout.
2016-01-14 16:03:52 -08:00
Antoine Mercadal 678f4fca8a Merge pull request #2409 from primalmotion/predicate-fix
FIXED: CPPredicate's predicateFormat with a CPNull value as right exp…
2016-01-14 15:54:56 -08:00
Antoine Mercadal 5d88d07edc FIXED: CPPredicate's predicateFormat with a CPNull value as right expression wasn't working
Previously a CPPredicate created with format `value == nil` was converted back to `value == <CPNull @ xxxx>`

This patch ensures `predicateFormat` returns `value == nil`

Test added in CPPredicateTest.j
2016-01-14 15:50:24 -08:00
cacaodev 08aa591c65 NEW CPArray -arrayByApplyingBlock:(Function/*element, index*/)aBlock
Test for CPArray -arrayByApplyingBlock:
2016-01-14 21:57:57 +01:00
Alexander Ljungberg f2a8edb29f Fixed: some uneven margins and spacing in XcodeCapp settings.
The horizontal margin was larger than the vertical margin, and larger than the margin guideline for Cocoa.

Also one of the hint text lines was much closer to its textbox than the other two, and not well aligned within its textfield (should be right aligned).
2016-01-14 11:40:09 +00:00
Alexander Ljungberg 50d7e8dc3d Fixed: some typos/grammar in XcodeCapp settings. 2016-01-14 11:40:09 +00:00
Alexander Ljungberg 8403e36d4d New: arrange the kitchen sink test windows a little.
It just looks better.
2016-01-14 11:28:58 +00:00
Alexander Ljungberg 2dda326cc4 Fixed: radio button cropped in Theme Kitchen Sink test.
Without this fix the radio group was too short for Aristo 2 and as a result the bottom radio button had its bottom pixels cut off.
2016-01-14 11:20:31 +00:00
Antoine Mercadal 9002a8db04 Merge pull request #2407 from t00f/fix_2406_splitview
Test: Added a test that verifies CPSplitView behavior
2016-01-12 09:31:40 -08:00
Christophe Serafin 5ec5198dda Test: Added a test that verifies CPSplitView divider behavior when having only 1 subview.
Issue#2406 reported a behavior different from Cocoa Framework. This test validates that Cappuccino CPSplitView is working well when adding
and removing a subview. A CPSplitView that has only one subview should resize it to take the whole space available.

This PR adds a new test to Tests/Manual
2016-01-12 16:29:24 +01:00
Antoine Mercadal 0326b7d7ac Merge pull request #2405 from Dogild/CPBoxNib2CibFixed
Fixed: nib2cib on a NSBox fails when having a CPTableView in it
2016-01-05 13:08:04 -08:00
Aparajita Fishman 2f5e7f3b2d Allow custom formatters in xibs
Previously, adding a "Custom Formatter" (direct subclass of NSFormatter) to a cell in Xcode would cause a failure in nib2cib, because it didn't know how to deal with it.

This commit adds support for custom formatters in nib2cib (and thus in xibs), and updates the CPFormatter test app to demonstrate that this works.
2016-01-03 15:26:06 -08:00
Alexandre Wilhelm 9fba72cbab Fixed: nib2cib on a NSBox fails when having a CPTableView in it
Previously, when having a CPTableView in a CPBox, nib2cib failed due to a superview not defined yet. This was raised because in nib2cib we used the method setFrame on the CPBox to modify the frame of the CPBox (we need to modify this frame because the sizing difference between cappuccino and cocoa). We now directly modify the attribute _frame from the object and update the bounds in the same time.

This PR fixed another bug as well. Previously the contentView of the CPBox was not encoded, we now encode it. This allows us to get a full CPView object for the contentView, previously we got a weird (I have no idea how this object was created though...) object CPView with some missing attributes. This raised a crash because _trackingAreas or _themeState were not defined in this object.

Fixed #2400
2015-12-28 17:58:10 -08:00
cacaodev 912fdeaf09 Merge pull request #2403 from cacaodev/cptabviewitem-test
TEST: CPTabViewBindings manual test
Adds a test for tab view item label change.
2015-12-28 16:37:52 +01:00
cacaodev e6e2f4c22c TEST: CPTabViewBindings manual test
Add test for pr #1921 *Label size not calculated when they change*

closes #1921
2015-12-27 18:19:48 +01:00
cacaodev 0ac08456bc Merge pull request #2401 from cacaodev/CPSubqueryExpression
FIXED: Subquery expressions were ignoring evaluation context
2015-12-26 00:12:13 +01:00
cacaodev 80a3549ef5 FIXED: Subquery expressions were ignoring evaluation context
When the predicate part of a subquery expression was containing
variables, the substitution was ignored.
Now, the subpredicate can contain variables that will be substituted
when calling evaluateWithObject:substitutionVariables:.

Added expression test and predicate parsing test.
2015-12-23 18:49:07 +01:00
Alexandre Wilhelm 770d5eb257 Fixed: nib2cib is broken due to the new feature CPTrackingArea. The array _trackingAres is not initialize when creating NS* object 2015-12-22 11:10:31 -08:00
Aparajita Fishman 932b825818 Merge pull request #2395 from didierkorthoudt/CPTrackingArea
Implementation of CPTrackingArea
2015-12-21 15:23:51 -08:00
Didier Korthoudt 2ab50c6b61 code style fixed 2015-12-20 09:29:54 +01:00
Didier Korthoudt d7426f6d35 Several fixes 2015-12-19 21:56:27 +01:00
Didier Korthoudt 8f15266f0a delayed cursorUpdates modification 2015-12-17 21:48:27 +01:00
Didier Korthoudt 936c7190c7 manual test update 2015-12-16 22:16:31 +01:00
Didier Korthoudt 3452984b8e 2 cases added 2015-12-16 19:29:14 +01:00
Didier Korthoudt 3e17890528 cursor tests in CPViewTest 2015-12-15 23:33:10 +01:00
Didier Korthoudt 2a7c50d356 events-during-drag-fix 2015-12-15 15:09:19 +01:00
Didier Korthoudt 05bca368c2 several-fixes 2015-12-10 18:29:34 +01:00
Didier Korthoudt 89c78f6030 unit-test-additions 2015-12-09 21:38:50 +01:00
Alexandre Wilhelm b59b0259ca Fixed: CPDateFormatter did not work with symbols MM and LL with the month of december 2015-12-09 10:51:51 -08:00
Didier Korthoudt e46e1b20de unit-test-v2 2015-12-08 23:09:20 +01:00
Didier Korthoudt 7089abcef2 missing-fix 2015-12-08 22:41:18 +01:00
Didier Korthoudt e6148a97a3 _CPWindowView-fix 2015-12-08 18:33:27 +01:00
Didier Korthoudt 9e40033a92 skipped-fix 2015-12-07 15:13:31 +01:00
Didier Korthoudt 08d4e19851 various-fixes
Following @aparajita indications
2015-12-07 14:58:42 +01:00
Didier Korthoudt dcc0768411 Unit-test+fix 2015-12-05 20:14:24 +01:00
Didier Korthoudt f14062c721 Style-fixes 2015-12-03 20:44:34 +01:00
Alexandre Wilhelm 0508b70658 Refactored: refactoring of the file CFHTTPRequest.js 2015-12-02 11:32:04 -08:00
Alexandre Wilhelm 04f8545397 Merge pull request #2368 from Rosch/master
Fix startup on IE10
2015-12-02 11:25:59 -08:00
cacaodev 633f985360 CFHTTPRequest : removed unused variable. 2015-12-02 17:51:49 +01:00
Didier Korthoudt a649a6ebac CIB-fix
For some (yet unknown) reason, when declaring a view with tracking area
in a CIB, there’s some weird things happening that try to use the
_trackingAreas array when it’s not yet initialized… So, replacing
_trackingAreas.length by [_trackingAreas count] resolves the problem.
2015-12-02 00:03:59 +01:00
Didier Korthoudt 634a8b4532 cocoadev-fix 2015-12-01 21:16:02 +01:00
Didier Korthoudt f3709a41e3 removeAllTrackingAreas 2015-12-01 21:06:55 +01:00
Didier Korthoudt 2bfebfee3c Manual-test-fix 2015-12-01 05:58:36 +01:00
Didier Korthoudt 76ab5fbf78 CPControl-fix 2015-11-30 09:07:33 +01:00
Didier Korthoudt c0b0c74638 CPTableHeaderView-fix 2015-11-30 05:49:11 +01:00
Didier Korthoudt 6c0b771758 CPSplitView-fix 2015-11-29 21:54:36 +01:00
Didier Korthoudt 2f983fc305 optimization 2015-11-29 15:37:33 +01:00
Antoine Mercadal 3cf0d60f5d Merge pull request #2399 from mrcarlberg/default_include_method_types
Default include type signatures for all kind of builds
2015-11-23 13:38:14 -08:00
Alexandre Wilhelm 751e76b453 Merge pull request #2396 from mrcarlberg/objj_runtime_method_type_accessors
Objective-J runtime method type accessor functions
2015-11-23 10:40:44 -08:00
Martin Carlberg c2f95f7953 Fixed: Runtime method argument functions will now comply to how Objective-C runtime works. 2015-11-23 10:48:15 +01:00
Didier Korthoudt 924b994df2 First updateTrackingAreas implementation 2015-11-21 17:25:43 +01:00
Didier Korthoudt b10de5e82c Code-refactoring 2015-11-21 09:20:28 +01:00
Martin Carlberg 58aeb2e40f Fixed: Add test cases 2015-11-20 14:54:23 +01:00
Martin Carlberg 141d737ca5 Fixed: Removed old function instead of mark it deprecated 2015-11-20 14:54:11 +01:00
Martin Carlberg d0fd53a2ca Added: Function method_getNumberOfArguments 2015-11-20 14:53:46 +01:00
Martin Carlberg 3e0a6ea2cf Fixed: Return NULL when out of bounce 2015-11-20 14:52:53 +01:00
Martin Carlberg 758ad3eb06 Fixed: Method types was not copied into new KVO implementation of a class that is observed with KVO.
Some test cases are also added for this.
2015-11-20 12:43:53 +01:00
Martin Carlberg b132a66707 Fixed: Compiler option 'IncludeTypeSignatures' is default turn on for all type of builds. It can optionally be turned off. 2015-11-20 12:33:00 +01:00
Didier Korthoudt e1daf9467c Code-style-fixes 2015-11-19 06:13:18 +01:00
Alexandre Wilhelm 5457ddb44f Fixed: ignored compiled nib file of xcodecapp when having xcodecapp folder in a XcodeCapp project 2015-11-18 13:09:39 -08:00
Alexandre Wilhelm 6fee13b0a5 Fixed: XcodeCapp did not ignore folder when sourcing them 2015-11-18 11:29:09 -08:00
Alexandre Wilhelm 2b17e173f5 Fixed: only reload the operations and errors dataview when they are currently displayed in XcodeCapp 2015-11-18 11:01:09 -08:00
cacaodev 569d4a5d8b Merge branch 'master' of git://github.com/cappuccino/cappuccino 2015-11-15 19:34:39 +01:00
Alexandre Wilhelm 51a88a23ac Fixed: removed files Lumberjack 2015-11-13 10:48:48 -08:00
Alexandre Wilhelm f41504ca57 Fixed: remove library CocoaLumberjack in XcodeCapp. This lib added memory leaks when using it in an NSOperation. We now simply use NSLog 2015-11-13 10:48:02 -08:00
Alexandre Wilhelm bacb7018cb Fixed: thread of the spinning was always launched even if the spinner was not displayed in XcodeCapp 2015-11-12 16:29:46 -08:00
Alexandre Wilhelm 671ed3c2fe Fixed: memory leak in XcodeCapp due to NSAppleScript. We now use osascript 2015-11-12 11:59:00 -08:00
Alexandre Wilhelm 372e994617 Fixed: XcodeCapp ignored by defaul the ressources folder 2015-11-11 13:24:18 -08:00
Martin Carlberg e4560df62f Fixed: Use correct property in decorator 2015-11-10 11:31:04 +01:00
Martin Carlberg 920daf7fde Fixed: Marked function 'method_getTypes' as deprecated 2015-11-10 11:30:29 +01:00
Martin Carlberg ad1a15faaa New: Added runtime functions 'method_copyReturnType' and 'method_copyArgumentType'. 2015-11-10 11:29:52 +01:00
Didier Korthoudt 459cf2e380 Tests/Manual TrackingArea test app 2015-11-08 19:46:19 +01:00
Didier Korthoudt 8d058a53fd CursorUpdate management + various fixes 2015-11-08 12:06:13 +01:00
Didier Korthoudt d864ae9fef Code style 2015-11-07 07:07:14 +01:00
Didier Korthoudt f1eeb61264 Fix: log traces removal 2015-11-06 15:05:34 +01:00
Didier Korthoudt 1418dd8b29 First submit for CPTrackingArea 2015-11-06 14:09:23 +01:00
cacaodev 0297980ca3 Merge branch 'master' of git://github.com/cappuccino/cappuccino 2015-11-02 19:46:36 +01:00
cacaodev 5e94aaa2cb Merge pull request #2391 from zittix/fix_initial_cptabview_selection
Fixed: default selection of a CPTabView item
2015-11-02 09:25:04 +01:00
Martin Carlberg b929a8fe2e Merge pull request #2389 from mrcarlberg/faster_theme_attribute
Faster theme attribute handling
2015-11-01 20:42:13 +01:00
Martin Carlberg 8c2f0bacf1 Merge pull request #2384 from mrcarlberg/even_faster_objj_msg_send
Speedup of the Cappuccino framework by more efficient objj_msgSend
2015-11-01 20:39:18 +01:00
Alexandre Wilhelm 0bc02de012 Fixed: method loadCibNamed:(CPString)aName owner:(id)anOwner is broken
Previously, the method CPBundle loadCibNamed:(CPString)aName owner:(id)anOwner was broken, it loaded the cib asynchronous instead of synchronous. This was due to bad refactoring of a previous commit refs#c5b250236fa662e3da04e442f4414f95d64d9308

This PR refactor this piece of code and make works the method synchronous as it should.

More information here : https://github.com/cappuccino/cappuccino/commit/c5b250236fa662e3da04e442f4414f95d64d9308#commitcomment-14109453
2015-11-01 08:57:06 -08:00
Alexandre Wilhelm 411a282063 Test: fixed manual test SmartFoldersDemo 2015-11-01 08:56:49 -08:00
Alexandre Wilhelm 6c4d55622b Fixed: travis was broken because variable named char 2015-10-31 18:09:53 -07:00
Alexandre Wilhelm e214bf3581 Refactoring: refacroting of the method charPositionOfString:(CPString)aString withFont:(CPFont)aFont forPoint:(CGPoint)aPoint refs#78e29d2bbfe43f5a8bda2e65d974f7effb8fcbd4 2015-10-31 14:50:41 -07:00
Alexandre Wilhelm 78e29d2bbf Fixed: position of cursor in a CPTextField was wrong when becoming firstResponder
Previously, when a CPTextField became firstResponder, the cursor was always set at the first position. Now it position it at the expected location.
2015-10-31 12:31:49 -07:00
Mathieu Monneyandcacaodev a93c10b794 Fixed: default selection of a CPTabView
The selected tab item view is now loaded correctly after awakeFromCib, which wasn't the case before.
2015-10-30 15:27:34 +01:00
Martin Carlberg 3240d6f11a Fixed: Added TODO comments that deprecated use should be remove in future release 2015-10-30 11:20:51 +01:00
Antoine Mercadal d5f38fe2f2 Merge pull request #2392 from Dogild/RunLoopBlock
New: added the method performBlock in CPRunLoop
2015-10-27 17:36:22 -07:00
Alexandre Wilhelm d4b1680727 Fixed: new CPPLatformWindow did not have a platformPasteboard. copy and paste did not work for other platformWindow except the main one 2015-10-27 12:08:10 -07:00
Alexandre Wilhelm b04e5760fb Test: added unittest for method stringByReplacingCharactersInRange of CPString 2015-10-27 12:06:47 -07:00
Martin Carlberg 42c629ca45 Fixed: Use ’;’ instead of ’,’ 2015-10-27 09:03:55 +01:00
Martin Carlberg 68f45beb20 Fixed: Code formatting 2015-10-27 09:03:26 +01:00
Mathieu MonneyandMathieu Monney ea95897bbd Fixed: default selection of a CPTabView
The selected tab item view is now loaded correctly after awakeFromCib, which wasn't the case before.
2015-10-27 08:09:17 +01:00
Alexandre Wilhelm 4d1dc0c949 New: added the method performBlock in CPRunLoop
Previously, we could only give a selector and a target for perform in the runLoop. Now we can give a block.
This feature is used in the CPTextField class. Previously, when we wanted to call a function at the end of the stack we used window.setTimeout, however due to HTML5 specifications this wasn't called just at the end of the stack but at least 4ms (more information here https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout#Minimum_delay_and_timeout_nesting). Now we give a block to perform, and this block will be performed in the next runloop.

Unittest has been added in Tests/Foundation/CPRunLoopTest.j
2015-10-26 15:36:13 -07:00
Martin Carlberg 9fc892f723 Fixed: Check if ’OBJJ_COMPILER_FLAGS’ exists in a correct way 2015-10-26 14:47:51 +01:00
Martin Carlberg 5541381afe Fixed: Only have one ’release’ option for Jake.
Removed the ’release-inline’ option when building as it is not practical to have two different options. The release build now always has inlined msgSend functions. If a release build is needed without inlined msgSend functions an edit of the ”-O2” option to ”-O” in the Jakefile is needed.
2015-10-26 09:53:39 +01:00
Martin Carlberg ed12bae6aa Fixed: Removed unused global variable 2015-10-23 16:38:12 +02:00
Martin Carlberg 1520c1ebab Fixed: Linting 2015-10-23 15:40:45 +02:00
Martin Carlberg 790190e5c3 Fixed: Removed code collecting statistic of theme attribute creation and some logging 2015-10-23 10:39:52 +02:00
Martin Carlberg 24889f7dcd Fixed: Adjust test cases 2015-10-23 10:19:14 +02:00
Martin Carlberg 8397685430 Fixed: Joined caches for theme attributes for better performance
Conflicts:
	AppKit/CPTheme.j
2015-10-23 10:19:02 +02:00
Martin Carlberg fc411c5c1c Fixed: Cache ThemeAttribute
Conflicts:
	AppKit/CPTheme.j
2015-10-23 10:18:03 +02:00
Martin Carlberg 3a91f43bbb Fixed: Test cases are working correctly 2015-10-21 12:26:39 +02:00
Martin Carlberg 5e1f1675b8 Fixed: Method ’_themeAttributes’ never saved anything in the cache 2015-10-21 12:26:31 +02:00
Martin Carlberg 9946987f69 Fixed: Cache ThemeState for better performance 2015-10-21 12:26:12 +02:00
Aparajita Fishman 526181a76b Fixed: CPImage -size returned internal object ref
Previously, CPImage -size returned a reference to the internal _size object, which allowed the caller to directly change _size. In Cocoa a copy of the size is returned.

This commit returns a copy of _size to ensure no unwanted side effects.
2015-10-16 16:20:24 -04:00
Alexandre Wilhelm ce8f8c05e1 Merge pull request #2385 from Dogild/CPNotificationQueue
New: added the class CPNotificationQueue
2015-10-14 11:18:41 -07:00
Martin Carlberg 3844b88f7e Fixed: The browser can’t promise when a javascript block will execute so compiler flags can be set after the files are compiled. This implementation works all the time. 2015-10-09 16:06:53 +02:00
Alexandre Wilhelm db3c232173 Fixed: add CPNotificationQueue.j in Foundation.j 2015-10-08 16:07:50 -07:00
Alexandre Wilhelm 22b77af9d3 New: added the class CPNotificationQueue
This PR adds the feature of CPNotificationQueue.

Cappuccino provides a framework for sending messages between objects within
a process called notifications. CPNotificationQueue objects (or simply notification queues)
act as buffers for notification centers (instances of CPNotificationCenter).
Whereas a notification center distributes notifications when posted,
notifications placed into the queue can be delayed until the end of the current pass through the run loop
or until the run loop is idle. Duplicate notifications can also be coalesced so that only one notification
is sent although multiple notifications are posted. A notification queue maintains notifications
(instances of C¨Notification) generally in a first in first out (FIFO) order.
When a notification rises to the front of the queue, the queue posts it to the notification center,
which in turn dispatches the notification to all objects registered as observers.

More informations here :https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationQueue_Class/index.html#//apple_ref/occ/instm/NSNotificationQueue/enqueueNotification:postingStyle:coalesceMask:forModes:

Unit-Tests in Tests/Foundation/CPNotificationQueueTest.j
2015-10-08 16:06:16 -07:00
Alexandre Wilhelm 3e29732e46 Fixed: OperationQueue for the CPNotificationCenter is not ignored anymore
Previously, the operationQueue given to the notificationCenter was ignored.

Tests in CPNotificationCenterTest.j
2015-10-08 11:23:07 -07:00
Alexandre Wilhelm 54e1efd794 Test: refactoring of the manual test CPURLConnectionAsyncTest 2015-10-08 11:17:12 -07:00
Alexandre Wilhelm 8809c1634f Merge pull request #2286 from cacaodev/CPURLConnection
CPURLConnection +sendAsynchronousRequest:queue:completionHandler:
2015-10-08 10:25:12 -07:00
cacaodev 13e1be1f74 CPURLConnectionAsyncTest manual test. 2015-10-07 18:04:28 +02:00
Martin Carlberg 26a123f5a8 Fixed: Added code generation unit test cases with inlined objj_msgSend functions for all tests in OutputTest. 2015-10-07 15:58:28 +02:00
Alexandre Wilhelm f062fda2f7 Fixed: optimization with layoutSubviews and viewWillLayout 2015-10-06 14:41:01 -07:00
Martin Carlberg e45e31d223 Fixed: Introduce ’jake install-inline’ task to generate the cappuccino framework with objj_msgSend function inlined. Also introduced options in ’index-debug.html’ templates to allow inline of objj_msgSend function when compiling in browser. 2015-10-06 13:34:28 +02:00
Martin Carlberg 101261ca63 Fixed: Optionally inline objj_msgSend function. Generate faster code for Array and Dictionary Literals. 2015-10-06 13:30:09 +02:00
Martin Carlberg 4ba9a19b5c Fixed: Speed improvements in objj_msgSend and objj_msgSendSuper functions. More efficient implementation of objj_method. 2015-10-06 13:27:10 +02:00
Alexandre Wilhelm f8eab7d611 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-10-02 16:03:04 -07:00
Alexandre Wilhelm 40a9f19352 Fixed: crash when opening a platformWindow with a popup blocker
Previously, when opening a CPPlatformWindow a crash happened when this new window was blocked by the browser (addblock, or browser feature).
Now when opening an external window, we check if the DOMWindow has been created.

This PR has modified the method isVisible of CPPlatformWindow. We now check if the _DOMWindow is not NULL and undefined.
2015-10-02 16:00:57 -07:00
Martin Carlberg 4b81e0d8af Merge pull request #2383 from mrcarlberg/use_more_fast_objj__msg_send
Use the faster objj_msgSend instead of the old slower objj_msgSend
2015-10-02 20:35:32 +02:00
Alexandre Wilhelm ea37bf8b45 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-10-02 11:10:01 -07:00
Alexandre Wilhelm c30f8b09b0 Fixed: warning when compiling in CPWindow, CPPanel and _CPCibKeyedUnarchiver 2015-10-02 11:09:48 -07:00
Alexandre Wilhelm 686f931fb4 Merge pull request #2343 from Dogild/Localization
New: first work on localization
2015-10-02 11:04:35 -07:00
Alexandre Wilhelm 1fe1e6f265 Merge pull request #2374 from Dogild/ThemeForObject
New: possibility to create a theme for every object of the Appkit
2015-10-02 10:35:02 -07:00
Alexandre Wilhelm 860decdac5 Fixed: CPColor and CPView implemented the category CPTheme 2015-10-02 10:12:50 -07:00
cacaodev d5b90c91f5 Merge pull request #2339 from cacaodev/CPTabView
CPTabView improvements
2015-10-02 14:00:17 +02:00
Martin Carlberg 20d6e365a1 Fixed: Use the faster objj_msgSend in nib2cib too. 2015-10-01 22:19:04 +02:00
Martin Carlberg e5e50c2220 Fixed: Use the faster objj_msgSend instead of the old slower objj_msgSend
In some places the objj_msgSend function is called directly. Most of the times the old slower version is called. This commit will use the never faster version instead.
2015-10-01 22:17:13 +02:00
cacaodev e8da7baee2 Perform operation on network error (try/catch). 2015-10-01 13:19:40 +02:00
cacaodev 8ab9e6bac7 NEW +sendAsynchronousRequest:queue:completionHandler: If queue is nil, run handler immediately.
NEW: CPURLConnection -operation method, CPError CPURLErrorDomain constant.

    CPURLConnection -operation gives access to the operation generated by the new CPURLConnection creator.
    This allows to create dependencies between operations and setup priorities early.
    When the connection fails with a status code 404 or is cancelled, the
    operation is also cancelled and
    the next operation in the queue is started.
2015-10-01 12:36:57 +02:00
Alexandre Wilhelm 9129301c89 Merge pull request #2381 from mrcarlberg/cp_dictionary_duplicate_keys_in_init
CPDictionary handles duplicate keys in correct way on init
2015-09-30 14:54:24 -07:00
Martin Carlberg 1b0d053ab9 Fixed: Check the abbreviation from the correct place in the test 2015-09-30 18:15:32 +02:00
Martin Carlberg 5871c4f092 Fixed: Fixed test cases where CPTimeZone does not handle daylight saving time.
For example the method ’initWithName:’ will return different abbreviation depending on the order CPDictionary returns keys from the method ’keyEnumerator’. As this is undefined the answer can vary. Test cases now handle all cases.
2015-09-30 17:03:36 +02:00
Martin Carlberg 5d415e6679 Fixed: Speed improvement 2015-09-30 16:59:16 +02:00
Martin Carlberg 3a7384ec26 Fixed: CPDictionary initWithObjectsAndKeys was not compliant with Cocoa or the init method ’initWithObjects:forKeys:’ when duplicated keys was passed to the method.
Test cases are also added
2015-09-30 16:59:04 +02:00
Alexandre Wilhelm 32b7a314b1 Fixed: warning when compiling Foundation adn CPException 2015-09-29 15:36:22 -07:00
Alexandre Wilhelm c8d768c59a New: added protocol CPTheme 2015-09-29 12:54:32 -07:00
cacaodev 3ef1c9167f Merge remote-tracking branch 'upstream/master' into CPTabView
Conflicts:
	AppKit/CPTabView.j
2015-09-29 11:04:51 +02:00
Alexandre Wilhelm 36cdcbc3c1 Fixed: setupViewFlags was not called when creating a view from a xib
Previously, the method setupViewFlags was not called when creating from a xib. This issue was introduced by the commit #448c2a2

Fixed #2380
2015-09-28 11:37:37 -07:00
Martin Carlberg 5db9f28b33 Merge pull request #2379 from mrcarlberg/fix_cparray_initWithObjects_count
Fixed: The init method ’initWithObjects:count:’ for CPArray did not work
2015-09-25 22:24:49 +02:00
Martin Carlberg 8599ad4a3c Fixed: The init method ’initWithObjects:count:’ for CPArray did not work on native CPJavaScriptArray when ’count’ is not the same as the length on the provided array.
A test case for this is also added in the test class CPArrayTest
2015-09-25 17:07:15 +02:00
Antoine Mercadal 448c2a27d4 Merge pull request #2375 from Dogild/AppearanceCrash
Fixed: crash when using a viewBased CPTableView with appearance
2015-09-23 15:51:50 -07:00
Alexandre Wilhelm f2f4fa1b98 Fixed: readded _viewClassFlags. We only displayRect the view if drawRect: or viewWillDraw is implemented. layoutSubviews is still called even if the subclass does not implement it 2015-09-23 15:00:40 -07:00
Alexandre Wilhelm 6f43a189c0 Merge remote-tracking branch 'origin' into AppearanceCrash 2015-09-23 14:44:06 -07:00
Roland Schwingel dd4de9822f Adjustment for (a)sync request with timeout to work on IE and other browsers 2015-09-23 19:16:20 +02:00
Alexandre Wilhelm c9e83e0517 Merge pull request #2362 from cacaodev/cpview-skip_settransform
FIXED: In CPView, the transformation matrix was set at each drawing pass, even if the matrix was the identity matrix.
2015-09-22 17:02:30 -07:00
Alexandre Wilhelm 4ac63950ad Fixed: toolTip does not work on a external window
Previously, the toolTip created for an external window where shown on the primary platform window. We now display it on the desired platformWindow.
2015-09-22 15:58:53 -07:00
Alexandre Wilhelm 7a0b2c958b Fixed: crash with CPRuleEditor when dragging the first row
Previously, cappuccinos crashed when dragging to the top the first row of a CPRuleEditor.
2015-09-22 15:01:19 -07:00
Alexandre Wilhelm 3fcc37e086 Fixed: CPTabView did not accept first mouse event
Previously, the CPTabView did not react with the first mouse event (for instance when a CPTabView was in a not focused window). Now it does as in cocoa.

To do that, the CPTavView override the method acceptsFirstMouse. Previously, the tabView had a CPBox, it now has a _CPTabViewBox. This new box reacts differently than the CPBox for the method hitTests. It now returns nil when the click was performed on the segmentedControl of the CPTabView, previously this same click would be handle byt the CPBox.
2015-09-22 14:33:14 -07:00
Roland Schwingel 55181cec8c Set timeout in XHR requests AFTER they are opened to get them to work with IE 2015-09-17 14:45:35 +02:00
cacaodev 41d259006e Merge remote-tracking branch 'upstream/master' into CPTabView 2015-09-14 18:57:12 +02:00
Alexandre Wilhelm b30103c790 New: added methods viewWillLayout and viewDidLayout
Previously, when a view was about to layout, only layoutSubviews was called. Now the methods viewWillLayout and viewDidLayout are called as well. This is like in Cocoa.

This PR fixed the issue with the appearance and the view based tableView. We now handle everything related with appearance when making the layout of a view.

This PR removed a small optimization in CPView. Now when making setNeedsLayout and setNeedsDisplay, the methods drawRect: and layoutSubviews are always called even if there are not override. (As in cocoa)
2015-08-28 11:11:28 -07:00
Alexandre Wilhelm bb9408b5d0 Merge branch 'master' into Localization 2015-08-27 15:16:12 -07:00
Alexandre Wilhelm 42c3c7bc48 Fixed: crash when using a viewBased CPTableView with appearance
Previously, when using a viewBased CPTableView Cappuccino just crashed.
This was due to the new appearance feature. Indeed, at the end of the method initWithCoder: of CPView, we set the appearance of the current view and the themeStates of the view and its subviews. For an unknown reason some of these subviews were not completely initialized (see comment line 3742), this made crashed cappuccino because _themeState was null.

To fix this issue, we send the method setAppearance when the current stack was performed. (Magic setTimeout...).

This is for sure not the ultimate best fix, but it's prevent to have a broken master. We will need in the future to fix this issue in a better way.
2015-08-25 16:46:00 -07:00
Alexandre Wilhelm 464040895c Fixed: remove log... 2015-08-21 16:06:25 -07:00
Alexandre Wilhelm 525747dc53 New: possibility to create a theme for every object of the Appkit
The purpose of this feature is to create theme for every object of the AppKit. Previously, we could not theme object like CPColor, however CPColor could be very interesting to theme, value like alternateSelectedControlColor were hard coded in the framework. With this PR, we can now theme a CPColor and take some values from the current theme of the application. Then, this PR offers the possibility to refactor the themeDescriptors, some classes contains theme attributes for other class (CPTabView and  CPTabViewItem for instance).

How doe it work ? A new category _CPObject+Theme.j has been added to the AppKit. This category contains every theme methods needed to theme an object (previously this category was in CPView). We add utils method like _encodeThemeObjectsWithCoder: and _decodeThemeObjectsWithCoder: in this category to be able to code and decode easily a coder for every object (this is generic).

This PR did not refactor the themeDescriptors, it only add this new mechanism for the methods alternateSelectedControlColor and secondarySelectedControlColor of CPColor. We will need to refactor that gradually
2015-08-21 15:54:00 -07:00
Alexandre Wilhelm c61d992559 Merge pull request #2373 from Dogild/menu-context
New: default menu system is now disable for every right click except for the CPTextField
2015-08-20 19:47:20 -07:00
Alexandre Wilhelm 2f79b079fd New: default menu system is now disable for every right click except for the CPTextField
Previously, when making a right click and when a cappuccino menu was not displayed, the system menu was displayed. Now we don't display this menu anymore as in cocoa. We only display when making a right click on a CPTextField.
2015-08-20 16:14:14 -07:00
Alexandre Wilhelm 655bc39d85 Fixed: blinking popover animation on Chrome and Opera
Previously, when opening a popover, the animation was blinking on Chrome and Opera.
To resolve (temporary I hope) this issue, we launch the last part of the animation at the beginning of the next runloop. We do that by wrapping the desired code in window.setTimeout(function(){},0).

A google chrome issue has been opened here : https://code.google.com/p/chromium/issues/detail?id=523044&thanks=523044&ts=1440095724
2015-08-20 11:36:57 -07:00
Roland Schwingel 1eb12d4fa0 Damned... corrected formatting 2015-08-20 14:57:28 +02:00
Roland Schwingel 2ab9186259 Fix Chrome warning when using synchronous XHR requests 2015-08-20 14:52:19 +02:00
Alexandre Wilhelm ab61aef985 Merge pull request #2371 from primalmotion/CPVisualEffectView
CPVisualEffectView & CPAppearance
2015-08-18 15:15:03 -07:00
Roland Schwingel 65b3e9f8c1 Merge remote-tracking branch 'upstream/master' 2015-08-18 14:10:21 +02:00
Alexandre Wilhelm 1f57aa1ea5 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-08-16 15:44:01 -07:00
Alexandre Wilhelm 340d827377 Fixed: command cmd+h did not work for the new XcodeCapp 2015-08-16 15:43:56 -07:00
Antoine Mercadal 5cd25e24e4 Merge pull request #2369 from tancred/preserve_boolean_type_when_parsing_predicate_constant_value_expressions
Fixed: preserve type of boolean values when parsing CPPredicate format
2015-08-14 13:55:18 -07:00
Antoine Mercadal 58afc92a26 Merge pull request #2370 from Dogild/new-cache-timeout-urlrequest
New: added timeout and cache feature in CPURLRequest
2015-08-14 13:51:20 -07:00
Alexandre Wilhelm acb9931fe6 Fixed: not possible to to synchrone request and set a timeout 2015-08-14 13:28:27 -07:00
Alexandre Wilhelm d4b035ac1a Fixed: style in CPURLConnection.j CPURLRequest.j CFHTTPRequest.js 2015-08-14 12:40:22 -07:00
Antoine Mercadal 403dd5c005 Updated CPVisualEffectViewTest
The test now show how to use the automatic theme state beased on
CPAppearance
2015-08-14 01:45:19 -07:00
Antoine Mercadal 7e4e1ec68f FIXED: lot's of error with CPAppearance 2015-08-14 01:44:22 -07:00
Antoine Mercadal 547764e3c1 FIXED: Always call [super awakeFromCib] in CPView subclasses 2015-08-14 01:43:55 -07:00
Antoine Mercadal 541a7974e0 NEW: effectiveAppearance and new CPThemeState
Each default (and only supported) CPAppearance now have an associated
theme state. As the appearance is correctly propagated in CPViews
hierarchy, it is now possible to have dark controls when the an
ancestor view has a dark appearance.

Added automated tests
2015-08-13 23:40:27 -07:00
Antoine Mercadal 6bf6f82cad Small refactoring 2015-08-13 21:29:33 -07:00
Antoine Mercadal 7be2325a95 Fix few mistakes 2015-08-13 21:12:04 -07:00
Antoine Mercadal fa1f739458 NEW: Support for basic CPVisualEffectView
This patch contains a very naive implementation of the
NSVisualEffectView. This will only work on very recent unreleased
version of Safari, but should be supported by all at some point. This
allow to use the Yosemite/iOS blurry effect.

Not all options are supported (especially the
`CPVisualEffectBlendingModeBehindWindow` mode…). But hey! it’s a start
:)

Tests in Manual/CPVisualEffectViewTest
2015-08-13 20:53:53 -07:00
Antoine Mercadal 20289ff81b fix two small bugs in CPApperance 2015-08-13 20:25:45 -07:00
Antoine Mercadal 6d67d30aee NEW: Initial support for CPAppearance
This patch adds the `CPAppearance` class and uses it where needed.
There is nearly no impact, but in `_CPPopoverWindowView` where the
`CPPopoverAppearance` needs to be converted to a `CPAppearance` object

nib2cib support has also been added.

*This patch brings no new functionality.*
2015-08-13 19:56:10 -07:00
Alexandre Wilhelm bd0bd2e087 New: added timeout and cache feature in CPURLRequest
This PR adds the possibility to set the timeout and the cache policy of a CPURLRequest.
For that, there are new things in the framework :

- CFHTTPRequest has now the functions setTimeout(), getTimeout() and isTimeoutRequest
- CPURLRequest has now the methods +requestWithURL:cachePolicy:timeoutInterval: and -initWithURL:cachePolicy:timeoutInterval:
- CPURLConnection does not call the delegate didReceiveData: when a request has timed out. I will call the delegate didFailWithError:
- Cache is now possible for a CPURLRequest, only CPURLRequestReturnCacheDataElseLoad, CPURLRequestReturnCacheDataDontLoad and CPURLRequestReloadIgnoringLocalCacheData are supported. By default CPURLRequestReloadIgnoringLocalCacheData.
- Default timeout has been set to 60sec as in cocoa.
- A request is considered as timeout when there isn't any response (text/xml/type) and when the status of the response is 0.
2015-08-13 17:08:59 -07:00
Antoine Mercadal 3d79799450 Do not fail Cappuccino installation if XcodeCapp build fails 2015-08-13 12:13:38 -07:00
Alexandre Wilhelm c99f9205b7 Merge pull request #2367 from primalmotion/xcodecapp4.0
Xcodecapp4.0
2015-08-12 11:26:55 -07:00
Malte Tancred 26bb54472f Fixed: preserve type of boolean values when parsing CPPredicate format
When parsing a predicate format that contains a constant boolean expression
(e.g., 'key = YES') the value of the returned constant value expression has
the wrong type. Instead of using native types as values, the parser uses
[CPNumber +numberWithBool:].

This commit changes the format parser (CPPredicateScanner) to use native
booleans for constant value expression.

There's a test case that checks the parse result and an additional test
that ensures boolean expressions evaluate as expected with different kinds
of objects. The latter test passed before the fix but was added to ensure
compliance with Cocoa.
2015-08-12 17:05:56 +02:00
Roland Schwingel 73e13fee37 Fix for startup on IE10. Reformatted patch 2015-08-11 09:03:21 +02:00
Roland Schwingel 205c1ac3b6 Fix startup on IE10 2015-08-10 17:08:28 +02:00
Antoine Mercadal d317744f05 Update pbxproj to auto deploy 2015-08-04 10:43:59 -07:00
Antoine Mercadal 47611b7615 NEW: XcodeCapp 4.0
XcodeCapp 4.0 is a major release of our beloved tool. In a nutshell it:

- allows to manage multiple projects simultaneously
- allows to follow operations and cancel them
- has a per project Error and Warning reporting
- supports capp_env as you can define additional paths and objj include path per project
- much more things
2015-08-04 10:35:23 -07:00
Antoine Mercadal 6420a51dd0 FIXED: crash when a cell based table view contains a dataview that contains an editable text field
Previously, CPTableView was listening to all end editing notifications in cell-based mode.
This caused a crash if the textfield is an actually subview of a dataview.

This patch ensures to listen only editing did end notification for direct subview of tableView.
2015-08-03 15:00:04 -07:00
Antoine Mercadal 7c17d5bac0 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-08-03 14:57:15 -07:00
Antoine Mercadal 0efd7c93c5 FIXED: Fonts from IB not correctly interpreted in OSX El Capitain
This patch adds support for the new way of defining the default font from Xcode 7 beta. The patch also supports the old explicit 'Lucida Grande' of previous version.
2015-07-29 23:39:07 -07:00
Alexandre Wilhelm 806be9551c New: added protocol CPURLConnectionDelegate 2015-07-23 14:38:06 -07:00
Alexandre Wilhelm 6b4a1b6758 New: Added gitter link in the readme
New: Added gitter link in the readme
2015-07-15 15:42:07 -07:00
Antoine Mercadal d6b0fccbda FIXED: Previous patch by @dogild was not correctly setting the CSS class
This patch adds a reference to the current CSS selectable field in and
reset the style when user clicks on a different one.
2015-07-08 15:35:20 -07:00
Alexandre Wilhelm d1e31da2e5 Fixed: selection did not work in Cappuccino when doing a right click or dragging
Previously, the selection of a textField did not work as expected when the user did a right click or a drag even if the label was set to none selectable.
Now, with the css style user-select, a textField can only be selected if it is selectable and enable.

Credit to @primalmotion
2015-07-08 13:34:57 -07:00
Alexandre Wilhelm 923d32ee6d Test: finally put the init of the CPApplication in the instance method setUp. See previous commit #6fe9dae3d62af995f2481bfebdd706aeb0b21365 2015-07-07 15:04:29 -07:00
Alexandre Wilhelm 6fe9dae3d6 Fixed: AppKit unit-tests are not standalone
Previously, every AppKit tests used the same sharedApplication. Due to this implementation, a could not pass because a previous test made failed the current test. For instance, a test could fail because the window of the previous test resigned (just imagine a new window is the key window in the current test), and this resign could raise an error. The error was displayed for the current test thought this test was perfect !

We now instead of using sharedApplication create a new CPApplication per unit-test file in the class method setUp.
2015-07-07 14:12:19 -07:00
Alexandre Wilhelm 4c1e22b0f6 Fixed: travis failed because the unittests tried to display a layer
Previously, the unittests for the CPDatePicker broke the tests suite of cappuccino. A CPDatePicker uses a CALayer, and the CALayer display methods wasn't wrap in a #if PLATFOrM(DOM). Now it does, so during the tests we do not try to access do the element document anymore.

This PR fixes also some warnings when compiling.
2015-07-06 14:14:39 -07:00
Alexandre Wilhelm 058a73f8a0 New: added delegate methods applicationShouldTerminate and applicationShouldTerminateMessage
This PR adds the delegate methods applicationShouldTerminate and applicationShouldTerminateMessage in CPApplicationDelegateProtocol.

The delegate applicationShouldTerminate does not exactly work as in Cocoa. In Cocoa, this method is called in CPApp -terminate, but in cappuccino it is called in the method onbeforeunload of the window. In JS, this is the only time where we can prevent to reload the HTML page. If the developer cancel to reload the page, the browser will ask the user if he wants to reload or not the page thought (natural behavior of js).

The method applicationShouldTerminateMessage allows you to define what will be the text displayed in the confirmation alert.

Test app in Tests/Manual/CPPlatformWindow/
2015-07-02 16:35:35 -07:00
Alexandre Wilhelm 1ad8d34ecb Fixed: fixed travis with datePicker test 2015-07-02 15:12:18 -07:00
Alexandre Wilhelm 3f9f926c8e New: added method closeAllPlatformWindows in CPPlatform
Added the method closeAllPlatformWindows in CPPlatform. This platform close all platform windows of the application (except the main one). This method is now used when reloading or leaving a cappuccino application, the application will now close every external window openend by the application.
2015-07-02 13:41:37 -07:00
Alexandre Wilhelm 1f983db47c Fixed: refactoring of the class _CPDatePickerTextField.j. We now use the method called by interpreteKeyEvents instead of handling the key equivalent in performKeyEquivalent 2015-06-29 14:28:28 -07:00
Alexandre Wilhelm 3a6a84917b New: added CPUserNotification and CPUserNotificationCenter in Foundation
This PR adds the features CPUserNotification and CPUserNotificationCenter in Foundation.
The CPUserNotificationCenter allows you to send user notification to the system.
Right now, we only propose what the W3C proposes. We can set for a notification the title, informativeText and the icon.
We only support local notification yet.

The protocol CPUserNotificationCenterDelegate has been added as well.

Test app in Tests/Manual/CPUserNotificationTest
2015-06-26 16:36:11 -07:00
Alexandre Wilhelm 9694c796e3 Revert "fix disappearing buttons"
This reverts commit 7323a5e393.

Fixed #2361
2015-06-24 14:35:23 -07:00
Antoine Mercadal da6698b216 Merge branch 'master' into capp_env 2015-06-19 10:12:02 -07:00
Antoine Mercadal c9f391dfc1 Update the installation process 2015-06-19 10:11:40 -07:00
Antoine Mercadal 0f62926623 rename cappenv_deactivate to capp_env_deactivate 2015-06-19 09:59:14 -07:00
cacaodev c7eb2ffe5c Merge remote-tracking branch 'upstream/master' into cpview-skip_settransform 2015-06-15 21:10:05 +02:00
Alexandre Wilhelm a419694f70 Test: updated unit test ToolsTest.j. Fixed travis... 2015-06-13 21:58:09 -06:00
cacaodev 0a20f77b08 FIXED: In CPView, the transformation matrix was set at each drawing pass, even if the matrix was the identity matrix.
Now we check if the highDPI ratio is == 1 and skip the setTransform call in this case.
2015-06-13 21:06:17 +02:00
Alexandre Wilhelm 472372c9ed Test: updated unittest for the tools 2015-06-08 10:35:13 -07:00
Alexandre Wilhelm d358f0d27b Fixed: xml format now has the key sourcePath instead of path for the command objj and objj2objcskeleton 2015-06-08 10:30:54 -07:00
Alexandre Wilhelm 3f88e02587 New: added the option -n to specify the name of the cocoa files generated by objj2objcskeleton 2015-06-01 15:22:56 -07:00
Antoine Mercadal 245da9b422 Add capp_env 2015-06-01 15:20:20 -07:00
Alexandre Wilhelm 8cc075aff3 Fixed: objj2objcskeleton does not work with categories on cappuccino class
Previously, when making objj2objcskeleton on a category of a cappuccino class, the cocoa category was not a cocoa class but still a cappuccino class. For instance, a CPView category was still a CPView category instead of being a NSView category.

Fixed #2344
2015-06-01 14:35:12 -07:00
Alexandre Wilhelm 128a243166 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-06-01 13:53:56 -07:00
Alexandre Wilhelm 0e41365020 Fixed: some objj errors were not print in xml when necessary 2015-06-01 13:53:41 -07:00
Alexandre Wilhelm de7951f4db New: added build passing tag to the realm markdown 2015-05-27 18:38:10 -07:00
Alexandre Wilhelm bdbf1fbb43 Merge pull request #2348 from Dogild/BaseWritingDirection
New: added property baseWritingDirection in CPControl
2015-05-27 15:37:09 -07:00
Alexandre Wilhelm a7b6010ce2 Merge pull request #2358 from Dogild/CPScrollView-documentVisibleRect
Fixed: documentVisibleRect in CPScrollView does not return the expected result
2015-05-27 15:27:12 -07:00
Alexandre Wilhelm 5bba109873 Merge pull request #2346 from primalmotion/fix-popen
FIXED: Closes all streams after a OS.popen()
2015-05-27 15:25:36 -07:00
Alexandre Wilhelm d69ae60bcb Fixed: themeState of an editing CPTextField in a cell based CPTableView
Previously, when having a CPTableView cell based, the themeState of the textField were wrong. The text color was black as every textField was considered as editable. The CPTableView handled differently the textField, as long a textField is not editing, the textField is mark as non editable. Previously, the textFields were considered editable all the time.

More informations here : https://groups.google.com/forum/?fromgroups#!topic/objectivej/zImy4sj0Xz4
2015-05-27 14:57:28 -07:00
Alexandre Wilhelm a0277aba2e Merge pull request #2360 from Dogild/XMLOutputFormatObjj
New: added the option --xml-output-format for the command objj
2015-05-27 13:09:15 -07:00
Alexandre Wilhelm caba631db9 Test: updated ToolsTest.j 2015-05-27 12:38:56 -07:00
Alexandre Wilhelm 5095620005 Fixed: changed --xml-output-format to --xml in objj 2015-05-27 11:46:43 -07:00
Alexandre Wilhelm abcdc0875c Fixed: added option -x for objj and changed xml-output-format to --xml 2015-05-27 11:12:36 -07:00
Alexandre Wilhelm 3e03d52fce Fixed: objj tests did only work on my system... 2015-05-25 15:22:30 -07:00
Alexandre Wilhelm 4f64d466fb Test: added unit test for option -xml-output-format of objj 2015-05-25 13:27:03 -07:00
Alexandre Wilhelm 3102c545ce Fixed: used url path (/Users/..) instead of absoluth path (file://Users/..) in the xml output format of objj 2015-05-25 13:26:37 -07:00
Alexandre Wilhelm f232b327c0 Fixed: issue with options --objj-include-paths and --multifiles. Long options were not taken 2015-05-25 11:38:16 -07:00
Alexandre Wilhelm 204ed1f257 New: added the option --xml-output-format for the command objj
Previously, it was not possible to specify the desired output format of the command objj.
Now we can have either the default format or a xml format if --xml-output-format is added.
2015-05-25 11:20:55 -07:00
Alexandre Wilhelm 65a379c8da Merge pull request #2359 from herbatnik/master
FIXED CPDatePicker AM/PM time format for GB locale
2015-05-21 17:24:01 -07:00
Marek Kresnicki 99df5fadbc Replaced confusing method _isEnglishFormat with _isAmericanFormat. Removed method was always true for US and GB locale while it shouldn't be. 2015-05-21 15:30:53 +01:00
Alexandre Wilhelm 92cdb27e23 Fixed: documentVisibleRect in CPScrollView does not return the expected result
Previously, the method documentVisibleRect did not return the expected result. An issue happened when using this method on a scaled view. The converted rect was converted in the wrong context.

Unit test in Tests/AppKit/CPScrollViewTest.j

Fixed #2356
2015-05-19 14:30:09 -07:00
Alexandre Wilhelm a439688b3c Doc: move private variables to the top of files to not show them in the documentation 2015-05-15 09:55:17 -07:00
Antoine Mercadal 0d17757729 FIXED: CPPogressIndicator needs display when updateing it's state 2015-05-14 15:20:48 -07:00
Alexandre Wilhelm 899bbc2dbc Fixed: issue with determinate spinning progressBar annd value not between 0 and 100 2015-05-13 10:06:54 -07:00
Alexandre Wilhelm 57b68880db New: added indeterminate spinning progressBar
Previously, it wasn't possible to display an indeterminate spinning progressBar. Now we can as in cocoa.

Test app in Tests/Manual/CPProgressIndicator
2015-05-12 17:18:31 -07:00
Martin Carlberg 06c59763bc Merge pull request #2353 from Dogild/optionObjc
New: added option --objc to objj to generate objc files from a .j file
2015-05-12 09:19:08 +02:00
Alexandre Wilhelm 71c1d16c06 Test: fixed test ToolsTest.j 2015-05-11 21:52:04 -07:00
Alexandre Wilhelm 64c1b72481 Test: added unit test for objj2objcskeleton 2015-05-11 17:03:45 -07:00
Alexandre Wilhelm e5742e057d Fixed: issue with objj2objcskeleton and destination folder 2015-05-11 16:27:06 -07:00
Alexandre Wilhelm a9d6176fd2 New: added command objj2objskeleton. This feature is now not available from the command objj 2015-05-11 11:33:52 -07:00
Antoine Mercadal c603326e58 Use try/finally to ensure popen streams are always closed 2015-05-09 10:22:13 -07:00
Alexandre Wilhelm a93e04a44e New: added option --objc to objj to generate objc files from a .j file
The command objj has now the option -c or --objc to generate objective-c files. The first arg is the file and the second the destination folder.

The objective-c class generated are basic and only contains IBOutlet and IBAction. To do that, the objective-c-parser is used. This parser is also used in xCodeCapp.
2015-05-08 23:05:58 -07:00
cacaodev db9e649f6e FIXED: missing return stmt in -indexOfTabViewItemWithIdenfier;
Refactored delegate notifications.
CPTabViewItem style.

Tests: All delegate methods are tested in CPTabViewNib manual test.
2015-05-07 22:35:02 +02:00
Alexandre Wilhelm c26aca4686 Fixed: memory leaks in CPSplitView with delegate 2015-05-04 16:43:37 -07:00
Alexandre Wilhelm f822268ae1 Test: updated manual test CPSplitViewTest 2015-05-04 16:43:13 -07:00
Alexandre Wilhelm 3be3a1f2e6 Merge pull request #2329 from daboe01/fix-issue-10
Added documentation for CPArray's hash method
2015-05-01 14:05:19 -07:00
Alexandre Wilhelm 62f9488829 New: added property baseWritingDirection in CPControl
This PR adds the support for baseWritingDirection in CPControl.
Support of nib2cib has been added as well.

Test app in Tests/Manual/CPTextFieldEditingStyleTest/
2015-05-01 12:11:03 -07:00
Antoine Mercadal 9bcaef192c Merge branch 'master' into fix-popen 2015-04-23 16:50:56 -07:00
Antoine Mercadal bcb1630f20 FIXED: Closes all streams after a OS.popen()
Previously, when popen was called, the 3 streams (stdin, stderr and stdourt) where never closed. This was the reason of the having an impossible number of open files.

This patch ensure all streams are closed after using them. This means that the ulimit trick is no more necessary
2015-04-23 16:46:01 -07:00
Alexandre Wilhelm 5f7922d7fd Fixed: crash in CPCib.j due to previous refactoring refs #c5b250236fa662e3da04e442f4414f95d64d9308 2015-04-22 16:21:24 -07:00
Alexandre Wilhelm 66b1d297ba Test: added unit test CPBundleTest 2015-04-22 15:11:47 -07:00
Alexandre Wilhelm eb4780a952 New: added protocol CPBundleDelegate 2015-04-22 15:11:19 -07:00
Alexandre Wilhelm 34f724a4a4 Fixed: capp_lint fixes 2015-04-22 11:22:44 -07:00
Alexandre Wilhelm c5b250236f New: support of base localization and nib localization 2015-04-21 18:26:28 -07:00
Alexandre Wilhelm e5b122ee61 New: added methods pathForResource:ofType, pathForResource:ofType:inDirectory: pathForResource:oftype:inDirectory:forLocalizations: 2015-04-21 18:25:58 -07:00
Alexandre Wilhelm 3b20021981 Test: updated test CPLocalizationTest 2015-04-21 18:24:50 -07:00
Alexandre Wilhelm a7d1f180a4 Test: updated manual test CPLocalizationTest to have base localization in the xib 2015-04-21 14:29:21 -07:00
Alexandre Wilhelm 68374e7c62 Fixed: fix minor issue with methods CPLocalizedStringFromTable and comment 2015-04-21 10:54:09 -07:00
Alexandre Wilhelm ba26a97ddc Test: updated test CPLocalizationTest 2015-04-21 10:53:36 -07:00
Alexandre Wilhelm 2db7ce8da1 New: added the methods CPLocalizedString CPLocalizedStringFromTable CPCopyLocalizedStringFromTableInBundle 2015-04-20 18:53:51 -07:00
Alexandre Wilhelm 6958da1adb Test: added manual test CPLocalizationTest 2015-04-20 18:53:17 -07:00
Alexandre Wilhelm cf48cd0ae5 Fixed: changed copyright of _CPLocalizableString.j 2015-04-20 10:15:05 -07:00
Alexandre Wilhelm b1b3966d41 Fixed compiling issue in CFBundle.js 2015-04-17 20:40:48 -07:00
Alexandre Wilhelm c171a37f24 New: first work on localization 2015-04-17 17:16:11 -07:00
Alexandre Wilhelm c13f1991c9 Fixed: removed stupid merging typo... 2015-04-13 16:18:46 -07:00
Alexandre Wilhelm da15cc4de7 Fixed: wrong nextValidKeyView in a CPScrollView
Previously, the nextValidKeyView was wrong when the views were in a scrollView. Cappuccino did not take in account the possibility of scrolling, the lowest views were considered as outside of the platformWindow.

Now, it works as cocoa! The nextValidKeyView is the good one in a scrollView.

This pull request fixes another issue as well. A CPTextField can now become firstResponder even if the textField is not visible (as in cocoa). Previously a jump of the (html)window occurred to the textField. Now, cappuccino will internally scroll if needed to the element, focus it and then go back to previous scrolling position.
2015-04-13 16:10:44 -07:00
Alexandre Wilhelm 44c686f494 Merge pull request #2323 from mrcarlberg/scroll_rect_to_visible_with_large_rect
Fixed: Method scrollRectToVisible:(CGRect)aRect in CPView didn’t work...
2015-04-13 13:12:27 -07:00
cacaodev 914a3d71c2 Merge pull request #2342 from cacaodev/CPSegmentedControl-ni2cib
FIXED: segmented control height in nib2cib. Fixes #2341.
2015-04-11 10:03:50 +02:00
Alexandre Wilhelm 5c2e0935c6 Fixed: CPApplication does not send willBecomeActive and didBecomeActive when having the focus on the application again
Previously, when clicking somewhere else in the system, cappuccino did no raise the notifications willResignActive and didResignActive of the application. As well cappuccino did not raise the notifications willBecomeActive and didBecomeActive when the user was back on the application.

Now it does !

Test app in Test/Manual/CPPlatform
2015-04-10 12:59:05 -07:00
Alexandre Wilhelm e1ec604655 Test: updated manual test CPPlatformWindow 2015-04-10 11:11:22 -07:00
Alexandre Wilhelm 0dad10d6ab Fixed: notifications becomeActive and willActive were called even if the application was already active. The method run sends now an event CPAppKitDefined as in cocoa, this event will raise the notification becomeActive and willActive of the application when the application just finished to load 2015-04-10 10:57:34 -07:00
Alexandre Wilhelm 136138daab Fixed: CPTextField should not blur when resigning from a window which are not keyWindow 2015-04-10 10:55:48 -07:00
Alexandre Wilhelm 6efe563a69 Fixed: method _initOtherEventWithType: in CPEvent did not save the given windowNumber 2015-04-10 10:51:47 -07:00
Alexandre Wilhelm fd068443aa Fixed: keyWindow and mainWindow did not work when jumping from window to window when popover were open
Previously, when jumping from a platformWindow to another platformWindow where popover where opened, the wrong windows became key and main. This things occurred weird behavior of the platformWindows.

Now, it works as in cocoa, canBecomKeyWindow, becomeKeyWindow, becomeMainWindow, resignKeyWindow and resignMainWindow are called in the good order.
2015-04-09 15:14:33 -07:00
Alexandre Wilhelm 01ba6ddecd Fixed: floating windows did not become keyWindow and mainWindow of the application when jumping from platformWindow to another platformWindow
Previously, when having several platformWindows, jumping from another platform to another platform did not update the good key window of the application, specially when the expected windows was a panel.

Now it does. The CPPlatformWindow keeps a reference to the previous keyWindow when the window browser is about to blur. The we use this reference to update the keyWindow of the application.
2015-04-07 17:40:26 -07:00
Alexandre Wilhelm 769b379a8b Fixed: a CPPopover were closed when a click was made in another CPPlatformWindow 2015-04-07 17:39:35 -07:00
cacaodev 1b981e0695 FIXED: segmented control height in nib2cib
The -tile method now leaves the frame height unchanged instead of
sizingToFit the theme attribute « min-size ».
The « min-size » is still in use when we layout the ephemeral subviews.

This is how other controls with a min-size work.
Also added -minimumFrameSize. This CPControl subclass will return the
frame for sizeToFit, i.e. when you want to sync the frame attribute
with the minimum/visible height.

Tests : CPSegmentedControlTest & Nib2CibAlignment manual tests.

Fixes: #2341
2015-04-07 18:29:06 +02:00
Alexandre Wilhelm 9798b9fd50 Fixed: it was possible to scroll in a new platformWindow. The overflow style of the body of the plarformWindow was not set to hidden 2015-04-06 18:17:31 -07:00
Alexandre Wilhelm 10792c0bc6 New: Added listener onblur and onfocus on the CPPlatformWindow for updating keyWindow and firstResponder
Previously, when switching from a platformWindow to another platformWindow or to another application, Cappuccino did not behave as Cocoa, Cappuccino still considered that the window was key and main.

Now it works as in cocoa, the platform window lost its focus and is not the keyWindow and mainWindow of the cappuccino application.

Test app in Tests/Manual/CPPlatformWindow/
2015-04-06 17:37:08 -07:00
Alexandre Wilhelm 1c61afd3ee Test: updated CPPlatformWindow manual test 2015-04-06 17:36:53 -07:00
Alexandre Wilhelm 737ef583ee New: added delegate method windowDidMiniaturize: windowWillMiniaturize: and windowDidDeminiaturize: in CPWindow 2015-04-06 13:50:30 -07:00
Alexandre Wilhelm 7b4365bd8a New: added delegate method - (CPSize)windowWillResize:(CPWindow)sender toSize:(CPSize)aSize; in CPWindow 2015-04-06 13:34:36 -07:00
Antoine Mercadal 007ca4c6b0 Merge pull request #2336 from cpasslack/cpasslack/corruptDecimalNumberAfterDecodingFix
Fixed: Encoding a [CPDecimalNumber zero] causes a corrupt CPDecimalNumbe...
2015-03-31 11:25:20 -07:00
Martin Carlberg b3dfce7b5a Fixed: Compared the rects minimum x value with it self instead of the visible minimum x value. Also fixed the test case to catch this. 2015-03-31 17:30:57 +02:00
Alexander Ljungberg 1925b7b324 Merge branch 'refs/heads/0.9.8' 2015-03-29 13:39:28 +01:00
cacaodev 88f7b30128 NEW: +tabViewItemWithViewController: 2015-03-27 14:38:59 +01:00
cacaodev 4b38389dc8 Update Manual test 2015-03-27 14:38:58 +01:00
cacaodev 865a45044f NEW: bindings support 2015-03-27 14:38:58 +01:00
cacaodev 7378d06bfd FIXED: CPTabView -insertTabViewItem:atIndex:
Before this commit, the tabViewItems and the segments were 2 different
collections. Any indexed change in the tabViewItems asked to re-sync
the segments making it difficult to maintain any persistent state. Now
the tabView items are the segments content.

Also fixed the selection update when items are removed.
See CPTabViewTest.j  and manual test Manual/CPTabViewNib/
2015-03-27 14:27:46 +01:00
Alexandre Wilhelm 55e19de8a1 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-03-26 10:41:33 -07:00
Alexandre Wilhelm 4d7cb06481 Fixed: changed theme attributes image-search-left-margin image-cancel-right-margin by image-search-inset and image-cancel-inset in CPSearchField 2015-03-26 10:41:21 -07:00
cacaodev 7179a2995d CPTabViewItem image support 2015-03-24 23:42:44 +01:00
cacaodev 2b853df75b Merge pull request #2337 from cacaodev/CPSegmentedControl-setSelectedSegment
FIXED: CPSegmentedControl -setSelectedSegment:-1
2015-03-24 23:21:54 +01:00
Christian Passlack bb9049bef4 Code-styling to match official style-guide. 2015-03-23 20:45:31 +01:00
Christian Passlack 94f15aba40 Updated Test-Cases of CPNumberFormater and CPTextfield because of the
changes for fix #2332. The test-cased failed, because now a
a CPDecimalNumber has a different UID compared to a CPNumber with the same
value. That means isEqual: will return false. In the test-cases the formatter now
generates CPNumbers to make the two Numbers compareable via isEqual.
2015-03-23 20:30:01 +01:00
Alexandre Wilhelm b16b8388ef Merge pull request #2335 from t00f/cpsearchfield_update_frame
Fixed: CPSearchField alignment from nib2cib
2015-03-23 11:39:04 -07:00
Alexandre Wilhelm b1e89e7904 New: added themeAttributes image-search-left-margin and image-cancel-right-margin in CPSearchField 2015-03-23 10:45:43 -07:00
Christophe Serafin f48e2f3b18 Fixed alignement content-inset and images for control size small and mini. 2015-03-20 15:44:37 -07:00
cacaodev bd673a3f26 FIXED: CPSegmentedControl -setSelectedSegment:-1
previously, -setSelectedSegment:-1 was not visualy deselecting the
segments and selectedSegment was not set to -1.
2015-03-20 21:19:36 +01:00
Christian Passlack 43c0eda2be Fixed: Encoding a [CPDecimalNumber zero] causes a corrupt CPDecimalNumber after decoding
Previously, CPNumber and CPDecimalNumber shared the same method UID and CPNumberUIDs-Dictionary.
This leads to some unexpected errors for example a [CPDecimalNumber zero] which refers to itself after
decoding. CPDecimalNumber now overwrites the method UID and has its own UID-Dictionary.

Fixes #2332
2015-03-20 11:21:07 +01:00
Antoine Mercadal f2c5515317 Merge pull request #2334 from Dogild/ReloadDataTableViewRunLoop
Fixed: reloadData in CPTableView run the runLoop
2015-03-19 18:22:24 -07:00
Christophe Serafin c1441eb758 Updated Aristo and Aristo2 alignments 2015-03-18 18:46:26 -07:00
Alexandre Wilhelm cf92fb921d Fixed: aligment for mini and small CPComboBox were wrong 2015-03-18 13:57:42 -07:00
Christophe Serafin 559ca3e97b Fixed: CPSearchField alignment from nib2cib
CPSearchField were not properly aligned in Aristo2 theme.
This fix uses the nib2cib-adjustment-frame information to align it.
It also improve the Nib2CibAlignement test.
2015-03-18 08:26:26 -07:00
cacaodev 7c095175ab Merge pull request #2324 from cacaodev/CPSegmentedControl-indexed-mutation
CPSegmentedControl
FIXED: widthForSegment: and frameForSegment: now return the correct values when the segments sizeToFit
Added indexed accessors for the segment object
Improved tile performance
With manual and ojtest
2015-03-16 12:13:03 +01:00
Alexandre Wilhelm 3b2635014c Fixed: the developer need to perform the run loop to get some informations in CPTableView. Now, the run loop is performed by the tableview 2015-03-15 19:38:06 -07:00
cacaodev 755040444a Fixed selection updating again.
This time it’s right. CPSegmentedControl does in fact allow empty
selection (-1) in cocoa.
Updated ojtets.
2015-03-15 20:42:17 +01:00
Alexandre Wilhelm 59afb012b6 Fixed: make sure to lay out the tableView when calling the method editColumn:row:withEvent:select: 2015-03-14 16:18:22 -07:00
Alexandre Wilhelm 7d9a52301c Fixed: reloadData in CPTableView run the runLoop
Previously, when reloading a CPTableView the run loop was explicitly call to layout the tableView. This is not the case in Cocoa.
You can call several times the method reloadData and this will only lay out the tableView one time.
2015-03-14 14:25:04 -07:00
cacaodev 83eeb488f9 Remove xcodeproj 2015-03-14 20:14:19 +01:00
cacaodev 5394a35e1a NEW: add -tile : layout all segments
Refactor -tileWithSegment: that still accepts a control with zero
segments.
2015-03-14 20:09:35 +01:00
cacaodev 0cd68ff8f8 FIXED: selection update when segmentCount changes n->0 or 0->n
If the segment count is set to 0, the [self selectedSegment] now
correctly return -1 (like cocoa).
If the segment count is set to n from 0, the first segment is selected.
See manual test.
2015-03-14 20:02:43 +01:00
Martin Carlberg 1c4e23512b Merge pull request #2326 from mrcarlberg/duplicate_ivar_superclass
Duplicate ivar on superclass
2015-03-13 10:36:40 +01:00
Alexandre Wilhelm 51bdfa44b1 New: added the protocol CPScrollViewDelegate 2015-03-12 14:39:42 -07:00
Alexandre Wilhelm 98d0a397a3 Fixed: _clickedRow and _clickedColumn not updated as in correclty
Previously, the var clickedRow and clickedColumn were only updated with a doubleClick.
Now clickedRow and clickedColumn are updated in the scope of a trackMouse as in cocoa. Once the method stopTracking:at:mouseIsUp is called, clickedRow and clickedColumn are set to -1 again. This var can't be used outside a user event.
2015-03-11 10:18:21 -07:00
Antoine Mercadal 1dc060123d NEW: Closing a platform window will correctly close its main CPWindow
Previously, when closing a platform window using the browser close
button, the represented `CPWindow` was not correctly closed.

This patch ensure `-(void)close` is called correctly by using the DOM
`unload` event. Also, delegate method `- (void)windowWillClose:` is
correctly called.
2015-03-10 16:35:03 -07:00
Antoine Mercadal 14349cb583 Merge pull request #2240 from Dogild/PlatformOrderOUt
New: added method initWithWindow in CPPlatformWindow
2015-03-10 13:24:00 -07:00
Aparajita Fishman eca959034c Merge pull request #2330 from daboe01/fix-issue-1686
Clarification for CPTableColumn's binding documentation
2015-03-10 11:25:58 +13:00
daboe01 845a35a9b8 rewording 2015-03-08 20:49:01 +01:00
daboe01 0e4751ebee wording 2015-03-08 20:46:56 +01:00
daboe01 56badd44f3 updated binding documentation 2015-03-08 19:20:57 +01:00
daboe01 399b3fd198 documentation for the hash method 2015-03-08 19:03:35 +01:00
cacaodev 43a7ca7fe8 Style & doc
Added @ignore directive for private methods.
Changed a parameter naming
Moved some layout code from indexed accessors to public method.
Accessors should only deal with the model, not the layout.
2015-03-07 23:04:32 +01:00
Antoine Mercadal 630aa53d98 Merge pull request #2321 from william57m/master
New: added the CPCache class
2015-03-05 18:08:18 -08:00
Antoine Mercadal 40c81b88d4 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-03-05 17:00:53 -08:00
Antoine Mercadal 027a317291 FIXED: Memory Leak with tooltips
Only populate the tooltips handler functions when necessary (when there is a tooltip and when the view is in a window) or clear them otherwise.

Tests in /Tests/AppKit/CPViewTest.j
2015-03-05 16:59:37 -08:00
Alexandre Wilhelm 81b538556d Fixed: removed unused var in CPScrollView.j 2015-03-04 13:01:32 -08:00
Alexandre Wilhelm 6aece1cf8c Fixed: changed default template for the generate ThemeDescriptor
Previously, the default template generated by capp gen for a new themeDescriptor project was not similar as the themeDescriptor of Aristo1 or 2.
This could confuse new developer who would like to create a new theme.
2015-03-03 22:50:39 -08:00
Martin Carlberg 03a6e6fc6a Fixed: Removed ivars that are already declared in superclass 2015-03-03 11:34:49 +01:00
Martin Carlberg 1d1aae03f3 Added: Test case for duplicate ivar in superclass 2015-03-03 11:33:50 +01:00
Martin Carlberg b47ad50d42 Fixed: Made the compiler manual test cases work again 2015-03-03 11:33:20 +01:00
Martin Carlberg a924fd0e7e Fixed: Generate compiler error when ivar is already declared in superclass 2015-03-03 11:32:18 +01:00
cacaodev 82ae908e6a Added Manual Test
Tests that widthForSegment: (the declared width) and frameForSegment:
(the effective frame) are disconnected.
Added a custom color for the divider.
2015-03-02 19:08:32 +01:00
Martin Carlberg 8e497e2a48 Fixed: Method scrollRectToVisible:(CGRect)aRect in CPView didn’t work correctly if aRect is larger then the visible rect.
If aRect is larger then the visible rect this method scrolled all they way to the opposite edge instead of the nearest.
2015-03-02 16:30:02 +01:00
william57m 471fbee090 Formatting: code readability and tests improved 2015-03-01 13:05:22 -05:00
william57m d66de937d9 Formatting: to pass capp_lint and to respect Cappuccino's standards 2015-02-28 20:53:32 -05:00
cacaodev 3ea230fd17 Improved tests for segmented control
Added manual test showing fix for setLabel:forSegment for flexible
segments.
Added ojtest showing that the selection is preserved when a selected
segment is removed.
2015-02-27 13:33:11 +01:00
cacaodev 34bc571f80 FIXED: setSelected:forSegment:
Do not add to the themes array a reference to a deleted segment.
2015-02-27 11:47:42 +01:00
cacaodev db46804bfe NEW: -insertSegments:atIndexes: and -removeSegmentsAtIndexes:
These methods are not useful with the current public API but necessary
if we want to implement CPTabView insertion/deletion methods and the
CPContentBinding.
2015-02-27 11:47:22 +01:00
cacaodev 2bd0faac2e Update NSSegmentedControl.j (nib2cib) after the new titleWithChangedSegment: usage. 2015-02-27 11:47:21 +01:00
cacaodev a9a6e58d60 Simplified -tileWithChangedSegment:
Now tileWithChangedSegment: invalidates the frame of changed segments
and the following segments on the right. Then we just ask for the frame
of the last segment which will recompute all the invalidated frames and
give the total width of the container.
Added -intrinsicContentSize : the container size based on the sizes of
its segments.
2015-02-27 11:46:46 +01:00
william57m b40f980c62 New: added the CPCache class 2015-02-26 22:07:15 -05:00
cacaodev 12ac781ef8 FIXED: -widthForSegment: and - frameForSegment:
Previously, widthForSegment was returning the current width of a
segment.
Now we separate the 2 concepts: the segment width can only be set
explicitly with setWith:forSgment: or in IB. If the width is 0, it
means the actual frame sizeTofit. In this case, we compute lazily the
frame and frameForSegment: return the actual frame.
The frame is cached and can also be invalidate by setting it to a zero
frame, for example when the content of a segment changes and we need to
recompute it.
Use frameForSegment: in the code when we mean to get the real width as
opposed to the declared width.
2015-02-27 01:09:04 +01:00
cacaodev 8373ffbf10 Replace some direct ivar access with objj 2015-02-27 01:08:34 +01:00
cacaodev 0233dca884 FIXED: replace _segments[i] with [_segment objectAtIndex:i]
This will raise an out of bound exception if needed in the methods
whose  documentation says it should.
2015-02-26 15:28:16 +01:00
cacaodev c1a3162a54 FIXED: CPSegmentedControl uninitialized ivars 2015-02-26 15:28:15 +01:00
Alexandre Wilhelm 63f634af00 Merge pull request #2300 from ahankinson/fix-domserver-dead-code
Fixed: Remove unreachable code in CPDOMDisplayServer.h
2015-02-25 17:28:15 -08:00
Antoine Mercadal 9cb3eefdc4 Merge pull request #2319 from Dogild/TokenFieldkeyEquivalent
Fixed: CPApplication dispatch the event when having the auto complete menu of a CPTokenField opened
2015-02-25 11:34:44 -08:00
Antoine Mercadal 1c9e9da10f Merge pull request #2315 from Dogild/CPTableView-ViewAtColumn
New: Added method viewAtColumn:row:makeIfNecessary: in CPTableView
2015-02-25 11:31:36 -08:00
Antoine Mercadal 71c0ba63ca Merge pull request #2318 from cacaodev/CPTableView-columnbinding
FIXED: Exception when rows were removed in a table with binded columns.
2015-02-25 11:25:53 -08:00
Antoine Mercadal c04d3b2ace Merge pull request #2316 from Dogild/MethodSetNeedsLayout
New: added the method setNeedsLayout:
2015-02-25 11:23:45 -08:00
Alexandre Wilhelm 3430349dd1 Fixed: CPApplication dispatch the event when having the auto complete menu of a CPTokenField opened
Previously, the CPApplication dispatched the current event when having the auto complete menu of a CPTokenField opened.
This occurs weird behavior. For instance when hitting enter on the menu and having a default button, the action of the button was triggered.
Now, the CPTokenField handles the key enter when the autocomplete menu is opened.
2015-02-25 10:16:56 -08:00
cacaodev 808411bc0b FIXED: Exception when rows were removed in a table with binded columns.
Before this fix, the CPTableColumn binder was not correctly reloading
the table when the number of rows changed. Now we reload fully the
dataviews when the number of rows changes. If no rows are
inserted/deleted, there is an optimization: we just need to reload the
objectValues and leave the dataviews untouched.

With Test in Tests/AppKitCPTableViewTest.j
Fixes #2317
2015-02-24 16:15:20 +01:00
Antoine Mercadal cbc653be37 Merge pull request #2313 from Dogild/UnionDistinct
New: added KVC operators unionOfObjects, distinctUnionOfObjects, unionOfArrays, distinctUnionOfArrays, distinctUnionOfSets
2015-02-21 11:38:26 -08:00
Alexandre Wilhelm bb64d0dd54 New: added method needsLayout in CPView 2015-02-20 15:49:08 -08:00
Alexandre Wilhelm f82b478a77 New: added the method setNeedsLayout:
This PR adds the possibility to layout or not a CPView.
Previously, once setNeedsLayout was called on a CPView, it wasn't possible to cancel the layout of the view.
Now we can as in cocoa. The method setNeedsLayout will still work (it calls the method setNeedsLayout: with YES).

UnitTests in Tests/AppKit/CPViewTest.j
2015-02-20 14:36:27 -08:00
Alexandre Wilhelm 2028642efe Typo: removed console.error in CPTableView 2015-02-20 10:58:25 -08:00
Alexandre Wilhelm 9af9bd0c4c New: Added method viewAtColumn:row:makeIfNecessary: in CPTableView
This PR adds the method viewAtColumn:row:makeIfNecessary: in CPTableView.
This method first attempts to return an available view, which is generally in the visible area. If there is no available view, and makeIfNecessary is YES, a prepared temporary view is returned. If makeIfNecessary is NO, and the view is not available, nil will be returned.
An exception will be thrown if row is an invalid row index and if column is an invalid column index.
The returned result should generally not be held onto for longer than the current run loop cycle. Instead they should re-query the table view for the row view.

UnitTests in Tests/AppKit/CPTableViewTests.j
2015-02-20 10:54:45 -08:00
Alexandre Wilhelm 662bf83a88 Typo: type in _CPCollectionKVCOperators 2015-02-17 14:42:23 -08:00
Antoine Mercadal 2d474ccbda Merge pull request #2215 from Dogild/RetinaDisplayed
New: possibility to draw automatically in high DPI in canvas2D
2015-02-17 14:34:47 -08:00
Antoine Mercadal cec746d632 Merge pull request #2312 from cacaodev/CPTableView-issue2310
Fix for CPTableView issue #2310: wrong textcolor in unfocused table.
2015-02-17 14:32:45 -08:00
Alexandre Wilhelm 8371ac5b0d New: added KVC operators unionOfObjects, distinctUnionOfObjects, unionOfArrays, distinctUnionOfArrays, distinctUnionOfSets
This PR adds the KVC operators unionOfObjects, distinctUnionOfObjects, unionOfArrays, distinctUnionOfArrays, distinctUnionOfSets for array and set.

Unit-Tests Tests/Foundation/CPKVCArrayTest.j
Unit-Tests Tests/Foundation/CPSetTest.j
2015-02-17 00:14:48 -08:00
cacaodev f248788d14 Test for #2310 fix, CPTableView whitespace.
Note: It seems that CPThemeStateKeyWindow cannot be tested in the
console. A manual test with multiple windows and table views exists in
Manual/TableTest/OldTest/
2015-02-15 19:29:29 +01:00
cacaodev bf202f25dd Merge remote-tracking branch 'upstream/master' into CPTableView-issue2310 2015-02-15 18:44:32 +01:00
cacaodev 2d2472dbe9 FIXED: The firstResponder state of a table dataView was not correctly removed on dismiss. fixes #2310 2015-02-15 11:00:25 +01:00
Antoine Mercadal 3adfa3c2a8 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-02-12 16:01:59 -08:00
Antoine Mercadal bf05155c5e [FIXED] Merge issues
During a previous merge the call to ..willDisplayView:.. and
…willRemoveView:.. for delegate methods were removed from CPTableView
and CPOutlineView
2015-02-12 16:01:38 -08:00
Antoine Mercadal d50df6a6fc Merge pull request #2306 from Dogild/WarningAppkit
Fixed: compilation warning for NSTableHeaderView.j
2015-02-12 15:58:42 -08:00
Antoine Mercadal a2b38c8d05 Merge pull request #2307 from cacaodev/patch-1
CPTableView & CPTableHeaderView : style, typos, tabs.
2015-02-12 15:57:05 -08:00
Alexandre Wilhelm d546da8b5f Fixed: issue with scaling a view. The associated canvas now upadtes its size when having a zoom 2015-02-12 12:49:18 -08:00
Antoine Mercadal 7c60ffa88d Merge pull request #2311 from Dogild/PopoverPosition
Fixed: CPPopover position does not update each time
2015-02-11 12:36:34 -08:00
Alexandre Wilhelm 6b235ab083 Fixed: CPPopover position does not update each time
Previously, when the frame of a superview of the targetedView would update, the popover did not update its position.
Now it does by observing each frame of each superviews of the targetedView.
2015-02-10 22:58:03 -08:00
Antoine Mercadal 7c4cdef66d Merge pull request #2309 from Dogild/SegmentedControlNice
Fixed: CPSegmentedControl divider wrong color
2015-02-10 11:42:07 -08:00
Antoine Mercadal c0a2da443a Merge pull request #2308 from Dogild/InvalidDateCPDatePicker
Fixed: The CPDatePicker can take invalid date
2015-02-10 11:41:56 -08:00
Alexandre Wilhelm e53658759b Merge remote-tracking branch 'origin' into RetinaDisplayed 2015-02-09 18:03:29 -08:00
Alexandre Wilhelm 6dc4c1c03a Fixed: CPSegmentedControl divider wrong color
Previously, when clicking on segment, the divider did not have the expected color. Now they does.
2015-02-09 17:56:23 -08:00
Alexandre Wilhelm 818c3b6092 Typo in CPDatePicker 2015-02-09 16:46:08 -08:00
Alexandre Wilhelm 6e5dee13e1 Fixed: The CPDatePicker can take invalid date
Previously, the CPDatePicker could get an invalid javascript date.
Now it will raise an exception when getting an invalidate date.
2015-02-09 16:44:59 -08:00
Alexandre Wilhelm cae78622f6 Fixed comiling warning for the class CPTableHeaderView 2015-02-08 22:36:25 -08:00
cacaodev c5db677663 Style, typos, tabs. 2015-02-08 21:07:27 +01:00
Alexandre Wilhelm c6942daf74 Fixed: compilation warning for NSTableHeaderView.j
Previously, there was a warning when compiling the file  NSTableHeaderView.j
2015-02-06 17:11:06 -08:00
Antoine Mercadal ea644a2293 Merge pull request #2301 from krodelin/fix-setValue_forKey
Fixed: Wrong value parameter in CPObjectController>>setValue:forKey:
2015-02-06 16:00:30 -08:00
Antoine Mercadal 01e69ce2b0 Merge pull request #2303 from cacaodev/check-box-theming
FIXED: The text color of a checkbox inside a selected table row was black
2015-02-06 16:00:11 -08:00
Antoine Mercadal 187a017c4d Merge pull request #2305 from Dogild/DuplicateTests
Fixed: Duplicate unit-tests
2015-02-06 15:58:27 -08:00
Antoine Mercadal f9ce1d17c8 Merge pull request #1892 from cacaodev/CPTableView-enumerateRows
CPTableView formatting and fixes [+1]
2015-02-06 15:52:36 -08:00
Antoine Mercadal ef4cd6a4a7 [NEW] --theme option for capp tool
This patch provides a new option to capp. You can do

```objj
capp gen [-f] [-l]  -T CustomTheme1 -T  CustomTheme2 MyProject
```
This will copy/symlink custom theme(s) from your CAPP_BUILD into the
current project’s `Resources` folder
2015-02-06 11:31:25 -08:00
cacaodev 32a73ad4b9 FIXED: The text color of a checkbox inside a selected table row was black.\n Now it is white. the text-color values match the CPTextField values for the same theme states 2015-01-28 14:45:35 +01:00
cacaodev 452e03b894 FIXED: remove observation in -init. It was already handled in -viewWillMoveToWindow: 2015-01-27 22:01:32 +01:00
cacaodev 01bdddaee4 Merge 34c7ded (CPSelectionHighlightStyleNone and mouse click) 2015-01-27 13:09:33 +01:00
cacaodev f7c82b0920 FIXED : -startObservingFirstResponder: remaining renaming. 2015-01-27 13:04:45 +01:00
cacaodev 820eeb7752 _stopObservingFirstResponder -> _stopObservingFirstResponderForWindow: 2015-01-25 17:30:14 +01:00
cacaodev 2e25ef6f81 Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows
Conflicts:
	AppKit/CPTableHeaderView.j
	AppKit/CPTableView.j
	Tests/AppKit/CPTableViewTest.j
2015-01-25 17:25:32 +01:00
Udo Schneider 056a1f6a33 Fixed: Wrong value parameter in CPObjectController>>setValue:forKey: 2015-01-21 23:53:49 +01:00
Andrew Hankinson f96e211cd6 Tests: New tests for CPDOMDisplayServer 2015-01-20 16:19:24 -05:00
Andrew Hankinson 0d473cbe18 Formatting
Adjusting line lengths
2015-01-20 16:17:03 -05:00
Andrew Hankinson 95514b05ca Fixed: Remove unreachable code in CPDOMDisplayServer.h
The DOM_OPTIMIZATION flag was not used by any of the current compilers. This resulted in unreachable code in CPDOMDisplayServer.h. This commit removes this code.
2015-01-20 14:16:23 -05:00
cacaodev db63c91f99 Manual/TableTest/ViewBasedCib: show how to bind in code a CPTableCellView subview 2014-11-03 16:59:42 +01:00
Alexandre Wilhelm 9c87c36644 Fixed: refactoring of CPPlatformWindow 2014-10-30 18:08:03 -07:00
Alexandre Wilhelm be9f5ad951 Fixed: issue when initialize with contentRect in CPPlatformWindow 2014-10-30 18:04:20 -07:00
Alexandre Wilhelm d065a0cbc4 New: added method initWithWindow in CPPlatformWindow
The constructor initWithWindow: initializes a new CPPlatformWindow and set the given CPWindow as a fullPlatformWindow and bridgless window.
When using this constructor, you don't need to work with the platformWindow anymore, you can use the method orderFront: and orderOut: of the CPWindow to open or close the CPPlatformWindow.
You can also use the method setFrame: of the CPWindow to automatically change the contentRect of the CPPlatformWindow. In one word we assume that the given CPWindow and the CPPlatformWindow will have the same behavior. When not using the constructor initWithWindow:, the method setFrame: of the given CPWindow won't do anything to the CPPlateformWindow.

Test app in Tests/Manual/CPPlatformWindow/
2014-10-30 17:35:09 -07:00
Alexandre Wilhelm 02dfa638ca Fixed: orderOut on a fullPlatformWindow doesn't close the parent platformWindow
Previously, when calling the method orderOut: on the full platform window of a CPPlatformWindow, cappuccino didn't close the parent CPPlatformWindow. When he does, this only works if the parent platformWindow is not the primary platform.

Test app in Tests/Manual/CPPlatformWindow/
2014-10-30 15:59:15 -07:00
cacaodev fc2e16dd4e Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows
Conflicts:
	AppKit/CPTableView.j
2014-10-01 14:11:57 +02:00
cacaodev e156badef6 FIXED: After a column removal, columns were not at the right place and non exposed views from the removed column were not removed 2014-09-25 23:14:54 +02:00
cacaodev ae6d379cce Improve TestTableColumn manual test to expose a bug when resizing after a column remove 2014-09-25 23:12:25 +02:00
cacaodev 1a414ac69c view-based table: always use the proto UID as view identifier
-setDataView: was not working because the caching system was picking
views cached with the column identifier which is persistent.

Test: CPTableViewTest -testLayout
2014-09-23 13:56:52 +02:00
cacaodev f82099143f CPTableView: -_reloadDataViewsImmediately -> -reloadData
CPTableColumn: binder:-setValueFor:  is a simple data reload.
2014-09-23 12:17:52 +02:00
Alexandre Wilhelm 93743b11b2 Fixed: typo in documentation in CPView.j 2014-09-22 10:13:51 -07:00
Alexandre Wilhelm a94696e037 Fixed: refactoring of the retina drawing feature 2014-09-21 22:46:46 -07:00
Alexandre Wilhelm 0255e8980b Fixed: remove stupid debug mode 2014-09-21 22:38:11 -07:00
Alexandre Wilhelm eff9c69069 Fixed: transform matrix for retina displayed is reset when changing the frame of the view 2014-09-21 22:35:50 -07:00
cacaodev 2dccd2f30b NEW: ojtest for method - (void)getColumn:(Function)columnRef row:(Function)rowRef forView:(CPView)aView
This internal method is used for editing and public methods rowForView: and columnForView
2014-09-18 19:23:16 +02:00
cacaodev b775cbb3bf Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows 2014-09-18 18:36:53 +02:00
Alexandre Wilhelm 79ef6f2f75 Fixed: dpi drawing didn't work when changing the frame of a view 2014-09-17 13:35:07 -07:00
Alexandre Wilhelm 451ac297d4 Fixed: change accessors for CPViewHighDPIDrawingEnabled 2014-09-17 10:52:22 -07:00
Alexandre Wilhelm 0fd8a4507c New: possibility to draw automatically in high DPI in canvas2D
Previously, Cappuccino didn't handle retina device when drawing for canvas2D. Now it does.
To do that, Cappuccino will firstly calculate the pixel ratio of the current device, then it needs to change the css style of the canvas by multiply it by the current pixel ratio and finally scale the canvas by this pixel ratio.

More information about high DPI drawing here : http://www.html5rocks.com/en/tutorials/canvas/hidpi/

Added the method `setAllowsHighDPIDrawing:` and `allowsHighDPIDrawing` to deactivate or activate this feature.

Fixed #2175
2014-09-17 10:27:26 -07:00
cacaodev 3e8afaeae4 Fixed: -removeTableColumn: now works without error. OJTest in AppKit/CPTableColumnTest.j, manual test in TableTest/TestTanleColumn/
Fixed: Added an out of bounds check to _unloadDataViews:...
Revert: revert -reloadData to the previous behavior where views & data were reloaded, not only data. That's what cocoa does for view based tables.
2014-09-11 23:14:21 +02:00
cacaodev abe2e544bb Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows 2014-09-06 21:33:18 +02:00
cacaodev 78c5522966 Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows
Conflicts:
	AppKit/CPTableView.j
2014-06-11 19:42:33 +02:00
cacaodev da82887cc0 Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows 2014-05-12 18:41:41 +02:00
cacaodev 45a59f9aec Code style
Rename _numberOfRowsDidChange -> _dataViewsNeedReloadAfterContentChange.
Subclasses use this method to tell if a full view reloading is needed
when calling -reloadData.
Currently CPOutlineView returns YES - this is the previous behavior.

Added private - (void)_reloadDataForRowIndexes:(CPIndexSet)rowIndexes
columnIndexes:(CPIndexSet)columnIndexes
This is the internal method for reloading objectValues only.
2014-04-19 09:10:51 +02:00
cacaodev 57408d18ae New: ojunit tests for CPTableView -reloadData & -removeTableColumn:
-removeTableColumn: is failing with an « index out of bounds » error.
2014-04-19 09:05:14 +02:00
cacaodev 3a97db804f Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows
Possible regression from #fe260a8
Regression: -reloadData does not reload views any more even if the table is empty (see CPOutlineViewCibTest).
BUG: -removeTableColumn: error.

Conflicts:
	AppKit/CPOutlineView.j
	AppKit/CPTableHeaderView.j
	AppKit/CPTableView.j
2014-04-18 18:12:55 +02:00
cacaodev 4d8f7c67d4 Fixed removeTableColumn: was not reloading views immediatly. Added test for issue 1913 in Manual/TableTest/TestTableColumn. Throws an exception when you click the button twice 2013-05-02 19:39:27 +02:00
cacaodev e53337bcdc Remove check in -hitTest: that was preventing reverse set binding 2013-04-13 21:39:20 +02:00
cacaodev 548a55792c Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows 2013-04-13 11:52:51 +02:00
cacaodev 43f0f2a0fd Simplified -numberOfRows
Added -_numberOfRows that computes the new number of rows. Removed the
nil check.

All tests in AppKit/CPTableViewTest are now passing with success.
2013-04-13 11:38:47 +02:00
cacaodev 7b1c696810 Fixed: CPTableViewTest: -deselectAll should send 1 CPtableViewSelectionDidChangeNotification, not 2.
Checked in cocoa.
Moved a failing test to the end of the method so it does not shadow other tests.
2013-04-12 19:48:07 +02:00
cacaodev 8d69c3764d Fixed: The data views were not loaded immediately
When reloading the table view, the actual loading (-load) is defered
until layout is needed (generally in the next run loop). This is an
advantage because it minimize reloads but in some case it is necessary
to force a reload, for example when we need to access data views, or
manually edit a view, or when we explicitely ask for a reload.
This commit adds _reloadDataViewsImmediately and make use of it when
necessary.

Tests: AppKit/CPTableViewTest -> -testEditCell
2013-04-12 19:35:03 +02:00
cacaodev 87acd9d7ee Fixed: in some circumstances, table views binded with the content binding were not correctly reloading the data.
Test: AppKit/WithOrWithoutbinding
2013-04-12 19:23:51 +02:00
cacaodev 06391c9de6 Fixed: data views were not reloaded after a CPTableColumn setDataView:
Cell-based table views with no identifier set for the table column:
Before this commit, the caching system was asking for a view identified
by the tableColumn UID. If the table column data view was changed
externaly, the previoulsy cached data views were loaded instead of the
new ones. The views are now identified by the -dataview UID.

Test: AppKit/TableTest -testLayout were a custom data view is set.
2013-04-12 19:21:16 +02:00
cacaodev b42b41216b Remove old code. Adds Equality check in -setDataView: 2013-04-12 19:08:27 +02:00
cacaodev 0a1d215560 Fixed: -reloadData: was not reloading views after a change in model rows count 2013-04-04 20:00:56 +02:00
cacaodev 44b6136ce2 Fixed CPOutlineView dragging column subclassing 2013-04-01 20:11:02 +02:00
cacaodev ba3c6023a8 Fixed CPOutlineView subclassing when settting CPThemeStateSelectedDataView 2013-04-01 20:11:02 +02:00
cacaodev be7f6ae7d1 Fixed CPOutlineView -reloadData and _layoutViewsForRowIndexes:columnIndexes: subclassing. 2013-04-01 20:11:02 +02:00
cacaodev 984c7a67f5 Fixed: -moveColumn:to: now preserve selected columns
Fixed: Starting a column drag is now faster.
Fixed: When dragging a selected column, selection is now drawn on the dragging view and the cursor is the closed hand.
Fixed: When dragging a table column, underlying columns were sliding according to the tracking location instead of the column lateral edges.
Fixed: In CPTableView, the drop indicator for rows could appear when dragging a column.

This commit creates directly the dragging column instead of relying on
built-in drag&drop. Also fixes a bug where the drop indicator would appear when
dragging a column if some rows were previously drag&dropped.
2013-04-01 20:08:37 +02:00
cacaodev a4bf6c1f7a Fixed: selectColumnIndexes:byExtendingSelection: returns early when there is no change in columns selection
Before this change, rows could be unselected even if the columns
selection did not change.
2013-04-01 20:08:36 +02:00
cacaodev 690ae1a7b6 Fixed: noteHeightOfRowsWithIndexesChanged: was not showing immediatly row height changes.
Todo: instead of reloading everything we should be able to just
relayout frames for visible views and then tile.
2013-04-01 19:52:37 +02:00
cacaodev eae9a10a7d Fixed: editingColumn and editingRow return the correct index in view based tables
Fixed: An edited view no longer send its action (view-based) or commit its object value (cell-based) when unexposed.
Fixed: Cell based tables support for editing any control, not just textfield and buttons.
2013-04-01 19:52:30 +02:00
cacaodev 7c6765b969 Fixed: After drag, views were cached.
This commit reverts #1478. It appears it is a bad idea to enqueue
visible views. The views generally stay in the cache forever and they
are repeatedly asked to be removed from the table at each load, causing
a performance penalty.
2013-04-01 19:50:41 +02:00
cacaodev a6c143a1c7 Fixed: -reloadDataForRowsIndexes:columnIndexes: behavior.
Fixed: Column dragging performance, content binding performance.

After this commit, -reloadDataForRowsIndexes:columnIndexes: reloads the data and the data only.
The -reloadData method no longer tries to reuse the views, instead it just reloads the data for visible views.
To flush and reload the views cache for visible rows, use _reloadDataViews.
To internaly layout the views geometry, use _layouViewsForRowIndexes:columnIndexes:

In CPTableView dragging column code, relayout the views whose frame changed instead of reloading everything.
2013-04-01 19:50:41 +02:00
cacaodev 714da0ab3b New: -enumerateAvailableViewsUsingBlock: and private -_enumerateViewsInRows:columns:usingBlock: and -_enumerateViewsInRows:tableColumns:usingBlock: methods.
New: preparedViewAtColumn:row:

These methods allow to enumerate visible data views or data views in specified columns and rows.
-enumerateAvailableViewsUsingBlock: is the counterpart of cocoa's -enumerateAvailableRowViewsUsingBlock: except that it enumerates data views instead of CPTableRowView and the block has an additional column parameter.

This commit reverses the way views are stored and accessed in the table view: rows>columns instead of columns>rows.
Applied the second method where it is relevant, making the code more compact and readable.
2013-04-01 19:49:41 +02:00
cacaodev 377ec37257 Fixed: In CPTrace, fixed error when an argument was not a cappuccino object.
Arguments such as CGPoint, CGRect and functions are now correctly
printed to the console.
2013-04-01 19:34:04 +02:00
535 changed files with 33796 additions and 20080 deletions
+5 -1
View File
@@ -20,12 +20,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "_CPObject+Theme.j"
@import "_CPToolTip.j"
@import "CALayer.j"
@import "CGGeometry.j"
@import "CPAccordionView.j"
@import "CPAlert.j"
@import "CPAnimation.j"
@import "CPAppearance.j"
@import "CPApplication.j"
@import "CPArrayController.j"
@import "CPBezierPath.j"
@@ -101,12 +103,14 @@
@import "CPTokenField.j"
@import "CPToolbar.j"
@import "CPToolbarItem.j"
@import "CPTrackingArea.j"
@import "CPTreeNode.j"
@import "CPUserDefaultsController.j"
@import "CPView.j"
@import "CPViewAnimation.j"
@import "CPViewController.j"
@import "CPVisualEffectView.j"
@import "CPWebView.j"
@import "CPWindow.j"
@import "CPWindowController.j"
@import "CPWorkspace.j"
@import "CPWorkspace.j"
+2 -2
View File
@@ -821,12 +821,12 @@ var bottomHeight = 71;
else if (_modalDelegate)
{
if (_didEndSelector)
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
_modalDelegate.isa.objj_msgSend3(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
}
else if (_delegate)
{
if (_didEndSelector)
objj_msgSend(_delegate, _didEndSelector, self, returnCode);
_delegate.isa.objj_msgSend2(_delegate, _didEndSelector, self, returnCode);
else
[self _sendDelegateAlertDidEndReturnCode:returnCode];
}
+162
View File
@@ -0,0 +1,162 @@
/*
* CPAppearance.j
* AppKit
*
* Created by Antoine Mercadal.
* Copyright 2015, Cappuccino Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPTheme.j"
CPAppearanceNameAqua = @"CPAppearanceNameAqua";
CPAppearanceNameLightContent = @"CPAppearanceNameLightContent";
CPAppearanceNameVibrantDark = @"CPAppearanceNameVibrantDark";
CPAppearanceNameVibrantLight = @"CPAppearanceNameVibrantLight";
var _CPAppearanceCurrent = nil,
_CPAppearancesRegistry = @{};
@protocol CPAppearanceCustomization <CPObject>
@required
- (CPAppearance)appearance;
- (void)setAppearance:(CPAppearance)appearance;
- (CPAppearance)effectiveAppearance;
- (void)setEffectiveAppearance:(CPAppearance)appearance;
@end
CPThemeStateAppearanceAqua = CPThemeState("appearance-aqua");
CPThemeStateAppearanceLightContent = CPThemeState("appearance-light-content");
CPThemeStateAppearanceVibrantLight = CPThemeState("appearance-vibrant-light");
CPThemeStateAppearanceVibrantDark = CPThemeState("appearance-vibrant-dark");
/*!
@ingroup appkit
A CPAppareance represents the appearance of an to a subset of UI elements.
This is a very lightweight implementation of the NSAppearance system, but
We are using it for compliance, and especially for the CPVisualEffectView
*/
@implementation CPAppearance : CPObject
{
BOOL _allowsVibrancy @accessors(property=allowsVibrancy);
CPString _name;
}
#pragma mark -
#pragma mark Class Methods
/*! Returns the current default CPAppearance
*/
+ (CPAppearance)currentAppearance
{
if (!_CPAppearanceCurrent)
_CPAppearanceCurrent = [CPAppearance appearanceNamed:CPAppearanceNameAqua];
return _CPAppearanceCurrent;
}
/*! Sets the current default CPAppearance
@param appearance the new current appearance
*/
+ (void)setCurrentAppearance:(CPAppearance)anAppearance
{
_CPAppearanceCurrent = anAppearance;
}
/*! Returns the CPAppearance object with the given name
@param name the name of the appearance
*/
+ (CPAppearance)appearanceNamed:(CPString)aName
{
if (![_CPAppearancesRegistry containsKey:aName])
{
[_CPAppearancesRegistry setObject:[[CPAppearance alloc] initWithAppearanceNamed:aName bundle:nil]
forKey:aName];
}
return [_CPAppearancesRegistry objectForKey:aName];
}
#pragma mark -
#pragma mark Initialization
/*! Creates a CPAppearance object initialized to the specified appearance file in the specified bundle
This method does actually nothing special. It just creates a default appearance object
*/
- (id)initWithAppearanceNamed:(CPString)aName bundle:(CPBundle)bundle
{
if (self = [super init])
{
_name = aName;
_allowsVibrancy = YES;
if ([_CPAppearancesRegistry containsKey:aName])
[CPException raise:CPInternalInconsistencyException reason:"Appearance with name '" + aName + "' is already declared."];
[_CPAppearancesRegistry setObject:self forKey:aName];
}
return self;
}
#pragma mark -
#pragma mark Implementation
- (BOOL)isEqual:(id)anObject
{
if (![anObject isKindOfClass:CPAppearance])
return NO;
return self._name == anObject._name;
}
- (CPString)description
{
return @"<CPAppearance @" + [self UID] + @" name: " + _name + ">";
}
#pragma mark -
#pragma mark CPCoding
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super init])
{
_name = [aCoder decodeObjectForKey:@"_name"];
_allowsVibrancy = [aCoder decodeBoolForKey:@"_allowsVibrancy"];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_name forKey:@"_name"];
[aCoder encodeBool:_allowsVibrancy forKey:@"_allowsVibrancy"];
}
@end
+49 -8
View File
@@ -47,6 +47,8 @@ var CPMainCibFile = @"CPMainCibFile",
@protocol CPApplicationDelegate <CPObject>
@optional
- (CPApplicationTerminateReply)applicationShouldTerminate:(CPApplication)sender;
- (CPString)applicationShouldTerminateMessage:(CPApplication)sender;
- (void)applicationDidBecomeActive:(CPNotification)aNotification;
- (void)applicationDidChangeScreenParameters:(CPNotification)aNotification;
- (void)applicationDidFinishLaunching:(CPNotification)aNotification;
@@ -58,6 +60,9 @@ var CPMainCibFile = @"CPMainCibFile",
@end
var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
CPApplicationDelegate_applicationShouldTerminateMessage_ = 1 << 1;
/*!
@ingroup appkit
@class CPApplication
@@ -103,6 +108,8 @@ var CPMainCibFile = @"CPMainCibFile",
//
id <CPApplicationDelegate> _delegate;
CPInteger _implementedDelegateMethods;
BOOL _finishedLaunching;
BOOL _isActive;
@@ -165,6 +172,8 @@ var CPMainCibFile = @"CPMainCibFile",
if (_delegate == aDelegate)
return;
_implementedDelegateMethods = 0;
var defaultCenter = [CPNotificationCenter defaultCenter],
delegateNotifications =
[
@@ -205,6 +214,12 @@ var CPMainCibFile = @"CPMainCibFile",
if ([_delegate respondsToSelector:selector])
[defaultCenter addObserver:_delegate selector:selector name:notificationName object:self];
}
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminate_
if ([_delegate respondsToSelector:@selector(applicationShouldTerminateMessage:)])
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminateMessage_
}
/*!
@@ -426,12 +441,7 @@ var CPMainCibFile = @"CPMainCibFile",
{
// callback method for terminate:
if (didCloseAll)
{
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
[self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]];
else
[self replyToApplicationShouldTerminate:YES];
}
[self replyToApplicationShouldTerminate:[self _sendDelegateApplicationShouldTerminate]];
}
- (void)replyToApplicationShouldTerminate:(BOOL)terminate
@@ -445,6 +455,9 @@ var CPMainCibFile = @"CPMainCibFile",
- (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps
{
if (_isActive)
return;
[self _willBecomeActive];
[CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps];
@@ -455,6 +468,9 @@ var CPMainCibFile = @"CPMainCibFile",
- (void)deactivate
{
if (!_isActive)
return;
[self _willResignActive];
[CPPlatform deactivate];
@@ -480,6 +496,15 @@ var CPMainCibFile = @"CPMainCibFile",
- (void)run
{
[self finishLaunching];
[self sendEvent:[CPEvent otherEventWithType:CPAppKitDefined
location:CGPointMakeZero()
modifierFlags:0
timestamp:[CPEvent currentTimestamp]
windowNumber:[_keyWindow windowNumber]
context:nil
subtype:nil
data1:nil
data2:nil]];
}
// Managing the Event Loop
@@ -959,7 +984,7 @@ var CPMainCibFile = @"CPMainCibFile",
*/
- (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
{
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }, shouldDequeue));
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { if (aTarget != null) aTarget.isa.objj_msgSend1(aTarget, aSelector, anEvent); }, shouldDequeue));
}
/*!
@@ -1195,6 +1220,22 @@ var CPMainCibFile = @"CPMainCibFile",
userInfo:nil];
}
- (BOOL)_sendDelegateApplicationShouldTerminate
{
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminate_))
return YES;
return [_delegate applicationShouldTerminate:self];
}
- (CPString)_sendDelegateApplicationShouldTerminateMessage
{
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminateMessage_))
return @"You have attempted to leave this page. Are you sure you want to exit this page?";
return [_delegate applicationShouldTerminateMessage:self];
}
- (void)_didResignActive
{
if (self._activeMenu)
@@ -1317,7 +1358,7 @@ var _CPAppBootstrapperActions = nil;
{
var action = _CPAppBootstrapperActions.shift();
if (objj_msgSend(self, action))
if (self.isa.objj_msgSend0(self, action))
return;
}
+1
View File
@@ -31,6 +31,7 @@ CPApplicationWillResignActiveNotification = @"CPApplicationWillResignA
CPApplicationDidResignActiveNotification = @"CPApplicationDidResignActiveNotification";
CPApplicationDidChangeScreenParametersNotification = @"CPApplicationDidChangeScreenParametersNotification";
@typedef CPApplicationTerminateReply
CPTerminateNow = YES;
CPTerminateCancel = NO;
CPTerminateLater = -1; // not currently supported
+3 -3
View File
@@ -280,7 +280,7 @@ CPButtonImageOffset = 3.0;
break;
case CPOffState:
[self unsetThemeState:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
[self unsetThemeStates:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
}
}
@@ -665,10 +665,10 @@ CPButtonImageOffset = 3.0;
*/
- (void)sizeToFit
{
[self setFrameSize:[self _minimumFrameSize]];
[self layoutSubviews];
[self setFrameSize:[self _minimumFrameSize]];
if ([self ephemeralSubviewNamed:@"content-view"])
[self layoutSubviews];
}
+8 -6
View File
@@ -112,6 +112,8 @@
- (void)awakeFromCib
{
[super awakeFromCib];
var view = [self superview],
subview = self;
@@ -270,15 +272,15 @@
currentButtonOffset += width - 1;
}
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered]];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, ]];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered]];
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered]];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, ]];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered]];
[button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
// FIXME shouldn't need this
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[self addSubview:button];
}
+1 -1
View File
@@ -195,7 +195,7 @@
- (CGRect)documentVisibleRect
{
return [self convertRect:[self bounds] fromView:_documentView];
return [_documentView visibleRect];
}
@end
+2 -3
View File
@@ -60,6 +60,8 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
@end
var HORIZONTAL_MARGIN = 2;
/*!
@ingroup appkit
@class CPCollectionView
@@ -88,9 +90,6 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
@param indices the indices to obtain drag types
@return an array of drag types (CPString)
*/
var HORIZONTAL_MARGIN = 2;
@implementation CPCollectionView : CPView
{
CPArray _content;
+55 -6
View File
@@ -25,6 +25,7 @@
@import "CGColor.j"
@import "_CPObject+Theme.j"
@import "CPCompatibility.j"
@import "CPImage.j"
@@ -65,7 +66,8 @@ var cachedBlackColor,
cachedOrangeColor,
cachedPurpleColor,
cachedShadowColor,
cachedClearColor;
cachedClearColor,
cachedThemeColor;
/// @endcond
@@ -78,7 +80,7 @@ var cachedBlackColor,
<p>It also provides some class helper methods that
returns instances of commonly used colors.</p>
*/
@implementation CPColor : CPObject
@implementation CPColor : CPObject <CPTheme>
{
CPArray _components;
@@ -86,6 +88,27 @@ var cachedBlackColor,
CPString _cssString;
}
#pragma mark -
#pragma mark Theming
+ (CPString)defaultThemeClass
{
return "color";
}
+ (CPDictionary)themeAttributes
{
return @{
@"alternate-selected-control-color": [CPNull null],
@"secondary-selected-control-color" : [CPNull null]
};
}
#pragma mark -
#pragma mark Static methods
/*!
Creates a color in the RGB colorspace, with an alpha value.
Each component should be between the range of 0.0 to 1.0. For
@@ -436,14 +459,22 @@ var cachedBlackColor,
return cachedClearColor;
}
+ (CPColor)_cachedThemeColor
{
if (!cachedThemeColor)
cachedThemeColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
return cachedThemeColor;
}
+ (CPColor)alternateSelectedControlColor
{
return [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]];
return [[self _cachedThemeColor] valueForThemeAttribute:@"alternate-selected-control-color"];
}
+ (CPColor)secondarySelectedControlColor
{
return [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]];
return [[self _cachedThemeColor] valueForThemeAttribute:@"secondary-selected-control-color"];
}
/*!
@@ -489,6 +520,10 @@ var cachedBlackColor,
// use it (issue #1413.)
[self _initCSSStringFromComponents];
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
[self _loadThemeAttributes];
return self;
}
@@ -502,6 +537,10 @@ var cachedBlackColor,
_components = components;
[self _initCSSStringFromComponents];
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
[self _loadThemeAttributes];
}
return self;
@@ -528,6 +567,10 @@ var cachedBlackColor,
_patternImage = anImage;
_cssString = "url(\"" + [_patternImage filename] + "\")";
_components = [0.0, 0.0, 0.0, 1.0];
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
[self _loadThemeAttributes];
}
return self;
@@ -835,9 +878,13 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
- (id)initWithCoder:(CPCoder)aCoder
{
if ([aCoder containsValueForKey:CPColorPatternImageKey])
return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
self = [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
else
self = [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
[self _decodeThemeObjectsWithCoder:aCoder];
return self;
}
/*!
@@ -850,6 +897,8 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
[aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey];
else
[aCoder encodeObject:_components forKey:CPColorComponentsKey];
[self _encodeThemeObjectsWithCoder:aCoder];
}
@end
-1
View File
@@ -71,7 +71,6 @@ var SharedColorPanel = nil,
*/
@implementation CPColorPanel : CPPanel
{
_CPColorPanelToolbar _toolbar;
_CPColorPanelSwatches _swatchView;
_CPColorPanelPreview _previewView;
+12 -2
View File
@@ -74,7 +74,7 @@ var CPComboBoxTextSubview = @"text",
BOOL _usesDataSource;
CGSize _intercellSpacing;
CPArray _items;
id<CPComboBoxDataSource> _dataSource;
id <CPComboBoxDataSource> _dataSource;
CPInteger _implementedDelegateComboBoxMethods;
CPString _selectedStringValue;
float _itemHeight;
@@ -843,7 +843,17 @@ var CPComboBoxTextSubview = @"text",
// In FireFox this needs to be done in setTimeout, otherwise there is no caret
// We have to save the input element now, when we lose focus it will change.
var element = [self _inputElement];
window.setTimeout(function() { element.focus(); }, 0);
[[CPRunLoop mainRunLoop] performBlock:function()
{
// This will prevent to jump to the focused element
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
element.focus();
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
#endif
return NO;
+9
View File
@@ -438,3 +438,12 @@ function CPBrowserCSSProperty(aProperty)
return browserProperty.toLowerCase();
}
function CPBrowserBackingStorePixelRatio(context)
{
return context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
}
+66 -9
View File
@@ -27,6 +27,7 @@
@import "CPShadow.j"
@import "CPView.j"
@import "CPKeyValueBinding.j"
@import "CPTrackingArea.j"
@global CPApp
@@ -42,13 +43,6 @@
@end
@typedef CPTextAlignment
CPLeftTextAlignment = 0;
CPRightTextAlignment = 1;
CPCenterTextAlignment = 2;
CPJustifiedTextAlignment = 3;
CPNaturalTextAlignment = 4;
@typedef CPControlSize
CPRegularControlSize = 0;
CPSmallControlSize = 1;
@@ -126,6 +120,8 @@ var CPControlBlackColor = [CPColor blackColor];
CGPoint _previousTrackingLocation;
CPControlSize _controlSize;
CPWritingDirection _baseWritingDirection @accessors(property=baseWritingDirection);
}
+ (CPDictionary)themeAttributes
@@ -205,7 +201,6 @@ var CPControlBlackColor = [CPColor blackColor];
return self;
}
#pragma mark -
#pragma mark Control Size
@@ -240,7 +235,7 @@ var CPControlBlackColor = [CPColor blackColor];
*/
- (ThemeState)_controlSizeThemeState
{
switch(_controlSize)
switch (_controlSize)
{
case CPSmallControlSize:
return CPThemeStateControlSizeSmall;
@@ -1016,6 +1011,62 @@ var CPControlBlackColor = [CPColor blackColor];
return [self hasThemeState:CPThemeStateHighlighted];
}
#pragma mark -
#pragma mark Base writing direction
/*!
Sets the initial writing direction of the receiver
@param writingDirection - It could be CPWritingDirectionNatural, CPWritingDirectionLeftToRight, CPWritingDirectionRightToLeft
*/
- (void)setBaseWritingDirection:(CPWritingDirection)writingDirection
{
if (writingDirection == _baseWritingDirection)
return;
[self willChangeValueForKey:@"baseWritingDirection"];
_baseWritingDirection = writingDirection;
[self didChangeValueForKey:@"baseWritingDirection"];
#if PLATFORM(DOM)
var style;
switch (_baseWritingDirection)
{
case CPWritingDirectionNatural:
style = "initial";
break;
case CPWritingDirectionLeftToRight:
style = "ltr";
break;
case CPWritingDirectionRightToLeft:
style = "rtl";
break;
default:
style = "initial";
}
_DOMElement.style.direction = style;
#endif
}
@end
@implementation CPControl (CPTrackingArea)
- (void)updateTrackingAreas
{
[self removeAllTrackingAreas];
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect
owner:self
userInfo:nil]];
}
@end
var CPControlActionKey = @"CPControlActionKey",
@@ -1027,6 +1078,7 @@ var CPControlActionKey = @"CPControlActionKey",
CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
CPControlTargetKey = @"CPControlTargetKey",
CPControlValueKey = @"CPControlValueKey",
CPControlBaseWrittingDirectionKey = @"CPControlBaseWrittingDirectionKey";
__Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
@@ -1055,6 +1107,8 @@ var CPControlActionKey = @"CPControlActionKey",
[self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
[self setControlSize:[aCoder decodeIntForKey:CPControlControlSizeKey]];
[self setBaseWritingDirection:[aCoder decodeIntForKey:CPControlBaseWrittingDirectionKey]];
}
return self;
@@ -1089,6 +1143,9 @@ var CPControlActionKey = @"CPControlActionKey",
[aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
[aCoder encodeInt:_controlSize forKey:CPControlControlSizeKey];
[aCoder encodeInt:_baseWritingDirection forKey:CPControlBaseWrittingDirectionKey];
}
@end
+3
View File
@@ -123,6 +123,9 @@ var currentCursor = nil,
- (void)set
{
if (currentCursor === self)
return;
currentCursor = self;
#if PLATFORM(DOM)
+8 -8
View File
@@ -323,6 +323,14 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)_setDateValue:(CPDate)aDateValue timeInterval:(CPTimeInterval)aTimeInterval
{
// Make sure to have a valid date and avoid NaN values
if (!isFinite(aDateValue))
{
[CPException raise:CPInvalidArgumentException
reason:@"aDateValue is not valid"];
return;
}
if (_minDate)
aDateValue = new Date (MAX(aDateValue, _minDate));
@@ -622,14 +630,6 @@ CPEraDatePickerElementFlag = 0x0100;
return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
}
/*! Check if we are in the english format or not. Depending on the locale
*/
- (BOOL)_isEnglishFormat
{
return [[_locale objectForKey:CPLocaleLanguageCode] isEqualToString:@"en"];
}
#pragma mark -
#pragma mark Key event
+16 -16
View File
@@ -1114,30 +1114,30 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:CPThemeStateHighlighted] forThemeAttribute:@"font" inState:CPThemeStateHighlighted];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-color" inState:CPThemeStateHighlighted];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateHighlighted];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateHighlighted];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[self addSubview:_textField];
+1 -1
View File
@@ -139,7 +139,7 @@ var RADIANS = Math.PI / 180;
[_minuteHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"minute-hand-image"]];
[_secondHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"second-hand-image"]];
if ([_datePicker _isEnglishFormat])
if ([_datePicker _isAmericanFormat])
{
if (dateValue.getHours() > 11)
[_PMAMTextField setStringValue:@"PM"]
+102 -75
View File
@@ -198,7 +198,6 @@ var CPZeroKeyCode = 48,
else
[self _selectTextField:_firstTextField];
}
}
/*! Select a textField
@@ -277,74 +276,99 @@ var CPZeroKeyCode = 48,
}
}
/*! performKeyEquivalent event
Used for moving in the textField
/*!
PerformKeyEquivalent event
We need to override that to handle the tab key
*/
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
{
if (![self isEnabled] || !_currentTextField || [[self window] firstResponder] != _datePicker)
return NO;
var key = [anEvent charactersIgnoringModifiers];
if (key == CPUpArrowFunctionKey)
if ([anEvent charactersIgnoringModifiers] === CPTabCharacter)
{
[_currentTextField _invalidTimer];
[_stepper setDoubleValue:[_currentTextField intValue]];
[_stepper performClickUp:self];
if ([anEvent modifierFlags] & CPShiftKeyMask)
[self insertBacktab:self];
else
[self insertTab:self];
return YES;
}
if (key == CPDownArrowFunctionKey)
else if ([anEvent charactersIgnoringModifiers] === CPBackTabCharacter)
{
[_currentTextField _invalidTimer];
[_stepper setDoubleValue:[_currentTextField intValue]];
[_stepper performClickDown:self];
[self insertBacktab:self];
return YES;
}
if (key == CPLeftArrowFunctionKey || [anEvent keyCode] == CPTabKeyCode && [anEvent modifierFlags] & CPShiftKeyMask)
{
if (_currentTextField == _firstTextField && [anEvent keyCode] == CPTabKeyCode)
{
var previousValidKeyView = [_datePicker previousValidKeyView];
if (previousValidKeyView)
[[self window] makeFirstResponder:previousValidKeyView];
return YES;
}
[self _selectTextField:[_currentTextField previousTextField]];
return YES;
}
if (key == CPRightArrowFunctionKey || [anEvent keyCode] == CPTabKeyCode)
{
if (_currentTextField == _lastTextField && [anEvent keyCode] == CPTabKeyCode)
{
var nextValidKeyView = [_datePicker nextValidKeyView];
if (nextValidKeyView)
[[self window] makeFirstResponder:nextValidKeyView];
return YES;
}
[self _selectTextField:[_currentTextField nextTextField]];
return YES;
}
if ([anEvent keyCode] == CPReturnKeyCode)
{
[_currentTextField _endEditing];
return [super performKeyEquivalent:anEvent];
}
return [super performKeyEquivalent:anEvent];
}
- (void)insertTab:(id)sender
{
if (!_currentTextField)
return;
if (_currentTextField == _lastTextField)
[[self window] selectNextKeyView:self];
else
[self moveRight:sender];
}
- (void)moveRight:(id)sender
{
if (!_currentTextField)
return;
[self _selectTextField:[_currentTextField nextTextField]];
}
- (void)insertBacktab:(id)sender
{
if (!_currentTextField)
return;
if (_currentTextField == _firstTextField)
[[self window] selectPreviousKeyView:self];
else
[self moveLeft:sender];
}
- (void)moveLeft:(id)sender
{
if (!_currentTextField)
return;
[self _selectTextField:[_currentTextField previousTextField]];
}
- (void)moveDown:(id)sender
{
if (!_currentTextField)
return;
[_currentTextField _invalidTimer];
[_stepper setDoubleValue:[_currentTextField intValue]];
[_stepper performClickDown:self];
}
- (void)moveUp:(id)sender
{
if (!_currentTextField)
return;
[_currentTextField _invalidTimer];
[_stepper setDoubleValue:[_currentTextField intValue]];
[_stepper performClickUp:self];
}
- (void)insertNewline:(id)sender
{
if (!_currentTextField)
return;
[_currentTextField _endEditing];
}
/*! KeyDown event
We just care care about the event A/P and every numbers
*/
@@ -353,7 +377,9 @@ var CPZeroKeyCode = 48,
if (![self isEnabled])
return;
if ([_datePicker _isEnglishFormat] && [_currentTextField dateType] == CPAMPMDateType && ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPPKeyCode || [anEvent keyCode] == CPMajAKeyCode || [anEvent keyCode] == CPMajPKeyCode))
[self interpretKeyEvents:[anEvent]];
if ([_datePicker _isAmericanFormat] && [_currentTextField dateType] == CPAMPMDateType && ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPPKeyCode || [anEvent keyCode] == CPMajAKeyCode || [anEvent keyCode] == CPMajPKeyCode))
{
if ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPMajAKeyCode)
[_currentTextField setStringValue:@"AM"];
@@ -368,6 +394,7 @@ var CPZeroKeyCode = 48,
[_currentTextField setValueForKeyEvent:anEvent];
}
#pragma mark -
#pragma mark Layout methods
@@ -725,7 +752,7 @@ var CPZeroKeyCode = 48,
if (hour != currentHour)
{
if (([_datePicker _isEnglishFormat] || [_datePicker _isAmericanFormat]))
if ([_datePicker _isAmericanFormat])
{
if (![self _isAMHour])
{
@@ -1050,7 +1077,7 @@ var CPZeroKeyCode = 48,
- (void)_updateHiddenTextFields
{
var datePickerElements = [_datePicker datePickerElements],
isEnglishFormat = [_datePicker _isEnglishFormat];
isAmericanFormat = [_datePicker _isAmericanFormat];
if (datePickerElements & CPYearMonthDatePickerElementFlag)
{
@@ -1083,7 +1110,7 @@ var CPZeroKeyCode = 48,
[_textFieldSeparatorThree setHidden:NO];
[_textFieldSeparatorFour setHidden:YES];
if (isEnglishFormat)
if (isAmericanFormat)
[_textFieldPMAM setHidden:NO];
else
[_textFieldPMAM setHidden:YES];
@@ -1115,9 +1142,9 @@ var CPZeroKeyCode = 48,
verticalInset = contentInset.top - contentInset.bottom,
firstTexField = _textFieldMonth,
secondTextField = _textFieldDay,
isEnglishFormat = [_datePicker _isEnglishFormat];
isAmericanFormat = [_datePicker _isAmericanFormat];
if (!isEnglishFormat)
if (!isAmericanFormat)
{
firstTexField = _textFieldDay;
secondTextField = _textFieldMonth;
@@ -1131,7 +1158,7 @@ var CPZeroKeyCode = 48,
else
[secondTextField setFrameOrigin:CGPointMake(CGRectGetMaxX([_textFieldSeparatorOne frame]) + separatorContentInset.right, verticalInset)];
if (isEnglishFormat && [secondTextField isHidden])
if (isAmericanFormat && [secondTextField isHidden])
[_textFieldSeparatorTwo setFrameOrigin:CGPointMake(CGRectGetMaxX([firstTexField frame]) + separatorContentInset.left, verticalInset)];
else
[_textFieldSeparatorTwo setFrameOrigin:CGPointMake(CGRectGetMaxX([secondTextField frame]) + separatorContentInset.left, verticalInset)];
@@ -1206,7 +1233,7 @@ var CPZeroKeyCode = 48,
{
var datePickerElements = [_datePicker datePickerElements];
if ([_datePicker _isEnglishFormat])
if ([_datePicker _isAmericanFormat])
{
if (datePickerElements & CPYearMonthDayDatePickerElementFlag || datePickerElements & CPYearMonthDatePickerElementFlag)
[[self superview] setFirstTextField:_textFieldMonth];
@@ -1249,9 +1276,9 @@ var CPZeroKeyCode = 48,
var datePickerElements = [_datePicker datePickerElements],
firstTexField = _textFieldMonth,
secondTextField = _textFieldDay,
isEnglishFormat = [_datePicker _isEnglishFormat];
isAmericanFormat = [_datePicker _isAmericanFormat];
if (!isEnglishFormat)
if (!isAmericanFormat)
{
firstTexField = _textFieldDay;
secondTextField = _textFieldMonth;
@@ -1266,7 +1293,7 @@ var CPZeroKeyCode = 48,
if (datePickerElements & CPHourMinuteSecondDatePickerElementFlag || datePickerElements & CPHourMinuteDatePickerElementFlag)
[_textFieldYear setNextTextField:_textFieldHour];
else if (isEnglishFormat || (datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
else if (isAmericanFormat || (datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
[_textFieldYear setNextTextField:firstTexField];
else
[_textFieldYear setNextTextField:secondTextField];
@@ -1275,7 +1302,7 @@ var CPZeroKeyCode = 48,
if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
[_textFieldMinute setNextTextField:_textFieldSecond];
else if (isEnglishFormat)
else if (isAmericanFormat)
[_textFieldMinute setNextTextField:_textFieldPMAM];
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
[_textFieldMinute setNextTextField:firstTexField];
@@ -1284,7 +1311,7 @@ var CPZeroKeyCode = 48,
else
[_textFieldMinute setNextTextField:_textFieldHour];
if (isEnglishFormat)
if (isAmericanFormat)
[_textFieldSecond setNextTextField:_textFieldPMAM];
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
[_textFieldSecond setNextTextField:firstTexField];
@@ -1304,9 +1331,9 @@ var CPZeroKeyCode = 48,
var datePickerElements = [_datePicker datePickerElements],
firstTexField = _textFieldMonth,
secondTextField = _textFieldDay,
isEnglishFormat = [_datePicker _isEnglishFormat];
isAmericanFormat = [_datePicker _isAmericanFormat];
if (!isEnglishFormat)
if (!isAmericanFormat)
{
firstTexField = _textFieldDay;
secondTextField = _textFieldMonth;
@@ -1322,14 +1349,14 @@ var CPZeroKeyCode = 48,
if (datePickerElements & CPYearMonthDatePickerElementFlag)
[_textFieldHour setPreviousTextField:_textFieldYear];
else if (isEnglishFormat)
else if (isAmericanFormat)
[_textFieldHour setPreviousTextField:_textFieldPMAM];
else if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
[_textFieldHour setPreviousTextField:_textFieldSecond];
else
[_textFieldHour setPreviousTextField:_textFieldMinute];
if (!isEnglishFormat)
if (!isAmericanFormat)
[_textFieldYear setPreviousTextField:_textFieldMonth];
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
[_textFieldYear setPreviousTextField:_textFieldDay];
@@ -1338,7 +1365,7 @@ var CPZeroKeyCode = 48,
[secondTextField setPreviousTextField:firstTexField];
if (isEnglishFormat && datePickerElements & CPHourMinuteDatePickerElementFlag)
if (isAmericanFormat && datePickerElements & CPHourMinuteDatePickerElementFlag)
[firstTexField setPreviousTextField:_textFieldPMAM];
else if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
[firstTexField setPreviousTextField:_textFieldSecond];
@@ -1530,7 +1557,7 @@ var CPMonthDateType = 0,
}
}
if (parseInt(newValue) > [self _maxNumberWithMaxDate] || ([_datePicker _isEnglishFormat] && _dateType == CPHourDateType && parseInt(newValue) > 12))
if (parseInt(newValue) > [self _maxNumberWithMaxDate] || ([_datePicker _isAmericanFormat] && _dateType == CPHourDateType && parseInt(newValue) > 12))
return;
_firstEvent = NO;
@@ -1549,7 +1576,7 @@ var CPMonthDateType = 0,
if ([stringValue length])
{
if ([_datePicker _isEnglishFormat] && [self dateType] == CPHourDateType)
if ([_datePicker _isAmericanFormat] && [self dateType] == CPHourDateType)
{
var isAMHour = [[self superview] _isAMHour];
@@ -1593,7 +1620,7 @@ var CPMonthDateType = 0,
if (![objectValue length])
objectValue = [self objectValue];
if ([_datePicker _isEnglishFormat] && [self dateType] == CPHourDateType)
if ([_datePicker _isAmericanFormat] && [self dateType] == CPHourDateType)
{
var isAMHour = [[self superview] _isAMHour];
@@ -1621,7 +1648,7 @@ var CPMonthDateType = 0,
}
else if (_dateType != CPAMPMDateType)
{
if (_dateType == CPHourDateType && [_datePicker _isEnglishFormat])
if (_dateType == CPHourDateType && [_datePicker _isAmericanFormat])
{
var value = parseInt(aStringValue);
+22 -13
View File
@@ -515,7 +515,9 @@ var CPDocumentUntitledCount = 0;
alert("There was an error retrieving the document.");
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
var theDelegate = session.delegate;
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
}
else
{
@@ -540,7 +542,9 @@ var CPDocumentUntitledCount = 0;
_writeRequest = nil;
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
var theDelegate = session.delegate;
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
[self _sendDocumentSavedNotification:NO];
}
}
@@ -553,14 +557,15 @@ var CPDocumentUntitledCount = 0;
*/
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
{
var session = aConnection.session;
var session = aConnection.session,
theDelegate = session.delegate;
// READ
if (aConnection == _readConnection)
{
[self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil];
objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, YES, session.contextInfo);
}
else
{
@@ -569,7 +574,7 @@ var CPDocumentUntitledCount = 0;
_writeRequest = nil;
objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, YES, session.contextInfo);
[self _sendDocumentSavedNotification:YES];
}
}
@@ -580,10 +585,11 @@ var CPDocumentUntitledCount = 0;
*/
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
{
var session = aConnection.session;
var session = aConnection.session,
theDelegate = session.delegate;
if (_readConnection == aConnection)
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
else
{
@@ -597,7 +603,7 @@ var CPDocumentUntitledCount = 0;
alert("There was an error saving the document.");
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
[self _sendDocumentSavedNotification:NO];
}
}
@@ -858,21 +864,24 @@ var CPDocumentUntitledCount = 0;
[self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}];
else if ([delegate respondsToSelector:selector])
objj_msgSend(delegate, selector, self, YES, info);
delegate.isa.objj_msgSend3(delegate, selector, self, YES, info);
}
- (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
{
var theDelegate = context.delegate;
if (aDocument === self && shouldClose)
[self close];
objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context);
if (theDelegate != null)
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, aDocument, shouldClose, context.context);
}
- (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context
{
if (![self isDocumentEdited])
return [aDelegate respondsToSelector:aSelector] && objj_msgSend(aDelegate, aSelector, self, YES, context);
return [aDelegate respondsToSelector:aSelector] && aDelegate.isa.objj_msgSend3(aDelegate, aSelector, self, YES, context);
_canCloseAlert = [[CPAlert alloc] init];
@@ -901,8 +910,8 @@ var CPDocumentUntitledCount = 0;
if (returnCode === 0)
[self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context];
else
objj_msgSend(delegate, selector, self, returnCode === 2, context);
else if (delegate != null)
delegate.isa.objj_msgSend3(delegate, selector, self, returnCode === 2, context);
_canCloseAlert = nil;
}
+4 -2
View File
@@ -402,8 +402,10 @@ var CPSharedDocumentController = nil;
}
}
if ([context.delegate respondsToSelector:context.selector])
objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
var theDelegate = context.delegate;
if ([theDelegate respondsToSelector:context.selector])
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, self, [[self documents] count] === 0, context.context);
}
@end
+55
View File
@@ -29,6 +29,7 @@
@import "CPCompatibility.j"
@import "CGGeometry.j"
@import "CPText.j"
@import "CPTrackingArea.j"
@class CPTextField
@class CPWindow
@@ -82,6 +83,8 @@ var _CPEventPeriodicEventPeriod = 0,
BOOL _suppressCappuccinoCut;
BOOL _suppressCappuccinoPaste;
#endif
CPTrackingArea _trackingArea;
}
/*!
@@ -141,6 +144,27 @@ var _CPEventPeriodicEventPeriod = 0,
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure];
}
/*!
Creates a new mouse tracking event.
@param anEventType the event type
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
@param aTimestamp the time the event occurred
@param aWindowNumber the number of the CPWindow where the event occurred
@param aGraphicsContext the graphics context where the event occurred
@param anEventNumber a number for this event
@param aTrackingArea the tracking area that triggered the event
@throws CPInternalInconsistencyException if an invalid event type is provided
@return the new mouse event
*/
+ (id)enterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
{
return [[self alloc] _initEnterExitEventWithType:anEventType location:aPoint modifierFlags:modifierFlags timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber trackingArea:aTrackingArea];
}
/*!
Creates a new custom event.
@@ -201,6 +225,28 @@ var _CPEventPeriodicEventPeriod = 0,
return self;
}
/* @ignore */
- (id)_initEnterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
{
if ((anEventType != CPMouseEntered) && (anEventType != CPMouseExited) && (anEventType != CPCursorUpdate))
[CPException raise:CPInternalInconsistencyException reason:"Invalid event type"];
if (self = [self _initWithType:anEventType])
{
_location = CGPointCreateCopy(aPoint);
_modifierFlags = modifierFlags;
_timestamp = aTimestamp;
_context = aGraphicsContext;
_eventNumber = anEventNumber;
_trackingArea = aTrackingArea;
_window = [CPApp windowWithWindowNumber:aWindowNumber];
}
return self;
}
/* @ignore */
- (id)_initKeyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
@@ -236,6 +282,7 @@ var _CPEventPeriodicEventPeriod = 0,
_subtype = aSubtype;
_data1 = aData1;
_data2 = aData2;
_windowNumber = aWindowNumber;
}
return self;
@@ -582,6 +629,14 @@ var _CPEventPeriodicEventPeriod = 0,
}
}
- (CPTrackingArea)trackingArea
{
if ((_type !== CPMouseEntered) && (_type !== CPMouseExited) && (_type !== CPCursorUpdate))
[CPException raise:CPInternalInconsistencyException format:@"You can't call trackingArea for events of type %#x", _type]
return _trackingArea;
}
@end
function _CPEventFirePeriodEvent()
+1 -1
View File
@@ -275,7 +275,7 @@ function CPAppKitImage(aFilename, aSize)
*/
- (CGSize)size
{
return _size;
return CGSizeMakeCopy(_size);
}
+ (id)imageNamed:(CPString)aName
+1 -1
View File
@@ -182,7 +182,7 @@ var CPBindingOperationAnd = 0,
allBindings = [bindingsForObject allKeys],
count = [allBindings count];
while(count--)
while (count--)
{
if ([[anObject class] isBindingExclusive:allBindings[count]])
return NO;
-1
View File
@@ -32,7 +32,6 @@
@implementation _CPMenuBarWindow : CPPanel
{
CPMenu _menu;
CPView _highlightView;
CPArray _menuItemViews;
+1 -1
View File
@@ -749,7 +749,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (void)setValue:(id)theValue forKey:(CPString)theKeyPath
{
[self setValue:theKeyPath forKeyPath:theKeyPath];
[self setValue:theValue forKeyPath:theKeyPath];
}
- (unsigned)count
+58 -97
View File
@@ -223,6 +223,22 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
return self;
}
- (void)_initSubclass
{
_BlockDeselectView = function(view, row, column)
{
[view unsetThemeState:CPThemeStateSelectedDataView];
[_disclosureControlsForRows[row] unsetThemeState:CPThemeStateSelected];
};
_BlockSelectView = function(view, row, column)
{
[view setThemeState:CPThemeStateSelectedDataView];
[_disclosureControlsForRows[row] setThemeState:CPThemeStateSelected];
};
}
/*!
In addition to standard delegation, the outline view also supports data
source delegation. This method sets the data source object. Just like the
@@ -679,7 +695,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
[self _cleanPendingItem];
[super reloadData];
[super _reloadDataViews];
}
- (void)_reloadItem:(id)anItem
@@ -1308,7 +1324,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
/*!
Reloads all the data of the outlineview.
*/
- (void)reloadData
- (void)_reloadDataViews
{
[self reloadItem:nil reloadChildren:YES];
}
@@ -1340,6 +1356,25 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
else
[super removeTableColumn:aTableColumn];
}
- (void)_addDraggedDataView:(CPView)aDataView toView:(CPView)aSuperview forColumn:(CPInteger)column row:(CPInteger)row offset:(CGPoint)offset
{
var control;
[super _addDraggedDataView:aDataView toView:aSuperview forColumn:column row:row offset:offset];
if (_tableColumns[column] === _outlineTableColumn && (control = _disclosureControlsForRows[row]))
{
var controlFrame = [self frameOfOutlineDisclosureControlAtRow:row];
controlFrame.origin.x -= offset.x;
controlFrame.origin.y -= offset.y;
[control setFrame:controlFrame];
[aSuperview addSubview:control];
}
}
/*!
@ignore
We override this because we need a special behavior for the outline
@@ -1355,67 +1390,6 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
return [super frameOfDataViewAtColumn:aColumn row:aRow];
}
/*!
@ignore
We need to offset the dataview and add the disclosure triangle.
*/
- (CPView)_dragViewForColumn:(CPInteger)theColumnIndex event:(CPEvent)theDragEvent offset:(CGPoint)theDragViewOffset
{
var dragView = [[_CPColumnDragView alloc] initWithLineColor:[self gridColor]],
tableColumn = [[self tableColumns] objectAtIndex:theColumnIndex],
defaultRowHeight = [self valueForThemeAttribute:@"default-row-height"],
bounds = CGRectMake(0.0, 0.0, [tableColumn width], CGRectGetHeight([self exposedRect]) + defaultRowHeight),
columnRect = [self rectOfColumn:theColumnIndex],
headerView = [tableColumn headerView],
row = [_exposedRows firstIndex];
while (row !== CPNotFound)
{
var dataView = [self _newDataViewForRow:row tableColumn:tableColumn],
dataViewFrame = [self frameOfDataViewAtColumn:theColumnIndex row:row];
// Only one column is ever dragged so we just place the view at
dataViewFrame.origin.x = 0.0;
// Offset by table header height - scroll position
dataViewFrame.origin.y = (CGRectGetMinY(dataViewFrame) - CGRectGetMinY([self exposedRect])) + defaultRowHeight;
[dataView setFrame:dataViewFrame];
[dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
if (tableColumn === _outlineTableColumn)
{
// first inset the dragview
var indentationWidth = ([self levelForRow:row] + 1) * [self indentationPerLevel];
dataViewFrame.origin.x += indentationWidth;
dataViewFrame.size.width -= indentationWidth;
[dataView setFrame:dataViewFrame];
}
[dragView addSubview:dataView];
row = [_exposedRows indexGreaterThanIndex:row];
}
// Add the column header view
var headerFrame = [headerView frame];
headerFrame.origin = CGPointMakeZero();
var columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
[columnHeaderView setStringValue:[headerView stringValue]];
[columnHeaderView setThemeState:[headerView themeState]];
[dragView addSubview:columnHeaderView];
[dragView setBackgroundColor:[CPColor whiteColor]];
[dragView setAlphaValue:0.7];
[dragView setFrame:bounds];
return dragView;
}
/*!
Retargets the drop item for the outlineview.
@@ -1443,7 +1417,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
if (_dropItem)
{
[_dropOperationFeedbackView blink];
[CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO];
[CPTimer scheduledTimerWithTimeInterval:.3 callback:[self expandItem:_dropItem] repeats:NO];
}
};
@@ -1539,43 +1513,22 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
@ignore
We need to move the disclosure control too.
*/
- (void)_layoutDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
- (void)_layoutViewsForRowIndexes:(CPIndexSet)rowIndexes columnIndexes:(CPIndexSet)columnIndexes
{
var rowArray = [],
columnArray = [];
[rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
[columns getIndexes:columnArray maxCount:-1 inIndexRange:nil];
var columnIndex = 0,
columnsCount = columnArray.length;
for (; columnIndex < columnsCount; ++columnIndex)
[self _enumerateViewsInRows:rowIndexes columns:columnIndexes usingBlock:function(view, row, column, stop)
{
var column = columnArray[columnIndex],
tableColumn = _tableColumns[column],
tableColumnUID = [tableColumn UID],
dataViewsForTableColumn = _dataViewsForTableColumns[tableColumnUID],
rowIndex = 0,
rowsCount = rowArray.length;
var control;
for (; rowIndex < rowsCount; ++rowIndex)
[view setFrame:[self frameOfDataViewAtColumn:column row:row]];
if (_tableColumns[column] === _outlineTableColumn && (control = _disclosureControlsForRows[row]))
{
var row = rowArray[rowIndex],
dataView = dataViewsForTableColumn[row],
dataViewFrame = [self frameOfDataViewAtColumn:column row:row];
[dataView setFrame:dataViewFrame];
if (tableColumn === _outlineTableColumn)
{
var control = _disclosureControlsForRows[row],
frame = [self frameOfOutlineDisclosureControlAtRow:row];
[control setFrame:frame];
}
var frame = [self frameOfOutlineDisclosureControlAtRow:row];
[control setFrame:frame];
}
}
}];
[self setNeedsDisplay:YES];
}
/*!
@@ -1587,7 +1540,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
var outlineColumn = [[self tableColumns] indexOfObjectIdenticalTo:[self outlineTableColumn]];
if (![columns containsIndex:outlineColumn] || [self outlineTableColumn] === _draggedColumn)
if (![columns containsIndex:outlineColumn] || outlineColumn === _draggedColumnIndex)
return;
var rowArray = [];
@@ -1890,6 +1843,14 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
return _implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_dataViewForTableColumn_item_;
}
- (id)_hitTest:(CPView)aView
{
if ([aView isKindOfClass:[CPDisclosureButton class]])
return aView;
return [super _hitTest:aView];
}
- (BOOL)_delegateRespondsToShouldExpandItem
{
return _implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldExpandItem_;
+3 -2
View File
@@ -22,10 +22,13 @@
@import "CPWindow.j"
@global CPApp
CPOKButton = 1;
CPCancelButton = 0;
CPDocModalWindowMask = 1 << 6;
/*!
@ingroup appkit
@class CPPanel
@@ -51,8 +54,6 @@ CPCancelButton = 0;
@global
@class CPWindow
*/
CPDocModalWindowMask = 1 << 6;
@implementation CPPanel : CPWindow
{
BOOL _becomesKeyOnlyIfNeeded;
+55
View File
@@ -86,6 +86,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
@"spinning-mini-gif": [CPNull null],
@"spinning-small-gif": [CPNull null],
@"spinning-regular-gif": [CPNull null],
@"circular-border-color": [CPNull null],
@"circular-border-size": 1,
@"circular-color": [CPNull null]
};
}
@@ -368,6 +371,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
- (void)drawBar
{
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
- (CPView)createEphemeralSubviewNamed:(CPString)aName
@@ -413,6 +417,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
{
if (_style == CPProgressIndicatorSpinningStyle)
{
if (!_indeterminate)
return;
// This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle.
[self layoutEphemeralSubviewNamed:"bar-view"
positioned:CPWindowBelow
@@ -438,6 +445,54 @@ var CPProgressIndicatorSpinningStyleColors = [];
[self setBackgroundColor:nil];
}
- (void)drawRect:(CGRect)aRect
{
if (_style == CPProgressIndicatorSpinningStyle && !_indeterminate)
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
rect = CGRectMakeCopy(aRect),
borderSize = [self currentValueForThemeAttribute:@"circular-border-size"];
rect.origin.x += borderSize;
rect.origin.y += borderSize;
rect.size.width = rect.size.width - borderSize * 2;
rect.size.height = rect.size.height - borderSize * 2;
if ([self doubleValue] > [self minValue] && [self doubleValue] < [self maxValue])
{
var midX = CGRectGetMidX(rect),
midY = CGRectGetMidY(rect),
endAngle = Math.PI * 2 * (([self doubleValue] - [self minValue]) / ([self maxValue] - [self minValue])) - Math.PI / 2,
radius = MIN(rect.size.width / 2, rect.size.height / 2)
CGContextBeginPath(context);
CGContextSetLineWidth(context, borderSize);
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"])
CGContextMoveToPoint(context, midX, midY);
CGContextAddArc(context, midX, midY, radius, 3 * Math.PI / 2, endAngle, YES)
CGContextAddLineToPoint(context, midX, midY);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextStrokePath(context);
}
else if ([self doubleValue] == [self maxValue])
{
CGContextBeginPath(context);
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"])
CGContextAddEllipseInRect(context, rect);
CGContextClosePath(context);
CGContextFillPath(context);
}
CGContextBeginPath(context);
CGContextSetStrokeColor(context , [self currentValueForThemeAttribute:@"circular-border-color"]);
CGContextSetLineWidth(context, borderSize);
CGContextAddEllipseInRect(context, rect);
CGContextClosePath(context);
CGContextStrokePath(context);
}
}
@end
+4 -6
View File
@@ -29,6 +29,7 @@
@global CPApp
CPRadioImageOffset = 4.0;
/*!
@ingroup appkit
@@ -67,9 +68,6 @@
option.
*/
CPRadioImageOffset = 4.0;
@implementation CPRadio : CPButton
{
CPRadioGroup _radioGroup;
@@ -269,9 +267,9 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
- (BOOL)selectRadioWithTag:(int)tag
{
var index = [_radios indexOfObjectPassingTest:function(radio)
{
return [radio tag] === tag;
}];
{
return [radio tag] === tag;
}];
if (index !== CPNotFound)
{
+13
View File
@@ -24,6 +24,7 @@
@import <Foundation/CPObjJRuntime.j>
@import "CPEvent.j"
@import "CPCursor.j"
@class CPKeyBinding
@class CPMenu
@@ -200,6 +201,18 @@ CPDeleteForwardKeyCode = 46;
[_nextResponder performSelector:_cmd withObject:anEvent];
}
/*!
Notifies the receiver that the mouse entered the receiver's area and that it can adapt the cursor.
@param anEvent contains information about the exit
*/
- (void)cursorUpdate:(CPEvent)anEvent
{
if (_nextResponder)
[_nextResponder performSelector:_cmd withObject:anEvent];
else
[[CPCursor arrowCursor] set];
}
/*!
Notifies the receiver that the mouse scroll wheel has moved.
@param anEvent information about the scroll
@@ -487,9 +487,9 @@ CPTransformableAttributeType = 1800;
- (id)copy
{
var views = [CPArray array];
var views = [CPArray array],
copy = [[[self class] alloc] init];
var copy = [[[self class] alloc] init];
[copy _setTemplateType:_templateType];
[copy _setOptions:_predicateOptions];
[copy _setModifier:_predicateModifier];
+1 -1
View File
@@ -2121,7 +2121,7 @@ TODO: implement
return;
var point = [self convertPoint:[event locationInWindow] fromView:nil],
view = [_slices objectAtIndex:FLOOR(point.y / _sliceHeight)];
view = [_slices objectAtIndex:FLOOR(MAX(0, point.y) / _sliceHeight)];
if ([self _dragShouldBeginFromMouseDown:view])
[self _performDragForSlice:view withEvent:event];
@@ -29,7 +29,6 @@
CGRect _animationTargetRect @accessors(property=_animationTargetRect);
BOOL _selected @accessors(getter=_isSelected, setter=_setSelected:);
BOOL _lastSelected @accessors(getter=_isLastSelected, setter=_setLastSelected:);
CPColor _backgroundColor @accessors(property=backgroundColor);
BOOL _editable @accessors(getter=isEditable, setter=setEditable:);
}
+40 -31
View File
@@ -33,6 +33,15 @@
#define SHOULD_SHOW_CORNER_VIEW() (_scrollerStyle === CPScrollerStyleLegacy && _verticalScroller && ![_verticalScroller isHidden])
@protocol CPScrollViewDelegate <CPObject>
@optional
- (void)scrollViewWillScroll:(CPScrollView)aScrollView;
- (void)scrollViewDidScroll:(CPScrollView)aScrollView;
@end
/*! @ignore */
var _isBrowserUsingOverlayScrollers = function()
{
@@ -77,15 +86,6 @@ var _isBrowserUsingOverlayScrollers = function()
#endif
};
/*!
@ingroup appkit
@class CPScrollView
Used to display views that are too large for the viewing area. the CPScrollView
places scroll bars on the side of the view to allow the user to scroll and see the entire
contents of the view.
*/
var TIMER_INTERVAL = 0.2,
CPScrollViewDelegate_scrollViewWillScroll_ = 1 << 0,
CPScrollViewDelegate_scrollViewDidScroll_ = 1 << 1,
@@ -95,38 +95,45 @@ var TIMER_INTERVAL = 0.2,
var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
CPScrollerStyleGlobalChangeNotification = @"CPScrollerStyleGlobalChangeNotification";
/*!
@ingroup appkit
@class CPScrollView
Used to display views that are too large for the viewing area. the CPScrollView
places scroll bars on the side of the view to allow the user to scroll and see the entire
contents of the view.
*/
@implementation CPScrollView : CPView
{
CPClipView _contentView;
CPClipView _headerClipView;
CPView _cornerView;
CPView _bottomCornerView;
CPClipView _contentView;
CPClipView _headerClipView;
CPView _cornerView;
CPView _bottomCornerView;
id _delegate;
CPTimer _scrollTimer;
id <CPScrollViewDelegate> _delegate;
CPTimer _scrollTimer;
BOOL _hasVerticalScroller;
BOOL _hasHorizontalScroller;
BOOL _autohidesScrollers;
BOOL _hasVerticalScroller;
BOOL _hasHorizontalScroller;
BOOL _autohidesScrollers;
CPScroller _verticalScroller;
CPScroller _horizontalScroller;
CPScroller _verticalScroller;
CPScroller _horizontalScroller;
CPInteger _recursionCount;
CPInteger _implementedDelegateMethods;
CPInteger _recursionCount;
CPInteger _implementedDelegateMethods;
float _verticalLineScroll;
float _verticalPageScroll;
float _horizontalLineScroll;
float _horizontalPageScroll;
float _verticalLineScroll;
float _verticalPageScroll;
float _horizontalLineScroll;
float _horizontalPageScroll;
CPBorderType _borderType;
CPBorderType _borderType;
CPTimer _timerScrollersHide;
CPTimer _timerScrollersHide;
int _scrollerStyle;
int _scrollerKnobStyle;
int _scrollerStyle;
int _scrollerKnobStyle;
}
@@ -296,7 +303,7 @@ Notifies the delegate when the scroll view has finished scrolling.
@endcode
*/
- (void)setDelegate:(id)aDelegate
- (void)setDelegate:(id <CPScrollViewDelegate>)aDelegate
{
if (aDelegate === _delegate)
return;
@@ -1575,6 +1582,8 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
*/
- (void)awakeFromCib
{
[super awakeFromCib];
[self _updateScrollerStyle];
[self _updateCornerAndHeaderView];
}
+5 -5
View File
@@ -50,11 +50,6 @@ CPNoScrollerParts = 0;
CPOnlyScrollerArrows = 1;
CPAllScrollerParts = 2;
/*!
@ingroup appkit
@class CPScroller
*/
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
NAMES_FOR_PARTS = {},
PARTS_FOR_NAMES = {};
@@ -78,6 +73,11 @@ CPThemeStateScrollViewLegacy = CPThemeState("scroller-style-legacy");
CPThemeStateScrollerKnobLight = CPThemeState("scroller-knob-light");
CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
/*!
@ingroup appkit
@class CPScroller
*/
@implementation CPScroller : CPControl
{
CPUsableScrollerParts _usableParts;
+11 -8
View File
@@ -34,9 +34,8 @@ CPSearchFieldRecentsMenuItemTag = 1001;
CPSearchFieldClearRecentsMenuItemTag = 1002;
CPSearchFieldNoRecentsMenuItemTag = 1003;
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
var RECENT_SEARCH_PREFIX = @" ";
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification",
RECENT_SEARCH_PREFIX = @" ";
/*!
@ingroup appkit
@@ -73,7 +72,9 @@ var RECENT_SEARCH_PREFIX = @" ";
@"image-search": [CPNull null],
@"image-find": [CPNull null],
@"image-cancel": [CPNull null],
@"image-cancel-pressed": [CPNull null]
@"image-cancel-pressed": [CPNull null],
@"image-search-inset" : CGInsetMake(0, 0, 0, 5),
@"image-cancel-inset" : CGInsetMake(0, 5, 0, 0)
};
}
@@ -266,9 +267,10 @@ var RECENT_SEARCH_PREFIX = @" ";
*/
- (CGRect)searchButtonRectForBounds:(CGRect)rect
{
var size = [[self valueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero();
var size = [[self currentValueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero(),
inset = [self currentValueForThemeAttribute:@"image-search-inset"];
return CGRectMake(5, (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
return CGRectMake(inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
}
/*!
@@ -278,9 +280,10 @@ var RECENT_SEARCH_PREFIX = @" ";
*/
- (CGRect)cancelButtonRectForBounds:(CGRect)rect
{
var size = [[self valueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero();
var size = [[self currentValueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero(),
inset = [self currentValueForThemeAttribute:@"image-cancel-inset"];
return CGRectMake(CGRectGetWidth(rect) - size.width - 5, (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
return CGRectMake(CGRectGetWidth(rect) - size.width + inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
}
// Managing Menu Templates
+195 -141
View File
@@ -42,7 +42,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
@implementation CPSegmentedControl : CPControl
{
CPArray _segments;
CPArray _segments @accessors(getter=segments);
CPArray _themeStates;
int _selectedSegment;
@@ -87,6 +87,8 @@ CPSegmentSwitchTrackingMomentary = 2;
_selectedSegment = -1;
_trackingMode = CPSegmentSwitchTrackingSelectOne;
_trackingHighlighted = NO;
_trackingSegment = -1;
}
return self;
@@ -107,7 +109,54 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)selectedTag
{
return [_segments[_selectedSegment] tag];
return [[_segments objectAtIndex:_selectedSegment] tag];
}
/*! @ignore */
- (void)setSegments:(CPArray)segments
{
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self segmentCount])]];
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [segments count])]];
}
/*! @ignore */
- (void)insertSegments:(CPArray)segments atIndexes:(CPIndexSet)indices
{
if ([segments count] == 0)
return;
var newStates = @[],
count = [indices count];
while (count--)
[newStates addObject:CPThemeStateNormal];
[_segments insertObjects:segments atIndexes:indices];
[_themeStates insertObjects:newStates atIndexes:indices];
if (_selectedSegment >= [indices firstIndex])
_selectedSegment += [indices count];
}
/*! @ignore */
- (void)removeSegmentsAtIndexes:(CPIndexSet)indices
{
if ([indices count] == 0)
return;
[indices enumerateIndexesUsingBlock:function(idx, stop)
{
[[_segments objectAtIndex:idx] setSelected:NO];
}];
if ([indices containsIndex:_selectedSegment])
_selectedSegment = -1;
else if ([indices lastIndex] < _selectedSegment)
_selectedSegment -= [indices count];
[_segments removeObjectsAtIndexes:indices];
[_themeStates removeObjectsAtIndexes:indices];
}
// Specifying the number of segments
@@ -117,41 +166,32 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setSegmentCount:(unsigned)aCount
{
if (_segments.length == aCount)
var prevCount = [_segments count];
if (aCount == prevCount)
return;
var height = CGRectGetHeight([self bounds]),
dividersBefore = MAX(0, _segments.length - 1),
dividersAfter = MAX(0, aCount - 1);
if (_segments.length < aCount)
if (aCount > prevCount)
{
for (var index = _segments.length; index < aCount; ++index)
{
_segments[index] = [[_CPSegmentItem alloc] init];
_themeStates[index] = CPThemeStateNormal;
}
}
else if (aCount < _segments.length)
{
_segments.length = aCount;
_themeStates.length = aCount;
}
var count = aCount - prevCount,
segments = @[];
if (_selectedSegment >= _segments.length)
while (count--)
[segments addObject:[[_CPSegmentItem alloc] init]];
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(prevCount, aCount - prevCount)]];
}
else
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(aCount, prevCount - aCount)]];
[self _updateSelectionIfNeeded];
[self tileWithChangedSegment:MAX(MIN(prevCount, aCount) - 1, 0)];
}
- (void)_updateSelectionIfNeeded
{
if (_selectedSegment >= [self segmentCount])
_selectedSegment = -1;
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
frame = [self frame],
widthOfAllSegments = 0,
dividerExtraSpace = ([_segments count] - 1) * thickness;
for (var i = 0; i < [_segments count]; i++)
widthOfAllSegments += [_segments[i] width];
[self setFrameSize:CGSizeMake(widthOfAllSegments + dividerExtraSpace, frame.size.height)];
[self tileWithChangedSegment:0];
}
/*!
@@ -159,7 +199,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (unsigned)segmentCount
{
return _segments.length;
return [_segments count];
}
// Specifying Selected Segment
@@ -171,7 +211,20 @@ CPSegmentSwitchTrackingMomentary = 2;
- (void)setSelectedSegment:(unsigned)aSegment
{
// setSelected:forSegment throws the exception for us (if necessary)
[self setSelected:YES forSegment:aSegment];
if (_selectedSegment == aSegment)
return;
if (aSegment == -1)
{
var count = [self segmentCount];
while (count--)
[self setSelected:NO forSegment:count];
_selectedSegment = -1;
}
else
[self setSelected:YES forSegment:aSegment];
}
/*!
@@ -189,8 +242,8 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
if (_segments[index].tag == aTag)
for (; index < [_segments count]; ++index)
if ([[_segments objectAtIndex:index] tag] == aTag)
{
[self setSelectedSegment:index];
@@ -204,8 +257,8 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
if (_segments[index].label == aLabel)
for (; index < [_segments count]; ++index)
if ([[_segments objectAtIndex:index] label] == aLabel)
{
[self setSelectedSegment:index];
@@ -216,7 +269,7 @@ CPSegmentSwitchTrackingMomentary = 2;
}
// Specifying Tracking Mode
/*! @ignore */
- (BOOL)isTracking
{
@@ -234,7 +287,7 @@ CPSegmentSwitchTrackingMomentary = 2;
var index = 0,
selected = NO;
for (; index < _segments.length; ++index)
for (; index < [self segmentCount]; ++index)
if ([_segments[index] selected])
if (selected)
[self setSelected:NO forSegment:index];
@@ -246,7 +299,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
for (; index < [self segmentCount]; ++index)
if ([_segments[index] selected])
[self setSelected:NO forSegment:index];
}
@@ -269,7 +322,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
{
[_segments[aSegment] setWidth:aWidth];
[[_segments objectAtIndex:aSegment] setWidth:aWidth];
[self tileWithChangedSegment:aSegment];
}
@@ -280,7 +333,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (float)widthForSegment:(unsigned)aSegment
{
return [_segments[aSegment] width];
return [[_segments objectAtIndex:aSegment] width];
}
/*!
@@ -291,7 +344,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
{
[_segments[aSegment] setImage:anImage];
[[_segments objectAtIndex:aSegment] setImage:anImage];
[self tileWithChangedSegment:aSegment];
}
@@ -303,7 +356,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPImage)imageForSegment:(unsigned)aSegment
{
return [_segments[aSegment] image];
return [[_segments objectAtIndex:aSegment] image];
}
/*!
@@ -314,7 +367,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
{
[_segments[aSegment] setLabel:aLabel];
[[_segments objectAtIndex:aSegment] setLabel:aLabel];
[self tileWithChangedSegment:aSegment];
}
@@ -326,7 +379,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPString)labelForSegment:(unsigned)aSegment
{
return [_segments[aSegment] label];
return [[_segments objectAtIndex:aSegment] label];
}
/*!
@@ -337,7 +390,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
{
[_segments[aSegment] setMenu:aMenu];
[[_segments objectAtIndex:aSegment] setMenu:aMenu];
}
/*!
@@ -347,7 +400,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPMenu)menuForSegment:(unsigned)aSegment
{
return [_segments[aSegment] menu];
return [[_segments objectAtIndex:aSegment] menu];
}
/*!
@@ -359,7 +412,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
{
var segment = _segments[aSegment];
var segment = [_segments objectAtIndex:aSegment];
// If we're already in this state, bail.
if ([segment selected] == isSelected)
@@ -376,7 +429,7 @@ CPSegmentSwitchTrackingMomentary = 2;
_selectedSegment = aSegment;
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1 && oldSelectedSegment < _segments.length)
{
[_segments[oldSelectedSegment] setSelected:NO];
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
@@ -399,7 +452,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isSelectedForSegment:(unsigned)aSegment
{
return [_segments[aSegment] selected];
return [[_segments objectAtIndex:aSegment] selected];
}
/*!
@@ -410,10 +463,12 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(unsigned)aSegment
{
if ([_segments[aSegment] enabled] === shouldBeEnabled)
var segment = [_segments objectAtIndex:aSegment];
if ([segment enabled] === shouldBeEnabled)
return;
[_segments[aSegment] setEnabled:shouldBeEnabled];
[segment setEnabled:shouldBeEnabled];
if (shouldBeEnabled)
_themeStates[aSegment] = _themeStates[aSegment].without(CPThemeStateDisabled);
@@ -431,7 +486,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isEnabledForSegment:(unsigned)aSegment
{
return [_segments[aSegment] enabled];
return [[_segments objectAtIndex:aSegment] enabled];
}
/*!
@@ -441,7 +496,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
{
[_segments[aSegment] setTag:aTag];
[[_segments objectAtIndex:aSegment] setTag:aTag];
}
/*!
@@ -450,7 +505,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)tagForSegment:(unsigned)aSegment
{
return [_segments[aSegment] tag];
return [[_segments objectAtIndex:aSegment] tag];
}
// Drawings
@@ -461,7 +516,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
{
if(aSegment < _themeStates.length)
if (aSegment < _themeStates.length)
{
if (shouldHighlight)
_themeStates[aSegment] = _themeStates[aSegment].and(CPThemeStateHighlighted);
@@ -475,14 +530,12 @@ CPSegmentSwitchTrackingMomentary = 2;
- (float)_leftOffsetForSegment:(unsigned)segment
{
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
if (segment == 0)
return bezelInset.left;
return [self currentValueForThemeAttribute:@"bezel-inset"].left;
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
return [self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness;
}
- (unsigned)_indexOfLastSegment
@@ -516,7 +569,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("segment-bezel") === 0)
{
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
frame = CGRectCreateCopy([_segments[segment] frame]);
frame = CGRectCreateCopy([self frameForSegment:segment]);
if (segment === 0)
{
@@ -524,7 +577,7 @@ CPSegmentSwitchTrackingMomentary = 2;
frame.size.width -= contentInset.left;
}
if (segment === _segments.length - 1)
if (segment === [self segmentCount] - 1)
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
return frame;
@@ -532,7 +585,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("divider-bezel") === 0)
{
var segment = parseInt(aName.substring("divider-bezel-".length), 10),
width = [self widthForSegment:segment],
width = CGRectGetWidth([self frameForSegment:segment]),
left = [self _leftOffsetForSegment:segment],
thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
@@ -558,7 +611,7 @@ CPSegmentSwitchTrackingMomentary = 2;
- (void)layoutSubviews
{
if (_segments.length <= 0)
if ([self segmentCount] <= 0)
return;
var themeState = _themeStates[0],
@@ -646,9 +699,8 @@ CPSegmentSwitchTrackingMomentary = 2;
if (i == count - 1)
continue;
var borderState = _themeStates[i].and(_themeStates[i + 1]);
borderState = (borderState.hasThemeState(CPThemeStateSelected) && !borderState.hasThemeState(CPThemeStateHighlighted)) ? CPThemeStateSelected : CPThemeStateNormal;
var borderState = _themeStates[i].and(_themeStates[i + 1]);
borderState = isDisabled ? borderState.and(CPThemeStateDisabled) : borderState;
@@ -678,60 +730,33 @@ CPSegmentSwitchTrackingMomentary = 2;
{
}
- (void)tileWithChangedSegment:(unsigned)aSegment
/*! @ignore */
- (void)tile
{
if (aSegment >= _segments.length)
[self tileWithChangedSegment:0];
}
/*! @ignore */
- (void)tileWithChangedSegment:(CPInteger)aSegment
{
var segmentCount = [self segmentCount];
// Corner case: when segmentCount == 0 and aSegment == 0, we do not return here because we still need to set the new frameSize bellow.
if (aSegment < 0 || (segmentCount > 0 && aSegment >= segmentCount))
return;
var segment = _segments[aSegment],
segmentWidth = [segment width],
themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
font = [self font];
var width = 0;
if (!segmentWidth)
if (segmentCount > 0)
{
if ([segment image] && [segment label])
segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.image)
segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.label)
segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
else
segmentWidth = 0.0;
// Invalidate frames for segments on the right. They will be lazily computed by -frameForSegment:.
for (var i = aSegment; i < segmentCount; i++)
[_segments[i] setFrame:CGRectMakeZero()];
width = CGRectGetMaxX([self frameForSegment:(segmentCount - 1)]);
}
var delta = segmentWidth - CGRectGetWidth([segment frame]);
if (!delta)
{
[self setNeedsLayout];
[self setNeedsDisplay:YES];
return;
}
// Update control size
var frame = [self frame];
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
// Update segment width
[segment setWidth:segmentWidth];
[segment setFrame:[self frameForSegment:aSegment]];
// Update following segments widths
var index = aSegment + 1;
for (; index < _segments.length; ++index)
{
[_segments[index] frame].origin.x += delta;
[self drawSegmentBezel:index highlight:NO];
[self drawSegment:index highlight:NO];
}
[self drawSegmentBezel:aSegment highlight:NO];
[self drawSegment:aSegment highlight:NO];
[self setFrameSize:CGSizeMake(width, CGRectGetHeight([self frame]))];
[self setNeedsLayout];
[self setNeedsDisplay:YES];
@@ -743,29 +768,57 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CGRect)frameForSegment:(unsigned)aSegment
{
return [self bezelFrameForSegment:aSegment];
var segment = [_segments objectAtIndex:aSegment],
frame = [segment frame];
if (CGRectEqualToRect(frame, CGRectMakeZero()))
{
frame = [self bezelFrameForSegment:aSegment];
[segment setFrame:frame];
}
return frame;
}
- (CGRect)bezelFrameForSegment:(unsigned)aSegment
{
var height = [self currentValueForThemeAttribute:@"min-size"].height,
bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
var left = [self _leftOffsetForSegment:aSegment],
top = [self currentValueForThemeAttribute:@"bezel-inset"].top,
width = [self widthForSegment:aSegment],
left = [self _leftOffsetForSegment:aSegment];
height = [self currentValueForThemeAttribute:@"min-size"].height;
return CGRectMake(left, bezelInset.top, width, height);
if (width == 0)
{
var themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
contentInsetWidth = contentInset.left + contentInset.right,
segment = _segments[aSegment],
label = [segment label],
image = [segment image];
width = (label ? [label sizeWithFont:[self font]].width : 4.0) + (image ? [image size].width : 0) + contentInsetWidth;
}
return CGRectMake(left, top, width, height);
}
- (CGRect)contentFrameForSegment:(unsigned)aSegment
{
var height = [self currentValueForThemeAttribute:@"min-size"].height,
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
width = [self widthForSegment:aSegment],
width = CGRectGetWidth([self frameForSegment:aSegment]),
left = [self _leftOffsetForSegment:aSegment];
return CGRectMake(left + contentInset.left, contentInset.top, width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
}
- (CGSize)_minimumFrameSize
{
// The current width is always the minimum width.
return CGSizeMake(CGRectGetWidth([self frame]), [self currentValueForThemeAttribute:@"min-size"].height);
}
/*!
Returns the segment that is hit by the specified point.
@param aPoint the point to test for a segment hit
@@ -774,19 +827,19 @@ CPSegmentSwitchTrackingMomentary = 2;
- (unsigned)testSegment:(CGPoint)aPoint
{
var location = [self convertPoint:aPoint fromView:nil],
count = _segments.length;
count = [self segmentCount];
while (count--)
if (CGRectContainsPoint([_segments[count] frame], aPoint))
if (CGRectContainsPoint([self frameForSegment:count], aPoint))
return count;
if (_segments.length)
if ([self segmentCount])
{
var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
var adjustedLastFrame = CGRectCreateCopy([self frameForSegment:(_segments.length - 1)]);
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
if (CGRectContainsPoint(adjustedLastFrame, aPoint))
return _segments.length - 1;
return [self segmentCount] - 1;
}
return -1;
@@ -837,7 +890,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{
[self setSelected:NO forSegment:_trackingSegment];
_selectedSegment = -1;
_selectedSegment = CPNotFound;
}
}
@@ -881,7 +934,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{
[super setFont:aFont];
[self tileWithChangedSegment:0];
[self tile];
}
@end
@@ -913,7 +966,7 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
else
_selectedSegment = -1;
_selectedSegment = CPNotFound;
if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
_trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
@@ -921,23 +974,24 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
_trackingMode = CPSegmentSwitchTrackingSelectOne;
// Here we update the themeStates array for each segments to know if there are selected or not
for (var i = 0; i < _segments.length; i++)
for (var i = 0; i < [self segmentCount]; i++)
_themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
// We do this in a second loop because it relies on all the themeStates being set first
for (var i = 0; i < _segments.length; i++)
[self tileWithChangedSegment:i];
[self tile];
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
dividerExtraSpace = ([_segments count] - 1) * thickness,
difference = MAX(originalWidth - [self frame].size.width - dividerExtraSpace, 0.0),
remainingWidth = FLOOR(difference / _segments.length),
remainingWidth = FLOOR(difference / [self segmentCount]),
widthOfAllSegments = 0;
for (var i = 0; i < _segments.length; i++)
// We do this in a second loop because it relies on all the themeStates being set first
for (var i = 0; i < [self segmentCount]; i++)
{
[self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
widthOfAllSegments += [_segments[i] width];
var frame = [_segments[i] frame];
frame.size.width += remainingWidth;
widthOfAllSegments += CGRectGetWidth(frame);
}
// Here we handle the leftovers pixel, and we will add one pixel to each segment cell till we have the same size as the originalSize.
@@ -945,16 +999,16 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
var leftOversPixel = originalWidth - (widthOfAllSegments + dividerExtraSpace);
// Make sure we don't make an out of range
if (leftOversPixel < _segments.length - 1)
if (leftOversPixel < [self segmentCount] - 1)
{
for (var i = 0; i < leftOversPixel; i++)
{
[self setWidth:[_segments[i] width] + 1 forSegment:i];
[_segments[i] frame].size.width += 1;
}
}
[self setFrameSize:CGSizeMake(originalWidth, [self frame].size.height)];
[self tileWithChangedSegment:0];
[self setFrameSize:CGSizeMake(originalWidth, CGRectGetHeight([self frame]))];
[self tile];
}
return self;
+70 -55
View File
@@ -26,6 +26,7 @@
@import "CPImage.j"
@import "CPView.j"
@import "CPCursor.j"
@import "CPTrackingArea.j"
@class CPUserDefaults
@global CPApp
@@ -55,7 +56,9 @@ var CPSplitViewDelegate_splitView_canCollapseSubview_
CPSplitViewDelegate_splitView_constrainMaxCoordinate_ofSubviewAt_ = 1 << 5,
CPSplitViewDelegate_splitView_constrainMinCoordinate_ofSubviewAt_ = 1 << 6,
CPSplitViewDelegate_splitView_constrainSplitPosition_ofSubviewAt_ = 1 << 7,
CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_ = 1 << 8;
CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_ = 1 << 8,
CPSplitViewDelegate_splitViewDidResizeSubviews_ = 1 << 9,
CPSplitViewDelegate_splitViewWillResizeSubviews_ = 1 << 10;
#define SPLIT_VIEW_MAYBE_POST_WILL_RESIZE() \
if ((_suppressResizeNotificationsMask & DidPostWillResizeNotification) === 0) \
@@ -560,26 +563,6 @@ var ShouldSuppressResizeNotifications = 1,
//[[self window] setAcceptsMouseMovedEvents:YES];
}
- (void)mouseEntered:(CPEvent)anEvent
{
// Tracking code handles cursor by itself.
if (_currentDivider == CPNotFound)
[self _updateResizeCursor:anEvent];
}
- (void)mouseMoved:(CPEvent)anEvent
{
if (_currentDivider == CPNotFound)
[self _updateResizeCursor:anEvent];
}
- (void)mouseExited:(CPEvent)anEvent
{
if (_currentDivider == CPNotFound)
// FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
[[CPCursor arrowCursor] set];
}
- (void)_updateResizeCursor:(CPEvent)anEvent
{
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
@@ -967,26 +950,14 @@ The sum of the views and the sum of the dividers should be equal to the size of
if (_delegate === aDelegate)
return;
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewDidResizeSubviewsNotification object:self];
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewWillResizeSubviewsNotification object:self];
_implementedDelegateMethods |= CPSplitViewDelegate_splitViewWillResizeSubviews_;
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
[[CPNotificationCenter defaultCenter] addObserver:_delegate
selector:@selector(splitViewDidResizeSubviews:)
name:CPSplitViewDidResizeSubviewsNotification
object:self];
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
[[CPNotificationCenter defaultCenter] addObserver:_delegate
selector:@selector(splitViewWillResizeSubviews:)
name:CPSplitViewWillResizeSubviewsNotification
object:self];
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
_implementedDelegateMethods |= CPSplitViewDelegate_splitViewDidResizeSubviews_;
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
_implementedDelegateMethods |= CPSplitViewDelegate_splitView_canCollapseSubview_;
@@ -1064,27 +1035,12 @@ The sum of the views and the sum of the dividers should be equal to the size of
- (void)_postNotificationWillResize
{
var userInfo = nil;
if (_currentDivider !== CPNotFound)
userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification
object:self
userInfo:userInfo];
[self _sendDelegateSplitViewWillResizeSubviews];
}
- (void)_postNotificationDidResize
{
var userInfo = nil;
if (_currentDivider !== CPNotFound)
userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification
object:self
userInfo:userInfo];
[self _sendDelegateSplitViewDidResizeSubviews];
// TODO Cocoa always autosaves on "viewDidEndLiveResize". If Cappuccino adds support for this we
// should do the same.
@@ -1230,6 +1186,29 @@ The sum of the views and the sum of the dividers should be equal to the size of
@end
@implementation CPSplitView (CPTrackingArea)
- (void)updateTrackingAreas
{
[self removeAllTrackingAreas];
var options = CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow;
for (var i = 0; i < _subviews.length - 1; i++)
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self effectiveRectOfDividerAtIndex:i]
options:options
owner:self
userInfo:nil]];
}
- (void)cursorUpdate:(CPEvent)anEvent
{
if (_currentDivider === CPNotFound)
[self _updateResizeCursor:anEvent];
}
@end
@implementation CPSplitView (CPSplitViewDelegate)
@@ -1369,6 +1348,42 @@ The sum of the views and the sum of the dividers should be equal to the size of
[_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
}
/*!
@ignore
Call the delegate splitViewWillResizeSubviews:
*/
- (void)_sendDelegateSplitViewWillResizeSubviews
{
var userInfo = nil;
if (_currentDivider !== CPNotFound)
userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
if (_implementedDelegateMethods & CPSplitViewDelegate_splitViewWillResizeSubviews_)
[_delegate splitViewWillResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewWillResizeSubviewsNotification object:self userInfo:userInfo]];
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self userInfo:userInfo];
}
/*!
@ignore
Call the delegate splitViewDidResizeSubviews:
*/
- (void)_sendDelegateSplitViewDidResizeSubviews
{
var userInfo = nil;
if (_currentDivider !== CPNotFound)
userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
if (_implementedDelegateMethods & CPSplitViewDelegate_splitViewDidResizeSubviews_)
[_delegate splitViewDidResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo]];
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo];
[self updateTrackingAreas];
}
@end
+6 -6
View File
@@ -188,12 +188,12 @@
[_buttonUp setFrame:upFrame];
[_buttonDown setFrame:downFrame];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
}
- (void)_sizeToFit
+365 -85
View File
@@ -34,6 +34,8 @@ CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
CPNoTabsNoBorder = 6; //Displays no tabs and no border.
@class _CPTabViewBox
var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
CPTabViewShouldSelectTabViewItemSelector = 1 << 2,
CPTabViewWillSelectTabViewItemSelector = 1 << 3,
@@ -64,9 +66,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
CPArray _items;
CPSegmentedControl _tabs;
CPBox _box;
_CPTabViewBox _box;
CPView _placeHolderView;
CPNumber _selectedIndex;
CPTabViewItem _selectedTabViewItem;
CPTabViewType _type;
CPFont _font;
@@ -79,9 +82,8 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
{
if (self = [super initWithFrame:aFrame])
{
_items = [CPArray array];
[self _init];
_selectedTabViewItem = nil;
[self setTabViewType:CPTopTabsBezelBorder];
}
@@ -90,19 +92,26 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
- (void)_init
{
_selectedIndex = CPNotFound;
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMakeZero()];
[_tabs setHitTests:NO];
[_tabs setSegments:[CPArray array]];
var height = [_tabs valueForThemeAttribute:@"min-size"].height;
[_tabs setFrameSize:CGSizeMake(0, height)];
_box = [[CPBox alloc] initWithFrame:[self bounds]];
_box = [[_CPTabViewBox alloc] initWithFrame:[self bounds]];
[_box setTabView:self];
[self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
[self addSubview:_box];
[self addSubview:_tabs];
_placeHolderView = nil;
}
- (CPArray)items
{
return [_tabs segments];
}
// Adding and Removing Tabs
@@ -112,7 +121,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
{
[self insertTabViewItem:aTabViewItem atIndex:[_items count]];
[self insertTabViewItem:aTabViewItem atIndex:[self numberOfTabViewItems]];
}
/*!
@@ -122,15 +131,18 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPUInteger)anIndex
{
[_items insertObject:aTabViewItem atIndex:anIndex];
[self _insertTabViewItems:[aTabViewItem] atIndexes:[CPIndexSet indexSetWithIndex:anIndex]];
}
[self _updateItems];
[self _repositionTabs];
- (void)_insertTabViewItems:(CPArray)tabViewItems atIndexes:(CPIndexSet)indexes
{
[_tabs insertSegments:tabViewItems atIndexes:indexes];
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
[aTabViewItem _setTabView:self];
[self tileWithChangedItem:[tabViewItems firstObject]];
[self _reverseSetContent];
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
}
/*!
@@ -139,23 +151,39 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
{
var count = [_items count];
for (var i = 0; i < count; i++)
{
if ([_items objectAtIndex:i] === aTabViewItem)
{
[_items removeObjectAtIndex:i];
break;
}
}
var idx = [[self items] indexOfObjectIdenticalTo:aTabViewItem];
[self _updateItems];
[self _repositionTabs];
if (idx == CPNotFound)
return;
[_tabs removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndex:idx]];
[aTabViewItem _setTabView:nil];
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
[self tileWithChangedItem:nil];
[self _didRemoveTabViewItem:aTabViewItem atIndex:idx];
[self _reverseSetContent];
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
}
- (void)_didRemoveTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPInteger)idx
{
// If the selection is managed by bindings, let the binder do that.
if ([self binderForBinding:CPSelectionIndexesBinding] || [self binderForBinding:CPSelectedIndexBinding])
return;
if (_selectedTabViewItem == aTabViewItem)
{
var didSelect = NO;
if (idx > 0)
didSelect = [self selectTabViewItemAtIndex:idx - 1];
else if ([self numberOfTabViewItems] > 0)
didSelect = [self selectTabViewItemAtIndex:0];
if (didSelect == NO)
_selectedTabViewItem == nil;
}
}
// Accessing Tabs
@@ -166,7 +194,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
{
return [_items indexOfObjectIdenticalTo:aTabViewItem];
return [[self items] indexOfObjectIdenticalTo:aTabViewItem];
}
/*!
@@ -176,11 +204,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
{
for (var index = [_items count]; index >= 0; index--)
if ([[_items[index] identifier] isEqual:anIdentifier])
return index;
return CPNotFound;
return [[self items] indexOfObjectPassingTest:function(item, idx, stop)
{
return [[item identifier] isEqual:anIdentifier];
}];
}
/*!
@@ -189,7 +216,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (unsigned)numberOfTabViewItems
{
return [_items count];
return [[self items] count];
}
/*!
@@ -198,7 +225,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (CPTabViewItem)tabViewItemAtIndex:(CPUInteger)anIndex
{
return [_items objectAtIndex:anIndex];
return [[self items] objectAtIndex:anIndex];
}
/*!
@@ -207,7 +234,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (CPArray)tabViewItems
{
return [_items copy]; // Copy?
return [[self items] copy]; // Copy?
}
// Selecting a Tab
@@ -217,7 +244,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)selectFirstTabViewItem:(id)aSender
{
if ([_items count] === 0)
if ([self numberOfTabViewItems] === 0)
return; // throw?
[self selectTabViewItemAtIndex:0];
@@ -229,10 +256,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)selectLastTabViewItem:(id)aSender
{
if ([_items count] === 0)
if ([self numberOfTabViewItems] === 0)
return; // throw?
[self selectTabViewItemAtIndex:[_items count] - 1];
[self selectTabViewItemAtIndex:[self numberOfTabViewItems] - 1];
}
/*!
@@ -241,12 +268,12 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)selectNextTabViewItem:(id)aSender
{
if (_selectedIndex === CPNotFound)
if (_selectedTabViewItem === nil)
return;
var nextIndex = _selectedIndex + 1;
var nextIndex = [self indexOfTabViewItem:_selectedTabViewItem] + 1;
if (nextIndex === [_items count])
if (nextIndex === [self numberOfTabViewItems])
// does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:)
return;
@@ -259,10 +286,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)selectPreviousTabViewItem:(id)aSender
{
if (_selectedIndex === CPNotFound)
if (_selectedTabViewItem === nil)
return;
var previousIndex = _selectedIndex - 1;
var previousIndex = [self indexOfTabViewItem:_selectedTabViewItem] - 1;
if (previousIndex < 0)
return; // does nothing. See above.
@@ -285,22 +312,32 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (BOOL)selectTabViewItemAtIndex:(CPUInteger)anIndex
{
if (anIndex === _selectedIndex)
return;
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
if (![self _selectTabViewItemAtIndex:anIndex])
return NO;
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
[self _reverseSetSelectedIndex];
[_tabs selectSegmentWithTag:anIndex];
[self _setSelectedIndex:anIndex];
return YES;
}
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
// Like selectTabViewItemAtIndex: but without bindings interaction
- (BOOL)_selectTabViewItemAtIndex:(CPUInteger)anIndex
{
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
if (aTabViewItem == _selectedTabViewItem)
return NO;
if (![self _sendDelegateShouldSelectTabViewItem:aTabViewItem])
return NO;
[self _sendDelegateWillSelectTabViewItem:aTabViewItem];
[_tabs setSelectedSegment:anIndex];
_selectedTabViewItem = aTabViewItem;
[self _displayItemView:[aTabViewItem view]];
[self _sendDelegateDidSelectTabViewItem:aTabViewItem];
return YES;
}
@@ -311,10 +348,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (CPTabViewItem)selectedTabViewItem
{
if (_selectedIndex != CPNotFound)
return [_items objectAtIndex:_selectedIndex];
return nil;
return _selectedTabViewItem;
}
// Modifying the font
@@ -375,6 +409,14 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
[self setNeedsLayout];
}
- (void)tileWithChangedItem:(CPTabViewItem)aTabViewItem
{
var segment = aTabViewItem ? [self indexOfTabViewItem:aTabViewItem] : 0;
[_tabs tileWithChangedSegment:segment];
[self setNeedsLayout];
}
- (void)layoutSubviews
{
// Even if CPTabView's autoresizesSubviews is NO, _tabs and _box has to be laid out.
@@ -392,7 +434,6 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
[self _updateItems];
[self _repositionTabs];
}
}
@@ -470,30 +511,238 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
}
- (void)_setSelectedIndex:(CPNumber)index
- (void)_displayItemView:(CPView)aView
{
_selectedIndex = index;
[self _setContentViewFromItem:[_items objectAtIndex:_selectedIndex]];
[_box setContentView:aView];
}
- (void)_setContentViewFromItem:(CPTabViewItem)anItem
// DELEGATE METHODS
- (BOOL)_sendDelegateShouldSelectTabViewItem:(CPTabViewItem)aTabViewItem
{
[_box setContentView:[anItem view]];
if (_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector)
return [_delegate tabView:self shouldSelectTabViewItem:aTabViewItem];
return YES;
}
- (void)_updateItems
- (void)_sendDelegateWillSelectTabViewItem:(CPTabViewItem)aTabViewItem
{
var count = [_items count];
[_tabs setSegmentCount:count];
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
}
for (var i = 0; i < count; i++)
- (void)_sendDelegateDidSelectTabViewItem:(CPTabViewItem)aTabViewItem
{
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
}
- (void)_sendDelegateTabViewDidChangeNumberOfTabViewItems
{
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
}
@end
@implementation CPTabView (BindingSupport)
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding == CPContentBinding)
return [_CPTabViewContentBinder class];
else if (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding)
return [_CPTabViewSelectionBinder class];
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding);
}
- (void)_reverseSetContent
{
var theBinder = [self binderForBinding:CPContentBinding];
[theBinder reverseSetValueFor:@"items"];
}
- (void)_reverseSetSelectedIndex
{
var theBinder = [self binderForBinding:CPSelectionIndexesBinding];
if (theBinder !== nil)
[theBinder reverseSetValueFor:@"selectionIndexes"];
else
{
[_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
[_tabs setTag:i forSegment:i];
theBinder = [self binderForBinding:CPSelectedIndexBinding];
[theBinder reverseSetValueFor:@"selectedIndex"];
}
}
- (CPBinder)binderForBinding:(CPString)aBinding
{
var cls = [[self class] _binderClassForBinding:aBinding]
return [cls getBinding:aBinding forObject:self];
}
- (void)setItems:(CPArray)tabViewItems
{
if ([tabViewItems isEqualToArray:[_tabs segments]])
return;
[[self items] makeObjectsPerformSelector:@selector(_setTabView:) withObject:nil];
[_tabs setSegments:tabViewItems];
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
[self tileWithChangedItem:nil];
// Update the selection because setSegments: did remove all previous segments AND the selection.
[_tabs setSelectedSegment:[self indexOfTabViewItem:_selectedTabViewItem]];
// should we send delegate methods in bindings mode ?
//[self _delegateTabViewDidChangeNumberOfTabViewItems:self];
}
- (void)_deselectAll
{
[_tabs setSelectedSegment:-1];
_selectedTabViewItem = nil;
}
- (void)_displayPlaceholder:(CPString)aPlaceholder
{
if (_placeHolderView == nil)
{
_placeHolderView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[textField setTag:1000];
[textField setTextColor:[CPColor whiteColor]];
[textField setFont:[CPFont boldFontWithName:@"Geneva" size:18 italic:YES]];
[_placeHolderView addSubview:textField];
}
if (_selectedIndex === CPNotFound)
[self selectFirstTabViewItem:self];
var textField = [_placeHolderView viewWithTag:1000];
[textField setStringValue:aPlaceholder];
[textField sizeToFit];
var boxBounds = [_box bounds],
textFieldBounds = [textField bounds],
origin = CGPointMake(CGRectGetWidth(boxBounds)/2 - CGRectGetWidth(textFieldBounds)/2, CGRectGetHeight(boxBounds)/2 - CGRectGetHeight(textFieldBounds));
[textField setFrameOrigin:origin];
[self _displayItemView:_placeHolderView];
}
#pragma mark -
#pragma mark Override
/*!
Enabled controls accept first mouse by default.
*/
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
{
return YES;
}
@end
var _CPTabViewContentBinderNull = @"NO CONTENT";
@implementation _CPTabViewContentBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:_CPTabViewContentBinderNull forMarker:CPNullMarker isDefault:YES];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[_source setItems:@[]];
[_source _setPlaceholderView:aValue];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source setItems:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
return [_source items];
}
@end
var _CPTabViewSelectionBinderMultipleValues = @"Multiple Selection",
_CPTabViewSelectionBinderNoSelection = @"No Selection";
@implementation _CPTabViewSelectionBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:_CPTabViewSelectionBinderMultipleValues forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:_CPTabViewSelectionBinderNoSelection forMarker:CPNoSelectionMarker isDefault:YES];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
if (aMarker == CPNoSelectionMarker || aMarker == CPNullMarker)
[_source _deselectAll];
[_source _displayPlaceholder:aValue];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
if (aBinding == CPSelectionIndexesBinding)
{
if (aValue == nil || [aValue count] == 0)
{
[_source _deselectAll];
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
}
else if ([aValue count] > 1)
[_source _displayPlaceholder:_CPTabViewSelectionBinderMultipleValues];
else if ([aValue firstIndex] < [_source numberOfTabViewItems])
[_source _selectTabViewItemAtIndex:[aValue firstIndex]];
}
else if (aBinding == CPSelectedIndexBinding)
{
if (aValue == CPNotFound)
{
[_source _deselectAll];
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
}
else if (aValue < [_source numberOfTabViewItems])
[_source _selectTabViewItemAtIndex:aValue];
}
}
- (id)valueForBinding:(CPString)aBinding
{
if (aBinding == CPSelectionIndexesBinding)
{
var result = [CPIndexSet indexSet],
idx = [_source indexOfTabViewItem:[_source selectedTabViewItem]];
if (idx !== CPNotFound)
[result addIndex:idx];
return result;
}
else if (aBinding == CPSelectedIndexBinding)
return [_source indexOfTabViewItem:[_source selectedTabViewItem]];
}
@end
@@ -515,12 +764,13 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
_font = [aCoder decodeObjectForKey:CPTabViewFontKey];
[_tabs setFont:_font];
_items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
[_items makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
var items = [aCoder decodeObjectForKey:CPTabViewItemsKey] || [CPArray array];
[self _insertTabViewItems:items atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [items count])]];
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
self.selectOnAwake = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
_selectedTabViewItem = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
_type = [aCoder decodeIntForKey:CPTabViewTypeKey];
}
@@ -529,14 +779,23 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
- (void)awakeFromCib
{
[super awakeFromCib];
// This cannot be run in initWithCoder because it might call selectTabViewItem:, which is
// not safe to call before the views of the tab views items are fully decoded.
[self _updateItems];
if (self.selectOnAwake)
if (_selectedTabViewItem)
{
[self selectTabViewItem:self.selectOnAwake];
delete self.selectOnAwake;
var idx = [self indexOfTabViewItem:_selectedTabViewItem];
if (idx !== CPNotFound)
{
// Temporarily set the selected item to not selected.
// It allows the initial selection to be made correctly.
_selectedTabViewItem = nil;
[self selectTabViewItemAtIndex:idx];
}
}
var type = _type;
@@ -558,9 +817,7 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
[aCoder encodeObject:_items forKey:CPTabViewItemsKey];
var selected = [self selectedTabViewItem];
if (selected)
[aCoder encodeObject:selected forKey:CPTabViewSelectedItemKey];
[aCoder encodeConditionalObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
[aCoder encodeInt:_type forKey:CPTabViewTypeKey];
[aCoder encodeObject:_font forKey:CPTabViewFontKey];
@@ -569,3 +826,26 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
}
@end
@implementation _CPTabViewBox : CPBox
{
CPTabView _tabView @accessors(property=tabView);
}
#pragma mark -
#pragma mark Override
- (CPView)hitTest:(CGPoint)aPoint
{
// Here we check if we have clicked on the segmentedControl of the tabView or not
// If YES, the CPBox should not handle the click
var segmentIndex = [_tabView._tabs testSegment:[_tabView._tabs convertPoint:aPoint fromView:[self superview]]];
if (segmentIndex != CPNotFound)
return nil;
return [super hitTest:aPoint];
}
@end
+102 -10
View File
@@ -24,6 +24,7 @@
@import "CPView.j"
@class CPTabView
@class CPViewController
/*
The tab is currently selected.
@@ -53,14 +54,34 @@ CPPressedTab = 2;
*/
@implementation CPTabViewItem : CPObject
{
id _identifier;
CPString _label;
id _identifier;
CPString _label;
CPInteger _tag @accessors(property=tag);
CPView _view;
CPView _auxiliaryView;
CPView _view;
CPView _auxiliaryView;
CPTabView _tabView;
unsigned _tabState; // Looks like it is not yet implemented
CPTabView _tabView;
unsigned _tabState; // Looks like it is not yet implemented
CPImage _image @accessors(property=image);
CPViewController _viewController @accessors(getter=viewController);
BOOL _enabled @accessors(property=enabled);
BOOL _selected @accessors(property=selected);
CGRect _tabRect @accessors(property=frame);
float _width @accessors(property=width);
}
/*
*/
+ (CPTabViewItem)tabViewItemWithViewController:(CPViewController)aViewController
{
var item = [[CPTabViewItem alloc] init];
[item setViewController:aViewController];
return item;
}
- (id)init
@@ -68,6 +89,19 @@ CPPressedTab = 2;
return [self initWithIdentifier:@""];
}
- (void)_init
{
_tag = 0;
_viewController = nil;
_image = nil;
_tabState = 0;
_tabView = nil;
_enabled = YES;
_selected = NO;
_tabRect = CGRectMakeZero();
_width = 0;
}
/*!
Initializes the tab view item with the specified identifier.
@return the initialized CPTabViewItem
@@ -76,8 +110,12 @@ CPPressedTab = 2;
{
self = [super init];
if (self)
_identifier = anIdentifier;
[self _init];
_identifier = anIdentifier;
_label = nil;
_view = nil;
//_auxiliaryView = nil;
return self;
}
@@ -89,8 +127,11 @@ CPPressedTab = 2;
*/
- (void)setLabel:(CPString)aLabel
{
if ([aLabel isEqualToString:_label])
return;
_label = aLabel;
[_tabView setNeedsLayout];
[_tabView tileWithChangedItem:self];
}
/*!
@@ -101,6 +142,28 @@ CPPressedTab = 2;
return _label;
}
// Working With Images
/*!
Sets the CPTabViewItem's image.
@param anImage the image for the item
*/
- (void)setImage:(CPImage)anImage
{
if ([anImage isEqual:_image])
return;
_image = anImage;
[_tabView tileWithChangedItem:self];
}
/*!
Returns the CPTabViewItem's image
*/
- (CPImage)image
{
return _image;
}
// Checking the Tab Display State
/*!
Returns the tab's current state.
@@ -140,7 +203,7 @@ CPPressedTab = 2;
_view = aView;
if ([_tabView selectedTabViewItem] == self)
[_tabView _setContentViewFromItem:self];
[_tabView _displayItemView:_view];
}
/*!
@@ -148,6 +211,9 @@ CPPressedTab = 2;
*/
- (CPView)view
{
if (!_view && _viewController)
return [_viewController view]; // The view controller loads here.
return _view;
}
@@ -186,10 +252,32 @@ CPPressedTab = 2;
_tabView = aView;
}
/*!
Sets the specified view controller for the tab view item.
@param aViewController an instance of CPViewController.
*/
- (void)setViewController:(CPViewController)aViewController
{
_viewController = aViewController;
var identifier = [aViewController cibName],
title = [_viewController title];
if (identifier)
_identifier = identifier;
if (title)
[self setLabel:title];
if ([_tabView selectedTabViewItem] == self)
[_tabView _displayItemView:[_viewController view]];
}
@end
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
CPTabViewItemLabelKey = "CPTabViewItemLabelKey",
CPTabViewItemImageKey = "CPTabViewItemImageKey",
CPTabViewItemViewKey = "CPTabViewItemViewKey",
CPTabViewItemAuxViewKey = "CPTabViewItemAuxViewKey";
@@ -202,8 +290,11 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
if (self)
{
[self _init];
_identifier = [aCoder decodeObjectForKey:CPTabViewItemIdentifierKey];
_label = [aCoder decodeObjectForKey:CPTabViewItemLabelKey];
_image = [aCoder decodeObjectForKey:CPTabViewItemImageKey];
_view = [aCoder decodeObjectForKey:CPTabViewItemViewKey];
_auxiliaryView = [aCoder decodeObjectForKey:CPTabViewItemAuxViewKey];
@@ -216,6 +307,7 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
{
[aCoder encodeObject:_identifier forKey:CPTabViewItemIdentifierKey];
[aCoder encodeObject:_label forKey:CPTabViewItemLabelKey];
[aCoder encodeObject:_image forKey:CPTabViewItemImageKey];
[aCoder encodeObject:_view forKey:CPTabViewItemViewKey];
[aCoder encodeObject:_auxiliaryView forKey:CPTabViewItemAuxViewKey];
+19 -8
View File
@@ -180,7 +180,7 @@ CPTableColumnUserResizingMask = 1 << 1;
columns = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(index, [tableView._exposedColumns lastIndex] - index + 1)];
// FIXME: Would be faster with some sort of -setNeedsDisplayInColumns: that updates a dirtyTableColumnForDisplay cache; then marked columns would relayout their data views at display time.
[tableView _layoutDataViewsInRows:rows columns:columns];
[tableView _layoutViewsForRowIndexes:rows columnIndexes:columns];
[tableView tile];
if (!_disableResizingPosting)
@@ -393,8 +393,8 @@ CPTableColumnUserResizingMask = 1 << 1;
*/
- (void)setDataView:(CPView)aView
{
if (_dataView)
_dataViewData = nil;
if (_dataView === aView)
return;
[aView setThemeState:CPThemeStateTableDataView];
@@ -546,11 +546,22 @@ CPTableColumnUserResizingMask = 1 << 1;
- (void)setValueFor:(CPString)aBinding
{
var tableView = [_source tableView],
column = [[tableView tableColumns] indexOfObjectIdenticalTo:_source],
rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [tableView numberOfRows])],
columnIndexes = [CPIndexSet indexSetWithIndex:column];
newNumberOfRows = [tableView _numberOfRows];
[tableView reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
if ([tableView numberOfRows] == newNumberOfRows)
{
var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, newNumberOfRows)],
column = [[tableView tableColumns] indexOfObjectIdenticalTo:_source],
columnIndexes = [CPIndexSet indexSetWithIndex:column];
// Reloads objectValues only, not the views.
// FIXME: reload data for all rows or just rows intersecting exposed rows ?
[tableView _reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
}
else
{
[tableView reloadData];
}
}
- (CPSortDescriptor)_defaultSortDescriptorPrototype
@@ -590,7 +601,7 @@ CPTableColumnUserResizingMask = 1 << 1;
}
/*!
Binds the receiver to an object.
Binds the receiver to an object. Note that unlike Cocoa, this works only *after* the receiver has been added to a \c CPTableView.
@param CPString aBinding - The binding you wish to make. Typically CPValueBinding.
@param id anObject - The object to bind the receiver to.
+392 -363
View File
File diff suppressed because it is too large Load Diff
+781 -647
View File
File diff suppressed because it is too large Load Diff
+13 -1
View File
@@ -38,4 +38,16 @@ CPLeftTextMovement = 19;
CPRightTextMovement = 20;
CPUpTextMovement = 21;
CPDownTextMovement = 22;
CPCancelTextMovement = 23;
CPCancelTextMovement = 23;
@typedef CPWritingDirection
CPWritingDirectionNatural = -1;
CPWritingDirectionLeftToRight = 0;
CPWritingDirectionRightToLeft = 1;
@typedef CPTextAlignment
CPLeftTextAlignment = 0;
CPRightTextAlignment = 1;
CPCenterTextAlignment = 2;
CPJustifiedTextAlignment = 3;
CPNaturalTextAlignment = 4;
+109 -16
View File
@@ -28,12 +28,12 @@
@import "_CPImageAndTextView.j"
@class CPPasteboard
@class CPScrollView
@global CPApp
@global CPStringPboardType
@global CPCursor
@protocol CPTextFieldDelegate <CPControlTextEditingDelegate>
@end
@@ -65,11 +65,11 @@ var CPTextFieldDOMCurrentElement = nil,
CPTextFieldCachedSelectStartFunction = nil,
CPTextFieldCachedDragFunction = nil,
CPTextFieldBlurHandler = nil,
CPTextFieldInputFunction = nil;
CPTextFieldInputFunction = nil,
CPTexFieldCurrentCSSSelectableField = nil;
var CPSecureTextFieldCharacter = "\u2022";
function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resigning, didBlurRef)
{
if (owner && domElement != inputElement.parentNode)
@@ -86,10 +86,16 @@ function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resig
*/
if ([owner _isWithinUsablePlatformRect])
{
window.setTimeout(function()
[[CPRunLoop mainRunLoop] performBlock:function()
{
// This will prevent to jump to the focused element
var previousScrollingOrigin = [owner _scrollToVisibleRectAndReturnPreviousOrigin];
inputElement.focus();
}, 0.0);
[owner _restorePreviousScrollingOrigin:previousScrollingOrigin];
}
argument:nil order:0 modes:[CPDefaultRunLoopMode]];
}
}
@@ -370,7 +376,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
_sendActionOn = CPKeyUpMask | CPKeyDownMask;
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
[self setValue:CPNaturalTextAlignment forThemeAttribute:@"alignment"];
}
return self;
@@ -643,6 +649,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
_stringValue = [self stringValue];
#if PLATFORM(DOM)
[self _setCSSStyleForInputElement];
@@ -664,30 +671,56 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldInputOwner = self;
window.setTimeout(function()
[[CPRunLoop mainRunLoop] performBlock:function()
{
/*
setTimeout handlers are not guaranteed to fire in the order they were initiated. This can cause a race condition when several windows with text fields are opened quickly, resulting in several instances of this timeout function being fired, perhaps out of order. So we have to check that by the time this function is fired, CPTextFieldInputOwner has not been changed to another text field in the meantime.
*/
if (CPTextFieldInputOwner !== self)
return;
// This will prevent to jump to the focused element
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
element.focus();
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
// Select the text if the textfield became first responder through keyboard interaction
if (!_willBecomeFirstResponderByClick)
{
[self _selectText:self immediately:YES];
}
else
{
var point = CGPointMake([self convertPointFromBase:[[CPApp currentEvent] locationInWindow]].x - [self currentValueForThemeAttribute:@"content-inset"].left, 0),
position = [CPPlatformString charPositionOfString:[self stringValue] withFont:[self font] forPoint:point];
[self setSelectedRange:CPMakeRange(position, 0)];
}
_willBecomeFirstResponderByClick = NO;
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
}, 0.0);
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
#endif
return YES;
}
/*!
Set the selection css style for the DOM element of the textField
@ignore
*/
- (void)_setEnableCSSSelection:(BOOL)shouldEnable
{
#if PLATFORM (DOM)
if (CPTexFieldCurrentCSSSelectableField)
CPTexFieldCurrentCSSSelectableField._DOMElement.style[CPBrowserStyleProperty(@"user-select")] = @"none";
CPTexFieldCurrentCSSSelectableField = self;
_DOMElement.style[CPBrowserStyleProperty(@"user-select")] = shouldEnable ? @"text" : @"none";
#endif
}
/*!
Set the css style for the input element of the textField
@ignore
@@ -741,6 +774,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
element.style.textAlign = "right";
break;
case CPNaturalTextAlignment:
element.style.textAlign = "";
break;
default:
element.style.textAlign = "left";
}
@@ -783,7 +820,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
// even if the value has not changed.
if ([self _valueIsValid:newValue] === NO)
{
// This will prevent to jump to the focused element
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
element.focus();
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
return NO;
}
}
@@ -792,9 +835,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
// When we are no longer the first responder we don't worry about the key status of our window anymore.
[self _setObserveWindowKeyNotifications:NO];
[self _resignFirstKeyResponder];
if ([[self window] isKeyWindow])
[self _resignFirstKeyResponder];
_isEditing = NO;
if ([self isEditable])
{
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:@{"CPTextMovement": [self _currentTextMovement]}]];
@@ -970,6 +1015,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
else if ([self isSelectable])
{
[self _setEnableCSSSelection:YES];
if (document.attachEvent)
{
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
@@ -1011,6 +1057,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
}
- (void)rightMouseDown:(CPEvent)anEvent
{
if ([self menuForEvent:anEvent] || [[self nextResponder] isKindOfClass:CPView])
[super rightMouseDown:anEvent];
else
[[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
}
- (void)mouseDragged:(CPEvent)anEvent
{
if (![self isEnabled] || !([self isSelectable] || [self isEditable]))
@@ -1161,7 +1215,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)textDidFocus:(CPNotification)note
{
// this looks to prevent false propagation of notifications for other objects
if ([note object] != self)
if ([note object] !== self)
return;
if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidFocus_)
@@ -1463,7 +1517,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (immediately)
element.select();
else
window.setTimeout(function() { element.select(); }, 0);
[[CPRunLoop mainRunLoop] performBlock:function(){ element.select(); } argument:nil order:0 modes:[CPDefaultRunLoopMode]];
}
else if (wind !== nil && [wind makeFirstResponder:self])
[self _selectText:sender immediately:immediately];
@@ -1950,17 +2004,55 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (!wind)
return NO;
var scrollView = [self enclosingScrollView],
previousContentViewBoundsOrigin;
// Here we scroll to the textField, otherwise the textField could not be in the usable platformRect
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
var frame = [self convertRectToBase:[self contentRectForBounds:[self bounds]]],
usableRect = [[wind platformWindow] usableContentFrame];
frame.origin = [wind convertBaseToGlobal:frame.origin];
// Here we restore the the previous scrolling posiition
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
return (CGRectGetMinX(frame) >= CGRectGetMinX(usableRect) &&
CGRectGetMaxX(frame) <= CGRectGetMaxX(usableRect) &&
CGRectGetMinY(frame) >= CGRectGetMinY(usableRect) &&
CGRectGetMaxY(frame) <= CGRectGetMaxY(usableRect));
}
/*!
@ignore
*/
- (CGPoint)_scrollToVisibleRectAndReturnPreviousOrigin
{
var scrollView = [self enclosingScrollView],
previousContentViewBoundsOrigin;
// Here we scroll to the textField, otherwise the textField could not be in the usable platformRect
if ([scrollView isKindOfClass:[CPScrollView class]])
{
previousContentViewBoundsOrigin = CGPointMakeCopy([[scrollView contentView] boundsOrigin]);
if (![[self superview] scrollRectToVisible:[self frame]])
previousContentViewBoundsOrigin = nil;
}
return previousContentViewBoundsOrigin;
}
/*!
@ignore
*/
- (void)_restorePreviousScrollingOrigin:(CGPoint)scrollingOrigin
{
if (scrollingOrigin)
[[[self enclosingScrollView] contentView] setBoundsOrigin:scrollingOrigin];
}
@end
var secureStringForString = function(aString)
@@ -2082,7 +2174,8 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
newValue = [self valueForBinding:aBinding],
value = [destination valueForKeyPath:keyPath];
if (CPIsControllerMarker(value) && newValue === nil) return;
if (CPIsControllerMarker(value) && newValue === nil)
return;
newValue = [self reverseTransformValue:newValue withOptions:options];
@@ -2109,4 +2202,4 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
[_source setObjectValue:aValue];
}
@end
@end
+135 -55
View File
@@ -36,7 +36,6 @@ var CPThemesByName = { },
/*!
@ingroup appkit
*/
@implementation CPTheme : CPObject
{
CPString _name;
@@ -142,20 +141,19 @@ var CPThemesByName = { },
if (!className)
{
if ([aClass isKindOfClass:[CPView class]])
if ([aClass respondsToSelector:@selector(defaultThemeClass)])
{
if ([aClass respondsToSelector:@selector(defaultThemeClass)])
className = [aClass defaultThemeClass];
else if ([aClass respondsToSelector:@selector(themeClass)])
{
CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass", CPStringFromClass(aClass));
className = [aClass themeClass];
}
else
return nil;
className = [aClass defaultThemeClass];
}
else if ([aClass respondsToSelector:@selector(themeClass)])
{
CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass", CPStringFromClass(aClass));
className = [aClass themeClass];
}
else
[CPException raise:CPInvalidArgumentException reason:@"aClass must be a class object or a string."];
{
return nil;
}
}
return [_attributes objectForKey:className];
@@ -328,6 +326,7 @@ function ThemeState(stateNames)
{
if (!stateNames.hasOwnProperty(key))
continue;
if (key !== 'normal')
{
this._stateNames[key] = true;
@@ -345,8 +344,10 @@ function ThemeState(stateNames)
this._stateNameString = stateNameKeys[0];
var stateNameLength = stateNameKeys.length;
for (var stateIndex = 1; stateIndex < stateNameLength; stateIndex++)
this._stateNameString = this._stateNameString + "+" + stateNameKeys[stateIndex];
this._stateNameCount = stateNameLength;
}
@@ -393,7 +394,19 @@ ThemeState.prototype.without = function(aState)
if (!aState || aState === [CPNull null])
return this;
var firstTransform = CPThemeWithoutTransform[this._stateNameString],
result;
if (firstTransform)
{
result = firstTransform[aState._stateNameString];
if (result)
return result;
}
var newStates = {};
for (var stateName in this._stateNames)
{
if (!this._stateNames.hasOwnProperty(stateName))
@@ -403,25 +416,54 @@ ThemeState.prototype.without = function(aState)
newStates[stateName] = true;
}
return ThemeState._cacheThemeState(new ThemeState(newStates));
result = ThemeState._cacheThemeState(new ThemeState(newStates));
if (!firstTransform)
firstTransform = CPThemeWithoutTransform[this._stateNameString] = {};
firstTransform[aState._stateNameString] = result;
return result;
}
ThemeState.prototype.and = function(aState)
{
return CPThemeState(this, aState);
var firstTransform = CPThemeAndTransform[this._stateNameString],
result;
if (firstTransform)
{
result = firstTransform[aState._stateNameString];
if (result)
return result;
}
result = CPThemeState(this, aState);
if (!firstTransform)
firstTransform = CPThemeAndTransform[this._stateNameString] = {};
firstTransform[aState._stateNameString] = result;
return result;
}
var CPThemeStates = {};
var CPThemeStates = {},
CPThemeWithoutTransform = {},
CPThemeAndTransform = {};
ThemeState._cacheThemeState = function(aState)
{
// We do this caching so themeState equality works. Basically, doing CPThemeState('foo+bar') === CPThemeState('bar', 'foo') will return true.
var themeState = CPThemeStates[String(aState)];
if (themeState === undefined)
{
themeState = aState;
CPThemeStates[String(themeState)] = themeState;
}
return themeState;
}
@@ -439,14 +481,17 @@ function CPThemeState()
throw "CPThemeState() must be called with at least one string argument";
var themeState;
if (arguments.length === 1 && typeof arguments[0] === 'string')
{
themeState = CPThemeStates[arguments[0]];
if (themeState !== undefined)
return themeState;
}
var stateNames = {};
for (var argIndex = 0; argIndex < arguments.length; argIndex++)
{
if (arguments[argIndex] === [CPNull null] || !arguments[argIndex])
@@ -458,12 +503,14 @@ function CPThemeState()
{
if (!arguments[argIndex]._stateNames.hasOwnProperty(stateName))
continue;
stateNames[stateName] = true;
}
}
else
{
var allNames = arguments[argIndex].split('+');
for (var nameIndex = 0; nameIndex < allNames.length; nameIndex++)
stateNames[allNames[nameIndex]] = true;
}
@@ -523,6 +570,9 @@ CPThemeStateControlSizeRegular = CPThemeState("controlSizeRegular");
CPThemeStateControlSizeSmall = CPThemeState("controlSizeSmall");
CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
CPThemeStateNormalString = String(CPThemeStateNormal);
@implementation _CPThemeAttribute : CPObject
{
CPString _name;
@@ -533,7 +583,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
_CPThemeAttribute _themeDefaultAttribute;
}
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute
{
self = [super init];
@@ -542,7 +592,9 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
_cache = { };
_name = aName;
_defaultValue = aDefaultValue;
_values = @{};
if (aDefaultAttribute)
_themeDefaultAttribute = aDefaultAttribute;
}
return self;
@@ -563,24 +615,41 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
return [_values count] > 0;
}
- (void)setValue:(id)aValue
- (_CPThemeAttribute)attributeBySettingValue:(id)aValue
{
_cache = {};
var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute];
if (aValue === undefined || aValue === nil)
_values = @{};
else
_values = @{ String(CPThemeStateNormal): aValue };
if (aValue !== undefined && aValue !== nil)
attribute._values = @{ CPThemeStateNormalString: aValue };
return attribute;
}
- (void)setValue:(id)aValue forState:(ThemeState)aState
- (_CPThemeAttribute)attributeBySettingValue:(id)aValue forState:(ThemeState)aState
{
_cache = { };
var shouldRemoveValue = aValue === undefined || aValue === nil,
attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute],
values = _values;
if ((aValue === undefined) || (aValue === nil))
[_values removeObjectForKey:String(aState)];
else
[_values setObject:aValue forKey:String(aState)];
if (values != null)
{
values = [values copy];
if (shouldRemoveValue)
[values removeObjectForKey:String(aState)];
else
[values setObject:aValue forKey:String(aState)];
attribute._values = values;
}
else if (!shouldRemoveValue)
{
values = [[CPDictionary alloc] init];
[values setObject:aValue forKey:String(aState)];
attribute._values = values;
}
return attribute;
}
- (id)value
@@ -605,7 +674,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
if (aState._stateNameCount > 1)
{
var states = [_values allKeys],
count = states.length,
count = states ? states.length : 0,
largestThemeState = 0;
while (count--)
@@ -643,27 +712,41 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
return value;
}
- (void)setParentAttribute:(_CPThemeAttribute)anAttribute
- (_CPThemeAttribute)attributeBySettingParentAttribute:(_CPThemeAttribute)anAttribute
{
if (_themeDefaultAttribute === anAttribute)
return;
return self;
_cache = { };
_themeDefaultAttribute = anAttribute;
var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:anAttribute];
attribute._values = [_values copy];
return attribute;
}
- (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
{
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute];
mergedAttribute._values = [_values copy];
[mergedAttribute._values addEntriesFromDictionary:anAttribute._values];
if (anAttribute._values)
mergedAttribute._values ? [mergedAttribute._values addEntriesFromDictionary:anAttribute._values] : [anAttribute._values copy];
return mergedAttribute;
}
- (CPString)description
{
return [super description] + @" Name: " + _name + @", defaultAttribute: " + _themeDefaultAttribute + @", defaultValue: " + _defaultValue + @", values: " + _values;
}
@end
// This is used to pass 'parrentAttribute' to the coder
var ParentAttributeForCoder = nil;
@implementation _CPThemeAttribute (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
@@ -677,13 +760,16 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
_name = [aCoder decodeObjectForKey:@"name"];
_defaultValue = [aCoder decodeObjectForKey:@"defaultValue"];
_values = @{};
_themeDefaultAttribute = ParentAttributeForCoder;
if ([aCoder containsValueForKey:@"value"])
{
var state = String(CPThemeStateNormal);
var state;
if ([aCoder containsValueForKey:@"state"])
state = [aCoder decodeObjectForKey:@"state"];
else
state = CPThemeStateNormalString
[_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state];
}
@@ -711,7 +797,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
[aCoder encodeObject:_defaultValue forKey:@"defaultValue"];
var keys = [_values allKeys],
count = keys.length;
count = keys ? keys.length : 0;
if (count === 1)
{
@@ -749,7 +835,7 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute)
{
var state = [values allKeys][0];
if (state === String(CPThemeStateNormal))
if (state === CPThemeStateNormalString)
{
[aCoder encodeObject:[values objectForKey:state] forKey:key];
@@ -767,30 +853,24 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute)
return NO;
}
function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
function CPThemeAttributeDecode(aCoder, attribute)
{
var key = "$a" + anAttributeName;
var key = "$a" + attribute._name;
if (![aCoder containsValueForKey:key])
var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
else
if ([aCoder containsValueForKey:key])
{
var attribute = [aCoder decodeObjectForKey:key];
ParentAttributeForCoder = attribute._themeDefaultAttribute;
if (!attribute || !attribute.isa || ![attribute isKindOfClass:[_CPThemeAttribute class]])
{
var themeAttribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
var decodedAttribute = [aCoder decodeObjectForKey:key];
[themeAttribute setValue:attribute];
ParentAttributeForCoder = nil;
attribute = themeAttribute;
}
if (!decodedAttribute || !decodedAttribute.isa || ![decodedAttribute isKindOfClass:[_CPThemeAttribute class]])
attribute = [attribute attributeBySettingValue:decodedAttribute];
else
attribute = decodedAttribute;
}
if (aTheme && aClass)
[attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]];
return attribute;
}
+25 -10
View File
@@ -448,21 +448,27 @@ CPTokenFieldDeleteButtonType = 1;
element.style.width = CGRectGetWidth(contentRect) + "px";
element.style.height = [font defaultLineHeightForFont] + "px";
window.setTimeout(function()
[[CPRunLoop mainRunLoop] performBlock:function()
{
[_tokenScrollView documentView]._DOMElement.appendChild(element);
//post CPControlTextDidBeginEditingNotification
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
window.setTimeout(function()
[[CPRunLoop mainRunLoop] performBlock:function()
{
// This will prevent to jump to the focused element
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
element.focus();
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
CPTokenFieldInputOwner = self;
}, 0.0);
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
}, 0.0);
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
@@ -1033,6 +1039,21 @@ CPTokenFieldDeleteButtonType = 1;
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
}
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
{
var characters = [anEvent characters];
// Here we handle the event when getting a CPNewlineCharacter or CPCarriageReturnCharacter when the menu is open
// We don't want that the application dispatches the event to the other controls
if ([self hasThemeState:CPThemeStateAutocompleting] && (characters === CPNewlineCharacter || characters === CPCarriageReturnCharacter))
{
[self keyDown:anEvent];
return YES;
}
return [super performKeyEquivalent:anEvent];
}
- (void)textDidChange:(CPNotification)aNotification
{
if ([aNotification object] !== self)
@@ -1454,9 +1475,6 @@ CPTokenFieldDeleteButtonType = 1;
- (BOOL)setThemeState:(ThemeState)aState
{
if (aState.isa && [aState isKindOfClass:CPArray])
aState = CPThemeState.apply(null, aState);
var r = [super setThemeState:aState];
// Share hover state with the disclosure and delete buttons.
@@ -1471,9 +1489,6 @@ CPTokenFieldDeleteButtonType = 1;
- (BOOL)unsetThemeState:(ThemeState)aState
{
if (aState.isa && [aState isKindOfClass:CPArray])
aState = CPThemeState.apply(null, aState);
var r = [super unsetThemeState:aState];
// Share hover state with the disclosure and delete button.
+164
View File
@@ -0,0 +1,164 @@
/*
* CPTrackingArea.j
* AppKit
*
* Created by Didier Korthoudt.
* Copyright 2015, Cappuccino Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@class CPView
/* @group CPTrackingAreaOptions */
@typedef CPTrackingAreaOptions
CPTrackingMouseEnteredAndExited = 1 << 1;
CPTrackingMouseMoved = 1 << 2;
CPTrackingCursorUpdate = 1 << 3;
CPTrackingActiveWhenFirstResponder = 1 << 4;
CPTrackingActiveInKeyWindow = 1 << 5;
CPTrackingActiveInActiveApp = 1 << 6;
CPTrackingActiveAlways = 1 << 7;
CPTrackingAssumeInside = 1 << 8;
CPTrackingInVisibleRect = 1 << 9;
CPTrackingEnabledDuringMouseDrag = 1 << 10;
var CPTrackingAreaViewRectKey = @"CPTrackinkAreaViewRectKey",
CPTrackingAreaOptionsKey = @"CPTrackingAreaOptionsKey",
CPTrackingAreaOwnerKey = @"CPTrackingAreaOwnerKey",
CPTrackingAreaUserInfoKey = @"CPTrackingAreaUserInfoKey",
CPTrackingAreaReferencingViewKey = @"CPTrackingAreaReferencingViewKey",
CPTrackingAreaWindowRect = @"CPTrackingAreaWindowRect";
CPTrackingOwnerImplementsMouseEntered = 1 << 1;
CPTrackingOwnerImplementsMouseExited = 1 << 2;
CPTrackingOwnerImplementsMouseMoved = 1 << 3;
CPTrackingOwnerImplementsCursorUpdate = 1 << 4;
/*!
@ingroup appkit
A CPTrackingArea defines a region of view that generates mouse-tracking and
cursor-update events when the mouse is over that region.
*/
@implementation CPTrackingArea : CPObject
{
CGRect _viewRect @accessors(getter=rect);
CPTrackingAreaOptions _options @accessors(getter=options);
id _owner @accessors(getter=owner);
CPDictionary _userInfo @accessors(getter=userInfo);
CPView _referencingView @accessors(property=view);
CGRect _windowRect @accessors(getter=windowRect);
unsigned _implementedOwnerMethods @accessors(getter=implementedOwnerMethods);
}
#pragma mark -
#pragma mark Initialization
/*!
Initializes and returns an object defining a region of a view to receive mouse-tracking events, mouse-moved events, cursor-update events, or possibly
all these events.
*/
- (CPTrackingArea)initWithRect:(CGRect)aRect options:(CPTrackingAreaOptions)options owner:(id)owner userInfo:(CPDictionary)userInfo
{
if (owner === nil)
[CPException raise:CPInternalInconsistencyException reason:"No owner specified"];
if (options === 0)
[CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingArea options"];
// Check options:
// - at least one of CPTrackingMouseEnteredAndExited, CPTrackingMouseMoved, CPTrackingCursorUpdate
// - exactly one of CPTrackingActiveWhenFirstResponder, CPTrackingActiveInKeyWindow, CPTrackingActiveInActiveApp, CPTrackingActiveAlways
// - no check on CPTrackingAssumeInside, CPTrackingInVisibleRect, CPTrackingEnableDuringMouseDrag
if (!((options & CPTrackingMouseEnteredAndExited) || (options & CPTrackingMouseMoved) || (options & CPTrackingCursorUpdate)))
[CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingAreaOptions: must use at least one of [CPTrackingMouseEnteredAndExited | CPTrackingMouseMoved | CPTrackingCursorUpdate]"];
if ((((options & CPTrackingActiveWhenFirstResponder) > 0) + ((options & CPTrackingActiveInKeyWindow) > 0) + ((options & CPTrackingActiveInActiveApp) > 0) + ((options & CPTrackingActiveAlways) > 0)) !== 1)
[CPException raise:CPInternalInconsistencyException reason:"Tracking area options may only specify one of [CPTrackingActiveWhenFirstResponder | CPTrackingActiveInKeyWindow | CPTrackingActiveInActiveApp | CPTrackingActiveAlways]."];
if (self = [super init])
{
_viewRect = aRect;
_options = options;
_owner = owner;
_userInfo = userInfo;
// Cache owner implemented methods
if ([_owner respondsToSelector:@selector(mouseEntered:)])
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseEntered;
if ([_owner respondsToSelector:@selector(mouseExited:)])
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseExited;
if ([_owner respondsToSelector:@selector(mouseMoved:)])
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseMoved;
if ([_owner respondsToSelector:@selector(cursorUpdate:)])
_implementedOwnerMethods |= CPTrackingOwnerImplementsCursorUpdate;
}
return self;
}
#pragma mark -
#pragma mark Implementation
- (void)_updateWindowRect
{
_windowRect = [_referencingView convertRect:((_options & CPTrackingInVisibleRect) ? [_referencingView visibleRect] : _viewRect) toView:[[_referencingView window] _windowView]];
}
@end
#pragma mark -
#pragma mark CPCoding
@implementation CPTrackingArea (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super init])
{
_viewRect = [aCoder decodeObjectForKey:CPTrackingAreaViewRectKey];
_options = [aCoder decodeObjectForKey:CPTrackingAreaOptionsKey];
_owner = [aCoder decodeObjectForKey:CPTrackingAreaOwnerKey];
_userInfo = [aCoder decodeObjectForKey:CPTrackingAreaUserInfoKey];
_referencingView = [aCoder decodeObjectForKey:CPTrackingAreaReferencingViewKey];
_windowRect = [aCoder decodeObjectForKey:CPTrackingAreaWindowRect];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_viewRect forKey:CPTrackingAreaViewRectKey];
[aCoder encodeObject:_options forKey:CPTrackingAreaOptionsKey];
[aCoder encodeObject:_owner forKey:CPTrackingAreaOwnerKey];
[aCoder encodeObject:_userInfo forKey:CPTrackingAreaUserInfoKey];
[aCoder encodeObject:_referencingView forKey:CPTrackingAreaReferencingViewKey];
[aCoder encodeObject:_windowRect forKey:CPTrackingAreaWindowRect];
}
@end
+485 -334
View File
File diff suppressed because it is too large Load Diff
+186
View File
@@ -0,0 +1,186 @@
/*
* CPVisualEffectView.j
* AppKit
*
* Created by Antoine Mercadal.
* Copyright 2015, 280 Cappuccino Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPAppearance.j"
@import "CPView.j"
@typedef CPVisualEffectMaterial
CPVisualEffectMaterialAppearanceBased = 0;
CPVisualEffectMaterialLight = 1;
CPVisualEffectMaterialDark = 2;
CPVisualEffectMaterialTitlebar = 3;
@typedef CPVisualEffectBlendingMode
CPVisualEffectBlendingModeBehindWindow = 0;
CPVisualEffectBlendingModeWithinWindow = 1;
@typedef CPVisualEffectState
CPVisualEffectStateFollowsWindowActiveState = 0;
CPVisualEffectStateActive = 1;
CPVisualEffectStateInactive = 2;
/*! @ingroup appkit
Very naive implementation of CPVisualEffectView. This view allows
to use vibrancy effect. This is only working with Safari 9+ and the
support in Chrome/ium should come quite soon.
Using this class with a browser that doesn't support backdrop-filter
While still work, but you will not get the blurry effect.
*/
@implementation CPVisualEffectView : CPView
{
CPImage _maskImage @accessors(property=maskImage);
CPVisualEffectBlendingMode _blendingMode @accessors(property=blendingMode);
CPVisualEffectMaterial _material @accessors(property=material);
CPVisualEffectState _state @accessors(property=state);
}
#pragma mark -
#pragma mark Initialization
- (id)initWithFrame:(CGRect)aFrame
{
if (self = [super initWithFrame:aFrame])
{
_material = CPVisualEffectMaterialAppearanceBased;
_blendingMode = CPVisualEffectBlendingModeWithinWindow;
_state = CPVisualEffectStateFollowsWindowActiveState;
_appearance = [CPAppearance appearanceNamed:CPAppearanceNameVibrantDark];
}
return self;
}
#pragma mark -
#pragma mark CPVisualEffectView API
/*! Sets the appearance of the CPVisualEffectView.
Only CPAppearance named CPAppearanceNameVibrantDark or CPAppearanceNameVibrantLight are valid
@param anAppearance the CPAppearance.
*/
- (void)setAppearance:(CPAppearance)anAppearance
{
if (![self _validAppearance:anAppearance])
[CPException raise:CPInvalidArgumentException reason:"Appearance can only be CPAppearanceNameVibrantDark or CPAppearanceNameVibrantLight in CPVisualEffectView, but is " + anAppearance];
[super setAppearance:anAppearance];
[self setNeedsLayout:YES];
}
/*! Sets the received effect state.
Possible values:
<pre>
CPVisualEffectStateFollowsWindowActiveState (default)
CPVisualEffectStateActive
CPVisualEffectStateInactive
</pre>
*/
- (void)setState:(CPVisualEffectState)aState
{
if (_state == aState)
return;
[self willChangeValueForKey:"state"];
_state = aState;
[self didChangeValueForKey:"state"];
[self setNeedsLayout:YES];
}
#pragma mark -
#pragma mark Utilities
- (void)_setEffectEnabled:(BOOL)shouldEnable
{
var dark = [[self appearance] isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]],
prop = CPBrowserStyleProperty("backdrop-filter"),
color = (dark ? [CPColor colorWithHexString:@"1e1e1e"] : [CPColor whiteColor]),
finalColor = shouldEnable ? [color colorWithAlphaComponent:0.6] : color;
[self setBackgroundColor:finalColor];
#if PLATFORM(DOM)
self._DOMElement.style[prop] = shouldEnable ? "blur(30px)" : nil;
#endif
}
- (void)layoutSubviews
{
switch (_state)
{
case CPVisualEffectStateFollowsWindowActiveState:
[self _setEffectEnabled:[self hasThemeState:CPThemeStateKeyWindow]];
break;
case CPVisualEffectStateActive:
[self _setEffectEnabled:YES];
break;
case CPVisualEffectStateInactive:
[self _setEffectEnabled:NO];
break;
}
}
- (BOOL)_validAppearance:(CPAppearance)anAppearance
{
return [anAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]] || [anAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantLight]];
}
#pragma mark -
#pragma mark CPCoding
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super initWithCoder:aCoder])
{
_blendingMode = [aCoder decodeIntForKey:@"_blendingMode"] || CPVisualEffectBlendingModeWithinWindow;
_maskImage = [aCoder decodeObjectForKey:@"_maskImage"];
_material = [aCoder decodeIntForKey:@"_material"] || CPVisualEffectMaterialAppearanceBased;
_state = [aCoder decodeIntForKey:@"_state"] || CPVisualEffectStateFollowsWindowActiveState;
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_maskImage forKey:@"_maskImage"];
[aCoder encodeInt:_blendingMode forKey:@"_blendingMode"];
[aCoder encodeInt:_material forKey:@"_material"];
[aCoder encodeInt:_state forKey:@"_state"];
}
@end
+497 -56
View File
@@ -36,6 +36,7 @@
@import "CPResponder.j"
@import "CPScreen.j"
@import "CPText.j"
@import "CPTrackingArea.j"
@import "CPView.j"
@import "CPWindow_Constants.j"
@import "_CPBorderlessBridgeWindowView.j"
@@ -55,6 +56,7 @@
@class _CPWindowFrameAnimation
@global CPApp
@global _CPPlatformWindowWillCloseNotification
@typedef _CPWindowFullPlatformWindowSession
@@ -63,22 +65,28 @@
@optional
- (BOOL)windowShouldClose:(CPWindow)aWindow;
- (CGSize)windowWillResize:(CPWindow)sender toSize:(CGSize)aSize;
- (CPUndoManager)windowWillReturnUndoManager:(CPWindow)window;
- (void)windowDidBecomeKey:(CPNotification)aNotification;
- (void)windowDidBecomeMain:(CPNotification)aNotification;
- (void)windowDidDeminiaturize:(CPNotification)notification;
- (void)windowDidEndSheet:(CPNotification)aNotification;
- (void)windowDidMiniaturize:(CPNotification)notification;
- (void)windowDidMove:(CPNotification)aNotification;
- (void)windowDidResignKey:(CPNotification)aNotification;
- (void)windowDidResignMain:(CPNotification)aNotification;
- (void)windowDidResize:(CPNotification)aNotification;
- (void)windowWillMiniaturize:(CPNotification)notification;
- (void)windowWillBeginSheet:(CPNotification)aNotification;
- (void)windowWillClose:(CPWindow)aWindow;
@end
var CPWindowDelegate_windowShouldClose_ = 1 << 1
var CPWindowDelegate_windowShouldClose_ = 1 << 1,
CPWindowDelegate_windowWillReturnUndoManager_ = 1 << 2,
CPWindowDelegate_windowWillClose_ = 1 << 3;
CPWindowDelegate_windowWillClose_ = 1 << 3,
CPWindowDelegate_windowWillResize_toSize_ = 1 << 4;
var CPWindowSaveImage = nil,
@@ -191,6 +199,10 @@ var CPWindowActionMessageKeys = [
CPView _toolbarView;
CPArray _mouseEnteredStack;
CPArray _cursorUpdateStack;
CPArray _trackingAreaViews;
id _activeCursorTrackingArea;
CPArray _queuedTrackingEvents;
CPView _leftMouseDownView;
CPView _rightMouseDownView;
@@ -328,6 +340,12 @@ CPTexturedBackgroundWindowMask
[self setLevel:CPNormalWindowLevel];
_trackingAreaViews = [];
_mouseEnteredStack = [];
_cursorUpdateStack = [];
_queuedTrackingEvents = [];
_activeCursorTrackingArea = nil;
// Create our border view which is the actual root of our view hierarchy.
_windowView = [[windowViewClass alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame)) styleMask:aStyleMask];
@@ -550,6 +568,11 @@ CPTexturedBackgroundWindowMask
}
}
- (CPView)_windowView
{
return _windowView;
}
/*!
Sets the receiver as a full platform window. If you pass YES the CPWindow instance will fill the entire browser content area,
otherwise the CPWindow will be a window inside of your browser window which the user can drag around, and resize (if you allow).
@@ -575,6 +598,9 @@ CPTexturedBackgroundWindowMask
var fullPlatformWindowViewClass = [[self class] _windowViewClassForFullPlatformWindowStyleMask:_styleMask],
windowView = [[fullPlatformWindowViewClass alloc] initWithFrame:CGRectMakeZero() styleMask:_styleMask];
if (_platformWindow != [CPPlatformWindow primaryPlatformWindow] && [_platformWindow _hasInitializeInstanceWithWindow])
[_platformWindow setContentRect:[self frame]];
[self _setWindowView:windowView];
[self setLevel:CPBackgroundWindowLevel];
@@ -744,6 +770,9 @@ CPTexturedBackgroundWindowMask
size.width = newSize.width;
size.height = newSize.height;
if (!_isAnimating)
size = [self _sendDelegateWindowWillResizeToSize:size];
[_windowView setFrameSize:size];
if (_hasShadow)
@@ -759,6 +788,9 @@ CPTexturedBackgroundWindowMask
if (originMoved)
[self _moveChildWindows:delta];
}
if ([_platformWindow _canUpdateContentRect] && _isFullPlatformWindow && _platformWindow != [CPPlatformWindow primaryPlatformWindow])
[_platformWindow setContentRect:aFrame];
}
/*
@@ -909,6 +941,10 @@ CPTexturedBackgroundWindowMask
{
#if PLATFORM(DOM)
if (!_isVisible)
[_platformWindow _setShouldUpdateContentRect:NO];
// -dw- if a sheet is clicked, the parent window should come up too
if (_isSheet)
[_parentView orderFront:self];
@@ -932,6 +968,8 @@ CPTexturedBackgroundWindowMask
if (!CPApp._mainWindow)
[self makeMainWindow];
[_platformWindow _setShouldUpdateContentRect:YES];
}
/*
@@ -947,6 +985,11 @@ CPTexturedBackgroundWindowMask
*/
- (void)_windowWillBeAddedToTheDOM
{
[[CPNotificationCenter defaultCenter] addObserver:self
selector:@selector(_didReceivePlatformWindowWillCloseNotification:)
name:_CPPlatformWindowWillCloseNotification
object:_platformWindow];
[[self contentView] _addObservers];
}
@@ -955,7 +998,10 @@ CPTexturedBackgroundWindowMask
*/
- (void)_windowWillBeRemovedFromTheDOM
{
[[CPNotificationCenter defaultCenter] removeObserver:self name:_CPPlatformWindowWillCloseNotification object:nil];
[[self contentView] _removeObservers];
_hasBecomeKeyWindow = NO;
}
@@ -1002,6 +1048,9 @@ CPTexturedBackgroundWindowMask
if ([self _sharesChromeWithPlatformWindow])
[_platformWindow orderOut:self];
if (_isFullPlatformWindow && _platformWindow != [CPPlatformWindow primaryPlatformWindow])
[_platformWindow orderOut:self];
[_platformWindow order:CPWindowOut window:self relativeTo:nil];
#endif
@@ -1407,6 +1456,9 @@ CPTexturedBackgroundWindowMask
[defaultCenter removeObserver:_delegate name:CPWindowDidResizeNotification object:self];
[defaultCenter removeObserver:_delegate name:CPWindowWillBeginSheetNotification object:self];
[defaultCenter removeObserver:_delegate name:CPWindowDidEndSheetNotification object:self];
[defaultCenter removeObserver:_delegate name:CPWindowDidMiniaturizeNotification object:self];
[defaultCenter removeObserver:_delegate name:CPWindowDidDeminiaturizeNotification object:self];
[defaultCenter removeObserver:_delegate name:CPWindowWillMiniaturizeNotification object:self];
_delegate = aDelegate;
_implementedDelegateMethods = 0;
@@ -1420,6 +1472,9 @@ CPTexturedBackgroundWindowMask
if ([_delegate respondsToSelector:@selector(windowWillClose:)])
_implementedDelegateMethods |= CPWindowDelegate_windowWillClose_;
if ([_delegate respondsToSelector:@selector(windowWillResize:toSize:)])
_implementedDelegateMethods |= CPWindowDelegate_windowWillResize_toSize_;
if ([_delegate respondsToSelector:@selector(windowDidResignKey:)])
[defaultCenter
addObserver:_delegate
@@ -1475,6 +1530,27 @@ CPTexturedBackgroundWindowMask
selector:@selector(windowDidEndSheet:)
name:CPWindowDidEndSheetNotification
object:self];
if ([_delegate respondsToSelector:@selector(windowDidMiniaturize:)])
[defaultCenter
addObserver:_delegate
selector:@selector(windowDidMiniaturize:)
name:CPWindowDidMiniaturizeNotification
object:self];
if ([_delegate respondsToSelector:@selector(windowWillMiniaturize:)])
[defaultCenter
addObserver:_delegate
selector:@selector(windowWillMiniaturize:)
name:CPWindowWillMiniaturizeNotification
object:self];
if ([_delegate respondsToSelector:@selector(windowDidDeminiaturize:)])
[defaultCenter
addObserver:_delegate
selector:@selector(windowDidDeminiaturize:)
name:CPWindowDidDeminiaturizeNotification
object:self];
}
/*!
@@ -1787,6 +1863,9 @@ CPTexturedBackgroundWindowMask
switch (type)
{
case CPAppKitDefined:
return [CPApp activateIgnoringOtherApps:YES];
case CPFlagsChanged:
return [[self firstResponder] flagsChanged:anEvent];
@@ -1860,6 +1939,9 @@ CPTexturedBackgroundWindowMask
_leftMouseDownView = nil;
// If mouseUp ends a drag operation, send delayed events for tracking views under the mouse, then flush delayed events
[self _flushTrackingEventQueueForMouseAt:point];
return;
case CPLeftMouseDown:
@@ -1895,6 +1977,11 @@ CPTexturedBackgroundWindowMask
case CPLeftMouseDragged:
case CPRightMouseDragged:
// First, we search for any tracking area requesting CPTrackingEnabledDuringMouseDrag.
// At the same time, we update the entered stack.
[self _handleTrackingAreaEvent:anEvent];
// Normal mouseDragged workflow
if (!_leftMouseDownView)
return [[_windowView hitTest:point] mouseDragged:anEvent];
@@ -1913,61 +2000,12 @@ CPTexturedBackgroundWindowMask
return [_leftMouseDownView performSelector:selector withObject:anEvent];
case CPMouseMoved:
[_windowView setCursorForLocation:point resizing:NO];
// Ignore mouse moves for parents of sheets
if (!_acceptsMouseMovedEvents || sheet)
return;
if (!_mouseEnteredStack)
_mouseEnteredStack = [];
var hitTestView = [_windowView hitTest:point];
if ([_mouseEnteredStack count] && [_mouseEnteredStack lastObject] === hitTestView)
return [hitTestView mouseMoved:anEvent];
var view = hitTestView,
mouseEnteredStack = [];
while (view)
{
mouseEnteredStack.unshift(view);
view = [view superview];
}
var deviation = MIN(_mouseEnteredStack.length, mouseEnteredStack.length);
while (deviation--)
if (_mouseEnteredStack[deviation] === mouseEnteredStack[deviation])
break;
var index = deviation + 1,
count = _mouseEnteredStack.length;
if (index < count)
{
var event = [CPEvent mouseEventWithType:CPMouseExited location:point modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0];
for (; index < count; ++index)
[_mouseEnteredStack[index] mouseExited:event];
}
index = deviation + 1;
count = mouseEnteredStack.length;
if (index < count)
{
var event = [CPEvent mouseEventWithType:CPMouseEntered location:point modifierFlags:[anEvent modifierFlags] timestamp:[anEvent timestamp] windowNumber:_windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0];
for (; index < count; ++index)
[mouseEnteredStack[index] mouseEntered:event];
}
_mouseEnteredStack = mouseEnteredStack;
[hitTestView mouseMoved:anEvent];
[self _handleTrackingAreaEvent:anEvent];
}
}
@@ -2001,6 +2039,7 @@ CPTexturedBackgroundWindowMask
[self _setupFirstResponder];
_hasBecomeKeyWindow = YES;
_platformWindow._currentKeyWindow = self;
[_windowView noteKeyWindowStateChanged];
[_contentView _notifyWindowDidBecomeKey];
@@ -2071,6 +2110,7 @@ CPTexturedBackgroundWindowMask
if (CPApp._keyWindow === self)
CPApp._keyWindow = nil;
_platformWindow._currentKeyWindow = nil;
[_windowView noteKeyWindowStateChanged];
[_contentView _notifyWindowDidResignKey];
@@ -2454,6 +2494,7 @@ CPTexturedBackgroundWindowMask
- (void)becomeMainWindow
{
CPApp._mainWindow = self;
_platformWindow._currentMainWindow = self;
[self _synchronizeSaveMenuWithDocumentSaving];
@@ -2476,6 +2517,7 @@ CPTexturedBackgroundWindowMask
if (CPApp._mainWindow === self)
CPApp._mainWindow = nil;
_platformWindow._currentMainWindow = nil;
[_windowView noteMainWindowStateChanged];
}
@@ -2714,7 +2756,7 @@ CPTexturedBackgroundWindowMask
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPWindowDidEndSheetNotification object:self];
var sheet = _sheetContext[@"nextSheet"],
modalDelegate =_sheetContext[@"nextModalDelegate"],
modalDelegate = _sheetContext[@"nextModalDelegate"],
endSelector = _sheetContext[@"nextEndSelector"],
contextInfo = _sheetContext[@"nextContextInfo"];
@@ -2790,7 +2832,7 @@ CPTexturedBackgroundWindowMask
if (delegate && endSelector)
{
if (_sheetContext["isAttached"])
objj_msgSend(delegate, endSelector, _sheetContext["sheet"], _sheetContext["returnCode"],
delegate.isa.objj_msgSend3(delegate, endSelector, _sheetContext["sheet"], _sheetContext["returnCode"],
_sheetContext["contextInfo"]);
else
_sheetContext["deferDidEndSelector"] = YES;
@@ -2854,7 +2896,8 @@ CPTexturedBackgroundWindowMask
_sheetContext = nil;
sheet._parentView = nil;
objj_msgSend(delegate, selector, sheet, returnCode, contextInfo);
if (delegate != null)
delegate.isa.objj_msgSend3(delegate, selector, sheet, returnCode, contextInfo);
}
else
{
@@ -3353,6 +3396,14 @@ CPTexturedBackgroundWindowMask
[super setValue:aValue forKey:aKey];
}
- (void)_didReceivePlatformWindowWillCloseNotification:(CPNotification)aNotification
{
if ([aNotification object] != _platformWindow)
return;
[self close];
}
@end
var keyViewComparator = function(lhs, rhs, context)
@@ -3443,6 +3494,18 @@ var keyViewComparator = function(lhs, rhs, context)
[_delegate windowWillClose:self];
}
/*!
@ignore
Call the delegate windowWillResize:toSize:
*/
- (CGSize)_sendDelegateWindowWillResizeToSize:(CGSize)aSize
{
if (!(_implementedDelegateMethods & CPWindowDelegate_windowWillResize_toSize_))
return aSize;
return [_delegate windowWillResize:self toSize:aSize];
}
@end
@@ -3756,6 +3819,384 @@ var interpolate = function(fromValue, toValue, progress)
@end
@implementation CPWindow (TrackingAreaAdditions)
- (void)_addTrackingAreaView:(CPView)aView
{
var trackingAreas = [aView trackingAreas];
for (var i = 0; i < trackingAreas.length; i++)
[self _addTrackingArea:trackingAreas[i]];
}
- (void)_removeTrackingAreaView:(CPView)aView
{
var trackingAreas = [aView trackingAreas];
for (var i = 0; i < trackingAreas.length; i++)
[self _removeTrackingArea:trackingAreas[i]];
}
- (void)_addTrackingArea:(CPTrackingArea)trackingArea
{
var trackingAreaView = [trackingArea view];
if (![_trackingAreaViews containsObjectIdenticalTo:trackingAreaView])
[_trackingAreaViews addObject:trackingAreaView];
// If CPTrackingAssumeInside option is set, put the tracking area in the _mouseEnteredStack
if ([trackingArea options] & CPTrackingAssumeInside)
[_mouseEnteredStack addObject:trackingArea];
}
- (void)_removeTrackingArea:(CPTrackingArea)trackingArea
{
// If mouse is in the tracking area, we remove it from the stack to avoid to fire a future mouseExited event
[_mouseEnteredStack removeObjectIdenticalTo:trackingArea];
var trackingAreaView = [trackingArea view];
[_trackingAreaViews removeObjectIdenticalTo:trackingAreaView];
}
- (void)_handleTrackingAreaEvent:(CPEvent)anEvent
{
var mouseEnteredStack = [],
cursorUpdateStack = [],
point = [anEvent locationInWindow],
dragging = ([anEvent type] !== CPMouseMoved);
// Handle mouse entering tracking areas (and calc mouseEnteredStack and cursorUpdateStack)
[self _handleMouseMovedAndEnteredEventsForEvent:anEvent atPoint:point dragging:dragging mouseEnteredStack:mouseEnteredStack cursorUpdateStack:cursorUpdateStack];
// Handle mouse exiting tracking areas
[self _handleMouseExitedEventsForEvent:anEvent atPoint:point dragging:dragging mouseEnteredStack:mouseEnteredStack];
// Cursor update
if (cursorUpdateStack.length > 0)
{
[self _handleCursorUpdateEventsForEvent:anEvent atPoint:point dragging:dragging cursorUpdateStack:cursorUpdateStack];
}
else if (!dragging)
{
// Here, we are outsite the window content view tracking area, so let _windowView set the cursor (resize cursor, ...)
[_windowView setCursorForLocation:point resizing:NO];
_activeCursorTrackingArea = nil;
}
// Prepare for next call
_mouseEnteredStack = mouseEnteredStack;
_cursorUpdateStack = cursorUpdateStack;
}
- (void)_handleMouseMovedAndEnteredEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging mouseEnteredStack:(CPArray)mouseEnteredStack cursorUpdateStack:(CPArray)cursorUpdateStack
{
var isKeyWindow = [self isKeyWindow];
for (var i = 0; i < _trackingAreaViews.length; i++)
{
var aView = _trackingAreaViews[i],
trackingAreas = [aView trackingAreas];
if ([aView isHidden])
continue;
for (var j = 0; j < trackingAreas.length; j++)
{
var aTrackingArea = trackingAreas[j],
trackingOptions = [aTrackingArea options],
trackingImplementedMethods = [aTrackingArea implementedOwnerMethods];
if (!(((trackingOptions & CPTrackingActiveAlways) ||
(trackingOptions & CPTrackingActiveInActiveApp) ||
((trackingOptions & CPTrackingActiveInKeyWindow) && isKeyWindow) ||
((trackingOptions & CPTrackingActiveWhenFirstResponder) && isKeyWindow && (_firstResponder === aView))) &&
(CGRectContainsPoint([aTrackingArea windowRect], point))))
{
continue;
}
[mouseEnteredStack addObject:aTrackingArea];
if ([_mouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
{
// Mouse was already in this rect so it's a mouseMoved
if (!dragging && (trackingOptions & CPTrackingMouseMoved) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseMoved))
[[aTrackingArea owner] mouseMoved:anEvent];
}
else if ((trackingOptions & CPTrackingMouseEnteredAndExited) && (trackingImplementedMethods & CPTrackingOwnerImplementsMouseEntered))
{
var mouseEnteredEvent = [CPEvent enterExitEventWithType:CPMouseEntered
location:point
modifierFlags:[anEvent modifierFlags]
timestamp:[anEvent timestamp]
windowNumber:_windowNumber
context:nil
eventNumber:-1
trackingArea:aTrackingArea];
if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag))
[self _queueTrackingEvent:mouseEnteredEvent];
else
[[aTrackingArea owner] mouseEntered:mouseEnteredEvent];
}
if ((trackingOptions & CPTrackingCursorUpdate) && (trackingImplementedMethods & CPTrackingOwnerImplementsCursorUpdate))
[cursorUpdateStack addObject:aTrackingArea];
}
}
}
- (void)_handleMouseExitedEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging mouseEnteredStack:(CPArray)mouseEnteredStack
{
// Search for exited views (were in _mouseEnteredStack but no more in mouseEnteredStack)
for (var i = 0; i < _mouseEnteredStack.length; i++)
{
var aTrackingArea = _mouseEnteredStack[i],
trackingOptions = [aTrackingArea options];
if ([mouseEnteredStack containsObjectIdenticalTo:aTrackingArea])
continue;
// Mouse is no more in this area so it's a mouseExited
if ((trackingOptions & CPTrackingMouseEnteredAndExited) && ([aTrackingArea implementedOwnerMethods] & CPTrackingOwnerImplementsMouseExited))
{
var mouseExitedEvent = [CPEvent enterExitEventWithType:CPMouseExited
location:point
modifierFlags:[anEvent modifierFlags]
timestamp:[anEvent timestamp]
windowNumber:_windowNumber
context:nil
eventNumber:-1
trackingArea:aTrackingArea];
if (dragging && !(trackingOptions & CPTrackingEnabledDuringMouseDrag))
[self _queueTrackingEvent:mouseExitedEvent];
else
[[aTrackingArea owner] mouseExited:mouseExitedEvent];
}
// If this is the active cursor area, we reset _cursorUpdateStack so a new active area will be computed
if (aTrackingArea === _activeCursorTrackingArea)
{
_cursorUpdateStack = [];
_activeCursorTrackingArea = nil;
}
}
}
- (void)_handleCursorUpdateEventsForEvent:(CPEvent)anEvent atPoint:(CGPoint)point dragging:(BOOL)dragging cursorUpdateStack:(CPArray)cursorUpdateStack
{
var overlappingTrackingAreas = [];
for (var i = 0; i < cursorUpdateStack.length; i++)
{
var aTrackingArea = cursorUpdateStack[i];
if ((![_cursorUpdateStack containsObjectIdenticalTo:aTrackingArea]) || (aTrackingArea === _activeCursorTrackingArea))
[overlappingTrackingAreas addObject:aTrackingArea];
}
var nbOverlappingTrackingAreas = overlappingTrackingAreas.length;
if (nbOverlappingTrackingAreas > 0)
{
var frontmostTrackingArea = overlappingTrackingAreas[0],
frontmostView = [frontmostTrackingArea view];
for (var i = 1; i < nbOverlappingTrackingAreas; i++)
{
var aTrackingArea = overlappingTrackingAreas[i],
aView = [aTrackingArea view];
// First, if aView is _windowView, skip to next overlapping tracking area
// as _windowView can't be the frontmost view if there's multiple overlapping tracking areas.
if (aView === _windowView)
continue;
// Then, if frontmostView is _windowView, aView must become frontmostView
if (frontmostView === _windowView)
{
frontmostTrackingArea = aTrackingArea;
frontmostView = aView;
continue;
}
// Next verify if aView is a subview of frontmostView
// If so, it's our new frontmost view
var searchingView = aView;
while ((searchingView !== _contentView) && ([searchingView superview] !== frontmostView))
searchingView = [searchingView superview];
if (searchingView !== _contentView)
{
frontmostTrackingArea = aTrackingArea;
frontmostView = aView;
continue;
}
// aView is not a subview of frontmostView
// Search in view hierarchy which one will be over the other
// (this is done by comparing their draw order)
var firstView = frontmostView,
firstSuperview = [firstView superview];
while (firstView !== _contentView)
{
var secondView = aView,
secondSuperview = [secondView superview];
while ((secondSuperview !== _contentView) && (firstSuperview !== secondSuperview))
{
secondView = secondSuperview;
secondSuperview = [secondView superview];
}
if (firstSuperview === secondSuperview)
break;
firstView = firstSuperview;
firstSuperview = [firstView superview];
}
if (firstSuperview !== secondSuperview)
[CPException raise:CPInternalInconsistencyException reason:"Problem with view hierarchy"];
var firstSuperviewSubviews = [firstSuperview subviews],
firstViewIndex = [firstSuperviewSubviews indexOfObject:firstView],
secondViewIndex = [firstSuperviewSubviews indexOfObject:secondView];
if (secondViewIndex > firstViewIndex)
{
frontmostTrackingArea = aTrackingArea;
frontmostView = aView;
}
}
if (frontmostTrackingArea !== _activeCursorTrackingArea)
{
var cursorUpdateEvent = [CPEvent enterExitEventWithType:CPCursorUpdate
location:point
modifierFlags:[anEvent modifierFlags]
timestamp:[anEvent timestamp]
windowNumber:_windowNumber
context:nil
eventNumber:-1
trackingArea:frontmostTrackingArea];
if (dragging)
[self _queueTrackingEvent:cursorUpdateEvent];
else
[[frontmostTrackingArea owner] cursorUpdate:cursorUpdateEvent];
_activeCursorTrackingArea = frontmostTrackingArea;
}
}
}
- (void)_queueTrackingEvent:(CPEvent)anEvent
{
// This will put a tracking event in the _queuedTrackingEvents queue.
//
// We optimize this queue with this policy :
// - if mouseEntered, search if queue contains a previous mouseExited for the same tracking area. If so, discard both.
// - if mouseExited, search if queue contains a previous mouseEntered for the same tracking area. If so, discard both.
//
// This is not Cocoa way of doing as it would send every event.
// But final result should be the same.
var eventType = [anEvent type],
trackingArea = [anEvent trackingArea];
switch ([anEvent type])
{
case CPMouseEntered:
for (var i = 0; i < _queuedTrackingEvents.length; i++)
{
var queuedEvent = _queuedTrackingEvents[i];
if (([queuedEvent trackingArea] === trackingArea) && ([queuedEvent type] === CPMouseExited))
{
[_queuedTrackingEvents removeObjectAtIndex:i];
return;
}
}
[_queuedTrackingEvents addObject:anEvent];
break;
case CPMouseExited:
for (var i = 0; i < _queuedTrackingEvents.length; i++)
{
var queuedEvent = _queuedTrackingEvents[i];
if (([queuedEvent trackingArea] === trackingArea) && ([queuedEvent type] === CPMouseEntered))
{
[_queuedTrackingEvents removeObjectAtIndex:i];
return;
}
}
[_queuedTrackingEvents addObject:anEvent];
break;
case CPCursorUpdate:
[_queuedTrackingEvents addObject:anEvent];
break;
}
}
- (void)_flushTrackingEventQueueForMouseAt:(CGPoint)point
{
for (var i = 0; i < _queuedTrackingEvents.length; i++)
{
var queuedEvent = _queuedTrackingEvents[i],
trackingArea = [queuedEvent trackingArea],
trackingOwner = [trackingArea owner];
switch ([queuedEvent type])
{
case CPMouseEntered:
[trackingOwner mouseEntered:queuedEvent];
break;
case CPMouseExited:
[trackingOwner mouseExited:queuedEvent];
break;
case CPCursorUpdate:
[trackingOwner updateTrackingAreas];
if (CGRectContainsPoint([trackingArea windowRect], point))
[trackingOwner cursorUpdate:queuedEvent];
break;
}
}
_queuedTrackingEvents = [];
}
@end
function _CPWindowFullPlatformWindowSessionMake(aWindowView, aContentRect, hasShadow, aLevel)
{
return { windowView:aWindowView, contentRect:aContentRect, hasShadow:hasShadow, level:aLevel };
-1
View File
@@ -27,7 +27,6 @@
@implementation _CPHUDWindowView : _CPTitleableWindowView
{
CPView _toolbarView;
CPButton _closeButton;
}
+2 -4
View File
@@ -26,7 +26,6 @@
@import "CGGradient.j"
@import "_CPWindowView.j"
@global CPPopoverAppearanceMinimal
var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
@@ -39,7 +38,6 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
{
float _arrowOffsetX @accessors(property=arrowOffsetX);
float _arrowOffsetY @accessors(property=arrowOffsetY);
int _appearance @accessors(property=appearance);
unsigned _preferredEdge @accessors(property=preferredEdge);
CGSize _cursorSize;
@@ -123,7 +121,7 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
{
_arrowOffsetX = 0.0;
_arrowOffsetY = 0.0;
_appearance = CPPopoverAppearanceMinimal;
_appearance = [CPAppearance appearanceNamed:CPAppearanceNameVibrantLight];
_cursorSize = CGSizeMakeCopy(_CPPopoverWindowViewDefaultCursorSize);
}
@@ -168,7 +166,7 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
gradient,
frame = [self bounds];
if (_appearance == CPPopoverAppearanceMinimal)
if ([_appearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantLight]])
{
gradient = [self valueForThemeAttribute:@"background-gradient"];
strokeColor = [self valueForThemeAttribute:@"stroke-color"];
-1
View File
@@ -94,7 +94,6 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
_CPTexturedWindowHeadView _headView;
CPView _dividerView;
CPView _bodyView;
CPView _toolbarView;
CPButton _closeButton;
CPButton _minimizeButton;
+17
View File
@@ -353,7 +353,9 @@ _CPWindowViewResizeSlop = 3;
if ([theWindow isFullPlatformWindow] ||
!(_styleMask & CPResizableWindowMask) ||
(CPWindowResizeStyle !== CPWindowResizeStyleModern))
{
return;
}
var globalPoint = [theWindow convertBaseToGlobal:aPoint],
resizeRegion = isResizing ? _resizeRegion : [self resizeRegionForPoint:globalPoint],
@@ -969,3 +971,18 @@ _CPWindowViewResizeSlop = 3;
}
@end
@implementation _CPWindowView (TrackingAreaAdditions)
- (void)updateTrackingAreas
{
[self removeAllTrackingAreas];
[self addTrackingArea:[[CPTrackingArea alloc] initWithRect:[self contentRectForFrameRect:[self frame]]
options:CPTrackingCursorUpdate | CPTrackingActiveInActiveApp
owner:self
userInfo:nil]];
}
@end
+17 -17
View File
@@ -32,6 +32,7 @@
@import "_CPCibObjectData.j"
@import "_CPCibProxyObject.j"
@import "_CPCibWindowTemplate.j"
@import "_CPLocalizableString.j"
CPCibOwner = @"CPCibOwner";
CPCibTopLevelObjects = @"CPCibTopLevelObjects";
@@ -46,10 +47,11 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
@implementation CPCib : CPObject
{
CPData _data;
CPBundle _bundle;
BOOL _awakenCustomResources;
BOOL _awakenCustomResources @accessors(property=_awakenCustomResources);
CPBundle _bundle;
CPData _data;
CPString _cibName;
id _loadDelegate;
}
@@ -59,6 +61,8 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
if (self)
{
_cibName = [aURL lastPathComponent];
_data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil];
if (!_data)
@@ -76,6 +80,8 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
if (self)
{
_cibName = [aURL lastPathComponent];
[CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:aURL] delegate:self];
_awakenCustomResources = YES;
@@ -92,7 +98,9 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
aName = [aName stringByAppendingString:@".cib"];
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName]];
var bundle = aBundle || [CPBundle mainBundle];
self = [self initWithContentsOfURL:[bundle _cibPathForResource:aName]];
if (self)
_bundle = aBundle;
@@ -106,7 +114,9 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
aName = [aName stringByAppendingString:@".cib"];
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName] loadDelegate:aLoadDelegate];
var bundle = aBundle || [CPBundle mainBundle];
self = [self initWithContentsOfURL:[bundle _cibPathForResource:aName] loadDelegate:aLoadDelegate];
if (self)
_bundle = aBundle;
@@ -114,16 +124,6 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
return self;
}
- (void)_setAwakenCustomResources:(BOOL)shouldAwakenCustomResources
{
_awakenCustomResources = shouldAwakenCustomResources;
}
- (BOOL)_awakenCustomResources
{
return _awakenCustomResources;
}
- (BOOL)instantiateCibWithExternalNameTable:(CPDictionary)anExternalNameTable
{
var bundle = _bundle,
@@ -132,7 +132,7 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
if (!bundle && owner)
bundle = [CPBundle bundleForClass:[owner class]];
var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data bundle:bundle awakenCustomResources:_awakenCustomResources],
var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data bundle:bundle awakenCustomResources:_awakenCustomResources cibName:_cibName],
replacementClasses = [anExternalNameTable objectForKey:CPCibReplacementClasses];
if (replacementClasses)
@@ -226,4 +226,4 @@ var CPCibDataFileKey = @"CPCibDataFileKey",
[aCoder encodeObject:[_data base64] forKey:CPCibDataFileKey];
}
@end
@end
-1
View File
@@ -24,7 +24,6 @@
@implementation CPCibHelpConnector : CPCibConnector
{
id _destination;
id _file;
id _marker;
}
+31 -17
View File
@@ -26,7 +26,11 @@
@import "CPCib.j"
var CPCibOwner = @"CPCibOwner";
var CPCibOwner = @"CPCibOwner",
CPBundleDefaultLanguage = @"CPBundleDefaultLanguage",
CPBundleTypeOfLocalization = @"CPBundleTypeOfLocalization",
CPBundleBaseLocalizationType = @"CPBundleBaseLocalizationType",
CPBundleInterfaceBuilderLocalizationType = @"CPBundleInterfaceBuilderLocalizationType";
@implementation CPObject (CPCibLoading)
@@ -45,14 +49,7 @@ var CPCibOwner = @"CPCibOwner";
+ (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner
{
if (![aName hasSuffix:@".cib"])
aName = [aName stringByAppendingString:@".cib"];
// Path is based solely on anOwner:
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
path = [bundle pathForResource:aName];
return [self loadCibFile:path externalNameTable:@{ CPCibOwner: anOwner }];
return [self loadCibFile:[self _cibPathForName:aName withOwner:anOwner] externalNameTable:@{ CPCibOwner: anOwner }];
}
- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable
@@ -71,14 +68,7 @@ var CPCibOwner = @"CPCibOwner";
+ (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate
{
if (![aName hasSuffix:@".cib"])
aName = [aName stringByAppendingString:@".cib"];
// Path is based solely on anOwner:
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
path = [bundle pathForResource:aName];
return [self loadCibFile:path externalNameTable:@{ CPCibOwner: anOwner } loadDelegate:aDelegate];
return [self loadCibFile:[self _cibPathForName:aName withOwner:anOwner] externalNameTable:@{ CPCibOwner: anOwner } loadDelegate:aDelegate];
}
- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate
@@ -91,6 +81,30 @@ var CPCibOwner = @"CPCibOwner";
externalNameTable:aNameTable]]);
}
- (CPString)_cibPathForResource:(CPString)aName
{
var defaultBundleLanguage = [self objectForInfoDictionaryKey:CPBundleDefaultLanguage],
typeOfLocalization = [self objectForInfoDictionaryKey:CPBundleTypeOfLocalization];
if (defaultBundleLanguage && (!typeOfLocalization || typeOfLocalization == CPBundleBaseLocalizationType))
aName = @"Base.lproj/" + aName;
else if (defaultBundleLanguage && typeOfLocalization == CPBundleInterfaceBuilderLocalizationType)
aName = _bundle.loadedLanguage() + ".lproj/" + aName;
return [self pathForResource:aName];
}
+ (CPString)_cibPathForName:(CPString)aName withOwner:(id)anOwner
{
if (![aName hasSuffix:@".cib"])
aName = [aName stringByAppendingString:@".cib"];
// Path is based solely on anOwner:
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle];
return [bundle _cibPathForResource:aName];
}
@end
@implementation _CPCibLoadDelegate : CPObject
+11 -19
View File
@@ -21,16 +21,22 @@
*/
@import <Foundation/CPKeyedUnarchiver.j>
@import <Foundation/CPBundle.j>
@implementation _CPCibKeyedUnarchiver : CPKeyedUnarchiver
{
CPBundle _bundle;
BOOL _awakenCustomResources;
CPDictionary _externalObjectsForProxyIdentifiers;
BOOL _awakenCustomResources @accessors(getter=awakenCustomResources);
CPBundle _bundle @accessors(getter=bundle);
CPDictionary _externalObjectsForProxyIdentifiers @accessors(setter=setExternalObjectsForProxyIdentifiers:);
CPString _cibName @accessors(getter=cibName);
}
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle awakenCustomResources:(BOOL)shouldAwakenCustomResources
{
return [self initForReadingWithData:data bundle:aBundle awakenCustomResources:shouldAwakenCustomResources cibName:@""];
}
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle awakenCustomResources:(BOOL)shouldAwakenCustomResources cibName:(CPString)aCibName
{
self = [super initForReadingWithData:data];
@@ -38,6 +44,7 @@
{
_bundle = aBundle;
_awakenCustomResources = shouldAwakenCustomResources;
_cibName = aCibName;
[self setDelegate:self];
}
@@ -45,21 +52,6 @@
return self;
}
- (CPBundle)bundle
{
return _bundle;
}
- (BOOL)awakenCustomResources
{
return _awakenCustomResources;
}
- (void)setExternalObjectsForProxyIdentifiers:(CPDictionary)externalObjectsForProxyIdentifiers
{
_externalObjectsForProxyIdentifiers = externalObjectsForProxyIdentifiers;
}
- (id)externalObjectForProxyIdentifier:(CPString)anIdentifier
{
return [_externalObjectsForProxyIdentifiers objectForKey:anIdentifier];
+62
View File
@@ -0,0 +1,62 @@
/*
* _CPLocalizableString.j
* AppKit
*
* Created by Alexandre Wilhelm.
* Copyright 2015, Cappuccino Project
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPString.j>
@import <Foundation/CPCharacterSet.j>
@implementation _CPLocalizableString : CPObject
{
CPString _dev @accessors(property=dev);
CPString _key @accessors(property=key);
CPString _value @accessors(property=value);
}
@end
var CPLocalizableStringDev = @"CPLocalizableStringDev",
CPLocalizableStringKey = @"CPLocalizableStringKey",
CPLocalizableStringValue = @"CPLocalizableStringValue";
@implementation _CPLocalizableString (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
[self init];
_dev = [aCoder decodeObjectForKey:CPLocalizableStringDev];
_key = [aCoder decodeObjectForKey:CPLocalizableStringKey];
_value = [aCoder decodeObjectForKey:CPLocalizableStringValue];
var tableName = [[aCoder cibName] stringByTrimmingCharactersInSet:[CPCharacterSet characterSetWithCharactersInString:@".cib"]];
return [[aCoder bundle] localizedStringForKey:_key value:_value table:tableName];
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_dev forKey:CPLocalizableStringDev];
[aCoder encodeObject:_key forKey:CPLocalizableStringKey];
[aCoder encodeObject:_value forKey:CPLocalizableStringValue];
}
@end
+4 -4
View File
@@ -27,7 +27,8 @@
@import "CGContext.j"
@import "CGGeometry.j"
@import "CPColor.j"
@import "CPView.j"
#define DOM(aLayer) aLayer._DOMElement
@@ -494,11 +495,11 @@ var CALayerRegisteredRunLoopUpdates = nil;
*/
- (void)display
{
#if PLATFORM(DOM)
if (!_context)
{
_context = CGBitmapGraphicsContextCreate();
#if PLATFORM(DOM)
_DOMContentsElement = _context.DOMElement;
_DOMContentsElement.style.zIndex = -100;
@@ -516,7 +517,6 @@ var CALayerRegisteredRunLoopUpdates = nil;
_DOMContentsElement.style.height = ROUND(CGRectGetHeight(_backingStoreFrame)) + "px";
_DOMElement.appendChild(_DOMContentsElement);
#endif
}
if (USE_BUFFER)
@@ -534,6 +534,7 @@ var CALayerRegisteredRunLoopUpdates = nil;
[self drawInContext:CABackingStoreGetContext(_contents)];
}
#endif
[self composite];
}
@@ -796,7 +797,6 @@ if (_DOMContentsElement && aLayer._zPosition > _DOMContentsElement.style.zIndex)
if (mask & CALayerDisplayUpdateMask)
[layer display];
else if (mask & CALayerFrameSizeUpdateMask || mask & CALayerCompositeUpdateMask)
[layer composite];
+3 -4
View File
@@ -1,8 +1,6 @@
require("../common.jake");
checkUlimit();
var framework = require("objective-j/jake").framework,
BundleTask = require("objective-j/jake").BundleTask;
@@ -35,8 +33,9 @@ appKitTask = framework ("AppKit", function(appKitTask)
return "--include \"" + aFilename + "\"";
}).join(" ");
if ($CONFIGURATION === "Release")
appKitTask.setCompilerFlags("-O " + INCLUDES);
if ($CONFIGURATION === "Release") {
appKitTask.setCompilerFlags("-O2 " + INCLUDES);
}
else
appKitTask.setCompilerFlags("-DDEBUG -g " + INCLUDES);
});
+38 -1
View File
@@ -43,6 +43,8 @@ var PrimaryPlatformWindow = NULL;
BOOL _hasShadow;
unsigned _shadowStyle;
CPString _title;
BOOL _shouldUpdateContentRect;
BOOL _hasInitializeInstanceWithWindow;
#if PLATFORM(DOM)
DOMWindow _DOMWindow;
@@ -73,6 +75,12 @@ var PrimaryPlatformWindow = NULL;
CPPlatformPasteboard _platformPasteboard;
CPString _overriddenEventType;
CPWindow _currentKeyWindow;
CPWindow _previousKeyWindow;
CPWindow _currentMainWindow;
CPWindow _previousMainWindow;
#endif
}
@@ -113,12 +121,25 @@ var PrimaryPlatformWindow = NULL;
_windowLayers = @{};
_charCodes = {};
_platformPasteboard = [CPPlatformPasteboard new];
#endif
}
return self;
}
- (id)initWithWindow:(CPWindow)aWindow
{
self = [self initWithContentRect:CGRectMakeCopy([aWindow frame])];
_hasInitializeInstanceWithWindow = YES;
[aWindow setPlatformWindow:self];
[aWindow setFullPlatformWindow:YES];
return self;
}
- (id)init
{
return [self initWithContentRect:CGRectMake(0.0, 0.0, 400.0, 500.0)];
@@ -194,7 +215,7 @@ var PrimaryPlatformWindow = NULL;
- (BOOL)isVisible
{
#if PLATFORM(DOM)
return _DOMWindow !== NULL;
return _DOMWindow !== NULL && _DOMWindow !== undefined;
#else
return NO;
#endif
@@ -284,6 +305,22 @@ var PrimaryPlatformWindow = NULL;
return _title;
}
- (BOOL)_canUpdateContentRect
{
// We onyl update the contentRect with the frame of the bridgeless window if we have initialized the platform with the method initWithWindow:
return _shouldUpdateContentRect && _hasInitializeInstanceWithWindow;
}
- (BOOL)_hasInitializeInstanceWithWindow
{
return _hasInitializeInstanceWithWindow;
}
- (void)_setShouldUpdateContentRect:(BOOL)aBoolean
{
_shouldUpdateContentRect = aBoolean;
}
@end
#if PLATFORM(BROWSER)
+6 -156
View File
@@ -20,34 +20,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define DOM_OPTIMIZATION 0
#define SetStyleOrigin 0
#define SetStyleLeftTop 0
#define SetStyleRightTop 1
#define SetStyleLeftBottom 2
#define SetStyleRightBottom 3
#define SetStyleSize 4
#define SetSize 5
#define AppendChild 6
#define InsertBefore 7
#define RemoveChild 8
#define CPDOMDisplayServerSetStyleOrigin(anInstruction, aDOMElement, aTransform, x, y)\
if (!aDOMElement.CPDOMDisplayContext)\
aDOMElement.CPDOMDisplayContext = [];\
var __index = aDOMElement.CPDOMDisplayContext[SetStyleOrigin];\
if (!(__index >= 0))\
{\
__index = aDOMElement.CPDOMDisplayContext[SetStyleOrigin] = CPDOMDisplayServerInstructionCount;\
CPDOMDisplayServerInstructionCount += 5;\
}\
CPDOMDisplayServerInstructions[__index] = anInstruction;\
CPDOMDisplayServerInstructions[__index + 1] = aDOMElement;\
CPDOMDisplayServerInstructions[__index + 2] = aTransform;\
CPDOMDisplayServerInstructions[__index + 3] = x;\
CPDOMDisplayServerInstructions[__index + 4] = y;
#if !DOM_OPTIMIZATION
#define CPDOMDisplayServerSetStyleLeftTop(aDOMElement, aTransform, aLeft, aTop) \
if (aTransform) var ____p = CGPointApplyAffineTransform(CGPointMake(aLeft, aTop), aTransform); \
else var ____p = CGPointMake(aLeft, aTop); \
@@ -83,136 +55,14 @@ aDOMElement.style.bottom = ROUND(____p.y) + "px";
#define CPDOMDisplayServerSetStyleBackgroundSize(aDOMElement, aWidth, aHeight)\
aDOMElement.style.backgroundSize = aWidth + ' ' + aHeight;
#define CPDOMDisplayServerAppendChild(aParentElement, aChildElement) aParentElement.appendChild(aChildElement)
#define CPDOMDisplayServerAppendChild(aParentElement, aChildElement) \
aParentElement.appendChild(aChildElement);
#define CPDOMDisplayServerInsertBefore(aParentElement, aChildElement, aBeforeElement) aParentElement.insertBefore(aChildElement, aBeforeElement)
#define CPDOMDisplayServerInsertBefore(aParentElement, aChildElement, aBeforeElement) \
aParentElement.insertBefore(aChildElement, aBeforeElement);
#define CPDOMDisplayServerRemoveChild(aParentElement, aChildElement) aParentElement.removeChild(aChildElement)
#define CPDOMDisplayServerRemoveChild(aParentElement, aChildElement) \
aParentElement.removeChild(aChildElement);
#define PREPARE_DOM_OPTIMIZATION()
#define EXECUTE_DOM_INSTRUCTIONS()
#else
#define CPDOMDisplayServerSetStyleLeftTop(aDOMElement, aTransform, aLeft, aTop) CPDOMDisplayServerSetStyleOrigin(SetStyleLeftTop, aDOMElement, aTransform, aLeft, aTop)
#define CPDOMDisplayServerSetStyleRightTop(aDOMElement, aTransform, aRight, aTop) CPDOMDisplayServerSetStyleOrigin(SetStyleRightTop, aDOMElement, aTransform, aRight, aTop)
#define CPDOMDisplayServerSetStyleLeftBottom(aDOMElement, aTransform, aLeft, aBottom) CPDOMDisplayServerSetStyleOrigin(SetStyleLeftBottom, aDOMElement, aTransform, aLeft, aBottom)
#define CPDOMDisplayServerSetStyleRightBottom(aDOMElement, aTransform, aRight, aBottom) CPDOMDisplayServerSetStyleOrigin(SetStyleRightBottom, aDOMElement, aTransform, aRight, aBottom)
#define CPDOMDisplayServerSetStyleSize(aDOMElement, aWidth, aHeight)\
if (!aDOMElement.CPDOMDisplayContext)\
aDOMElement.CPDOMDisplayContext = [];\
var __index = aDOMElement.CPDOMDisplayContext[SetStyleSize];\
if (!(__index >= 0))\
{\
__index = aDOMElement.CPDOMDisplayContext[SetStyleSize] = CPDOMDisplayServerInstructionCount;\
CPDOMDisplayServerInstructionCount += 4;\
}\
CPDOMDisplayServerInstructions[__index] = SetStyleSize;\
CPDOMDisplayServerInstructions[__index + 1] = aDOMElement;\
CPDOMDisplayServerInstructions[__index + 2] = aWidth;\
CPDOMDisplayServerInstructions[__index + 3] = aHeight;
#define CPDOMDisplayServerSetSize(aDOMElement, aWidth, aHeight)\
if (!aDOMElement.CPDOMDisplayContext)\
aDOMElement.CPDOMDisplayContext = [];\
var __index = aDOMElement.CPDOMDisplayContext[SetSize];\
if (!(__index >= 0))\
{\
__index = aDOMElement.CPDOMDisplayContext[SetSize] = CPDOMDisplayServerInstructionCount;\
CPDOMDisplayServerInstructionCount += 4;\
}\
CPDOMDisplayServerInstructions[__index] = SetSize;\
CPDOMDisplayServerInstructions[__index + 1] = aDOMElement;\
CPDOMDisplayServerInstructions[__index + 2] = aWidth;\
CPDOMDisplayServerInstructions[__index + 3] = aHeight;
#define CPDOMDisplayServerSetStyleBackgroundSize(aDOMElement, aWidth, aHeight)\
aDOMElement.style.backgroundSize = aWidth + ' ' + aHeight;
#define CPDOMDisplayServerAppendChild(aParentElement, aChildElement)\
if (aChildElement.CPDOMDisplayContext) aChildElement.CPDOMDisplayContext[SetStyleOrigin] = -1;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = AppendChild;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aParentElement;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aChildElement;
#define CPDOMDisplayServerInsertBefore(aParentElement, aChildElement, aBeforeElement)\
if (aChildElement.CPDOMDisplayContext) aChildElement.CPDOMDisplayContext[SetStyleOrigin] = -1;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = InsertBefore;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aParentElement;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aChildElement;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aBeforeElement;
#define CPDOMDisplayServerRemoveChild(aParentElement, aChildElement)\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = RemoveChild;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aParentElement;\
CPDOMDisplayServerInstructions[CPDOMDisplayServerInstructionCount++] = aChildElement;
#define PREPARE_DOM_OPTIMIZATION()\
CPDOMDisplayServerInstructions = [];\
CPDOMDisplayServerInstructionCount = 0;
#define EXECUTE_DOM_INSTRUCTIONS()\
var index = 0;\
while (index < CPDOMDisplayServerInstructionCount)\
{\
var instruction = CPDOMDisplayServerInstructions[index++];\
try{\
switch (instruction)\
{\
case SetStyleLeftTop:\
case SetStyleRightTop:\
case SetStyleLeftBottom:\
case SetStyleRightBottom: var element = CPDOMDisplayServerInstructions[index],\
style = element.style,\
x = (instruction == SetStyleLeftTop || instruction == SetStyleLeftBottom) ? "left" : "right",\
y = (instruction == SetStyleLeftTop || instruction == SetStyleRightTop) ? "top" : "bottom";\
CPDOMDisplayServerInstructions[index++] = nil;\
var transform = CPDOMDisplayServerInstructions[index++];\
if (transform)\
{\
var point = CGPointMake(CPDOMDisplayServerInstructions[index++], CPDOMDisplayServerInstructions[index++]),\
transformed = CGPointApplyAffineTransform(point, transform);\
style[x] = ROUND(transformed.x) + "px";\
style[y] = ROUND(transformed.y) + "px";\
}\
else\
{\
style[x] = ROUND(CPDOMDisplayServerInstructions[index++]) + "px";\
style[y] = ROUND(CPDOMDisplayServerInstructions[index++]) + "px";\
}\
element.CPDOMDisplayContext[SetStyleOrigin] = -1;\
break;\
case SetStyleSize: var element = CPDOMDisplayServerInstructions[index],\
style = element.style;\
CPDOMDisplayServerInstructions[index++] = nil;\
element.CPDOMDisplayContext[SetStyleSize] = -1;\
style.width = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++])) + "px";\
style.height = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++])) + "px";\
break;\
case SetSize: var element = CPDOMDisplayServerInstructions[index];\
CPDOMDisplayServerInstructions[index++] = nil;\
element.CPDOMDisplayContext[SetSize] = -1;\
element.width = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++]));\
element.height = MAX(0.0, ROUND(CPDOMDisplayServerInstructions[index++]));\
break;\
case AppendChild: CPDOMDisplayServerInstructions[index].appendChild(CPDOMDisplayServerInstructions[index + 1]);\
CPDOMDisplayServerInstructions[index++] = nil;\
CPDOMDisplayServerInstructions[index++] = nil;\
break;\
case InsertBefore: CPDOMDisplayServerInstructions[index].insertBefore(CPDOMDisplayServerInstructions[index + 1], CPDOMDisplayServerInstructions[index + 2]);\
CPDOMDisplayServerInstructions[index++] = nil;\
CPDOMDisplayServerInstructions[index++] = nil;\
CPDOMDisplayServerInstructions[index++] = nil;\
break;\
case RemoveChild: CPDOMDisplayServerInstructions[index].removeChild(CPDOMDisplayServerInstructions[index + 1]);\
CPDOMDisplayServerInstructions[index++] = nil;\
CPDOMDisplayServerInstructions[index++] = nil;\
break;\
}\
}\
catch(e) { CPLog("e " + e + " " + instruction); }\
}\
CPDOMDisplayServerInstructionCount = 0;
#endif
+25
View File
@@ -48,10 +48,21 @@ var screenNeedsInitialization = NO,
document.documentElement.style.overflow = "hidden";
if ([CPPlatform isBrowser])
{
// This differ from cocoa, where shouldTerminate is called in the method terminate of CPApp
// Cappuccino acts like this because we can not close a window openend by the user with a script (so not possible in terminate), and we can only prevent the action in the method onbeforeunload in js.
window.onbeforeunload = function()
{
if ([CPApp _sendDelegateApplicationShouldTerminate] != CPTerminateNow)
return [CPApp _sendDelegateApplicationShouldTerminateMessage];
};
window.onunload = function()
{
[self closeAllPlatformWindows];
[CPApp terminate:nil];
};
}
}
+ (BOOL)isBrowser
@@ -138,4 +149,18 @@ var screenNeedsInitialization = NO,
object:self];
}
+ (void)closeAllPlatformWindows
{
var platformWindows = [CPPlatformWindow visiblePlatformWindows],
primaryPlatformWindow = [CPPlatformWindow primaryPlatformWindow],
platformWindowEnumerator = [platformWindows objectEnumerator],
platformWindow = nil;
while ((platformWindow = [platformWindowEnumerator nextObject]) !== nil)
{
if (platformWindow != primaryPlatformWindow)
[platformWindow orderOut:self];
}
}
@end
+27
View File
@@ -129,6 +129,33 @@ var DOMFixedWidthSpanElement = nil,
DOMMetricsDivElement.appendChild(DOMMetricsImgElement);
}
+ (int)charPositionOfString:(CPString)aString withFont:(CPFont)aFont forPoint:(CGPoint)aPoint
{
if (!aString)
return 0;
var position = 0,
stringLength = aString.length,
currentString = "";
for (var i = 0; i < stringLength; i++)
{
var lastChar = aString[i];
currentString += lastChar;
var sizeOfString = [self sizeOfString:currentString withFont:aFont forWidth:nil].width,
sizeLastChar = [self sizeOfString:lastChar withFont:aFont forWidth:nil].width;
if (sizeOfString - sizeLastChar / 2 < aPoint.x)
position++;
else
break;
}
return position;
}
+ (CGSize)sizeOfString:(CPString)aString withFont:(CPFont)aFont forWidth:(float)aWidth
{
if (!DOMFixedWidthSpanElement)
+143 -6
View File
@@ -127,6 +127,7 @@
@import "CPText.j"
@import "CPWindow_Constants.j"
@class CPApplication
@class CPDragServer
@class _CPToolTip
@@ -202,6 +203,10 @@ var ModifierKeyCodes = [
var resizeTimer = nil;
var PreventScroll = true;
var blurTimer = nil;
_CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotification";
// When scrolling with an old-style scroll wheel with discete steps ('clicks'), the scroll amount can indicate how many "lines" to
// scroll.
@@ -360,6 +365,7 @@ var PreventScroll = true;
_DOMBodyElement.style["-khtml-user-select"] = "none";
_DOMBodyElement.webkitTouchCallout = "none";
_DOMBodyElement.style[CPBrowserStyleProperty(@"user-select")] = @"none";
[self createDOMElements];
[self _addLayers];
@@ -393,7 +399,15 @@ var PreventScroll = true;
touchEventSelector = @selector(touchEvent:),
touchEventImplementation = class_getMethodImplementation(theClass, touchEventSelector),
touchEventCallback = function (anEvent) { touchEventImplementation(self, nil, anEvent); };
touchEventCallback = function (anEvent) { touchEventImplementation(self, nil, anEvent); },
onFocusEventSelector = @selector(focusEvent:),
onFocusEventImplementation = class_getMethodImplementation(theClass, onFocusEventSelector),
onFocusEventCallback = function (anEvent) { onFocusEventImplementation(self, nil, anEvent); },
onBlurEventSelector = @selector(blurEvent:),
onBlurEventImplementation = class_getMethodImplementation(theClass, onBlurEventSelector),
onBlurEventCallback = function (anEvent) { onBlurEventImplementation(self, nil, anEvent); };
if (theDocument.addEventListener)
{
@@ -427,8 +441,15 @@ var PreventScroll = true;
_DOMWindow.addEventListener("resize", resizeEventCallback, NO);
_DOMWindow.addEventListener("blur", onBlurEventCallback, NO);
_DOMWindow.addEventListener("focus", onFocusEventCallback, NO);
_DOMWindow.addEventListener("unload", function()
{
_DOMWindow.removeEventListener("unload", arguments.callee, NO);
[self blurEvent:nil];
[self _notifyPlatformWindowWillClose];
[self updateFromNativeContentRect];
[self _removeLayers];
@@ -447,13 +468,14 @@ var PreventScroll = true;
_DOMWindow.removeEventListener("resize", resizeEventCallback, NO);
_DOMWindow.removeEventListener("blur", onBlurEventCallback, NO);
_DOMWindow.removeEventListener("focus", onFocusEventCallback, NO);
//FIXME: does firefox really need a different value?
_DOMWindow.removeEventListener("DOMMouseScroll", scrollEventCallback, NO);
_DOMWindow.removeEventListener("wheel", scrollEventCallback, NO);
_DOMWindow.removeEventListener("mousewheel", scrollEventCallback, NO);
//_DOMWindow.removeEventListener("beforeunload", this, NO);
[PlatformWindows removeObject:self];
[_platformPasteboard setDOMWindow:nil];
@@ -475,6 +497,9 @@ var PreventScroll = true;
_DOMWindow.attachEvent("onresize", resizeEventCallback);
_DOMWindow.attachEvent("onfocus", onFocusEventCallback);
_DOMWindow.attachEvent("onblur", onBlurEventCallback);
_DOMWindow.onmousewheel = scrollEventCallback;
theDocument.onmousewheel = scrollEventCallback;
@@ -483,6 +508,10 @@ var PreventScroll = true;
_DOMWindow.attachEvent("onunload", function()
{
_DOMWindow.detachEvent("unload", arguments.callee);
[self blurEvent:nil];
[self _notifyPlatformWindowWillClose];
[self updateFromNativeContentRect];
[self _removeLayers];
@@ -498,14 +527,16 @@ var PreventScroll = true;
_DOMWindow.detachEvent("onresize", resizeEventCallback);
_DOMWindow.detachEvent("onfocus", onBlurEventCallback);
_DOMWindow.detachEvent("onblur", onFocusEventCallback);
_DOMWindow.onmousewheel = NULL;
theDocument.onmousewheel = NULL;
_DOMBodyElement.ondrag = NULL;
_DOMBodyElement.onselectstart = NULL;
//_DOMWindow.removeEvent("beforeunload", this);
[PlatformWindows removeObject:self];
[_platformPasteboard setDOMWindow:nil];
@@ -537,10 +568,13 @@ var PreventScroll = true;
_DOMWindow = window.open("about:blank", "_blank", "menubar=no,location=no,resizable=yes,scrollbars=no,status=no,left=" + CGRectGetMinX(_contentRect) + ",top=" + CGRectGetMinY(_contentRect) + ",width=" + CGRectGetWidth(_contentRect) + ",height=" + CGRectGetHeight(_contentRect));
if (!_DOMWindow)
return;
[PlatformWindows addObject:self];
// FIXME: cpSetFrame?
_DOMWindow.document.write('<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body style="background-color:transparent;"></body></html>');
_DOMWindow.document.write('<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"></head><body style="background-color:transparent; overflow:hidden"></body></html>');
_DOMWindow.document.close();
if (self != [CPPlatformWindow primaryPlatformWindow])
@@ -988,6 +1022,7 @@ var PreventScroll = true;
- (void)_actualResizeEvent
{
_shouldUpdateContentRect = NO;
resizeTimer = nil;
// FIXME: This is not the right way to do this.
@@ -1021,8 +1056,100 @@ var PreventScroll = true;
//window.liveResize = NO;
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
_shouldUpdateContentRect = YES;
}
/*!
@ignore
*/
- (void)blurEvent:(DOMEvent)aDOMEvent
{
if ([CPApp keyWindow] == _currentKeyWindow)
[_currentKeyWindow resignKeyWindow];
if ([CPApp mainWindow] == _currentMainWindow)
[_currentMainWindow resignMainWindow];
_previousKeyWindow = aDOMEvent ? _currentKeyWindow : nil;
_previousMainWindow = aDOMEvent ? _currentMainWindow : nil;
[blurTimer invalidate];
blurTimer = [CPTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(_blurEventTimer:) userInfo:nil repeats:NO];
}
/*!
@ignore
*/
- (void)_blurEventTimer:(CPTimer)aTimer
{
if (![CPApp mainWindow])
[[CPApplication sharedApplication] deactivate];
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
}
/*!
@ignore
*/
- (void)focusEvent:(DOMEvent)aDOMEvent
{
[blurTimer invalidate];
[CPApp activateIgnoringOtherApps:YES];
var keyWindow = _previousKeyWindow;
if (!keyWindow)
keyWindow = [[[_windowLayers objectForKey:[_windowLevels firstObject]] orderedWindows] firstObject];
if (!keyWindow)
return;
[self _makeKeyWindow:keyWindow];
if ([keyWindow isKeyWindow] && ([keyWindow firstResponder] === keyWindow || ![keyWindow firstResponder]))
[keyWindow makeFirstResponder:[keyWindow initialFirstResponder]];
[self _makeMainWindow:keyWindow];
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
_previousKeyWindow = nil;
_previousMainWindow = nil;
}
/*!
@ignore
*/
- (void)_makeKeyWindow:(CPWindow)aWindow
{
if ([CPApp keyWindow] === aWindow || ![aWindow canBecomeKeyWindow])
return;
[[CPApp keyWindow] resignKeyWindow];
[aWindow becomeKeyWindow];
}
/*!
@ignore
*/
- (void)_makeMainWindow:(CPWindow)aWindow
{
// Sheets cannot be main. Their parent window becomes main.
if (aWindow._isSheet)
{
[self _makeMainWindow:aWindow._parentView];
return;
}
if ([CPApp mainWindow] === aWindow || ![aWindow canBecomeMainWindow])
return;
[[CPApp mainWindow] resignMainWindow];
[aWindow becomeMainWindow];
}
- (void)touchEvent:(DOMEvent)aDOMEvent
{
if (aDOMEvent.touches && (aDOMEvent.touches.length == 1 || (aDOMEvent.touches.length == 0 && aDOMEvent.changedTouches.length == 1)))
@@ -1290,6 +1417,9 @@ var PreventScroll = true;
- (void)order:(CPWindowOrderingMode)orderingMode window:(CPWindow)aWindow relativeTo:(CPWindow)otherWindow
{
if (!_DOMWindow)
return;
[CPPlatform initializeScreenIfNecessary];
// Grab the appropriate level for the layer, and create it if
@@ -1629,6 +1759,13 @@ var PreventScroll = true;
KeyCodesToPrevent = {};
}
- (void)_notifyPlatformWindowWillClose
{
[[CPNotificationCenter defaultCenter] postNotificationName:_CPPlatformWindowWillCloseNotification
object:self
userInfo:nil];
}
@end
#endif
+99 -12
View File
@@ -384,6 +384,19 @@ var themedButtonValues = nil,
"themedTokenFieldTokenCloseButton"];
}
+ (CPColor)themedColor
{
var color = [CPColor blackColor],
themedColorValues =
[
[@"alternate-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]]],
[@"secondary-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]]]
];
[self registerThemeValues:themedColorValues forObject:color];
return color;
}
+ (CPButton)makeButton
{
@@ -1186,13 +1199,28 @@ var themedButtonValues = nil,
// The new bezel is one pixel shorter, so we add one extra empty pixel at the bottom
// for size compatibility with an earlier version.
[@"bezel-inset", CGInsetMake(0.0, 0.0, 1.0, 0.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"content-inset", CGInsetMake(8.0, 13.0, 7.0, 14.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"content-inset", CGInsetMake(7.0, 13.0, 7.0, 14.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"text-color", textDisabledColor, [CPTextFieldStateRounded, CPThemeStateDisabled]],
[@"text-color", placeholderColor, [CPTextFieldStateRounded, CPTextFieldStatePlaceholder]],
[@"min-size", CGSizeMake(0.0, 30.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"max-size", CGSizeMake(-1.0, 30.0), [CPTextFieldStateRounded, CPThemeStateBezeled]]
[@"max-size", CGSizeMake(-1.0, 30.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"nib2cib-adjustment-frame", CGRectMake(-2.0, 7.0, 5.0, 10.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
// CPThemeStateControlSizeSmall
[@"content-inset", CGInsetMake(8.0, 6.0, 4.0, 6.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"bezel-inset", CGInsetMake(2.0, 4.0, 2.0, 4.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"min-size", CGSizeMake(0.0, 28.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"max-size", CGSizeMake(-1.0, 28.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"nib2cib-adjustment-frame", CGRectMake(-6.0, 5.0, 13.0, 9.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
// CPThemeStateControlSizeMini
[@"content-inset", CGInsetMake(8.0, 6.0, 4.0, 6.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"bezel-inset", CGInsetMake(2.0, 4.0, 2.0, 4.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"min-size", CGSizeMake(0.0, 26.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"max-size", CGSizeMake(-1.0, 26.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"nib2cib-adjustment-frame", CGRectMake(-6.0, 6.0, 13.0, 10.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]]
];
[self registerThemeValues:themedRoundedTextFieldValues forView:textfield];
@@ -1211,17 +1239,44 @@ var themedButtonValues = nil,
{
var searchField = [[CPSearchField alloc] initWithFrame:CGRectMake(0.0, 0.0, 160.0, 30.0)],
imageSearch = PatternImage("search-field-search.png", 25.0, 22.0),
imageFind = PatternImage("search-field-find.png", 25.0, 22.0),
imageCancel = PatternImage("search-field-cancel.png", 22.0, 22.0),
imageCancelPressed = PatternImage("search-field-cancel-pressed.png", 22.0, 22.0),
imageSearch = PatternImage("search-field-search.png", 25.0, 22.0),
imageFind = PatternImage("search-field-find.png", 25.0, 22.0),
imageCancel = PatternImage("search-field-cancel.png", 22.0, 22.0),
imageCancelPressed = PatternImage("search-field-cancel-pressed.png", 22.0, 22.0),
smallImageSearch = PatternImage("search-field-search.png", 25.0, 20.0),
smallImageFind = PatternImage("search-field-find.png", 25.0, 20.0),
smallImageCancel = PatternImage("search-field-cancel.png", 22.0, 20.0),
smallImageCancelPressed = PatternImage("search-field-cancel-pressed.png", 22.0, 21.0),
miniImageSearch = PatternImage("search-field-search.png", 25.0, 20.0),
miniImageFind = PatternImage("search-field-find.png", 25.0, 20.0),
miniImageCancel = PatternImage("search-field-cancel.png", 22.0, 21.0),
miniImageCancelPressed = PatternImage("search-field-cancel-pressed.png", 22.0, 21.0),
overrides =
[
[@"image-search", imageSearch],
[@"image-find", imageFind],
[@"image-cancel", imageCancel],
[@"image-cancel-pressed", imageCancelPressed]
[@"image-search-inset", CGInsetMake(0, 0, 0, 5)],
[@"image-cancel-inset", CGInsetMake(0, 5, 0, 0)],
[@"image-search", imageSearch],
[@"image-find", imageFind],
[@"image-cancel", imageCancel],
[@"image-cancel-pressed", imageCancelPressed],
[@"image-search", smallImageSearch, CPThemeStateControlSizeSmall],
[@"image-find", smallImageFind, CPThemeStateControlSizeSmall],
[@"image-cancel", smallImageCancel, CPThemeStateControlSizeSmall],
[@"image-cancel-pressed", smallImageCancelPressed, CPThemeStateControlSizeSmall],
[@"image-search-inset", CGInsetMake(0, 0, 0, 8), CPThemeStateControlSizeSmall],
[@"image-cancel-inset", CGInsetMake(0, 8, 0, 0), CPThemeStateControlSizeSmall],
[@"image-search", miniImageSearch, CPThemeStateControlSizeMini],
[@"image-find", miniImageFind, CPThemeStateControlSizeMini],
[@"image-cancel", miniImageCancel, CPThemeStateControlSizeMini],
[@"image-cancel-pressed", miniImageCancelPressed, CPThemeStateControlSizeMini],
[@"image-search-inset", CGInsetMake(0, 0, 0, 8), CPThemeStateControlSizeMini],
[@"image-cancel-inset", CGInsetMake(0, 8, 0, 0), CPThemeStateControlSizeMini],
];
[self registerThemeValues:overrides forView:searchField inherit:themedRoundedTextFieldValues];
@@ -1763,7 +1818,7 @@ var themedButtonValues = nil,
{
width: 8.0,
height: 26.0,
rightWidth: 23.0,
rightWidth: 19.0,
orientation: PatternIsHorizontal
}),
@@ -1801,7 +1856,7 @@ var themedButtonValues = nil,
{
width: 6.0,
height: 22.0,
rightWidth: 19.0,
rightWidth: 17.0,
orientation: PatternIsHorizontal
}),
@@ -1836,7 +1891,9 @@ var themedButtonValues = nil,
[@"border-inset", CGInsetMake(3.0, 3.0, 3.0, 3.0), CPThemeStateBezeled],
[@"bezel-inset", CGInsetMake(0.0, 1.0, 1.0, 1.0), [CPThemeStateBezeled, CPComboBoxStateButtonBordered]],
[@"bezel-inset", CGInsetMake(0.0, 1.0, 1.0, 1.0), [CPThemeStateBezeled, CPThemeStateEditing]],
[@"bezel-inset", CGInsetMake(0.0, 1.0, 1.0, 1.0), [CPThemeStateBezeled, CPThemeStateDisabled]],
// The right border inset has to make room for the focus ring and popup button
[@"content-inset", CGInsetMake(8.0, 27.0, 7.0, 8.0), [CPThemeStateBezeled, CPComboBoxStateButtonBordered]],
@@ -1860,6 +1917,12 @@ var themedButtonValues = nil,
[@"bezel-color", smallBezelNoBorderFocusedColor, [CPThemeStateControlSizeSmall, CPThemeStateBezeled, CPThemeStateEditing]],
[@"bezel-color", smallBezelNoBorderColor["disabled"], [CPThemeStateControlSizeSmall, CPThemeStateBezeled, CPThemeStateDisabled]],
[@"bezel-inset", CGInsetMake(1.0, 2.0, 1.0, 2.0), [CPThemeStateBezeled, CPComboBoxStateButtonBordered, CPThemeStateControlSizeSmall]],
[@"bezel-inset", CGInsetMake(1.0, 2.0, 1.0, 0.0), [CPThemeStateBezeled, CPThemeStateEditing, CPComboBoxStateButtonBordered, CPThemeStateControlSizeSmall]],
[@"bezel-inset", CGInsetMake(1.0, 2.0, 1.0, 2.0), [CPThemeStateBezeled, CPThemeStateDisabled, CPThemeStateControlSizeSmall]],
[@"content-inset", CGInsetMake(6.0, 27.0, 7.0, 8.0), [CPThemeStateBezeled, CPComboBoxStateButtonBordered, CPThemeStateControlSizeSmall]],
[@"min-size", CGSizeMake(0, 26.0), CPThemeStateControlSizeSmall],
[@"max-size", CGSizeMake(-1, 26.0), CPThemeStateControlSizeSmall],
[@"nib2cib-adjustment-frame", CGRectMake(-2.0, -1.0, 1.0, 0.0), CPThemeStateControlSizeSmall],
@@ -1873,6 +1936,12 @@ var themedButtonValues = nil,
[@"bezel-color", miniBezelNoBorderFocusedColor, [CPThemeStateControlSizeMini, CPThemeStateBezeled, CPThemeStateEditing]],
[@"bezel-color", miniBezelNoBorderColor["disabled"], [CPThemeStateControlSizeMini, CPThemeStateBezeled, CPThemeStateDisabled]],
[@"bezel-inset", CGInsetMake(1.0, 2.0, 1.0, 2.0), [CPThemeStateBezeled, CPComboBoxStateButtonBordered, CPThemeStateControlSizeMini]],
[@"bezel-inset", CGInsetMake(1.0, 2.0, 1.0, 1.0), [CPThemeStateBezeled, CPThemeStateEditing, CPComboBoxStateButtonBordered, CPThemeStateControlSizeMini]],
[@"bezel-inset", CGInsetMake(1.0, 2.0, 1.0, 2.0), [CPThemeStateBezeled, CPThemeStateDisabled, CPThemeStateControlSizeMini]],
[@"content-inset", CGInsetMake(6.0, 27.0, 7.0, 8.0), [CPThemeStateBezeled, CPComboBoxStateButtonBordered, CPThemeStateControlSizeMini]],
[@"min-size", CGSizeMake(0, 22.0), CPThemeStateControlSizeMini],
[@"max-size", CGSizeMake(-1, 22.0), CPThemeStateControlSizeMini],
[@"nib2cib-adjustment-frame", CGRectMake(-2.0, -1.0, 2.0, 0.0), CPThemeStateControlSizeMini],
@@ -2943,6 +3012,24 @@ var themedButtonValues = nil,
return progressBar;
}
+ (CPProgressIndicator)themedCircularProgressIndicator
{
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
[progressBar setStyle:CPProgressIndicatorSpinningStyle];
[progressBar setIndeterminate:NO];
var themeValues =
[
[@"circular-border-color", [CPColor colorWithHexString:@"C7C7C7"]],
[@"circular-border-size", 1],
[@"circular-color", [CPColor colorWithHexString:@"89B5CD"]]
];
[self registerThemeValues:themeValues forView:progressBar];
return progressBar;
}
+ (CPBox)themedBox
{
var box = [[CPBox alloc] initWithFrame:CGRectMake(0,0,100,100)],
+98 -16
View File
@@ -28,6 +28,7 @@
@import <AppKit/CPButtonBar.j>
@import <AppKit/CPCheckBox.j>
@import <AppKit/CPComboBox.j>
@import <AppKit/CPColor.j>
@import <AppKit/CPColorWell.j>
@import <AppKit/CPDatePicker.j>
@import <AppKit/CPLevelIndicator.j>
@@ -96,7 +97,22 @@ var themedButtonValues = nil,
"themedRuleEditor",
"themedTableDataView",
"themedCornerview",
"themedTokenFieldTokenCloseButton"];
"themedTokenFieldTokenCloseButton",
"themedColor"];
}
+ (CPColor)themedColor
{
var color = [CPColor redColor],
themedColorValues =
[
[@"alternate-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]]],
[@"secondary-selected-control-color", [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]]]
];
[self registerThemeValues:themedColorValues forObject:color];
return color;
}
+ (CPButton)makeButton
@@ -310,7 +326,7 @@ var themedButtonValues = nil,
var button = [self button];
[button setTitle:@"OK"];
[button setThemeState:[CPButtonStateBezelStyleRounded, CPThemeStateDefault]];
[button setThemeStates:[CPButtonStateBezelStyleRounded, CPThemeStateDefault]];
return button;
}
@@ -688,7 +704,8 @@ var themedButtonValues = nil,
[@"text-color", [CPColor blackColor], [CPThemeStateTableDataView, CPThemeStateSelectedDataView, CPThemeStateEditable, CPThemeStateFirstResponder, CPThemeStateKeyWindow]],
[@"content-inset", CGInsetMake(7.0, 7.0, 5.0, 10.0), [CPThemeStateTableDataView, CPThemeStateEditable]],
[@"font", [CPFont systemFontOfSize:CPFontCurrentSystemSize], [CPThemeStateTableDataView, CPThemeStateEditing]],
[@"bezel-inset", CGInsetMake(-2.0, -2.0, -2.0, -2.0), [CPThemeStateTableDataView, CPThemeStateEditing]],
[@"bezel-inset", CGInsetMake(-2.0, -2.0, -2.0, -2.0), [CPThemeStateTableDataView, CPThemeStateEditable, CPThemeStateEditing]],
[@"bezel-inset", CGInsetMake(1.0, 1.0, 1.0, 1.0), [CPThemeStateTableDataView, CPThemeStateEditable]],
[@"text-color", [CPColor colorWithCalibratedWhite:125.0 / 255.0 alpha:1.0], [CPThemeStateTableDataView, CPThemeStateGroupRow]],
[@"text-color", [CPColor whiteColor], [CPThemeStateTableDataView, CPThemeStateGroupRow, CPThemeStateSelectedDataView, CPThemeStateFirstResponder, CPThemeStateKeyWindow]],
@@ -760,8 +777,8 @@ var themedButtonValues = nil,
[@"font", [CPFont systemFontOfSize:CPFontCurrentSystemSize]],
[@"content-inset", CGInsetMake(8.0, 14.0, 6.0, 14.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"content-inset", CGInsetMake(8.0, 14.0, 6.0, 14.0), [CPTextFieldStateRounded, CPThemeStateBezeled, CPThemeStateEditing]],
[@"content-inset", CGInsetMake(7.0, 10.0, 4.0, 10.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"content-inset", CGInsetMake(7.0, 10.0, 4.0, 10.0), [CPTextFieldStateRounded, CPThemeStateBezeled, CPThemeStateEditing]],
[@"bezel-inset", CGInsetMake(3.0, 4.0, 3.0, 4.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"bezel-inset", CGInsetMake(0.0, 1.0, 0.0, 1.0), [CPTextFieldStateRounded, CPThemeStateBezeled, CPThemeStateEditing]],
@@ -772,9 +789,23 @@ var themedButtonValues = nil,
[@"min-size", CGSizeMake(0.0, 29.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"max-size", CGSizeMake(-1.0, 29.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"nib2cib-adjustment-frame", CGRectMake(-4.0, 7.0, 8.0, 10.0), [CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"min-size", CGSizeMake(0.0, 20.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"max-size", CGSizeMake(-1.0, 20.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]]
// CPThemeStateControlSizeSmall
[@"content-inset", CGInsetMake(7.0, 6.0, 4.0, 6.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"bezel-inset", CGInsetMake(2.0, 4.0, 2.0, 4.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"bezel-inset", CGInsetMake(0.0, 1.0, 0.0, 1.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled, CPThemeStateEditing]],
[@"min-size", CGSizeMake(0.0, 25.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"max-size", CGSizeMake(-1.0, 25.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"nib2cib-adjustment-frame", CGRectMake(-4.0, 7.0, 8.0, 9.0), [CPThemeStateControlSizeSmall, CPTextFieldStateRounded, CPThemeStateBezeled]],
// CPThemeStateControlSizeMini
[@"content-inset", CGInsetMake(7.0, 6.0, 4.0, 6.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"bezel-inset", CGInsetMake(2.0, 4.0, 2.0, 4.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"bezel-inset", CGInsetMake(0.0, 1.0, 0.0, 1.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled, CPThemeStateEditing]],
[@"min-size", CGSizeMake(0.0, 22.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"max-size", CGSizeMake(-1.0, 22.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
[@"nib2cib-adjustment-frame", CGRectMake(-4.0, 2.0, 8.0, 4.0), [CPThemeStateControlSizeMini, CPTextFieldStateRounded, CPThemeStateBezeled]],
];
[self registerThemeValues:themedRoundedTextFieldValues forView:textfield];
@@ -793,17 +824,40 @@ var themedButtonValues = nil,
{
var searchField = [[CPSearchField alloc] initWithFrame:CGRectMake(0.0, 0.0, 160.0, 29.0)],
imageSearch = PatternImage("search-field-search.png", 25.0, 22.0),
imageFind = PatternImage("search-field-find.png", 25.0, 22.0),
imageCancel = PatternImage("search-field-cancel.png", 22.0, 22.0),
imageCancelPressed = PatternImage("search-field-cancel-pressed.png", 22.0, 22.0),
imageSearch = PatternImage("search-field-search.png", 25.0, 22.0),
imageFind = PatternImage("search-field-find.png", 25.0, 22.0),
imageCancel = PatternImage("search-field-cancel.png", 22.0, 22.0),
imageCancelPressed = PatternImage("search-field-cancel-pressed.png", 22.0, 22.0),
smallImageSearch = PatternImage("search-field-search.png", 25.0, 22.0),
smallImageFind = PatternImage("search-field-find.png", 25.0, 22.0),
smallImageCancel = PatternImage("search-field-cancel.png", 22.0, 22.0),
smallImageCancelPressed = PatternImage("search-field-cancel-pressed.png", 22.0, 22.0),
miniImageSearch = PatternImage("search-field-search.png", 25.0, 22.0),
miniImageFind = PatternImage("search-field-find.png", 25.0, 22.0),
miniImageCancel = PatternImage("search-field-cancel.png", 22.0, 22.0),
miniImageCancelPressed = PatternImage("search-field-cancel-pressed.png", 22.0, 22.0),
overrides =
[
[@"image-search", imageSearch],
[@"image-find", imageFind],
[@"image-cancel", imageCancel],
[@"image-cancel-pressed", imageCancelPressed]
[@"image-search-inset", CGInsetMake(0, 0, 0, 5)],
[@"image-cancel-inset", CGInsetMake(0, 5, 0, 0)],
[@"image-search", imageSearch],
[@"image-find", imageFind],
[@"image-cancel", imageCancel],
[@"image-cancel-pressed", imageCancelPressed],
[@"image-search", smallImageSearch, CPThemeStateControlSizeSmall],
[@"image-find", smallImageFind, CPThemeStateControlSizeSmall],
[@"image-cancel", smallImageCancel, CPThemeStateControlSizeSmall],
[@"image-cancel-pressed", smallImageCancelPressed, CPThemeStateControlSizeSmall],
[@"image-search", miniImageSearch, CPThemeStateControlSizeMini],
[@"image-find", miniImageFind, CPThemeStateControlSizeMini],
[@"image-cancel", miniImageCancel, CPThemeStateControlSizeMini],
[@"image-cancel-pressed", miniImageCancelPressed, CPThemeStateControlSizeMini],
];
[self registerThemeValues:overrides forView:searchField inherit:themedRoundedTextFieldValues];
@@ -1248,7 +1302,7 @@ var themedButtonValues = nil,
{
width: 8.0,
height: 26.0,
rightWidth: 23.0,
rightWidth: 21.0,
orientation: PatternIsHorizontal
}),
@@ -1352,6 +1406,10 @@ var themedButtonValues = nil,
[@"bezel-color", smallBezelNoBorderFocusedColor, [CPThemeStateControlSizeSmall, CPThemeStateBezeled, CPThemeStateEditing]],
[@"bezel-color", smallBezelNoBorderColor["disabled"], [CPThemeStateControlSizeSmall, CPThemeStateBezeled, CPThemeStateDisabled]],
[@"bezel-inset", CGInsetMake(1.0, 2.0, 1.0, 2.0), [CPThemeStateBezeled, CPThemeStateEditing, CPComboBoxStateButtonBordered, CPThemeStateControlSizeSmall]],
[@"content-inset", CGInsetMake(7.0, 28.0, 7.0, 8.0), [CPThemeStateBezeled, CPComboBoxStateButtonBordered, CPThemeStateControlSizeSmall]],
[@"content-inset", CGInsetMake(7.0, 28.0, 7.0, 8.0), [CPThemeStateBezeled, CPThemeStateControlSizeSmall]],
[@"min-size", CGSizeMake(0, 26.0), CPThemeStateControlSizeSmall],
[@"max-size", CGSizeMake(-1, 26.0), CPThemeStateControlSizeSmall],
[@"nib2cib-adjustment-frame", CGRectMake(-4.0, -1.0, 5.0, 0.0), CPThemeStateControlSizeSmall],
@@ -1365,6 +1423,10 @@ var themedButtonValues = nil,
[@"bezel-color", miniBezelNoBorderFocusedColor, [CPThemeStateControlSizeMini, CPThemeStateBezeled, CPThemeStateEditing]],
[@"bezel-color", miniBezelNoBorderColor["disabled"], [CPThemeStateControlSizeMini, CPThemeStateBezeled, CPThemeStateDisabled]],
[@"bezel-inset", CGInsetMake(1.0, 2.0, 1.0, 2.0), [CPThemeStateBezeled, CPThemeStateEditing, CPComboBoxStateButtonBordered, CPThemeStateControlSizeMini]],
[@"content-inset", CGInsetMake(7.0, 26.0, 7.0, 8.0), [CPThemeStateBezeled, CPComboBoxStateButtonBordered, CPThemeStateControlSizeMini]],
[@"content-inset", CGInsetMake(7.0, 26.0, 7.0, 8.0), [CPThemeStateBezeled, CPThemeStateControlSizeMini]],
[@"min-size", CGSizeMake(0, 22.0), CPThemeStateControlSizeMini],
[@"max-size", CGSizeMake(-1, 22.0), CPThemeStateControlSizeMini],
[@"nib2cib-adjustment-frame", CGRectMake(-4.0, -2.0, 6.0, 0.0), CPThemeStateControlSizeMini],
@@ -1482,6 +1544,8 @@ var themedButtonValues = nil,
[@"font", [CPFont systemFontOfSize:CPFontCurrentSystemSize], CPThemeStateNormal],
[@"text-color", regularDisabledTextColor, CPThemeStateDisabled],
[@"text-color", [CPColor colorWithCalibratedWhite:51.0 / 255.0 alpha:1.0], CPThemeStateTableDataView],
[@"text-color", [CPColor whiteColor], [CPThemeStateTableDataView, CPThemeStateSelectedDataView, CPThemeStateFirstResponder, CPThemeStateKeyWindow]],
[@"image-offset", CPCheckBoxImageOffset],
// CPThemeStateControlSizeRegular
@@ -2452,6 +2516,24 @@ var themedButtonValues = nil,
return progressBar;
}
+ (CPProgressIndicator)themedCircularProgressIndicator
{
var progressBar = [[CPProgressIndicator alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
[progressBar setStyle:CPProgressIndicatorSpinningStyle];
[progressBar setIndeterminate:NO];
var themeValues =
[
[@"circular-border-color", [CPColor colorWithHexString:@"A0A0A0"]],
[@"circular-border-size", 1],
[@"circular-color", [CPColor colorWithHexString:@"5982DA"]]
];
[self registerThemeValues:themeValues forView:progressBar];
return progressBar;
}
+ (CPBox)themedBox
{
var box = [[CPBox alloc] initWithFrame:CGRectMake(0, 0, 100, 100)],
+22 -2
View File
@@ -217,6 +217,11 @@ var ItemSizes = { },
return [[self themeName] compare:[aThemeDescriptor themeName]];
}
+ (void)registerThemeValues:(CPArray)themeValues forObject:(id)anObject
{
[self registerThemeValues:themeValues forView:anObject];
}
+ (void)registerThemeValues:(CPArray)themeValues forView:(CPView)aView
{
for (var i = 0; i < themeValues.length; ++i)
@@ -227,12 +232,22 @@ var ItemSizes = { },
state = attributeValueState[2];
if (state)
[aView setValue:value forThemeAttribute:attribute inState:state];
{
if (state.isa && [state isKindOfClass:CPArray])
[aView setValue:value forThemeAttribute:attribute inStates:state];
else
[aView setValue:value forThemeAttribute:attribute inState:state];
}
else
[aView setValue:value forThemeAttribute:attribute];
}
}
+ (void)registerThemeValues:(CPArray)themeValues forObject:(id)anObject inherit:(CPArray)inheritedValues
{
[self registerThemeValues:themeValues forView:anObject inherit:inheritedValues];
}
+ (void)registerThemeValues:(CPArray)themeValues forView:(CPView)aView inherit:(CPArray)inheritedValues
{
// Register inherited values first, then override those with the subtheme values.
@@ -294,7 +309,12 @@ var ItemSizes = { },
}
if (state)
[aView setValue:value forThemeAttribute:attribute inState:state];
{
if (state.isa && [state isKindOfClass:CPArray])
[aView setValue:value forThemeAttribute:attribute inStates:state];
else
[aView setValue:value forThemeAttribute:attribute inState:state];
}
else
[aView setValue:value forThemeAttribute:attribute];
}
+1 -1
View File
@@ -20,7 +20,7 @@ blendKitTask = framework ("BlendKit", function(blendKitTask)
blendKitTask.setFlattensSources(true); // FIXME: how do we non flatten?
if ($CONFIGURATION === "Release")
blendKitTask.setCompilerFlags("-O");
blendKitTask.setCompilerFlags("-O2");
else
blendKitTask.setCompilerFlags("-DDEBUG -g");
});
+2 -2
View File
@@ -171,9 +171,9 @@ var _CPAutocompleteMenuMaximumHeight = 307;
var dataView = [tableColumn dataView],
fontNormal = [dataView valueForThemeAttribute:@"font" inState:CPThemeStateTableDataView],
fontSelected = [dataView valueForThemeAttribute:@"font" inState:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]],
fontSelected = [dataView valueForThemeAttribute:@"font" inStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]],
contentInsetNormal = [dataView valueForThemeAttribute:@"content-inset" inState:CPThemeStateTableDataView],
contentInsetSelected = [dataView valueForThemeAttribute:@"content-inset" inState:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]];
contentInsetSelected = [dataView valueForThemeAttribute:@"content-inset" inStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]];
var mergedString = contentArray.join("\n");
+480
View File
@@ -0,0 +1,480 @@
/*
* AppKit.j
* AppKit
*
* Created by Alexandre Wilhelm.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPTheme.j"
var CPViewThemeClassKey = @"CPViewThemeClassKey",
CPViewThemeStateKey = @"CPViewThemeStateKey";
@protocol CPTheme
- (CPString)themeClass;
- (void)setThemeClass:(CPString)theClass;
- (CPTheme)theme;
- (void)setTheme:(CPTheme)aTheme;
- (unsigned)themeState;
- (BOOL)hasThemeState:(ThemeState)aState;
- (BOOL)hasThemeStates:(CPArray)states;
- (BOOL)setThemeState:(ThemeState)aState;
- (BOOL)setThemeStates:(CPArray)aState;
- (BOOL)unsetThemeState:(ThemeState)aState;
- (BOOL)unsetThemeStates:(CPArray)aState;
- (BOOL)hasThemeAttribute:(CPString)aName;
- (void)objectDidChangeTheme;
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState;
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName;
- (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState;
- (id)valueForThemeAttribute:(CPString)aName;
- (id)currentValueForThemeAttribute:(CPString)aName;
- (void)registerThemeValues:(CPArray)themeValues;
- (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues;
@end
@implementation CPObject (ObjectTheming)
{
// Theming Support
CPTheme _theme;
CPString _themeClass;
JSObject _themeAttributes;
unsigned _themeState;
}
#pragma mark -
#pragma mark Theme State
- (unsigned)themeState
{
return _themeState;
}
- (BOOL)hasThemeState:(ThemeState)aState
{
#if DEBUG
// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as
// the preformance cost for this check is to high. We should remove this check in a future release.
if (aState && aState.isa && [aState isKindOfClass:CPArray])
[CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'hasThemeStates: instead: " + aState];
#endif
return _themeState.hasThemeState(aState);
}
- (BOOL)hasThemeStates:(CPArray)states
{
var i = [states count],
aState = [states objectAtIndex:--i];
while (i > 0)
aState = aState.and([states objectAtIndex:--i]);
return _themeState.hasThemeState(aState);
}
- (BOOL)setThemeState:(ThemeState)aState
{
#if DEBUG
// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as
// the preformance cost for this check is to high. We should remove this check in a future release.
if (aState && aState.isa && [aState isKindOfClass:CPArray])
[CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'setThemeStates: instead: " + aState];
#endif
if (_themeState.hasThemeState(aState))
return NO;
_themeState = _themeState.and(aState);
return YES;
}
- (BOOL)unsetThemeState:(ThemeState)aState
{
#if DEBUG
// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as
// the preformance cost for this check is to high. We should remove this check in a future release.
if (aState && aState.isa && [aState isKindOfClass:CPArray])
[CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'unsetThemeStates: instead: " + aState];
#endif
var oldThemeState = _themeState;
_themeState = _themeState.without(aState);
if (oldThemeState === _themeState)
return NO;
return YES;
}
- (BOOL)setThemeStates:(CPArray)states
{
var i = [states count],
aState = [states objectAtIndex:--i];
while (i > 0)
aState = aState.and([states objectAtIndex:--i]);
return [self setThemeState:aState];
}
- (BOOL)unsetThemeStates:(CPArray)states
{
var i = [states count],
aState = [states objectAtIndex:--i];
while (i > 0)
aState = aState.and([states objectAtIndex:--i]);
return [self unsetThemeState:aState];
}
#pragma mark Theme Attributes
+ (CPString)defaultThemeClass
{
return nil;
}
+ (CPDictionary)themeAttributes
{
return nil;
}
- (CPString)themeClass
{
if (_themeClass)
return _themeClass;
return [[self class] defaultThemeClass];
}
- (void)setThemeClass:(CPString)theClass
{
_themeClass = theClass;
[self _loadThemeAttributes];
}
var NULL_THEME = {};
+ (CPArray)_themeAttributesForTheme:(CPTheme)theme andThemeClass:(CPString)themeClassName
{
var theClass = [self class],
theClassName = class_getName(theClass),
themeClassNameCache = theme != nil ? theme._cachedThemeAttributes || (theme._cachedThemeAttributes = {}) : NULL_THEME,
themedCacheAttributes = themeClassNameCache[themeClassName] || (themeClassNameCache[themeClassName] = {}),
attributes = themedCacheAttributes[theClassName];
if (attributes)
return attributes;
else
attributes = [];
var CPObjectClass = [CPObject class];
for (; theClass && theClass !== CPObjectClass; theClass = [theClass superclass])
{
var cachedAttributes = themedCacheAttributes[class_getName(theClass)];
if (cachedAttributes)
{
attributes = attributes.length ? attributes.concat(cachedAttributes) : attributes;
break;
}
var attributeDictionary = [theClass themeAttributes];
if (!attributeDictionary)
continue;
var attributeKeys = [attributeDictionary allKeys],
attributeCount = attributeKeys.length;
while (attributeCount--)
{
var attributeName = attributeKeys[attributeCount],
attributeValue = [attributeDictionary objectForKey:attributeName],
themeAttribute = [[_CPThemeAttribute alloc] initWithName:attributeName defaultValue:attributeValue defaultAttribute:[theme attributeWithName:attributeName forClass:themeClassName]];
attributes.push(themeAttribute);
}
}
themedCacheAttributes[theClassName] = attributes;
return attributes;
}
- (void)_loadThemeAttributes
{
var theClass = [self class],
attributes = [theClass _themeAttributesForTheme:[self theme] andThemeClass:[self themeClass]],
count = attributes.length;
if (!count)
return;
_themeAttributes = {};
while (count--)
{
var attribute = attributes[count];
_themeAttributes[attribute._name] = attribute;
}
}
- (CPTheme)theme
{
return _theme;
}
- (void)setTheme:(CPTheme)aTheme
{
if (_theme === aTheme)
return;
_theme = aTheme;
[self objectDidChangeTheme];
}
- (void)objectDidChangeTheme
{
if (!_themeAttributes)
return;
var theme = [self theme],
themeClass = [self themeClass];
for (var attributeName in _themeAttributes)
{
if (_themeAttributes.hasOwnProperty(attributeName))
_themeAttributes[attributeName] = [_themeAttributes[attributeName] attributeBySettingParentAttribute:[theme attributeWithName:attributeName forClass:themeClass]];
}
}
- (CPDictionary)_themeAttributeDictionary
{
var dictionary = @{};
if (_themeAttributes)
{
var theme = [self theme];
for (var attributeName in _themeAttributes)
{
if (_themeAttributes.hasOwnProperty(attributeName))
[dictionary setObject:_themeAttributes[attributeName] forKey:attributeName];
}
}
return dictionary;
}
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inState:(ThemeState)aState
{
#if DEBUG
// TODO: To allow aState to be an array is now deprecated. An exception is thrown only in Debug version as
// the preformance cost for this check is to high. We should remove this check in a future release.
if (aState.isa && [aState isKindOfClass:CPArray])
[CPException raise:CPInvalidArgumentException reason:self + @": aState can't be an array. Please use 'setValue:forThemeAttribute:inStates:' instead: " + aState];
#endif
var themeAttr = _themeAttributes && _themeAttributes[aName];
if (!themeAttr)
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
_themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue forState:aState];
}
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName inStates:(CPArray)states
{
var i = [states count],
aState = [states objectAtIndex:--i];
while (i > 0)
aState = aState.and([states objectAtIndex:--i]);
var themeAttr = _themeAttributes && _themeAttributes[aName];
if (!themeAttr)
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
_themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue forState:aState];
}
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
{
var themeAttr = _themeAttributes && _themeAttributes[aName];
if (!themeAttr)
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
_themeAttributes[aName] = [themeAttr attributeBySettingValue:aValue];
}
- (id)valueForThemeAttribute:(CPString)aName inState:(ThemeState)aState
{
#if DEBUG
if (aState.isa && [aState isKindOfClass:CPArray])
[CPException raise:CPInvalidArgumentException reason:@"aState can't be an array. Please use 'valueForThemeAttribute:inStates:' instead: " + aState];
#endif
var themeAttr = _themeAttributes && _themeAttributes[aName];
if (!themeAttr)
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
return [themeAttr valueForState:aState];
}
- (id)valueForThemeAttribute:(CPString)aName inStates:(CPArray)states
{
var i = [states count],
aState = [states objectAtIndex:--i];
while (i > 0)
aState = aState.and([states objectAtIndex:--i]);
var themeAttr = _themeAttributes && _themeAttributes[aName];
if (!themeAttr)
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
return [themeAttr valueForState:aState];
}
- (id)valueForThemeAttribute:(CPString)aName
{
var themeAttr = _themeAttributes && _themeAttributes[aName];
if (!themeAttr)
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
return [themeAttr value];
}
- (id)currentValueForThemeAttribute:(CPString)aName
{
var themeAttr = _themeAttributes && _themeAttributes[aName];
if (!themeAttr)
[CPException raise:CPInvalidArgumentException reason:[self className] + " does not contain theme attribute '" + aName + "'"];
return [themeAttr valueForState:_themeState];
}
- (BOOL)hasThemeAttribute:(CPString)aName
{
return (_themeAttributes && _themeAttributes[aName] !== undefined);
}
/*!
Registers theme values encoded in an array at runtime. The format of the data in the array
is the same as that used by ThemeDescriptors.j, with the exception that you need to use
CPColorWithImages() in place of PatternColor(). For more information see the comments
at the top of ThemeDescriptors.j.
@param themeValues array of theme values
*/
- (void)registerThemeValues:(CPArray)themeValues
{
for (var i = 0; i < themeValues.length; ++i)
{
var attributeValueState = themeValues[i],
attribute = attributeValueState[0],
value = attributeValueState[1],
state = attributeValueState[2];
if (state)
if (state.isa && [state isKindOfClass:CPArray])
[self setValue:value forThemeAttribute:attribute inStates:state];
else
[self setValue:value forThemeAttribute:attribute inState:state];
else
[self setValue:value forThemeAttribute:attribute];
}
}
/*!
Registers theme values encoded in an array at runtime. The format of the data in the array
is the same as that used by ThemeDescriptors.j, with the exception that you need to use
CPColorWithImages() in place of PatternColor(). The values in \c inheritedValues are
registered first, then those in \c themeValues override/augment the inherited values.
For more information see the comments at the top of ThemeDescriptors.j.
@param themeValues array of base theme values
@param inheritedValues array of overridden/additional theme values
*/
- (void)registerThemeValues:(CPArray)themeValues inherit:(CPArray)inheritedValues
{
// Register inherited values first, then override those with the subtheme values.
if (inheritedValues)
[self registerThemeValues:inheritedValues];
if (themeValues)
[self registerThemeValues:themeValues];
}
- (void)_encodeThemeObjectsWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:[self themeClass] forKey:CPViewThemeClassKey];
[aCoder encodeObject:String(_themeState) forKey:CPViewThemeStateKey];
for (var attributeName in _themeAttributes)
{
if (_themeAttributes.hasOwnProperty(attributeName))
CPThemeAttributeEncode(aCoder, _themeAttributes[attributeName]);
}
}
- (void)_decodeThemeObjectsWithCoder:(CPCoder)aCoder
{
_theme = [CPTheme defaultTheme];
_themeClass = [aCoder decodeObjectForKey:CPViewThemeClassKey];
_themeState = CPThemeState([aCoder decodeObjectForKey:CPViewThemeStateKey]);
_themeAttributes = {};
var theClass = [self class],
themeClass = [self themeClass],
attributes = [theClass _themeAttributesForTheme:_theme andThemeClass:themeClass],
count = attributes.length;
while (count--)
{
var attribute = attributes[count];
_themeAttributes[attribute._name] = CPThemeAttributeDecode(aCoder, attribute);
}
}
@end
+55 -19
View File
@@ -63,7 +63,6 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
BOOL _browserAnimates;
BOOL _isObservingFrame;
BOOL _shouldPerformAnimation;
CPInteger _implementedDelegateMethods;
CGRect _targetRect;
CPWindow _targetWindow;
JSObject _orderOutTransitionFunction;
@@ -142,7 +141,12 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
if (_appearance === anAppearance)
return;
[_windowView setAppearance:anAppearance];
_appearance = anAppearance;
var appearanceName = _appearance == CPPopoverAppearanceMinimal ? CPAppearanceNameVibrantLight : CPAppearanceNameVibrantDark,
viewAppearance = [CPAppearance appearanceNamed:appearanceName];
[_windowView setAppearance:viewAppearance];
}
- (void)setStyleMask:(unsigned)aStyleMask
@@ -181,7 +185,14 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
return;
_isObservingFrame = YES;
[_targetView addObserver:self forKeyPath:@"frame" options:0 context:nil];
var view = _targetView;
while (view)
{
[view addObserver:self forKeyPath:@"frame" options:0 context:nil];
view = [view superview];
}
}
/*!
@@ -194,7 +205,14 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
return;
_isObservingFrame = NO;
[_targetView removeObserver:self forKeyPath:@"frame"];
var view = _targetView;
while (view)
{
[view removeObserver:self forKeyPath:@"frame"];
view = [view superview];
}
}
/*!
@@ -535,29 +553,36 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
// Now set up the pop-in to normal size transition.
// Because we are watching the -webkit-transform, it will occur now.
[self setCSS3Property:@"Transform" value:@"scale(1)"];
[self setCSS3Property:@"Transition" value:CPBrowserCSSProperty('transform') + @" 50ms linear"];
_transitionCompleteFunction = function()
window.setTimeout(function()
{
[self setCSS3Property:@"Transform" value:@"scale(1)"];
[self setCSS3Property:@"Transition" value:CPBrowserCSSProperty('transform') + @" 50ms linear"];
_transitionCompleteFunction = function()
{
#if PLATFORM(DOM)
_DOMElement.removeEventListener(CPBrowserStyleProperty('transitionend'), _transitionCompleteFunction, YES);
_DOMElement.removeEventListener(CPBrowserStyleProperty('transitionend'), _transitionCompleteFunction, YES);
// Make sure to clear these properties when the animation is done. Without this,
// the window becomes blurry in Chrome, presumably because the browser composits
// a layer with a transform differently even when it's an identity transform.
[self setCSS3Property:@"Transform" value:nil];
[self setCSS3Property:@"TransformOrigin" value:nil];
[self setCSS3Property:@"Transition" value:nil];
// Make sure to clear these properties when the animation is done. Without this,
// the window becomes blurry in Chrome, presumably because the browser composits
// a layer with a transform differently even when it's an identity transform.
[self setCSS3Property:@"Transform" value:nil];
[self setCSS3Property:@"TransformOrigin" value:nil];
[self setCSS3Property:@"Transition" value:nil];
#endif
_isOpening = NO;
_isOpening = NO;
[_delegate _popoverWindowDidShow];
}
[_delegate _popoverWindowDidShow];
}
#if PLATFORM(DOM)
_DOMElement.addEventListener(CPBrowserStyleProperty('transitionend'), _transitionCompleteFunction, YES);
_DOMElement.addEventListener(CPBrowserStyleProperty('transitionend'), _transitionCompleteFunction, YES);
#endif
}, 0); // There are some weird random conditions happening in Chrome 44. If we don't put a timeout to 0 for the end of
// the transition, the popover is blinking. It happens on Opera as well...
// When the condition is met, the popover will lag a bit when opening...nothing crazy ;)
// An issue has been opened here : https://code.google.com/p/chromium/issues/detail?id=523044&thanks=523044&ts=1440095724
};
#if PLATFORM(DOM)
@@ -655,6 +680,17 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
[_delegate _popoverWindowDidClose];
}
/*!
@ignore
Needed to be ignored to close a popover when a platform is closed.
Because the animation, the popover is not properly closed.
*/
- (void)_didReceivePlatformWindowWillCloseNotification:(CPNotification)aNotification
{
[super _didReceivePlatformWindowWillCloseNotification:aNotification];
[self _orderOutRecursively:YES];
}
#pragma mark -
#pragma mark Private
@@ -702,7 +738,7 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
// Consider clicks in child windows to be "inside". This keeps a transient popover from
// closing if e.g. the window containing the menu of a token field is clicked.
if (mouseWindow === self || [mouseWindow _hasAncestorWindow:self] || ![self _hasOnlyTransientChild:self])
if (mouseWindow === self || [mouseWindow _hasAncestorWindow:self] || ![self _hasOnlyTransientChild:self] || [mouseWindow platformWindow] != [self platformWindow])
{
[self _trapNextMouseDown];
}
+3
View File
@@ -176,6 +176,8 @@ var _CPToolTipHeight = 24.0,
_toolTipWindow = aWindow;
_constrainsToUsableScreen = NO;
[self setPlatformWindow:[_toolTipWindow platformWindow]];
textFrameSize.height += 4;
_content = [CPTextField labelWithTitle:aString];
@@ -193,6 +195,7 @@ var _CPToolTipHeight = 24.0,
[self setAlphaValue:0.9];
[_windowView setNeedsDisplay:YES];
}
return self;
+25 -10
View File
@@ -366,23 +366,38 @@ function pressEnvironment(rootPath, outputFiles, environment, options) {
return {executable:includedBytes, data:dataBytes, mhtml:mhtmlBytes};
}
function pngcrushDirectory(directory) {
var directoryPath = FILE.path(directory);
var pngs = directoryPath.glob("**/*.png");
function pngcrushDirectory(directory)
{
var directoryPath = FILE.path(directory),
pngs = directoryPath.glob("**/*.png");
system.stderr.print("Running pngcrush on " + pngs.length + " pngs:");
pngs.forEach(function(dstPath) {
pngs.forEach(function(dstPath)
{
var tmpPath = FILE.path(dstPath+".tmp");
var p = OS.popen(["pngcrush", "-rem", "alla", "-reduce", /*"-brute",*/ dstPath, tmpPath]);
if (p.wait()) {
CPLog.warn("pngcrush failed. Ensure it's installed and on your PATH.");
try
{
var p = OS.popen(["pngcrush", "-rem", "alla", "-reduce", /*"-brute",*/ dstPath, tmpPath]);
if (p.wait())
{
CPLog.warn("pngcrush failed. Ensure it's installed and on your PATH.");
}
else
{
FILE.move(tmpPath, dstPath);
system.stderr.write(".").flush();
}
}
else {
FILE.move(tmpPath, dstPath);
system.stderr.write(".").flush();
finally
{
p.stdin.close();
p.stdout.close();
p.stderr.close();
}
});
system.stderr.print("");
}
+15 -5
View File
@@ -3,10 +3,20 @@ var OS = require("os");
exports.fontinfo = function(name, size)
{
var p = OS.popen(["fontinfo", "-n", name, size || 12]);
var result;
if (p.wait() === 0)
return JSON.parse(p.stdout.read());
else
return null;
try
{
var p = OS.popen(["fontinfo", "-n", name, size || 12]);
if (p.wait() === 0)
result = p.stdout.read();
}
finally
{
p.stdin.close();
p.stdout.close();
p.stderr.close();
}
return result ? JSON.parse(result) : null;
};
+15 -5
View File
@@ -3,10 +3,20 @@ var OS = require("os");
exports.imagesize = function(path)
{
var p = OS.popen(["imagesize", "-n", path]);
var result;
if (p.wait() === 0)
return JSON.parse(p.stdout.read());
else
return null;
try
{
var p = OS.popen(["imagesize", "-n", path]);
if (p.wait() === 0)
result = p.stdout.read();
}
finally
{
p.stdin.close();
p.stdout.close();
p.stderr.close();
}
return result ? JSON.parse(result) : null;
};
+27
View File
@@ -208,6 +208,14 @@ var concat = Array.prototype.concat,
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
Returns a hash for the object. Unlike Cocoa, the hash value does not take content into account, so two arrays with the same content (\c isEqual: === YES) will not generate the same hash.
*/
- (unsigned)hash
{
return [self UID];
}
/*!
Returns the first object in the array. If the array is empty, returns \c nil
*/
@@ -872,6 +880,25 @@ var concat = Array.prototype.concat,
return join.call([self _javaScriptArrayCopy], aString);
}
/*!
Returns an Array formed by applying a function to the objects in the receiver.
@param aFunction a function taking two arguments: (element, index).
@return an Array containing the transformed elements.
*/
- (CPArray)arrayByApplyingBlock:(Function/*element, index*/)aFunction
{
var result = [],
count = [self count];
for (var idx = 0; idx < count; idx++)
{
var obj = aFunction([self objectAtIndex:idx], idx);
[result addObject:obj];
}
return result;
}
// Creating a description of the array
/*!
+16 -3
View File
@@ -91,7 +91,7 @@ var concat = Array.prototype.concat,
- (id)initWithObjects:(CPArray)objects count:(CPUInteger)aCount
{
if ([objects isKindOfClass:_CPJavaScriptArray])
return slice.call(objects, 0);
return slice.call(objects, 0, aCount);
var array = [],
index = 0;
@@ -196,7 +196,8 @@ var concat = Array.prototype.concat,
}
else
for (; index < count; ++index) {
for (; index < count; ++index)
{
var receiver = self[index];
receiver == nil ? nil : receiver.isa.objj_msgSend0(receiver, aSelector);
}
@@ -232,6 +233,19 @@ var concat = Array.prototype.concat,
return join.call(self, aString);
}
- (CPArray)arrayByApplyingBlock:(Function/*element, index*/)aFunction
{
var result = [];
for (var idx = 0; idx < self.length; idx++)
{
var obj = aFunction(self[idx], idx);
result.push(obj);
}
return result;
}
- (void)insertObject:(id)anObject atIndex:(CPUInteger)anIndex
{
if (anIndex > self.length || anIndex < 0)
@@ -334,7 +348,6 @@ var concat = Array.prototype.concat,
[super addObjectsFromArray:anArray];
}
- (id)copy
{
return slice.call(self, 0);
+58 -3
View File
@@ -25,8 +25,18 @@
@import "CPNotificationCenter.j"
@import "CPObject.j"
@global CFBundleCopyBundleLocalizations
@global CFBundleCopyLocalizedString
CPBundleDidLoadNotification = @"CPBundleDidLoadNotification";
@protocol CPBundleDelegate <CPObject>
@required
- (void)bundleDidFinishLoading:(CPBundle)aBundle;
@end
/*!
@class CPBundle
@ingroup foundation
@@ -37,8 +47,8 @@ var CPBundlesForURLStrings = { };
@implementation CPBundle : CPObject
{
CFBundle _bundle;
id _delegate;
CFBundle _bundle;
id <CPBundleDelegate> _delegate;
}
+ (CPBundle)bundleWithURL:(CPURL)aURL
@@ -94,6 +104,7 @@ var CPBundlesForURLStrings = { };
if (self)
{
_bundle = new CFBundle(aURL);
CPBundlesForURLStrings[URLString] = self;
}
@@ -154,6 +165,21 @@ var CPBundlesForURLStrings = { };
return _bundle.pathForResource(aFilename);
}
- (CPString)pathForResource:(CPString)aFilename ofType:(CPString)extension
{
return _bundle.pathForResource(aFilename, extension);
}
- (CPString)pathForResource:(CPString)aFilename ofType:(CPString)extension inDirectory:(CPString)subpath
{
return _bundle.pathForResource(aFilename, extension, subpath);
}
- (CPString)pathForResource:(CPString)aFilename ofType:(CPString)extension inDirectory:(CPString)subpath forLocalization:(CPString)localizationName
{
return _bundle.pathForResource(aFilename, extension, subpath, localizationName);
}
- (CPDictionary)infoDictionary
{
return _bundle.infoDictionary();
@@ -164,7 +190,7 @@ var CPBundlesForURLStrings = { };
return _bundle.valueForInfoDictionaryKey(aKey);
}
- (void)loadWithDelegate:(id)aDelegate
- (void)loadWithDelegate:(id <CPBundleDelegate>)aDelegate
{
_delegate = aDelegate;
@@ -212,4 +238,33 @@ var CPBundlesForURLStrings = { };
return [super description] + "(" + [self bundlePath] + ")";
}
#pragma mark -
#pragma mark Localization
- (CPArray)localizations
{
return CFBundleCopyBundleLocalizations(_bundle);
}
- (CPString)localizedStringForKey:(CPString)aKey value:(CPString)aValue table:(CPString)aTable
{
return CFBundleCopyLocalizedString(_bundle, aKey, aValue, aTable);
}
@end
function CPLocalizedString(key, comment)
{
return CFCopyLocalizedString(key, comment);
}
function CPLocalizedStringFromTable(key, table, comment)
{
return CFCopyLocalizedStringFromTable(key, table, comment);
}
function CPCopyLocalizedStringFromTableInBundle(key, table, bundle, comment)
{
return CFCopyLocalizedStringFromTableInBundle(key, table, bundle._bundle, comment);
}
+371
View File
@@ -0,0 +1,371 @@
/*
* CPCache.j
* Foundation
*
* Created by William Mura.
* Copyright 2015, William Mura.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPObject.j"
@import "CPDictionary.j"
@import "CPString.j"
@class _CPCacheItem;
/*
* Delegate CPCacheDelegate
*
* - cache:willEvictObject: is called when a object is going to be removed
* When the total cost or the count exceeds the total cost limit or the count limit
* And also when removeObjectForKey or removeAllObjects are called
*/
@protocol CPCacheDelegate <CPObject>
@optional
- (void)cache:(CPCache)cache willEvictObject:(id)obj;
@end
var CPCacheDelegate_cache_willEvictObject_ = 1 << 1;
/*!
@class CPCache
@ingroup foundation
@brief A collection-like container with discardable objects
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSCache_Class/index.html#//apple_ref/occ/instp/NSCache/delegate
A CPCache object is a collection-like container, or cache, that stores key-value pairs,
similar to the CPDictionary class. Developers often incorporate caches to temporarily
store objects with transient data that are expensive to create.
Reusing these objects can provide performance benefits, because their values do not have to be recalculated.
However, the objects are not critical to the application and can be discarded if memory is tight.
If discarded, their values will have to be recomputed again when needed.
*/
@implementation CPCache : CPObject
{
CPDictionary _items;
int _currentPosition;
BOOL _totalCostCache;
unsigned _implementedDelegateMethods;
CPString _name @accessors(property=name);
int _countLimit @accessors(property=countLimit);
int _totalCostLimit @accessors(property=totalCostLimit);
id <CPCacheDelegate> _delegate @accessors(property=delegate);
}
#pragma mark -
#pragma mark Initialization
/*!
Initializes the cache with default values
@return the initialized cache
*/
- (id)init
{
if (self = [super init])
{
_items = [[CPDictionary alloc] init];
_currentPosition = 0;
_totalCostCache = -1;
_implementedDelegateMethods = 0;
_name = @"";
_countLimit = 0;
_totalCostLimit = 0;
_delegate = nil;
}
return self;
}
#pragma mark -
#pragma mark Managing cache
/*!
Returns the object which correspond to the given key
@param aKey the key for the object's entry
@return the object for the entry
*/
- (id)objectForKey:(id)aKey
{
return [[_items objectForKey:aKey] object];
}
/*!
Adds an object with default cost into the cache.
@param anObject the object to add in the cache
@param aKey the object's key
*/
- (void)setObject:(id)anObject forKey:(id)aKey
{
[self setObject:anObject forKey:aKey cost:0];
}
/*!
Adds an object with a cost into the cache.
@param anObject the object to add in the cache
@param aKey the object's key
@param aCost the object's cost
*/
- (void)setObject:(id)anObject forKey:(id)aKey cost:(int)aCost
{
// Check if the key already exist
if ([_items objectForKey:aKey])
[self removeObjectForKey:aKey];
// Add object
[_items setObject:[_CPCacheItem cacheItemWithObject:anObject cost:aCost position:++_currentPosition] forKey:aKey];
// Invalid cost cache
_totalCostCache = -1;
// Clean cache to satisfy condition (< totalCostLimit & < countLimit) if necessary
[self _cleanCache];
}
/*!
Removes the object from the cache for the given key.
@param aKey the key of the object to be removed
*/
- (void)removeObjectForKey:(id)aKey
{
// Call delegate method to warn that the object is going to be removed
[self _sendDelegateWillEvictObjectForKey:aKey];
// Remove object
[_items removeObjectForKey:aKey];
// Invalid cost cache
_totalCostCache = -1;
}
/*!
Removes all the objects from the cache.
*/
- (void)removeAllObjects
{
// Call delegate method to warn that the objects are going to be removed
var enumerator = [_items keyEnumerator],
key;
while (key = [enumerator nextObject])
[self _sendDelegateWillEvictObjectForKey:key]
// Remove all objects
[_items removeAllObjects];
// Invalid cost cache and reset position counter
_totalCostCache = -1;
_currentPosition = 0;
}
#pragma mark -
#pragma mark Setters
/*!
Sets the count limit of the cache.
Remove objects if not enough place to keep all of them
@param aCountLimit the new count limit
*/
- (void)setCountLimit:(int)aCountLimit
{
_countLimit = aCountLimit;
[self _cleanCache];
}
/*!
Sets the total cost limit of the cache.
Remove objects if not enough place to keep all of them
@param aTotalCostLimit the new total cost limit
*/
- (void)setTotalCostLimit:(int)aTotalCostLimit
{
_totalCostLimit = aTotalCostLimit;
[self _cleanCache];
}
/*!
Sets the cache's delegate.
@param aDelegate the new delegate
*/
- (void)setDelegate:(id)aDelegate
{
if (_delegate === aDelegate)
return;
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(cache:willEvictObject:)])
_implementedDelegateMethods |= CPCacheDelegate_cache_willEvictObject_
}
#pragma mark -
#pragma mark Privates
/*
* This method return the number of objects in the cache
*/
- (int)_count
{
return [_items count];
}
/*
* This method return the total cost (addition of all object's cost in the cache)
*/
- (int)_totalCost
{
if (_totalCostCache >= 0)
return _totalCostCache;
var enumerator = [_items objectEnumerator],
value;
_totalCostCache = 0;
while (value = [enumerator nextObject])
_totalCostCache += [value cost];
return _totalCostCache;
}
/*
* This method resequence the position of objects
* Otherwise the position value could rise until to cause problem
*/
- (void)_resequencePosition
{
_currentPosition = 1;
// Sort keys by position
var sortedKeys = [[_items allKeys] sortedArrayUsingFunction:
function(k1, k2) {
return [[[_items objectForKey:k1] position] compare:[[_items objectForKey:k2] position]]; }];
// Affect new positions
for (var i = 0; i < sortedKeys.length; ++i)
[[_items objectForKey:sortedKeys[i]] setPosition:_currentPosition++];
}
/*
* Check if the totalCostLimit is exceeded
*/
- (BOOL)_isTotalCostLimitExceeded
{
return ([self _totalCost] > _totalCostLimit && _totalCostLimit > 0);
}
/*
* Check if the countLimit is exceeded
*/
- (BOOL)_isCountLimitExceeded
{
return ([self _count] > _countLimit && _countLimit > 0);
}
/*
* This method clean the cache if the totalCost or the count exceeds the totalCostLimit or the countLimit
* until to satisfy condition (totalCost < totalCostLimit and count < countLimit)
*/
- (void)_cleanCache
{
// Check if the condition is satisfied
if (![self _isTotalCostLimitExceeded] && ![self _isCountLimitExceeded])
return;
// Sort keys by position
var sortedKeys = [[_items allKeys] sortedArrayUsingFunction:
function(k1, k2) {
return [[[_items objectForKey:k1] position] compare:[[_items objectForKey:k2] position]]; }];
// Remove oldest objects until to satisfy the break condition
for (var i = 0; i < sortedKeys.length; ++i)
{
if (![self _isTotalCostLimitExceeded] && ![self _isCountLimitExceeded])
break;
// Call delegate method to warn that the object is going to be removed
[self _sendDelegateWillEvictObjectForKey:sortedKeys[i]];
// Remove object
[_items removeObjectForKey: sortedKeys[i]];
// Invalid cost cache
_totalCostCache = -1;
}
// Resequence position of all objects
[self _resequencePosition];
}
@end
@implementation CPCache (CPCacheDelegate)
- (void)_sendDelegateWillEvictObjectForKey:(id)aKey
{
if (_implementedDelegateMethods & CPCacheDelegate_cache_willEvictObject_)
[_delegate cache:self willEvictObject:[[_items objectForKey:aKey] object]];
}
@end
/*
* Class _CPCacheItem
* Represent an item of CPCache
* This class allow to associate a cost and a position to an object
*
* Attributes:
* - object: the stored object
* - cost: represent the cost (of memory) of the object
* - position: represent the insertion order to determine the oldest object
*/
@implementation _CPCacheItem : CPObject
{
CPObject _object @accessors(property=object);
int _cost @accessors(property=cost);
int _position @accessors(property=position);
}
+ (id)cacheItemWithObject:(CPObject)anObject cost:(int)aCost position:(int)aPosition
{
var cacheItem = [[self alloc] init];
if (cacheItem)
{
[cacheItem setObject:anObject];
[cacheItem setCost:aCost];
[cacheItem setPosition:aPosition];
}
return cacheItem;
}
@end
+2 -2
View File
@@ -1552,7 +1552,7 @@ var defaultDateFormatterBehavior = CPDateFormatterBehavior10_4,
month = [[self monthSymbols] indexOfObject:dateComponent] + 1;
}
if (month > 11 || length >= 5)
if (month > 12 || length >= 5)
return nil;
dateArray[1] = month;
@@ -1580,7 +1580,7 @@ var defaultDateFormatterBehavior = CPDateFormatterBehavior10_4,
month = [[self standaloneMonthSymbols] indexOfObject:dateComponent] + 1;
}
if (month > 11 || length >= 5)
if (month > 12 || length >= 5)
return nil;
dateArray[1] = month;
+16 -1
View File
@@ -26,7 +26,8 @@
@import "CPString.j"
// The default global behavior class, created lazily
var CPDefaultDcmHandler = nil;
var CPDefaultDcmHandler = nil,
CPDecimalNumberUIDs = new CFMutableDictionary();
/*!
@class CPDecimalNumberHandler
@@ -531,6 +532,20 @@ var CPDecimalNumberHandlerRoundingModeKey = @"CPDecimalNumberHandlerRoundi
}
// instance methods
- (CPString)UID
{
var UID = CPDecimalNumberUIDs.valueForKey(self);
if (!UID)
{
UID = objj_generateObjectUID();
CPDecimalNumberUIDs.setValueForKey(self, UID);
}
return UID + "";
}
/*!
Returns a new CPDecimalNumber object with the result of the summation of
the receiver object and \c decimalNumber. If overflow occurs then the
+8 -10
View File
@@ -222,13 +222,13 @@ var CPDictionaryMaxDescriptionRecursion = 10;
{
self = [super init];
if ([objects count] != [keyArray count])
var i = [keyArray count];
if ([objects count] != i)
[CPException raise:CPInvalidArgumentException reason:[CPString stringWithFormat:@"Counts are different.(%d != %d)", [objects count], [keyArray count]]];
if (self)
{
var i = [keyArray count];
while (i--)
{
var value = objects[i],
@@ -273,18 +273,16 @@ var CPDictionaryMaxDescriptionRecursion = 10;
if (self)
{
// The arguments array contains self and _cmd, so the first object is at position 2.
var index = 2;
for (; index < argCount; index += 2)
while (argCount-- > 2)
{
var value = arguments[index],
key = arguments[index + 1];
var key = arguments[argCount--],
value = arguments[argCount]
if (value === nil)
[CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + ((index / 2) - 1) + @"]"];
[CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil object from objects[" + ((argCount / 2) - 1) + @"]"];
if (key === nil)
[CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + ((index / 2) - 1) + @"]"];
[CPException raise:CPInvalidArgumentException reason:@"Attempt to insert nil key from keys[" + ((argCount / 2) - 1) + @"]"];
[self setObject:value forKey:key];
}
+2
View File
@@ -24,6 +24,8 @@
@import "CPObject.j"
@import "CPString.j"
@class CPString
CPInvalidArgumentException = @"CPInvalidArgumentException";
CPUnsupportedMethodException = @"CPUnsupportedMethodException";
CPRangeException = @"CPRangeException";
+21 -19
View File
@@ -164,10 +164,11 @@
+ (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
{
var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
selector = "automaticallyNotifiesObserversOf" + capitalizedKey;
selector = "automaticallyNotifiesObserversOf" + capitalizedKey,
aClass = [self class];
if ([[self class] respondsToSelector:selector])
return objj_msgSend([self class], selector);
if ([aClass respondsToSelector:selector])
return aClass.isa.objj_msgSend0(aClass, selector);
return YES;
}
@@ -175,10 +176,11 @@
+ (CPSet)keyPathsForValuesAffectingValueForKey:(CPString)aKey
{
var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
selector = "keyPathsForValuesAffecting" + capitalizedKey;
selector = "keyPathsForValuesAffecting" + capitalizedKey,
aClass = [self class];
if ([[self class] respondsToSelector:selector])
return objj_msgSend([self class], selector);
if ([aClass respondsToSelector:selector])
return aClass.isa.objj_msgSend0(aClass, selector);
return [CPSet set];
}
@@ -421,7 +423,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
setKey_method_imp(self, _cmd, anObject);
[self didChangeValueForKey:aKey];
}, "");
}, setKey_method.method_types);
}
// FIXME: Deprecated.
@@ -439,7 +441,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
_setKey_method_imp(self, _cmd, anObject);
[self didChangeValueForKey:aKey];
}, "");
}, _setKey_method.method_types);
}
// Ordered To-Many Relationships
@@ -476,7 +478,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChange:CPKeyValueChangeInsertion
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
forKey:aKey];
}, "");
}, insertObject_inKeyAtIndex_method.method_types);
}
if (insertKey_atIndexes_method)
@@ -494,7 +496,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChange:CPKeyValueChangeInsertion
valuesAtIndexes:[indexes copy]
forKey:aKey];
}, "");
}, insertKey_atIndexes_method.method_types);
}
if (removeObjectFromKeyAtIndex_method)
@@ -512,7 +514,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChange:CPKeyValueChangeRemoval
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
forKey:aKey];
}, "");
}, removeObjectFromKeyAtIndex_method.method_types);
}
if (removeKeyAtIndexes_method)
@@ -530,7 +532,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChange:CPKeyValueChangeRemoval
valuesAtIndexes:[indexes copy]
forKey:aKey];
}, "");
}, removeKeyAtIndexes_method.method_types);
}
// These are optional.
@@ -556,7 +558,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChange:CPKeyValueChangeReplacement
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
forKey:aKey];
}, "");
}, replaceObjectInKeyAtIndex_withObject_method.method_types);
}
var replaceKeyAtIndexes_withKey_selector =
@@ -579,7 +581,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChange:CPKeyValueChangeReplacement
valuesAtIndexes:[indexes copy]
forKey:aKey];
}, "");
}, replaceKeyAtIndexes_withKey_method.method_types);
}
}
@@ -613,7 +615,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChangeValueForKey:aKey
withSetMutation:CPKeyValueUnionSetMutation
usingObjects:[CPSet setWithObject:anObject]];
}, "");
}, addKeyObject_method.method_types);
}
if (addKey_method)
@@ -631,7 +633,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChangeValueForKey:aKey
withSetMutation:CPKeyValueUnionSetMutation
usingObjects:[objects copy]];
}, "");
}, addKey_method.method_types);
}
if (removeKeyObject_method)
@@ -649,7 +651,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChangeValueForKey:aKey
withSetMutation:CPKeyValueMinusSetMutation
usingObjects:[CPSet setWithObject:anObject]];
}, "");
}, removeKeyObject_method.method_types);
}
if (removeKey_method)
@@ -667,7 +669,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChangeValueForKey:aKey
withSetMutation:CPKeyValueMinusSetMutation
usingObjects:[objects copy]];
}, "");
}, removeKey_method.method_types);
}
// intersect<Key>: is optional.
@@ -689,7 +691,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[self didChangeValueForKey:aKey
withSetMutation:CPKeyValueIntersectSetMutation
usingObjects:[aSet copy]];
}, "");
}, intersectKey_method.method_types);
}
}
+51 -8
View File
@@ -25,6 +25,8 @@
@import "CPException.j"
@import "CPNotification.j"
@import "CPNull.j"
@import "CPOperationQueue.j"
@import "CPOperation.j"
@import "CPSet.j"
@class _CPNotificationRegistry
@@ -94,13 +96,13 @@ var CPNotificationDefaultCenter = nil;
Adds an entry to the receivers dispatch table with a block, and optional criteria: notification name and sender.
@param aNotificationName the name of the notification the observer wants to watch
@param anObject the object in the notification the observer wants to watch
@param queue is ignored for the moment
@param The operation queue to which block should be added. If you pass nil, the block is run synchronously on the posting thread.
@param block the block to be executed when the notification is received.
*/
- (id <CPObject>)addObserverForName:(CPString)aNotificationName object:(id)anObject queue:(id)queue usingBlock:(Function)block
- (id <CPObject>)addObserverForName:(CPString)aNotificationName object:(id)anObject queue:(CPOperationQueue)queue usingBlock:(Function)block
{
var registry = [self _registryForNotificationName:aNotificationName],
observer = [[_CPNotificationObserver alloc] initWithBlock:block];
observer = [[_CPNotificationObserver alloc] initWithBlock:block queue:queue];
[registry addObserver:observer object:anObject];
@@ -344,9 +346,10 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
/* @ignore */
@implementation _CPNotificationObserver : CPObject
{
id _observer;
Function _block;
SEL _selector;
CPOperationQueue _operationQueue;
id _observer;
Function _block;
SEL _selector;
}
- (id)initWithObserver:(id)anObserver selector:(SEL)aSelector
@@ -360,11 +363,12 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
return self;
}
- (id)initWithBlock:(Function)aBlock
- (id)initWithBlock:(Function)aBlock queue:(CPOperationQueue)aQueue
{
if (self)
{
_block = aBlock;
_operationQueue = aQueue;
}
return self;
@@ -384,7 +388,11 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
{
if (_block)
{
_block(aNotification);
if (!_operationQueue)
_block(aNotification);
else
[_operationQueue addOperation:[[_CPNotificationObserverOperation alloc] initWithBlock:_block notification:aNotification]];
return;
}
@@ -392,3 +400,38 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */
}
@end
/* @ignore */
@implementation _CPNotificationObserverOperation : CPOperation
{
CPNotification _notification;
Function _block;
}
/* @ignore */
- (id)initWithBlock:(Function)aBlock notification:(CPNotification)aNotification
{
self = [super init];
if (self)
{
_block = aBlock;
_notification = aNotification;
}
return self;
}
/* @ignore */
- (void)main
{
_block(_notification);
}
/* @ignore */
- (BOOL)isReady
{
return YES;
}
@end
+328
View File
@@ -0,0 +1,328 @@
/*
* CPNotificationQueue.j
* Foundation
*
* Created by Alexandre Wilhelm.
* Copyright 2015 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPObject.j"
@import "CPNotification.j"
@import "CPNotificationCenter.j"
@typedef CPPostingStyle
/*
@global
@group CPViewAutoresizingMasks
The default resizingMask, the view will not resize or reposition itself.
*/
CPPostWhenIdle = 1;
/*
@global
@group CPPostingStyle
The notification is posted at the end of the current notification callout or timer.
*/
CPPostASAP = 2;
/*
@global
@group CPPostingStyle
The notification is posted immediately after coalescing.
*/
CPPostNow = 3;
@typedef CPNotificationCoalescing
/*
@global
@group CPNotificationCoalescing
Do not coalesce notifications in the queue.
*/
CPNotificationNoCoalescing = 1 << 0;
/*
@global
@group CPNotificationCoalescing
Coalesce notifications with the same name.
*/
CPNotificationCoalescingOnName = 1 << 1;
/*
@global
@group CPNotificationCoalescing
Coalesce notifications with the same object.
*/
CPNotificationCoalescingOnSender = 1 << 2;
var CPNotificationDefaultQueue;
var runLoop = [CPRunLoop mainRunLoop];
/*!
@class CPPostingStyle
@ingroup foundation
@brief CPNotificationQueue objects act as buffers for notification centers (instances of CPNotificationCenter).
Cappuccino provides a framework for sending messages between objects within
a process called notifications. CPNotificationQueue objects (or simply notification queues)
act as buffers for notification centers (instances of CPNotificationCenter).
Whereas a notification center distributes notifications when posted,
notifications placed into the queue can be delayed until the end of the current pass through the run loop
or until the run loop is idle. Duplicate notifications can also be coalesced so that only one notification
is sent although multiple notifications are posted. A notification queue maintains notifications
(instances of C¨Notification) generally in a first in first out (FIFO) order.
When a notification rises to the front of the queue, the queue posts it to the notification center,
which in turn dispatches the notification to all objects registered as observers.
*/
@implementation CPNotificationQueue : CPObject
{
BOOL _runLoopLaunched;
CPMutableArray _postNowNotifications;
CPMutableArray _postIdleNotifications;
CPMutableArray _postASAPNotifications;
CPNotificationCenter _notificationCenter;
}
#pragma mark -
#pragma mark Class methods
/*!
Returns the application's notification queue. This notification queue uses the default notification center
*/
+ (id)defaultQueue
{
if (!CPNotificationDefaultQueue)
CPNotificationDefaultQueue = [[CPNotificationQueue alloc] initWithNotificationCenter:[CPNotificationCenter defaultCenter]];
return CPNotificationDefaultQueue;
}
#pragma mark -
#pragma mark Init methods
/*!
Initializes and returns a notification queue for the specified notification center.
@param anObserver the specified notification center
@return a new CPNotificationQueue
*/
- (id)initWithNotificationCenter:(CPNotificationCenter)aNotificationCenter
{
if (self = [super init])
{
_notificationCenter = aNotificationCenter;
_postNowNotifications = [CPMutableArray new];
_postIdleNotifications = [CPMutableArray new];
_postASAPNotifications = [CPMutableArray new];
}
return self;
}
#pragma mark -
#pragma mark Enqueue methods
/*!
Adds a notification to the notification queue with a specified posting style.
@param notification the notification to add to the queue
@param postingStyle the posting style for the notification
*/
- (void)enqueueNotification:(CPNotification)notification postingStyle:(CPPostingStyle)postingStyle
{
[self enqueueNotification:notification postingStyle:postingStyle coalesceMask:CPNotificationCoalescingOnName|CPNotificationCoalescingOnSender forModes:[CPDefaultRunLoopMode]];
}
/*!
Adds a notification to the notification queue with a specified posting style, criteria for coalescing, and runloop mode.
@param notification the notification to add to the queue
@param postingStyle the posting style for the notification
@param coalesceMask a mask indicating what criteria to use when matching attributes of notification to attributes notifications in the queue.
@modes modes the list of modes the notification may be posted in.
*/
- (void)enqueueNotification:(CPNotification)notification postingStyle:(CPPostingStyle)postingStyle coalesceMask:(CPNotificationCoalescing)coalesceMask forModes:(CPArray)modes
{
[self _removeNotification:notification coalesceMask:coalesceMask];
switch (postingStyle)
{
case CPPostWhenIdle:
[_postIdleNotifications addObject:notification];
break;
case CPPostASAP:
[_postASAPNotifications addObject:notification];
break;
case CPPostNow:
[_postNowNotifications addObject:notification];
break;
}
if ([_postIdleNotifications count] || [_postASAPNotifications count] || [_postNowNotifications count])
[self _runRunLoop];
if (postingStyle == CPPostNow)
{
for (var i = [modes count] - 1; i >= 0; i--)
[[CPRunLoop currentRunLoop] limitDateForMode:modes[i]];
}
}
#pragma mark -
#pragma mark Dequeue methods
/*!
Removes all notifications from the queue that match a provided notification using provided matching criteria.
@param notification the notification to add to the queue
@param coalesceMask mask indicating what criteria to use when matching attributes of notification to remove notifications in the queue.
*/
- (void)dequeueNotificationsMatching:(CPNotification)notification coalesceMask:(CPUInteger)coalesceMask
{
[self _removeNotification:notification coalesceMask:coalesceMask];
}
#pragma mark -
#pragma mark RunLoop methods
/*!
@ignore
*/
- (void)_runRunLoop
{
if (!_runLoopLaunched)
{
[runLoop performSelector:@selector(_launchNotificationsInQueue) target:self argument:nil order:0 modes:[CPDefaultRunLoopMode]];
_runLoopLaunched = YES;
}
}
/*!
@ignore
*/
- (void)_launchNotificationsInQueue
{
_runLoopLaunched = NO;
if ([_postNowNotifications count])
{
[self _launchNotificationsForArray:_postNowNotifications];
[self _runRunLoop];
return;
}
if ([_postASAPNotifications count])
{
[self _launchNotificationsForArray:_postASAPNotifications];
[self _runRunLoop];
return;
}
if ([_postIdleNotifications count])
{
[self _launchNotificationsForArray:_postIdleNotifications];
[self _runRunLoop];
return;
}
}
#pragma mark -
#pragma mark Posting methods
/*!
@ignore
*/
- (void)_launchNotificationsForArray:(CPArray)anArray
{
for (var i = [anArray count] - 1; i >= 0; i--)
{
var notification = anArray[i];
[_notificationCenter postNotification:notification];
}
[anArray removeAllObjects];
}
#pragma mark -
#pragma mark Remove methods
/*!
@ignore
*/
- (void)_removeNotification:(CPNotification)notification coalesceMask:(CPUInteger)coalesceMask
{
[self _removeNotification:notification coalesceMask:coalesceMask inNotifications:_postNowNotifications];
[self _removeNotification:notification coalesceMask:coalesceMask inNotifications:_postASAPNotifications];
[self _removeNotification:notification coalesceMask:coalesceMask inNotifications:_postIdleNotifications];
}
/*!
@ignore
*/
- (void)_removeNotification:(CPNotification)aNotification coalesceMask:(CPUInteger)coalesceMask inNotifications:(CPArray)notifications
{
var notificationsToRemove = [],
name = [aNotification name],
sender = [aNotification object];
for (var i = [notifications count] - 1; i >= 0; i--)
{
var notification = notifications[i];
if (notification == aNotification)
{
[notificationsToRemove addObject:notification];
continue;
}
if (coalesceMask & CPNotificationNoCoalescing)
continue;
if (coalesceMask & CPNotificationCoalescingOnName && coalesceMask & CPNotificationCoalescingOnSender)
{
if ([notification object] == sender && [notification name] == name)
[notificationsToRemove addObject:notification]
continue;
}
if (coalesceMask & CPNotificationCoalescingOnName)
{
if ([notification name] == name)
[notificationsToRemove addObject:notification]
continue;
}
if (coalesceMask & CPNotificationCoalescingOnSender)
{
if ([notification object] == sender)
[notificationsToRemove addObject:notification]
continue;
}
}
[notifications removeObjectsInArray:notificationsToRemove];
}
@end
@@ -66,6 +66,9 @@
if ([_value isKindOfClass:[CPString class]])
return @"\"" + _value + @"\"";
if (_value === [CPNull null])
return @"nil";
return [_value description];
}
+2 -2
View File
@@ -605,11 +605,11 @@
}
if ([self scanPredicateKeyword:@"TRUE"] || [self scanPredicateKeyword:@"YES"])
{
return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:YES]];
return [CPExpression expressionForConstantValue:YES];
}
if ([self scanPredicateKeyword:@"FALSE"] || [self scanPredicateKeyword:@"NO"])
{
return [CPExpression expressionForConstantValue:[CPNumber numberWithBool:NO]];
return [CPExpression expressionForConstantValue:NO];
}
if ([self scanPredicateKeyword:@"SELF"])
{
+12 -10
View File
@@ -47,23 +47,25 @@
_collection = collection;
_variableExpression = variableExpression;
}
return self;
}
- (id)expressionValueWithObject:(id)object context:(id)context
- (id)expressionValueWithObject:(id)object context:(id)aContext
{
var collection = [_collection expressionValueWithObject:object context:context],
count = [collection count],
var collection = [_collection expressionValueWithObject:object context:aContext],
result = [CPArray array],
bindings = @{ [self variable]: [CPExpression expressionForEvaluatedObject] },
i = 0;
variable = [self variable],
context = aContext || @{};
for (; i < count; i++)
if ([context objectForKey:variable] == nil)
[context setObject:[CPExpression expressionForEvaluatedObject] forKey:variable];
[collection enumerateObjectsUsingBlock:function(exp, idx, stop)
{
var item = [collection objectAtIndex:i];
if ([_subpredicate evaluateWithObject:item substitutionVariables:bindings])
[result addObject:item];
}
if ([_subpredicate evaluateWithObject:exp substitutionVariables:context])
[result addObject:exp];
}];
return result;
}
+59 -1
View File
@@ -42,6 +42,7 @@ var _CPRunLoopPerformPool = [],
/* @ignore */
@implementation _CPRunLoopPerform : CPObject
{
Function _block;
id _target;
SEL _selector;
id _argument;
@@ -64,6 +65,7 @@ var _CPRunLoopPerformPool = [],
{
var perform = _CPRunLoopPerformPool.pop();
perform._block = nil;
perform._target = aTarget;
perform._selector = aSelector;
perform._argument = anArgument;
@@ -77,6 +79,26 @@ var _CPRunLoopPerformPool = [],
return [[self alloc] initWithSelector:aSelector target:aTarget argument:anArgument order:anOrder modes:modes];
}
+ (_CPRunLoopPerform)performWithBlock:(Function)aBlock argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes
{
if (_CPRunLoopPerformPool.length)
{
var perform = _CPRunLoopPerformPool.pop();
perform._target = nil;
perform._selector = nil;
perform._block = aBlock;
perform._argument = anArgument;
perform._order = anOrder;
perform._runLoopModes = modes;
perform._isValid = YES;
return perform;
}
return [[self alloc] initWithBlock:aBlock argument:anArgument order:anOrder modes:modes];
}
- (id)initWithSelector:(SEL)aSelector target:(SEL)aTarget argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes
{
self = [super init];
@@ -94,6 +116,22 @@ var _CPRunLoopPerformPool = [],
return self;
}
- (id)initWithBlock:(Function)aBlock argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes
{
self = [super init];
if (self)
{
_block = aBlock;
_argument = anArgument;
_order = anOrder;
_runLoopModes = modes;
_isValid = YES;
}
return self;
}
- (SEL)selector
{
return _selector;
@@ -121,7 +159,10 @@ var _CPRunLoopPerformPool = [],
if ([_runLoopModes containsObject:aRunLoopMode])
{
[_target performSelector:_selector withObject:_argument];
if (_block)
_block(_argument);
else
[_target performSelector:_selector withObject:_argument];
return YES;
}
@@ -225,6 +266,23 @@ var CPRunLoopLastNativeRunLoop = 0;
_orderedPerforms.splice(count + 1, 0, perform);
}
/*!
@ignore
*/
- (void)performBlock:(Function)aBlock argument:(id)anArgument order:(int)anOrder modes:(CPArray)modes
{
var perform = [_CPRunLoopPerform performWithBlock:aBlock argument:anArgument order:anOrder modes:modes],
count = _orderedPerforms.length;
// We sort ourselves in reverse because we iterate this list backwards.
while (count--)
if (anOrder < [_orderedPerforms[count] order])
break;
_orderedPerforms.splice(count + 1, 0, perform);
}
/*!
Cancels the specified selector and target.
@param aSelector the selector of the method to invoke
+1 -1
View File
@@ -266,7 +266,7 @@ var abbreviationDictionary,
@"MST" : [@"Mountain Standard Time", @"MST", @"Mountain Daylight Time", @"MDT", @"Mountain Time", @"MT"],
@"PHT" : [@"Philippine Standard Time", @"GMT+08:00", @"Philippine Summer Time", @"GMT+08:00", @"Philippine Standard Time", @"Philippines Time"],
@"WET" : [@"Western European Standard Time", @"GMT", @"Western European Summer Time", @"GMT+01:00", @"Western European Time", @"Portugal Time (Lisbon)"]
};
};
var date = [CPDate date],
abbreviation = String(String(date).split("(")[1]).split(")")[0];
+178 -36
View File
@@ -25,6 +25,18 @@
@import "CPRunLoop.j"
@import "CPURLRequest.j"
@import "CPURLResponse.j"
@import "CPOperationQueue.j"
@import "CPOperation.j"
@protocol CPURLConnectionDelegate <CPObject>
- (void)connection:(CPURLConnection)anURLConnection didFailWithError:(CPException)anError;
- (void)connection:(CPURLConnection)anURLConnection didReceiveData:(CPString)aData;
- (void)connection:(CPURLConnection)anURLConnection didReceiveResponse:(CPString)aResponse;
- (void)connectionDidFinishLoading:(CPURLConnection)anURLConnection;
- (void)connectionDidReceiveAuthenticationChallenge:(CPURLConnection)anURLConnection;
@end
@typedef HTTPRequest
@@ -76,16 +88,19 @@ var CPURLConnectionDelegate = nil;
*/
@implementation CPURLConnection : CPObject
{
CPURLRequest _originalRequest @accessors(readonly, getter=originalRequest);
CPURLRequest _request @accessors(readonly, getter=currentRequest);
id _delegate;
BOOL _isCanceled;
BOOL _isLocalFileConnection;
CPURLRequest _originalRequest @accessors(readonly, getter=originalRequest);
CPURLRequest _request @accessors(readonly, getter=currentRequest);
id <CPURLConnectionDelegate> _delegate;
BOOL _isCanceled;
BOOL _isLocalFileConnection;
HTTPRequest _HTTPRequest;
HTTPRequest _HTTPRequest;
CPOperationQueue _operationQueue;
CPOperation _connectionOperation @accessors(readonly, getter=operation);
}
+ (void)setClassDelegate:(id)delegate
+ (void)setClassDelegate:(id <CPURLConnectionDelegate>)delegate
{
CPURLConnectionDelegate = delegate;
}
@@ -127,6 +142,18 @@ var CPURLConnectionDelegate = nil;
return nil;
}
/*
Loads the data for a URL request and executes a function on an operation queue when the request completes or fails.
@param aRequest contains the URL to obtain data from.
@param aQueue The operation queue to which the function is dispatched when the request completes or failed.
@param aHandler The function to execute.
@discussion If the request completes successfully, the data parameter of the function contains the resource data, and the error parameter is nil. If the request fails, the data parameter is nil and the error parameter contain information about the failure.
*/
+ (CPURLConnection)sendAsynchronousRequest:(CPURLRequest)aRequest queue:(CPOperationQueue)aQueue completionHandler:(Function)aHandler
{
return [[self alloc] _initWithRequest:aRequest queue:aQueue completionHandler:aHandler];
}
/*
Creates a url connection with a delegate to monitor the request progress.
@param aRequest contains the URL to obtain data from
@@ -145,37 +172,63 @@ var CPURLConnectionDelegate = nil;
@param shouldStartImmediately whether the \c -start method should be called from here
@return the initialized url connection
*/
- (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately
- (id)initWithRequest:(CPURLRequest)aRequest delegate:(id <CPURLConnectionDelegate>)aDelegate startImmediately:(BOOL)shouldStartImmediately
{
self = [super init];
if (self)
{
_request = aRequest;
_originalRequest = [aRequest copy];
_delegate = aDelegate;
_isCanceled = NO;
_operationQueue = nil;
_connectionOperation = nil;
var URL = [_request URL],
scheme = [URL scheme];
[self _initWithRequest:aRequest];
}
// Browsers use "file:", Titanium uses "app:"
_isLocalFileConnection = scheme === "file" ||
((scheme === "http" || scheme === "https") &&
window.location &&
(window.location.protocol === "file:" || window.location.protocol === "app:"));
if (shouldStartImmediately)
[self start];
_HTTPRequest = new CFHTTPRequest();
_HTTPRequest.setWithCredentials([aRequest withCredentials]);
return self;
}
if (shouldStartImmediately)
[self start];
- (void)_initWithRequest:(CPURLRequest)aRequest
{
_request = aRequest;
_originalRequest = [aRequest copy];
_isCanceled = NO;
var URL = [_request URL],
scheme = [URL scheme];
// Browsers use "file:", Titanium uses "app:"
_isLocalFileConnection = scheme === "file" ||
((scheme === "http" || scheme === "https") &&
window.location &&
(window.location.protocol === "file:" || window.location.protocol === "app:"));
_HTTPRequest = new CFHTTPRequest();
_HTTPRequest.setTimeout([aRequest timeoutInterval] * 1000);
_HTTPRequest.setWithCredentials([aRequest withCredentials]);
}
- (id)_initWithRequest:(CPURLRequest)aRequest queue:(CPOperationQueue)aQueue completionHandler:(Function)aHandler
{
self = [super init];
if (self)
{
_delegate = nil;
_operationQueue = aQueue;
_connectionOperation = [[_AsynchronousConnectionOperation alloc] initWithFunction:aHandler];
[self _initWithRequest:aRequest];
[self start];
}
return self;
}
- (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate
- (id)initWithRequest:(CPURLRequest)aRequest delegate:(id <CPURLConnectionDelegate>)aDelegate
{
return [self initWithRequest:aRequest delegate:aDelegate startImmediately:YES];
}
@@ -200,6 +253,7 @@ var CPURLConnectionDelegate = nil;
_HTTPRequest.open([_request HTTPMethod], [[_request URL] absoluteString], YES);
_HTTPRequest.onreadystatechange = function() { [self _readyStateDidChange]; };
_HTTPRequest.ontimeout = function() { [self _didTimeout]; };
var fields = [_request allHTTPHeaderFields],
key = nil,
@@ -212,11 +266,18 @@ var CPURLConnectionDelegate = nil;
}
catch (anException)
{
if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)])
[_delegate connection:self didFailWithError:anException];
[self _sendDelegateDidFailWithError:anException];
}
}
- (void)_sendDelegateDidFailWithError:(CPException)anException
{
if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)])
[_delegate connection:self didFailWithError:anException];
else if (_connectionOperation !== nil)
[self _connectionOperationDidReceiveResponse:nil data:nil error:anException];
}
/*
Cancels the current request.
*/
@@ -227,6 +288,9 @@ var CPURLConnectionDelegate = nil;
try
{
_HTTPRequest.abort();
if (_connectionOperation)
[_connectionOperation cancel];
}
// We expect an exception in some browsers like FireFox.
catch (anException)
@@ -239,10 +303,21 @@ var CPURLConnectionDelegate = nil;
return _isLocalFileConnection;
}
/*!
@ignore
*/
- (void)_didTimeout
{
var exception = [CPException exceptionWithName:@"Timeout exception"
reason:"The request timed out."
userInfo:@{}];
[self _sendDelegateDidFailWithError:exception];
}
/* @ignore */
- (void)_readyStateDidChange
{
if (_HTTPRequest.readyState() === CFHTTPRequest.CompleteState)
if (_HTTPRequest.readyState() === CFHTTPRequest.CompleteState && !_HTTPRequest.isTimeoutRequest())
{
var statusCode = _HTTPRequest.status(),
URL = [_request URL];
@@ -251,23 +326,27 @@ var CPURLConnectionDelegate = nil;
[CPURLConnectionDelegate connectionDidReceiveAuthenticationChallenge:self];
else
{
if ([_delegate respondsToSelector:@selector(connection:didReceiveResponse:)])
var response;
if (_isLocalFileConnection)
response = [[CPURLResponse alloc] initWithURL:URL];
else
{
if (_isLocalFileConnection)
[_delegate connection:self didReceiveResponse:[[CPURLResponse alloc] initWithURL:URL]];
else
{
var response = [[CPHTTPURLResponse alloc] initWithURL:URL];
[response _setStatusCode:statusCode];
[response _setAllResponseHeaders:_HTTPRequest.getAllResponseHeaders()];
[_delegate connection:self didReceiveResponse:response];
}
response = [[CPHTTPURLResponse alloc] initWithURL:URL];
[response _setStatusCode:statusCode];
[response _setAllResponseHeaders:_HTTPRequest.getAllResponseHeaders()];
}
if ([_delegate respondsToSelector:@selector(connection:didReceiveResponse:)])
[_delegate connection:self didReceiveResponse:response];
if (!_isCanceled)
{
if ([_delegate respondsToSelector:@selector(connection:didReceiveData:)])
[_delegate connection:self didReceiveData:_HTTPRequest.responseText()];
else if (_connectionOperation !== nil)
[self _connectionOperationDidReceiveResponse:response data:_HTTPRequest.responseText() error:nil];
if ([_delegate respondsToSelector:@selector(connectionDidFinishLoading:)])
[_delegate connectionDidFinishLoading:self];
}
@@ -283,6 +362,69 @@ var CPURLConnectionDelegate = nil;
return _HTTPRequest;
}
- (void)_connectionOperationDidReceiveResponse:(CPURLResponse)aResponse data:(CPData)aData error:(CPError)anError
{
[_connectionOperation _setResponse:aResponse data:aData error:anError];
if (_operationQueue)
[_operationQueue addOperation:_connectionOperation];
else
{
// Do we need to send CPOperation KVO notifications ?
[_connectionOperation main];
}
}
@end
/* @ignore */
@implementation _AsynchronousConnectionOperation : CPOperation
{
BOOL _didReceiveResponse;
CPURLResponse _response;
CPData _data;
CPError _error;
Function _operationFunction;
}
/* @ignore */
- (id)initWithFunction:(Function)aFunction
{
self = [super init];
if (self)
{
_didReceiveResponse = NO;
_response = nil;
_data = nil;
_error = nil;
_operationFunction = aFunction;
}
return self;
}
- (void)_setResponse:(CPURLResponse)aResponse data:(CPData)aData error:(CPError)anError
{
_didReceiveResponse = YES;
_response = aResponse;
_data = aData;
_error = anError;
}
/* @ignore */
- (void)main
{
_operationFunction(_response, _data, _error);
}
/* @ignore */
- (BOOL)isReady
{
return (_didReceiveResponse && [super isReady]);
}
@end
@implementation CPURLConnection (Deprecated)
+71 -9
View File
@@ -25,6 +25,12 @@
@import "CPString.j"
@import "CPURL.j"
@typedef CPURLRequestCachePolicy
CPURLRequestUseProtocolCachePolicy = 0;
CPURLRequestReloadIgnoringLocalCacheData = 1;
CPURLRequestReturnCacheDataElseLoad = 2;
CPURLRequestReturnCacheDataDontLoad = 3;
/*!
@class CPURLRequest
@ingroup foundation
@@ -35,14 +41,16 @@
*/
@implementation CPURLRequest : CPObject
{
CPURL _URL @accessors(property=URL);
CPURL _URL @accessors(property=URL);
// FIXME: this should be CPData
CPString _HTTPBody @accessors(property=HTTPBody);
CPString _HTTPMethod @accessors(property=HTTPMethod);
BOOL _withCredentials @accessors(property=withCredentials);
CPString _HTTPBody @accessors(property=HTTPBody);
CPString _HTTPMethod @accessors(property=HTTPMethod);
BOOL _withCredentials @accessors(property=withCredentials);
CPDictionary _HTTPHeaderFields @accessors(readonly, getter=allHTTPHeaderFields);
CPDictionary _HTTPHeaderFields @accessors(readonly, getter=allHTTPHeaderFields);
CPTimeInterval _timeoutInterval @accessors(readonly, getter=timeoutInterval);
CPURLRequestCachePolicy _cachePolicy @accessors(readonly, getter=cachePolicy);
}
/*!
@@ -55,6 +63,18 @@
return [[CPURLRequest alloc] initWithURL:aURL];
}
/*!
Creates a request with a specified URL, cachePolicy and timeoutInterval
@param aURL the URL of the request
@param aCachePolicy the cache policy of the request
@param aTimeoutInterval the timeoutInterval of the request
@return a CPURLRequest
*/
+ (id)requestWithURL:(CPURL)anURL cachePolicy:(CPURLRequestCachePolicy)aCachePolicy timeoutInterval:(CPTimeInterval)aTimeoutInterval
{
return [[CPURLRequest alloc] initWithURL:anURL cachePolicy:aCachePolicy timeoutInterval:aTimeoutInterval];
}
/*!
Equal to `[receiver initWithURL:nil]`.
*/
@@ -63,6 +83,25 @@
return [self initWithURL:nil];
}
/*!
Initializes the request with a URL. This is the designated initializer.
@param aURL the url to set
@param aCachePolicy the cache policy of the request
@param aTimeoutInterval the timeoutInterval of the request
@return the initialized CPURLRequest
*/
- (id)initWithURL:(CPURL)anURL cachePolicy:(CPURLRequestCachePolicy)aCachePolicy timeoutInterval:(CPTimeInterval)aTimeoutInterval
{
if (self = [self initWithURL:anURL])
{
_cachePolicy = aCachePolicy;
_timeoutInterval = aTimeoutInterval;
}
return self;
}
/*!
Initializes the request with a URL. This is the designated initializer.
@@ -71,9 +110,7 @@
*/
- (id)initWithURL:(CPURL)aURL
{
self = [super init];
if (self)
if (self = [super init])
{
[self setURL:aURL];
@@ -81,9 +118,34 @@
_HTTPMethod = @"GET";
_HTTPHeaderFields = @{};
_withCredentials = NO;
_timeoutInterval = 60.0;
_cachePolicy = CPURLRequestUseProtocolCachePolicy;
[self setValue:"Thu, 01 Jan 1970 00:00:00 GMT" forHTTPHeaderField:"If-Modified-Since"];
[self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"];
switch (_cachePolicy)
{
case CPURLRequestUseProtocolCachePolicy:
// TODO: implement everything about cache...
[self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"];
break;
case CPURLRequestReturnCacheDataElseLoad:
[self setValue:"max-stale=31536000" forHTTPHeaderField:"Cache-Control"];
break;
case CPURLRequestReturnCacheDataDontLoad:
[self setValue:"only-if-cached" forHTTPHeaderField:"Cache-Control"];
break;
case CPURLRequestReloadIgnoringLocalCacheData:
[self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"];
break;
default:
[self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"];
}
[self setValue:"XMLHttpRequest" forHTTPHeaderField:"X-Requested-With"];
}
+109
View File
@@ -0,0 +1,109 @@
/*
* CPUserNotification.j
* Foundation
*
* Created by Alexandre Wilhelm.
* Copyright 2015, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPAttributedString.j"
@import "CPArray.j"
@import "CPDate.j"
@import "CPDictionary.j"
@import "CPObject.j"
@import "CPTimeZone.j"
// We need something from the AppKit in Foundation ???
@class CPImage
@typedef CPUserNotificationAction
@typedef CPUserNotificationActivationType
/*!
@global CPUserNotificationActivationType
@group CPUserNotificationActivationType
The user did not interact with the notification alert.
*/
CPUserNotificationActivationTypeNone = 0;
/*!
@global CPUserNotificationActivationType
@group CPUserNotificationActivationType
The user clicked on the contents of the notification alert.
*/
CPUserNotificationActivationTypeContentsClicked = 1;
/*!
@global CPUserNotificationActivationType
@group CPUserNotificationActivationType
The user clicked on the action button of the notification alert.
*/
CPUserNotificationActivationTypeActionButtonClicked = 2;
/*!
@global CPUserNotificationActivationType
@group CPUserNotificationActivationType
The user replied to the notification.
*/
CPUserNotificationActivationTypeReplied = 3,
/*!
@global CPUserNotificationActivationType
@group CPUserNotificationActivationType
The user clicked on the additional action button of the notification alert.
*/
CPUserNotificationActivationTypeAdditionalActionClicked = 4;
/*!
@class CPUserNotification
@ingroup foundation
@brief The CPUserNotification class is used to configure a notification that is scheduled for display by the UserNotificationCenter class.
*/
@implementation CPUserNotification : CPObject
{
BOOL _presented @accessors(getter=isPresented);
BOOL _remote @accessors(getter=isRemote);
CPDate _actualDeliveryDate @accessors(getter=actualDeliveryDate);
CPDate _deliveryDate @accessors(property=deliveryDate);
CPDictionary _userInfo @accessors(property=userInfo);
CPImage _contentImage @accessors(property=contentImage);
CPString _identifier @accessors(property=identifier);
CPString _informativeText @accessors(property=informativeText);
CPString _title @accessors(property=title);
CPTimeInterval _deliveryRepeatInterval @accessors(property=deliveryRepeatInterval);
CPTimeZone _deliveryTimeZone @accessors(property=deliveryTimeZone);
CPUserNotificationActivationType _activationType @accessors(getter=activationType);
}
#pragma mark -
#pragma mark Creating an user notification
- (id)init
{
self = [super init];
if (self)
{
_identifier = [self UID];
}
return self
}
@end
+293
View File
@@ -0,0 +1,293 @@
/*
* CPUserNotificationCenter.j
* Foundation
*
* Created by Alexandre Wilhelm.
* Copyright 2015, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPArray.j"
@import "CPObject.j"
@import "CPTimer.j"
@import "CPUserNotification.j"
@protocol CPUserNotificationCenterDelegate <CPObject>
@optional
- (BOOL)userNotificationCenter:(CPUserNotificationCenter)center shouldPresentNotification:(CPUserNotification)notification;
- (void)userNotificationCenter:(CPUserNotificationCenter)center didDeliverNotification:(CPUserNotification)notification;
- (void)userNotificationCenter:(CPUserNotificationCenter)center didActivateNotification:(CPUserNotification)notification;
@end
// Remove compiling warnings
@class Notification
@global CPApp
var CPUserNotificationCenterDelegate_userNotificationCenter_shouldPresentNotification_ = 1 << 0,
CPUserNotificationCenterDelegate_userNotificationCenter_didDeliverNotification_ = 1 << 1,
CPUserNotificationCenterDelegate_userNotificationCenter_didActivateNotification_ = 1 << 2;
var CPUserNotificationDefaultCenter = nil;
/*!
@class CPUserNotificationCenter
@ingroup foundation
@brief The CPUserNotificationCenter class delivers user notifications to the user from applications or helper applications.
*/
@implementation CPUserNotificationCenter : CPObject
{
CPArray _deliveredNotifications @accessors(property=deliveredNotifications);
CPArray _scheduledNotifications @accessors(property=scheduledNotifications);
id <CPUserNotificationCenterDelegate> _delegate;
CPInteger _implementedDelegateMethods;
CPMutableDictionary _timersForUserNotification;
}
#pragma mark -
#pragma mark Creating Default User Notification Center
/*!
Returns the user's notification center
*/
+ (CPNotificationCenter)defaultUserNotificationCenter
{
if (!CPUserNotificationDefaultCenter)
CPUserNotificationDefaultCenter = [[CPUserNotificationCenter alloc] init];
return CPUserNotificationDefaultCenter;
}
- (id)init
{
self = [super init];
if (self)
{
_deliveredNotifications = [];
_scheduledNotifications = [];
_timersForUserNotification = @{};
}
return self;
}
#pragma mark -
#pragma mark Getting and Setting the Delegate
- (void)setDelegate:(id <CPUserNotificationCenterDelegate>)aDelegate
{
if (_delegate === aDelegate)
return;
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(userNotificationCenter:shouldPresentNotification:)])
_implementedDelegateMethods |= CPUserNotificationCenterDelegate_userNotificationCenter_shouldPresentNotification_;
if ([_delegate respondsToSelector:@selector(userNotificationCenter:didDeliverNotification:)])
_implementedDelegateMethods |= CPUserNotificationCenterDelegate_userNotificationCenter_didDeliverNotification_;
if ([_delegate respondsToSelector:@selector(userNotificationCenter:didActivateNotification:)])
_implementedDelegateMethods |= CPUserNotificationCenterDelegate_userNotificationCenter_didActivateNotification_;
}
#pragma mark -
#pragma mark Managing the Scheduled Notification Queue
/*!
Schedules the given user notification
@param anUserNotification the user notification
*/
- (void)scheduleNotification:(CPUserNotification)anUserNotification
{
var scheduledDate = [[anUserNotification deliveryDate] copy];
[scheduledDate _dateWithTimeZone:[anUserNotification deliveryTimeZone]];
var timer = [[CPTimer alloc] initWithFireDate:scheduledDate
interval:[anUserNotification deliveryRepeatInterval]
target:self
selector:@selector(_scheduledUserNotificationTimerDidFire:)
userInfo:anUserNotification
repeats:([anUserNotification deliveryRepeatInterval] ? YES : NO)];
[_scheduledNotifications addObject:anUserNotification];
_timersForUserNotification[[anUserNotification UID]] = timer;
[[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
}
- (void)_scheduledUserNotificationTimerDidFire:(CPTimer)aTimer
{
[self deliverNotification:[aTimer userInfo]];
}
/*!
Removes the given user notification for the scheduled notifications.
@param anUserNotification the user notification
*/
- (void)removeScheduledNotification:(CPUserNotification)anUserNotification
{
if ([_scheduledNotifications indexOfObject:anUserNotification] != CPNotFound)
{
[_scheduledNotifications removeObject:anUserNotification];
[_timersForUserNotification[[anUserNotification UID]] invalidate];
delete _timersForUserNotification[[anUserNotification UID]];
}
}
#pragma mark -
#pragma mark Managing the Delivered Notifications
/*!
Deliver the given user notification
@param aNotification the user notification
*/
- (void)deliverNotification:(CPUserNotification)aNotification
{
[self _launchUserNotification:aNotification];
}
/*!
Remove a delivered user notification from the user notification center.
@param aNotification the user notification
*/
- (void)removeDeliveredNotification:(CPUserNotification)aNotification
{
[_deliveredNotifications removeObject:aNotification];
}
/*!
Remove all delivered user notifications from the user notification center.
*/
- (void)removeAllDeliveredNotifications
{
[_deliveredNotifications removeAllObjects];
}
#pragma mark -
#pragma mark Permission Utilities
- (void)_askPermissionForUserNotification:(CPUserNotification)anUserNotification
{
Notification.requestPermission(function (permission) {
if (permission == "granted")
// We need to relaunch the notification if the permission are granted
[self _launchUserNotification:anUserNotification];
});
}
#pragma mark -
#pragma mark Notification Utilities
- (void)_launchUserNotification:(CPUserNotification)anUserNotification
{
// If the browser version is unsupported, remain silent.
if (!window || !'Notification' in window)
return;
if (Notification.permission === 'default')
[self _askPermissionForUserNotification:anUserNotification];
if (Notification.permission === 'granted')
{
if (([self _delegateRespondsToShouldPresentNotification] && [self _sendDelegateShouldPresentNotification:anUserNotification])
|| ![CPApp isActive])
{
var notification = new Notification(
[anUserNotification title],
{
'body': [anUserNotification informativeText],
'icon': [[anUserNotification contentImage] filename],
// ...prevent duplicate notifications
'tag' : [anUserNotification identifier]
}
);
anUserNotification._presented = YES;
notification.onclick = function () {
anUserNotification._activationType = CPUserNotificationActivationTypeContentsClicked;
[self _sendDelegateDidActivateNotification:anUserNotification];
// Remove the notification from Notification Center when clicked.
this.close();
};
// Callback function when the notification is closed.
notification.onclose = function () {
};
}
else
{
anUserNotification._presented = NO;
}
anUserNotification._activationType = CPUserNotificationActivationTypeNone;
anUserNotification._actualDeliveryDate = [CPDate date];
[_deliveredNotifications addObject:anUserNotification];
if (![anUserNotification deliveryRepeatInterval])
[self removeScheduledNotification:anUserNotification];
[self _sendDelegateDidDeliverNotification:anUserNotification];
}
}
@end
@implementation CPUserNotificationCenter (CPUserNotificationCenterDelegate)
- (BOOL)_delegateRespondsToShouldPresentNotification
{
return _implementedDelegateMethods & CPUserNotificationCenterDelegate_userNotificationCenter_shouldPresentNotification_;
}
- (BOOL)_sendDelegateShouldPresentNotification:(CPUserNotification)aNotification
{
return [_delegate userNotificationCenter:self shouldPresentNotification:aNotification];
}
- (void)_sendDelegateDidActivateNotification:(CPUserNotification)aNotification
{
if (!(_implementedDelegateMethods & CPUserNotificationCenterDelegate_userNotificationCenter_didActivateNotification_))
return;
[_delegate userNotificationCenter:self didActivateNotification:aNotification];
}
- (void)_sendDelegateDidDeliverNotification:(CPUserNotification)aNotification
{
if (!(_implementedDelegateMethods & CPUserNotificationCenterDelegate_userNotificationCenter_didDeliverNotification_))
return;
[_delegate userNotificationCenter:self didDeliverNotification:aNotification];
}
@end

Some files were not shown because too many files have changed in this diff Show More