Compare commits

...
239 Commits
Author SHA1 Message Date
release 535a38dec0 Changed version number to 1.0.0.021 2011-09-30 14:58:43 -05:00
release 46c78c3fe0 Changed version number to 1.0.0.021-SNAPSHOT 2011-09-20 19:58:56 -05:00
release 9b0a0b8bec Changed version number to 1.0.0.020 2011-09-20 19:58:50 -05:00
Aparajita Fishman db32b10551 Tweaked focus rings, fixed disabled placeholder color 2011-09-20 15:05:22 -07:00
Aparajita Fishman d8d2670bf4 Text tweak 2011-09-20 15:04:14 -07:00
Aparajita Fishman 9bc56bed86 Linting 2011-09-20 11:47:58 -07:00
Aparajita Fishman 66629d8e32 Merge branch 'master' of github.com:intalio/cappuccino 2011-09-20 11:46:57 -07:00
Aparajita Fishman c3c4a09c8c Fixed CPComboBox not resigning before first list popup 2011-09-20 11:46:34 -07:00
release be7ac48c2b Changed version number to 1.0.0.020-SNAPSHOT 2011-09-20 04:55:13 -05:00
release be0b97cb56 Changed version number to 1.0.0.019 2011-09-20 04:55:07 -05:00
Aparajita Fishman 6f7cbe21ef Main Jakefile improvements...
- 'jake clean' now cleans only the .build directories instead of doing the same as clobber.
- Added task 'clean-sprites', cleans sprited images.
- Added task 'clobber-theme', clobbers all Aristo.blend and Aristo.build directories.
2011-09-19 21:38:35 -07:00
Aparajita Fishman eff3dfc74a Merge branch 'master' of github.com:intalio/cappuccino 2011-09-19 11:12:57 -07:00
Aparajita Fishman 2ad1f4e4e5 Merge branch 'master' of github.com:cappuccino/cappuccino
Conflicts:
	Tests/Manual/CPTokenFieldTest/AppController.j
2011-09-18 16:04:42 -07:00
Alexander Ljungberg 4aec3f7466 Refs #1355. Manual window title test.
In the PlatformWindows test, change the window title both when theWindow is a non platform window and when it is a platform window to verify that the browser window title changes only when it should.
2011-09-17 11:07:38 +01:00
Alexander Ljungberg c8991e07b9 Merge pull request #1355 from primalmotion/cappuccino
---

if setTitle: is called on a CPWindow with the primaryPlatformWindow as platformWindow,
the main pages title is changed. This patch prevent changing the primaryPlatformWindows title.
This is not optimal, but this is better than changing the title at each window setTitle: call
2011-09-17 10:55:43 +01:00
Alexander Ljungberg 48157db973 Refs #1360. Fixed: index was always 0 in CPTokenField tokenField:shouldAddObjects:atIndex: delegate method. 2011-09-16 21:52:09 +01:00
Alexander Ljungberg d070e5fce5 Merge branch 'master' of github.com:cappuccino/cappuccino 2011-09-16 21:47:20 +01:00
Alexander Ljungberg a8fae51ea8 Refs #1360. Cursor movement fixes, optimisations.
Don't move the cursor forward if no token was inserted. Only count new tokens once.
2011-09-16 21:46:20 +01:00
Alexander Ljungberg 2815d462be Refs #1360. Test for CPTokenField tokenField:shouldAddObjects:atIndex:. 2011-09-16 20:51:45 +01:00
release ed002d7d3e Changed version number to 1.0.0.019-SNAPSHOT 2011-09-16 13:44:57 -05:00
release a7994c1480 Changed version number to 1.0.0.018 2011-09-16 13:44:51 -05:00
Alexander Ljungberg 85f8ca9241 Refs #1360. Format code. 2011-09-16 19:29:53 +01:00
Alexander Ljungberg b5fb9e8c23 Merge pull request #1360 from jigzat/cappuccino
---

Enabled tokenField:shouldAddObjects:atIndex delegate method and used on _autocompleteWithDOMEvent method to have some control over tokens. Not sure why wasnt enabled, it needed very few code. Maybe no one needed it til now.

This is my first collaboration ever, so Im not sure if is of any use.
2011-09-16 19:10:23 +01:00
release c605ca4531 Changed version number to 1.0.0.018-SNAPSHOT 2011-09-16 12:37:07 -05:00
release c288930a3c Changed version number to 1.0.0.017 2011-09-16 12:36:59 -05:00
release 596676a562 Changed version number to 1.0.0.017-SNAPSHOT 2011-09-16 06:01:02 -05:00
release cc3d9f337c Changed version number to 1.0.0.016 2011-09-16 06:00:55 -05:00
Klaas Pieter Annema 1addc002d3 Merge branch 'master' of github.com:cappuccino/cappuccino 2011-09-16 12:00:05 +02:00
Klaas Pieter Annema e99e2660e9 more fixes and tests for _CPKVCArray 2011-09-16 11:43:42 +02:00
Aparajita Fishman 03d1bb8bce Added missing statement separator, concatenated var block, fixed symlink deletion/creation sometimes failing. 2011-09-15 17:09:15 -07:00
Aparajita Fishman 8d210100a5 Fix for symlink creation sometimes failing. 2011-09-15 16:41:52 -07:00
Aparajita Fishman d25e96902f Added missing statement separator, concatenated var block 2011-09-15 14:56:24 -07:00
Aparajita Fishman ad792d9772 Fixed problem with FILE.remove sometimes failing to delete the symbolic link. 2011-09-15 14:28:57 -07:00
Klaas Pieter Annema 5b00f7f2e8 improve _CPKVCArray
_CPKVCArray now consistently calls both single and multiple indexes accessors consistently
Tests that verify the behavior are included
2011-09-15 22:32:23 +02:00
Klaas Pieter Annema 97b7f0901e add tests for key view loop behavior 2011-09-15 17:50:37 +02:00
Klaas Pieter Annema 67b2671dad improve key view loops
- include the window's content view at the start of the key view loop
- set the initial first responder to the window's content view (and keep it that way unless it is explicitly changed)
- CPWindow should return NO from acceptsFirstResponder (Cocoa compliance).
- change the type of setInitialFirstResponder, this is compliant with Cocoa. The new key view loop code will break if initial first responder is anything but a view
- change the implementation of selectNextKeyView:, selectPreviousKeyView:, selectKeyViewFollowingView: and selectKeyViewPrecedingView: to follow to Cocoa's documentation
2011-09-15 16:39:24 +02:00
Aparajita Fishman 9e7b737960 Merge branch 'master' of github.com:intalio/cappuccino 2011-09-13 12:36:53 -07:00
release 9bc6a69b76 Changed version number to 1.0.0.016-SNAPSHOT 2011-09-10 05:58:01 -05:00
release 1cb28e4206 Changed version number to 1.0.0.015 2011-09-10 05:57:56 -05:00
release 5311689265 Changed version number to 1.0.0.015-SNAPSHOT 2011-09-10 04:58:47 -05:00
release 4228a2a146 Changed version number to 1.0.0.014 2011-09-10 04:58:42 -05:00
aparajita 7886d49e65 Merge pull request #1359 from primalmotion/fix-and-improve-doxygen-theme
Fix and improve doxygen theme
2011-09-09 21:48:17 -07:00
Samir Gartner e5284e9b43 Fixed some possible error of my part where selectedRange is moved beyond bounds 2011-09-09 22:31:17 -05:00
Samir Gartner c6403baa04 Fixed cursor position issue in a little escapade...guess that's all for now folks 2011-09-09 13:28:37 -05:00
Samir Gartner 662b1ec696 Submitted the right CPTokenField file, was missing the part to use the delegate approved tokens. Still lacks a way to put the cursor after the last token (if the delegate returns more than one token) 2011-09-09 12:48:18 -05:00
Samir Gartner 043df71185 Enabled tokenField:shouldAddObjects:atIndex delegate method and used on _autocompleteWithDOMEvent method to have some control over tokens. Not sure why wasn't enabled, it was very few code. Maybe no one needed it 'til now 2011-09-08 22:56:52 -05:00
Antoine Mercadal 8dab47f4da mozilla compat 2011-09-08 18:12:32 +02:00
Alexander Ljungberg 90d069df2b Refs #1343. Remove now obsolete README.md.
Everything in the README has been fixed except 3 and 4. 4 is outside of the issue scope and 3 works as intended (nearly). The array controller does not have `setAvoidsEmptySelection:NO` so an empty selection should not be possible unless there are no items.
2011-09-08 17:02:15 +01:00
Antoine Mercadal dcbe3ee650 fix paremeters touching their description in bubbles 2011-09-08 17:15:12 +02:00
Antoine Mercadal e32489aba1 tiny padding improvment in generated html docs 2011-09-08 17:08:50 +02:00
Antoine Mercadal b619c0b793 some small graphical improvments 2011-09-08 16:55:12 +02:00
Antoine Mercadal fab61d14fa forget to restore the bg color of the tree view 2011-09-08 14:44:05 +02:00
Antoine Mercadal b4edc084f8 add Cappuccino logo 2011-09-08 14:00:20 +02:00
Antoine Mercadal c7518d0c49 merge Aparajita's previous improvments on the CSS 2011-09-08 12:44:10 +02:00
Antoine Mercadal a40345c14d fix the slider 2011-09-08 11:46:20 +02:00
Aparajita Fishman bacc50c271 Merge remote branch 'refs/remotes/cappuccino/master'
Conflicts:
	AppKit/CPArrayController.j
	Foundation/CPKeyValueObserving.j
	Tests/Manual/Bindings/AppController.j
2011-09-07 22:04:42 -04:00
Antoine Mercadal 909471a5d7 fix the documentation generation process 2011-09-08 01:30:58 +02:00
Antoine Mercadal 38f7ea00c2 update doxygen.css 2011-09-08 00:43:32 +02:00
Alexander Ljungberg 9f459f053e Fixed: several CPArrayController methods returned undefined instead of booleans. 2011-09-07 18:17:35 +01:00
Alexander Ljungberg 4195f3b8c6 Test that CPArrayController setSelectionIndexes: does not modify its input. 2011-09-07 18:02:53 +01:00
Alexander Ljungberg 4926824482 Fixed: CPArrayController setSelectionIndexes: sometimes modified its input.
Also very slightly improved performance as sometimes the input index set would be instantiated internally and then needlessly copied.
2011-09-07 17:53:48 +01:00
Alexander Ljungberg cb2f539cf7 Send didChange in reverse order of willChange in CPObjectController setContent:. 2011-09-07 17:32:19 +01:00
Alexander Ljungberg 7f31be7cb1 Fixed: array replaceObjectsInRange:withObjectsFromArray: always failed with a "'null' is not an object' error.
This was caused by the new range checks in f8404c7a97.
2011-09-07 16:18:08 +01:00
Alexander Ljungberg 78b9c5b576 Fixes #1343. Support nested willChange… didChange… calls.
When multiple calls are nested, only the first willChange… and the last didChange… will notify observers, avoiding duplicate notifications for the same change.
2011-09-07 16:14:01 +01:00
Alexander Ljungberg 01e35efdbb Refs #1343. Recreate manual bindings test as an automatic test. 2011-09-07 16:04:35 +01:00
Aparajita FishmanandAlexander Ljungberg bef1687b1f Test app for bindings fixes. 2011-09-07 12:51:36 +01:00
release da18a9eb24 Changed version number to 1.0.0.014-SNAPSHOT 2011-09-07 06:16:58 -05:00
release d975e90407 Changed version number to 1.0.0.013 2011-09-07 06:16:52 -05:00
Shao Fang abc8e14275 Change version back to 13-SNAPSHOT to prepare for next build. 2011-09-06 18:57:58 -07:00
Shao Fang b032cd5b7d Manually bump up the version to 1.0.0.012 and see if this helps. 2011-09-05 22:25:27 -07:00
release bb589285e3 Changed version number to 1.0.0.011-SNAPSHOT 2011-09-05 22:00:00 -05:00
release 1d52c3e957 Changed version number to 1.0.0.010 2011-09-05 21:59:49 -05:00
Aparajita Fishman df5ce9e0f3 Merge branch 'master' of github.com:intalio/cappuccino 2011-09-05 10:15:27 -04:00
Aparajita Fishman 16b446ae08 Finished CPComboBox, reworked _CPPopUpList to be more generic, factored some code from CPComboBox to _CPPopUpList 2011-09-05 02:02:05 -04:00
Antoine Mercadal afa691376e fix unable to change mainWindow's title 2011-09-02 14:00:49 +02:00
Antoine Mercadal be10bcf27a Fix CPPlatformWindow's title bug
if setTitle: is called on a CPWindow with the primaryPlatformWindow as platformWindow,
the main page's title is changed. This patch prevent changing the primaryPlatformWindow's title.
This is not optimal, but this is better than changing the title at each window setTitle: call
2011-09-02 12:42:09 +02:00
Aparajita Fishman d0a5b6e1c7 Added test app. 2011-09-02 03:41:17 -04:00
Aparajita Fishman 271a1ef3b4 Cocoa always sends NSControlTextDidEndEditingNotification and the action when resigning, whether or not editing had begun. 2011-09-02 02:48:34 -04:00
Aparajita Fishman b91042eb08 Merge remote branch 'refs/remotes/cappuccino/master' 2011-09-02 02:48:20 -04:00
Alexander Ljungberg 608cb86a4a Whitespace cleanup. 2011-08-31 18:51:15 +01:00
Alexander Ljungberg 88780ff0a9 Merge pull request #1352 from primalmotion/cappuccino
---

In latest versions of webkit window.open(url) return undefined if url == ""
2011-08-31 18:47:43 +01:00
Antoine Mercadal a5e241afd7 update PlatformWindows test (use the slider to play with the titles) 2011-08-31 19:17:41 +02:00
Antoine Mercadal 28772d3254 use CPWindow's title for platform window 2011-08-31 19:17:10 +02:00
Antoine Mercadal d1f5c1eebc patch for enventual webkit bug with window.open() 2011-08-31 18:28:47 +02:00
release d40ebaf369 Changed version number to 1.0.0.010-SNAPSHOT 2011-08-29 16:34:09 -05:00
release 06f74e5ef0 Changed version number to 1.0.0.009 2011-08-29 16:34:00 -05:00
release 5eaeb5f05e Changed version number to 1.0.0.009-SNAPSHOT 2011-08-29 15:42:45 -05:00
release 961cb2f6ed Changed version number to 1.0.0.008 2011-08-29 15:42:34 -05:00
alenovo 4dca2bbb85 Edited Buildfile via GitHub 2011-08-29 22:25:30 -03:00
alenovo 892d3d7120 Edited Buildfile via GitHub 2011-08-29 21:46:34 -03:00
alenovo c0fe499937 Edited Buildfile via GitHub 2011-08-29 21:26:34 -03:00
alenovo 01e8e9e597 Edited Buildfile via GitHub 2011-08-29 21:25:32 -03:00
alenovo 08b740c36f Edited Buildfile via GitHub 2011-08-29 21:24:23 -03:00
alenovo 31918bc0cd Edited Buildfile via GitHub 2011-08-29 21:22:21 -03:00
aparajita d45aeed224 Merge pull request #1 from correa-intalio/master
Changing version number
2011-08-29 07:47:40 -07:00
Diego Martin Correa 7d2835c522 Changing version numbre to complete build process 2011-08-29 11:45:47 -03:00
Alexander Ljungberg c77a282aaf Raise CPRangeException if CPString substringToIndex: is out of bounds. 2011-08-27 23:50:18 +01:00
Alexander Ljungberg 7a1cd5c545 Fix CPString substringToIndex documentation. The index parameter is not inclusive. 2011-08-27 23:49:40 +01:00
Aparajita Fishman cff26bd4e6 Instead of specifying a list class, pass a list delegate. 2011-08-24 13:51:57 -04:00
Aparajita Fishman 936ae1d563 Fixed the license 2011-08-24 13:24:55 -04:00
Aparajita Fishman 3d52935be0 Fixed no border CPTextField inset to match the default, changed license to LGPL 3.0, added missing PLATFORM(DOM) guard 2011-08-24 10:50:15 -04:00
Aparajita Fishman 777b167a2a Restored KVO exception, added mutating operations to test app 2011-08-24 09:58:03 -04:00
Aparajita Fishman c5d602bd6d Removed mistaken exception, it's okay if didChangeValueForKey: is called without willChangeValueForKey: 2011-08-24 01:35:33 -04:00
Aparajita Fishman 8545195cd7 Updated README 2011-08-24 00:58:58 -04:00
Aparajita Fishman 74fd73c18d Merge branch 'refs/heads/CPComboBox'
Conflicts:
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-2.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-8.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-2.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-3.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-5.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-disabled-6.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-0.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-1.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-2.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-3.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-5.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-6.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-7.png
	AppKit/Themes/Aristo/Resources/textfield-bezel-square-focused-8.png
	AppKit/Themes/Aristo/ThemeDescriptors.j
	Tests/Manual/NewTextField/AppController.j
2011-08-24 00:46:01 -04:00
Aparajita Fishman 2479f15b6b Merge remote branch 'refs/remotes/280north/master' 2011-08-24 00:30:39 -04:00
Aparajita Fishman 75875f4032 Reverted to previous theme layout system 2011-08-23 19:20:14 -04:00
Aparajita Fishman 0752b63b03 Adjusted tweak to match new bezel 2011-08-23 17:56:30 -04:00
Aparajita Fishman bde2f20fa3 - Tweaks to layout
- Fixed sizing of popup list to take intercell spacing into account
2011-08-23 17:17:01 -04:00
Alexander Ljungberg 29ff54054e Fix a typo. 2011-08-22 23:03:34 +01:00
Alexander Ljungberg 8e1bbe0132 Merge pull request #1345 from primalmotion/cappuccino
---

This patch allow to send play message before
the sound is actually loaded (by remembering the play request
and playing the sound when loaded if needed)

this allows to do:

```objj
var sound = [[CPSound alloc] initWithContentsOfURL:aSoundURL byReference:NO];
[sound play];
```
2011-08-22 22:59:39 +01:00
Aparajita Fishman c12305f40a Fixed a typo in CPTextField, CPComboBox should be using border-inset to account for the button, not content-inset. 2011-08-22 08:10:47 -04:00
Aparajita Fishman 2be149c486 Finished theming CPComboBox and reworked related themes...
- Using more logical scheme for widget layout. bezel-inset is inset from frame to the bezel image. border-inset is the inset from the frame to the visible border (if any), which usually is the width of a focus ring. content-inset is the inset from the border rect to the content frame, including the width of the border. This scheme makes it much easier to visualize what is happening, and also makes it possible to ensure a consistent margin between the content and the surrounding bezel border. It also allowed me to remove the funky +1 pixel hack in [CPTextField becomeFirstResponder].

- CPTextField (square and rounded) and CPTokenField had their theme insets reworked to fit the new scheme.

- Added missing disabled bezel for rounded textfield.

- All textfield focus rings look much nicer now (more like Cocoa). Previously the actual border of a textfield would shrink when focused, now it stays the same.

- In CPTokenField, wrapped all references to _inputElement in #if PLATFORM(DOM).

- Placeholder state was not being set correctly because a recent change to [CPTextField _updatePlaceholderState] uses the cached _stringValue instead of [self stringValue]. CPTokenField has its own override now that checks the token count.
2011-08-21 01:40:10 -04:00
Antoine Mercadal a2b6585c0d CPSound support play while loading
This patch allow to send 'play' message before
the sound is actually loaded (by remembering the play request
and playing the sound when loaded if needed)

this allow to do:

```objj
var sound = [[CPSound alloc] initWithContentsOfURL:aSoundURL byReference:NO];
[sound play];
```
2011-08-20 18:48:11 +02:00
Aparajita Fishman c7a16deb6f Fixed some edge case bugs, added full nib2cib support 2011-08-20 00:58:43 -04:00
Francisco Ryan Tolmasky I aa452952a8 Merge pull request #1344 from aparajita/valueForKeyPath-tweak
Much more efficient test for first character of string
2011-08-18 09:43:46 -07:00
Aparajita Fishman 1fb8c95b5d Merge branch 'refs/heads/CPTextField' 2011-08-18 09:22:56 -04:00
Aparajita Fishman 6ea4aa0a66 Much more efficient test for first character of string 2011-08-18 00:52:59 -04:00
Aparajita Fishman ad630c840b Much more efficient test for first character of string 2011-08-18 00:51:59 -04:00
Aparajita Fishman 16b52035c1 Much more efficient test for first character of string 2011-08-18 00:51:37 -04:00
Aparajita Fishman e98cd9e069 New bezel for CPTextField 2011-08-18 00:47:32 -04:00
Aparajita Fishman e99f7261f3 New bezel for CPTextField 2011-08-18 00:46:53 -04:00
Aparajita Fishman 85b370e6cf Merge remote branch 'refs/remotes/280north/master' 2011-08-17 18:06:25 -04:00
Randy Luecke 2462cd7b1d Merge pull request #1316 from primalmotion/cppopover
add CPPopover (Cocoa API compliant)
2011-08-17 11:17:02 -07:00
Randy Luecke 331e338c06 Merge pull request #1333 from eventualbuddha/CPAlert-subclass-builder-support
Allow CPAlert subclasses to use +alertWithMessageText:... et al.
2011-08-17 11:16:20 -07:00
Randy Luecke 2b68f40872 Merge pull request #1341 from cacaodev/CPTableView_drawBackgroundInClipRect
CPTableView: -drawBackroundInClipRect: bug
2011-08-17 11:15:33 -07:00
Randy Luecke 2f7b3c306d Merge pull request #1335 from alexmipego/master
CPBox autoresize settings
2011-08-17 11:13:46 -07:00
Randy Luecke eba5dbe654 Merge pull request #1334 from aparajita/array-range-checking
Added some missing range checking.
2011-08-17 11:11:49 -07:00
Randy Luecke 9ae1f548bf Merge pull request #1338 from purem/master
Added zsh to xcodecapp-cocoa
2011-08-17 11:05:18 -07:00
Aparajita Fishman c5ab90ae52 Test app for bindings fixes. 2011-08-17 11:28:23 -04:00
Aparajita Fishman 776cdf93fe Bindings fixes...
- [CPArrayController setSelectionIndexes] should NOT obey avoidsEmptySelection. This was preventing an empty selection in a CPTableView. In addition, when calling _selectionDidChange it should suppress notifications, because the reverseSetValueFor:@"selectionIndexes" in [CPArrayController __setSelectionIndexes] does the notifications.

- Added CPContentBinding and CPContentValuesBinding constants.

- [CPObjectController valueForKeyPath:] should return CPNotApplicableMarker for undefined keys.

- Spelling fix and consolidated var blocks in CPKeyValueObserving.j
2011-08-17 11:09:36 -04:00
Aparajita Fishman 2bbba19aba Merge branch 'refs/heads/suppress-notifications' into CPComboBox 2011-08-15 09:14:48 -04:00
Aparajita Fishman 635a57b036 Implemented mechanism to suppress KVO notifications, used this mechanism to prevent unwanted notifications when mutating arrangedObjects in CPArrayController. 2011-08-15 09:12:22 -04:00
Aparajita Fishman 5b231867dc Merge remote branch 'refs/remotes/280north/master'
Conflicts:
	AppKit/CPTextField.j
2011-08-15 08:42:07 -04:00
Alexander Ljungberg d191255a84 Merge branch 'master' of github.com:280north/cappuccino 2011-08-12 19:50:49 -04:00
Alexander Ljungberg 9bf8824109 nib2cib support for token fields. 2011-08-12 19:50:30 -04:00
Alexander Ljungberg f773bd217c Fixed with token field: remove false ivar '_tokenIndex' and fix tokenField:tokenField completionsForSubstring:indexOfToken:indexOfSelectedItem: ignoring its indexOfToken: parameter. 2011-08-12 19:46:39 -04:00
Alexander Ljungberg a5f0f0e3db CPTokenField archiving support. 2011-08-12 19:29:59 -04:00
Alexander Ljungberg 6f6bfc667b Inverted CPCharacterSet characterSetWithCharactersInString now archive properly. 2011-08-12 19:27:02 -04:00
Alexander Ljungberg 0789f49b94 CPCharacterSet isEqual support when the set is of the characterSetWithCharactersInString kind. 2011-08-12 19:12:47 -04:00
Alexander Ljungberg 92c94eab8f CPCharacterSet archiving support when the set is of the characterSetWithCharactersInString kind. 2011-08-12 18:50:06 -04:00
cacaodev 9f06ed725e CPTableView: -drawBackroundInClipRect:
Fixes a bug where we were drawing rows from 0 instead of the first exposed when usesAlternatingBackgroundColors:YES
2011-08-11 21:06:19 +02:00
Aparajita Fishman 6eee5543f9 First pass at CPComboBox. All functionality is there except for the drop-down button. 2011-08-10 16:03:35 -07:00
Jonathan R H Evans c45039fd70 Added zsh to the list of shell configs that xcodecapp-cocoa checks. 2011-08-09 10:15:23 +01:00
Aparajita Fishman 8e203ec78e Fixed range checking for insertObject:atIndex: 2011-08-08 15:14:30 -07:00
Aparajita Fishman 1918528710 Fixed range checking for insertObject:atIndex: 2011-08-08 15:13:37 -07:00
alexmipego e32515e5cc Make boxEnclosingView: copy the auto resizing settings of the view 2011-08-07 20:22:14 +01:00
Klaas Pieter Annema 2b77da821c always build xcodecapp with 10.6 SDK 2011-08-06 16:43:11 +02:00
Aparajita Fishman f8404c7a97 Added some missing range checking. 2011-08-05 14:40:13 -07:00
Aparajita Fishman b007f9899d Added some missing range checking. 2011-08-05 14:36:24 -07:00
Brian Donovan 156610ee07 Allow CPAlert subclasses to use +alertWithMessageText:... et al. 2011-08-05 08:32:44 -07:00
Aparajita Fishman 8a460b65ed Bumped version 2011-08-03 08:02:06 -07:00
Alexander Ljungberg fcaf672bf8 Merge branch 'master' of github.com:280north/cappuccino 2011-07-29 02:43:52 -04:00
Alexander Ljungberg dd5d8fcdce Closes #1305. Fixed: don't constrain max toolbar item width to the width of the label. 2011-07-29 02:42:42 -04:00
Alexander Ljungberg e0c4f6455d Tweak some messages. 2011-07-29 02:02:11 -04:00
Alexander Ljungberg dd5d7e9979 Whitespace cleanup. 2011-07-29 02:01:31 -04:00
Alexander Ljungberg 408baac91e Fix Xcode capitalisation in xcodecapp-cocoa. 2011-07-29 02:00:14 -04:00
Klaas Pieter Annema f241142216 don't disallow setEnabled for auto enabled items 2011-07-21 14:41:47 +02:00
Klaas Pieter Annema f0561844d8 don't close a menu item with a submenu
A submenu item should only close the menu if it has a non default action.
This is compliant with how Cocoa behavior.
2011-07-21 12:23:11 +02:00
Randall Luecke bd6294b806 Updated Jakefile for xcodecapp-cocoa to use the proper variable name. This should also fix the buildbot. 2011-07-17 22:46:27 -07:00
Alexander Ljungberg 70f8e3a832 Merge pull request #1278 from aparajita/cappuccino
---

 I discovered the following behavior that is not in conformance with Cocoa:

- Backspace, tab, escape, and space are considered key equivalents.
- Function keys (F1 - FNN) are not considered key equivalents.
- CPTextField executes `performKeyEquivalent` twice if a field is first responder.

Build this Cocoa project and debug it: http://d.pr/Xc3h

Take a look at the debug log while typing some keys. You will see that backspace, tab, escape and space do not trigger `performKeyEquivalent`, and that `performKeyEquivalent` is only executed once for the first responder.

Then run the performKeyEquivalentsTest app in this commit and look at the console log while trying keys in the text fields. If you turn the old code on, you will see how `performKeyEquivalent` is executed twice for the first responder.

For reference on key handling code, I consulted this:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html%23//apple_ref/doc/uid/10000060i-CH3-SW11
2011-07-17 22:55:18 -04:00
Alexander Ljungberg 59b302acde Merge pull request #1177 from cacaodev/cappuccino
---

Adds -scrollViewWillScroll: and -scrollviewDidScroll: delegate methods to CPScrollView.

These methods are not in AppKit but exist in UIKit. Useful for lazy loading data in a view, for example a CPTableview.
Tested in the CPScrollView example.

Conflicts:
	AppKit/CPScrollView.j
2011-07-17 22:05:55 -04:00
Alexander Ljungberg f54f8dfec9 Merge branch 'master' of github.com:280north/cappuccino 2011-07-17 21:48:53 -04:00
Alexander Ljungberg 93135e7e81 Header fixes. 2011-07-17 21:47:40 -04:00
Alexander Ljungberg b8026e19ba Merge pull request #1320 from MCF/cappuccino
---

If a CPTextFields target action changed the first responder then the target action was called twice if the textfield edit was completed by hitting the enter/return key.  This fixes the problem by setting the _isEditing flag to NO before the action is called in the insertNewline function.

See [this thread](https://groups.google.com/group/objectivej-dev/browse_thread/thread/ff64a4520102b3c5?hl=en) for more details.

Also included a new manual test for an editable CPTableView to make testing the fix easier.
2011-07-17 21:43:23 -04:00
Randy Luecke 147d9282fb Merge pull request #1323 from primalmotion/xcodecapp-cocoa-menu-for-error-panel
Small improvments in XcodeCapp-Cocoa
2011-07-17 15:49:21 -07:00
Antoine Mercadal 7c18b8bc5f Small improvments in XcodeCapp-Cocoa
- Do not release the Error & Warning window on close (or you can't open it again)
 - Small graphical updates
 - Remove the Growl notification in case of error (panel is sufficient)
 - Add a menu to open the error and warning panel when user wants to
2011-07-17 23:50:14 +02:00
Randall Luecke 2ccbf5d4e5 Errors in xcodecapp-cocoa now show up in their own panel. Should we keep growl? 2011-07-17 13:46:57 -07:00
Randall Luecke da6d8c86d1 Merge branch 'xcodecapp-cocoa-merge' of https://github.com/primalmotion/cappuccino into primalmotion-xcodecapp-cocoa-merge 2011-07-17 13:45:41 -07:00
Randall Luecke 78d997b404 Remove xcodecapp-cocoa in preparation for a proper merge 2011-07-17 13:44:09 -07:00
Randall Luecke c6e5219f2e Add Xcodecapp-cocoa build to Tools Jakefile 2011-07-16 23:11:13 -07:00
Randall Luecke 3bb450adbf Merge branch 'master' of git://github.com/280north/cappuccino 2011-07-16 22:57:56 -07:00
Randall Luecke a0f5257258 Errors in xcodecapp-cocoa now show up in their own panel. Should we keep growl? 2011-07-16 22:57:12 -07:00
Randall Luecke 6520615bb0 Merged xcodecapp-cocoa 2011-07-16 22:53:49 -07:00
Randy Luecke 4593212698 Merge pull request #1299 from farcaller/5d26066e02f6d1203981b27c8acf7f7991f4a419
Fixup for CPDictionary's dictionaryWithJSObject:recursively: and null objects in arrays
2011-07-16 19:45:45 -07:00
Randy Luecke 5363deb122 Merge pull request #1285 from aparajita/CPPredicate-fix
CPPredicate fix
2011-07-16 19:40:17 -07:00
Randy Luecke d5a651fc43 Merge pull request #1290 from hammerdr/include-ojtest
Adding OJTest installation to the bootstrap.sh file.
2011-07-16 19:39:52 -07:00
Randy Luecke 4740acbcc6 Merge pull request #1292 from aparajita/doxygen-font-fix
Use Menlo if Consolas is not available
2011-07-16 19:36:35 -07:00
Randy Luecke bfb2134329 Merge pull request #1293 from aparajita/CPKeyedArchiver-tweak
CPKeyedArchiver - Removed unused variable, fixed typo
2011-07-16 19:36:00 -07:00
Randy Luecke ec83977fe1 Merge pull request #1312 from aparajita/CPPlatformString-missing-methods
Make sure CPPlatformString +sizeOfString and +metricsOfFont work with CommonJS
2011-07-16 19:33:32 -07:00
Randy Luecke 98d08ff05d Merge pull request #1317 from aparajita/themeClass-fix
Have to set _themeClass when class changes
2011-07-16 19:32:54 -07:00
Randy Luecke 1eb6319b49 Merge pull request #1313 from cacaodev/CPViewAnimation
CPViewAnimation: current frame based on -currentValue.
2011-07-16 19:30:25 -07:00
Mike Fellows 8f7e06f9d6 Fix regression to CPTextField's insertNewline, duplicate target actions were being called when an edit on a textfield was ended with an enter/return keypress 2011-07-15 18:55:03 -07:00
Mike Fellows 81e3abe10c Add CPTableView test for editable table cells. 2011-07-15 18:54:53 -07:00
Klaas Pieter Annema edcee8934d fix empty contextual menus out of bounds error 2011-07-14 16:26:58 +02:00
Klaas Pieter Annema d115d2ef47 fix context menu only closes after two clicks
This is a regression from 73b4c33f05.
Because we were only tracking wether the left mouse button went up after a contextual menu (which is opened with the right mouse)
would only close after the second left mouse up.
2011-07-14 11:15:34 +02:00
Alexander Ljungberg d1dd725848 Merge pull request #1318 from aparajita/cappuccino
---

And several bugs fixed in the process.
2011-07-09 20:51:33 -04:00
Aparajita Fishman ebdba33082 Whitespace cleanup 2011-07-09 17:08:08 -07:00
Aparajita Fishman 918f6fbb8f CPFormatter support, bug fixes...
- Test application.

CPControl
---------
- Moved _formatter declaration up next to value, it was in the Target-Action Support section before.

- sendAction:to: should return a BOOL.

- Support for CPFormatter -editingStringForObjectValue.

- If a nil string is passed to -setStringValue in Cocoa, it raises an invalid parameter assertion and does nothing.

- Per Cocoa, if setting a string value fails, it tries again with an empty string.

- Removed a few extra blank lines.

- Changed NULL to nil.

CPTextField
-----------
- The string value is now cached in _stringValue to avoid constant calls to the formatter.

- There were several places where references to _inputElement were not wrapped with #if PLATFORM(DOM).

- When resigning first responder, if there is a formatter and formatting fails, reject the resignation and keep focus. Also, if the delegate responds to control:didFailToFormatString:errorDescription:, invoke it.

- _willBecomeFirstResponderByClick was not being set to NO when resigning. As a result, if you clicked in a field, tabbed to another field, and tabbed back to the original field, it would not select the text because it thought there was a mouse click.

- Don't send _sendStringValue: in keyUp: if the value has not changed. Previously it would do that for non-printing keys like navigation keys.

- insertNewLine: validates the string the same way resigning does.

- insertNewLine: would not send textDidEndEditing: if the field had no action, but it should.

- insertNewLine: should select all text (per Cocoa).

- insertNewLineIgnoringFieldEditor: and insertTabIgnoringFieldEditor: were not replacing the selection, Cocoa docs say they should.

- Per Cocoa, when setting an object value and there is a formatter, the value is first formatted as a string. If that fails, an object value for an empty string is attempted.

CPFormatter
-----------
- Fixed up the docs to match the method signatures.

- Indicated with "Ref" in argument types if the argument is pass by reference.

- isPartialStringValid:newEditingString:errorDescription: has a default implementation in Cocoa that nils the return values and returns YES.

- Fixed the name of isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:

- isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription: has a default implementation in Cocoa that invokes isPartialStringValid:newEditingString:errorDescription:
2011-07-09 17:05:23 -07:00
Antoine Mercadal 28733ec592 label are white in CPPopoverTest if popover is HUD 2011-07-09 15:29:37 +02:00
Antoine Mercadal 916b9a399b make color matching Aristo 2.0 ones 2011-07-09 15:29:13 +02:00
Antoine Mercadal 3d62791477 fix animation lack in FF prevents Popover to close 2011-07-09 11:50:52 +02:00
Aparajita Fishman 64e6764015 Have to set _themeClass when class changes 2011-07-08 19:04:33 -07:00
Antoine Mercadal cbed5c9cb1 support for CPPopover 2011-07-08 17:51:08 +02:00
Antoine Mercadal 0246d501e2 full support for CPPopover and nib2cib + enchanced delegates 2011-07-08 17:49:53 +02:00
Antoine Mercadal 3d5b30ab9e add CPPopover (Cocoa API compliant) 2011-07-08 15:50:29 +02:00
Antoine Mercadal 1f479c3f20 fix another problem with space in name that prevent launching XCode on the shadow project 2011-07-01 18:28:00 +02:00
Antoine Mercadal b5bb9c8ec3 fix a bug with NSURL and spaces 2011-07-01 17:29:12 +02:00
Aparajita Fishman 1ffc7ed6fb Make sure CPPlatformString +sizeOfString and +metricsOfFont work with CommonJS, so that OJUnitTest will work. 2011-06-29 10:52:22 -07:00
Antoine Mercadal af0a479f88 small style fix 2011-06-21 17:18:16 +02:00
Andrea D'Amore 86a28d8cc1 removed warning for Growl's framework path from relase target too 2011-06-21 17:05:00 +02:00
Andrea D'Amore 3dd178a69d fixes warnings while building: Growl bridging protocol, menu delegate, nonexistent framework search path, parser.j compiling 2011-06-21 16:09:21 +02:00
Antoine Mercadal 417334419a display the two last path components in title (usefull for localized bundles) + increase FSEvent latency 2011-06-20 15:37:13 +02:00
Antoine Mercadal c03b3c2d34 use terminationStatus, not lenght of the nib2cib output 2011-06-19 19:24:45 +02:00
Antoine Mercadal e97d4a8e72 fix typo in FS checking 2011-06-19 18:55:06 +02:00
Antoine Mercadal 24d0af3104 warn if something goes wrong about sourcing bash profile path + add quote on paths in NSTasks' arguments 2011-06-18 21:07:15 +02:00
Antoine Mercadal 5790e99359 small style fix 2011-06-16 20:39:29 +02:00
Tobias Adamson 0d6c2c9890 Changed from using system call to NSTask, this enables the app to report on incorrect nib2cib and objj settings and usage.
Examples where a Growl message will now be send to indicate error
1. nib2cib or objj not found in path
2. Compilation errors with objj
3. XIB files with Cocoa components that are not supported yet
2011-06-16 18:57:34 +08:00
Derek Hammer c5df9c973f Making the directions go before the prompt (doh!). Changing bootstrap to
+x.
2011-06-13 21:50:17 -07:00
Derek Hammer b89aa9af0d Merge branch 'master' of git://github.com/280north/cappuccino into include-ojtest 2011-06-12 10:07:15 -07:00
Vladimir Pouzanov 5d26066e02 Fixed dictionaryWithJSObject on arrays with null values 2011-06-08 14:10:10 +03:00
Antoine Mercadal de73d86684 Merge pull request #1 from gustaf-lindqvist/xcodecapp-cocoa-space-in-path-fix
xcodecapp-cocoa space in path fix

Reviewed.
2011-06-08 01:11:02 -07:00
Gustaf Lindqvist d6244f090e Fix white spaces in path for system() 2011-06-08 09:10:28 +02:00
Aparajita Fishman 1c44dc1052 Removed unused variable, fixed typo 2011-06-03 18:09:57 -07:00
Aparajita Fishman 4f7fbdc344 Use Menlo if Consolas is not available 2011-06-01 18:22:57 -07:00
Derek Hammer c5f37e665b Adding OJTest installation to the bootstrap.sh file. Also added the
cappuccino/objective-j packages back to the file.
2011-05-31 03:05:24 -07:00
Aparajita Fishman 99180ba001 Enable escape as key equivalent. 2011-05-30 23:20:06 -07:00
Aparajita Fishman 9377cf9128 Test app and unit test to validate pinned ranges in beginsWith/endsWith predicate comparison. 2011-05-26 14:31:20 -07:00
Aparajita Fishman e17d8ca302 Pin ranges to valid values. 2011-05-26 14:31:13 -07:00
Aparajita Fishman acbaa1eec4 Restored space, backspace and escape as key equivalents, but only if the first responder is not a CPTextField. Updated test app to show clearly the effect of this change. 2011-05-23 16:31:52 -07:00
Aparajita Fishman 65b6ff6ecc Fixed set of keys that can be considered a key equivalent, removed redundant call to performKeyEquivalent, test app 2011-05-22 18:05:48 -07:00
Antoine Mercadal df7aa30267 include xcodecapp-cocoa in the Cappuccino build process. 2011-05-21 00:39:50 +02:00
Antoine Mercadal 0f4aab926e add Jakefile to build xcodecapp-cocoa 2011-05-21 00:24:10 +02:00
Antoine Mercadal 7f3512d22c update .gitignore 2011-05-21 00:23:47 +02:00
Antoine Mercadal a6b5ed22b0 make parse.j LGPLv3 2011-05-21 00:02:22 +02:00
Antoine Mercadal e843b31287 update license of xcodecapp-cocoa 2011-05-20 23:39:13 +02:00
Antoine Mercadal 06a5aa45ba Merge remote branch 'xcodecapp-cocoa/master' into xcodecapp-cocoa-merge 2011-05-20 23:34:07 +02:00
Antoine Mercadal 45a5eed62f Merge xcodecapp-cocoa 2011-05-20 23:32:17 +02:00
Antoine Mercadal 188e27e931 update status bar icon 2011-05-20 23:05:41 +02:00
Antoine Mercadal ed560ed489 display ... when actually doing some jobs 2011-05-20 12:13:28 +02:00
Antoine Mercadal 186eb66f52 Make XCodeCapp-Cocoa a status bar application
+ fix bash_profile, thanks to @natemartinsf
 + add gitignore
2011-05-20 11:59:43 +02:00
Antoine Mercadal 8b566f06f0 set default build as Universal Binary 2011-04-30 15:50:36 +02:00
Antoine Mercadal 28d174d9d8 handle when user click on cancle on open panel 2011-04-28 18:19:18 +02:00
Antoine Mercadal dd892f746d enable garbage collection + growl support 2011-04-28 18:08:27 +02:00
Antoine Mercadal 95b1b8b19d at first launch, parse all the project in a thread 2011-04-28 17:00:11 +02:00
Antoine Mercadal 298b73ddfc initial commit 2011-04-28 16:35:57 +02:00
cacaodev 4b9695ff50 cache respondsToSelector: ; Fire the timer only if both methods exists 2011-03-24 18:40:25 +01:00
cacaodev a02b721944 CPScrollView: add -scrollView(Will|Did)Change: delegate methods.
CPScroller: send action when dragging starts instead of immediately on mouse down.

Test: added delegate methods to the top-left scroll view in CPScrollView example and log.
2011-03-19 10:17:12 +01:00
cacaodev f60a92d2c4 CPViewAnimation: current frame based on -currentValue. CPAnimation: early return in -currentValue if linear 2011-01-24 20:24:19 +01:00
248 changed files with 21509 additions and 1179 deletions
+2
View File
@@ -44,6 +44,7 @@
@import "CPColor.j"
@import "CPColorPanel.j"
@import "CPColorWell.j"
@import "CPComboBox.j"
@import "CPCompatibility.j"
@import "CPControl.j"
@import "CPCookie.j"
@@ -67,6 +68,7 @@
@import "CPOutlineView.j"
@import "CPPanel.j"
@import "CPPasteboard.j"
@import "CPPopover.j"
@import "CPPopUpButton.j"
@import "CPPredicateEditor.j"
@import "CPPredicateEditorRowTemplate.j"
+2 -2
View File
@@ -117,7 +117,7 @@ CPCriticalAlertStyle = 2;
*/
+ (CPAlert)alertWithMessageText:(CPString)aMessage defaultButton:(CPString)defaultButtonTitle alternateButton:(CPString)alternateButtonTitle otherButton:(CPString)otherButtonTitle informativeTextWithFormat:(CPString)informativeText
{
var alert = [[CPAlert alloc] init];
var alert = [[self alloc] init];
[alert setMessageText:aMessage];
[alert addButtonWithTitle:defaultButtonTitle];
@@ -142,7 +142,7 @@ CPCriticalAlertStyle = 2;
*/
+ (CPAlert)alertWithError:(CPString)anErrorMessage
{
var alert = [[CPAlert alloc] init];
var alert = [[self alloc] init];
[alert setMessageText:anErrorMessage];
[alert setAlertStyle:CPCriticalAlertStyle];
+3
View File
@@ -306,6 +306,9 @@ ACTUAL_FRAME_RATE = 0;
if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
return [_delegate animation:self valueForProgress:t];
if (_animationCurve == CPAnimationLinear)
return t;
var c1 = [],
c2 = [];
+24 -12
View File
@@ -564,8 +564,9 @@
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
{
[self _selectionWillChange]
[self __setSelectionIndexes:indexes];
var r = [self __setSelectionIndexes:indexes];
[self _selectionDidChange];
return r;
}
/*
@@ -574,7 +575,7 @@
*/
- (BOOL)__setSelectionIndex:(int)theIndex
{
[self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]];
return [self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]];
}
/*
@@ -583,28 +584,38 @@
*/
- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes
{
if (!indexes)
indexes = [CPIndexSet indexSet];
var newIndexes = indexes;
if (![indexes count])
if (!newIndexes)
newIndexes = [CPIndexSet indexSet];
if (![newIndexes count])
{
if (_avoidsEmptySelection && [[self arrangedObjects] count])
indexes = [CPIndexSet indexSetWithIndex:0];
newIndexes = [CPIndexSet indexSetWithIndex:0];
}
else
{
var objectsCount = [[self arrangedObjects] count];
// Don't trash the input - the caller might depend on it or we might have been
// given _selectionIndexes as the input in which case the equality test below
// would always succeed despite our change below.
newIndexes = [newIndexes copy];
// Remove out of bounds indexes.
[indexes removeIndexesInRange:CPMakeRange(objectsCount, [indexes lastIndex] + 1)];
[newIndexes removeIndexesInRange:CPMakeRange(objectsCount, [newIndexes lastIndex] + 1)];
// When avoiding empty selection and the deleted selection was at the bottom, select the last item.
if (![indexes count] && _avoidsEmptySelection && objectsCount)
indexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
if (![newIndexes count] && _avoidsEmptySelection && objectsCount)
newIndexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
}
if ([_selectionIndexes isEqualToIndexSet:indexes])
if ([_selectionIndexes isEqualToIndexSet:newIndexes])
return NO;
_selectionIndexes = [indexes copy];
// If we haven't already created our own index instance, make sure to copy it here so that
// the copy the user sent in is decoupled from our internal copy.
_selectionIndexes = indexes === newIndexes ? [indexes copy] : newIndexes;
// Push back the new selection to the model for selectionIndexes if we have one.
// There won't be an infinite loop because of the equality check above.
@@ -636,10 +647,11 @@
[self willChangeValueForKey:@"selectionIndexes"];
[self _selectionWillChange];
[self __setSelectedObjects:objects];
var r = [self __setSelectedObjects:objects];
[self didChangeValueForKey:@"selectionIndexes"];
[self _selectionDidChange];
return r;
}
/*
+1
View File
@@ -61,6 +61,7 @@ CPGrooveBorder = 3;
var box = [[self alloc] initWithFrame:CGRectMakeZero()],
enclosingView = [aView superview];
[box setAutoresizingMask:[aView autoresizingMask]];
[box setFrameFromContentFrame:[aView frame]];
[enclosingView replaceSubview:aView with:box];
+1188
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -526,7 +526,7 @@ var CPControlBlackColor = [CPColor blackColor];
// Cocoa raises an invalid parameter assertion and returns if you pass nil.
if (aString === nil || aString === undefined)
{
CPLog.warn("nil sent to CPControl -setStringValue");
CPLog.warn("nil or undefined sent to CPControl -setStringValue");
return;
}
@@ -536,10 +536,10 @@ var CPControlBlackColor = [CPColor blackColor];
{
value = nil;
if ([_formatter getObjectValue:AT_REF(value) forString:aString errorDescription:nil] === NO)
if (![_formatter getObjectValue:AT_REF(value) forString:aString errorDescription:nil])
{
// If the given string is non-empty and doesn't work, Cocoa tries an empty string.
if (!aString || [_formatter getObjectValue:AT_REF(value) forString:@"" errorDescription:nil] === NO)
if (!aString || ![_formatter getObjectValue:AT_REF(value) forString:@"" errorDescription:nil])
value = undefined; // Means the value is invalid
}
}
+4 -3
View File
@@ -204,7 +204,6 @@ var CPBindingOperationAnd = 0,
if (valueTransformer)
aValue = [valueTransformer transformedValue:aValue];
if (aValue === undefined || aValue === nil || aValue === [CPNull null])
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
@@ -311,7 +310,7 @@ var CPBindingOperationAnd = 0,
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
{
if (!anObject || !aKeyPath)
return CPLog.error("Invalid object or path on "+self+" for "+aBinding);
return CPLog.error("Invalid object or path on " + self + " for " + aBinding);
//if (![[self exposedBindings] containsObject:aBinding])
// CPLog.warn("No binding exposed on "+self+" for "+aBinding);
@@ -488,6 +487,8 @@ CPSelectedIndexBinding = @"selectedIndex";
CPTextColorBinding = @"textColor";
CPToolTipBinding = @"toolTip";
CPValueBinding = @"value";
CPContentBinding = @"content";
CPContentValuesBinding = @"contentValues";
//Binding options constants
CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection";
@@ -518,4 +519,4 @@ CPValueTransformerBindingOption = @"CPValueTransformer";
CPIsControllerMarker = function(/*id*/anObject)
{
return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker || anObject === CPNullMarker;
}
}
+3 -3
View File
@@ -622,11 +622,11 @@ var _CPMenuBarVisible = NO,
var validator = [CPApp targetForAction:[item action] to:[item target] from:item];
if (!validator || ![validator respondsToSelector:[item action]])
[item _setEnabled:NO];
[item setEnabled:NO];
else if ([validator respondsToSelector:@selector(validateMenuItem:)])
[item _setEnabled:[validator validateMenuItem:item]];
[item setEnabled:[validator validateMenuItem:item]];
else if ([validator respondsToSelector:@selector(validateUserInterfaceItem:)])
[item _setEnabled:[validator validateUserInterfaceItem:item]];
[item setEnabled:[validator validateUserInterfaceItem:item]];
}
[[_menuWindow _menuView] tile];
+12 -2
View File
@@ -61,6 +61,9 @@ var STICKY_TIME_INTERVAL = 500,
{
var menu = [aMenuContainer menu];
if ([menu numberOfItems] <= 0)
return;
CPApp._activeMenu = menu;
_startTime = [anEvent timestamp];//new Date();
@@ -102,7 +105,7 @@ var STICKY_TIME_INTERVAL = 500,
if (type === CPAppKitDefined)
return [self completeTracking];
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPKeyDownMask | CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask | CPScrollWheelMask untilDate:nil inMode:nil dequeue:YES];
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPKeyDownMask | CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPRightMouseUpMask | CPAppKitDefinedMask | CPScrollWheelMask untilDate:nil inMode:nil dequeue:YES];
if (type === CPKeyDown)
{
@@ -224,10 +227,17 @@ var STICKY_TIME_INTERVAL = 500,
[CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04];
}
}
else if (type === CPLeftMouseUp)
else if (type === CPLeftMouseUp || type === CPRightMouseUp)
{
if (_hasMouseGoneUpAfterStartedTracking)
{
// Don't close the menu if the current item has a submenu
// and did not override it's default action
if ([activeItem action] === @selector(submenuAction:))
return;
[trackingMenu cancelTracking];
}
else
_hasMouseGoneUpAfterStartedTracking = YES;
}
-8
View File
@@ -148,14 +148,6 @@ var CPMenuItemStringRepresentationDictionary = [CPDictionary dictionary];
@param isEnabled \c YES enables the item. \c NO disables it.
*/
- (void)setEnabled:(BOOL)isEnabled
{
if ([_menu autoenablesItems])
return;
[self _setEnabled:isEnabled];
}
- (void)_setEnabled:(BOOL)isEnabled
{
if (_isEnabled === isEnabled)
return;
+31 -8
View File
@@ -131,8 +131,8 @@
_contentObject = aContent;
[self didChangeValueForKey:@"contentObject"];
[self _selectionDidChange];
[self didChangeValueForKey:@"contentObject"];
}
/*!
@@ -323,12 +323,22 @@
@ignore
*/
- (void)_selectionDidChange
{
[self _selectionDidChangeNotify:YES];
}
/*!
@ignore
*/
- (void)_selectionDidChangeNotify:(BOOL)notify
{
if (_selection === undefined || _selection === nil)
_selection = [[CPControllerSelectionProxy alloc] initWithController:self];
[_selection controllerDidChange];
[self didChangeValueForKey:@"selection"];
if (notify)
[self didChangeValueForKey:@"selection"];
}
/*!
@@ -666,21 +676,21 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
var count = [theValues count];
if (!count)
value = CPNoSelectionMarker;
return CPNoSelectionMarker;
else if (count === 1)
value = [theValues objectAtIndex:0];
else
{
if ([_controller alwaysUsesMultipleValuesMarker])
value = CPMultipleValuesMarker;
return CPMultipleValuesMarker;
else
{
value = [theValues objectAtIndex:0];
for (var i = 0, count= [theValues count]; i < count && value != CPMultipleValuesMarker; i++)
for (var i = 0, count = [theValues count]; i < count; i++)
{
if (![value isEqual:[theValues objectAtIndex:i]])
value = CPMultipleValuesMarker;
return CPMultipleValuesMarker;
}
}
}
@@ -693,8 +703,21 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (id)valueForKeyPath:(CPString)theKeyPath
{
var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath];
value = [self _controllerMarkerForValues:values];
// If valueForKeyPath fails because of an undefined key, return CPNotApplicableMarker
var value;
try
{
var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath];
value = [self _controllerMarkerForValues:values];
}
catch (ex)
{
if ([ex name] === CPUndefinedKeyException)
value = CPNotApplicableMarker;
else
throw ex;
}
[_cachedValues setObject:value forKey:theKeyPath];
+304
View File
@@ -0,0 +1,304 @@
/*
* CPPopover.j
* AppKit
*
* Created by Antoine Mercadal.
* Copyright 2011 Antoine Mercadal.
*
* 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/CPObject.j>
@import "CPButton.j"
@import "CPColor.j"
@import "CPImage.j"
@import "CPImageView.j"
@import "CPResponder.j"
@import "CPView.j"
@import "_CPAttachedWindow.j"
CPPopoverBehaviorApplicationDefined = 0;
CPPopoverBehaviorTransient = 1;
CPPopoverBehaviorSemitransient = 2;
var CPPopoverDelegate_popover_willShow_ = 1 << 0,
CPPopoverDelegate_popover_didShow_ = 1 << 1,
CPPopoverDelegate_popover_shouldClose_ = 1 << 2,
CPPopoverDelegate_popover_willClose_ = 1 << 3,
CPPopoverDelegate_popover_didClose_ = 1 << 4;
/*! @ingroup appkit
@class CPPopover
This class represent a widget that displays a attached
view relative to another one.
Delegate can implement:
popoverShouldClose:(CPPopover)aPopOver
popoverWillShow:(CPPopover)aPopOver
popoverDidShow:(CPPopover)aPopOver
popoverWillClose:(CPPopover)aPopOver
popoverDidClose:(CPPopover)aPopOver
*/
@implementation CPPopover : CPResponder
{
@outlet CPViewController _contentViewController @accessors(property=contentViewController);
@outlet id _delegate @accessors(getter=delegate);
BOOL _animates @accessors(property=animates);
BOOL _shown @accessors(getter=shown);
int _appearance @accessors(property=appearance);
int _behavior @accessors(getter=behavior);
BOOL _needsCompute;
_CPAttachedWindow _attachedWindow;
int _implementedDelegateMethods;
}
#pragma mark -
#pragma mark INitialization
/*!
Initialize the CPPopover witn default values
@returns anInitialized CPPopover
*/
- (CPPopover)init
{
if (self = [super init])
{
_animates = YES;
_appearance = CPPopoverAppearanceMinimal;
_behavior = CPPopoverBehaviorApplicationDefined;
_needsCompute = YES;
_shown = NO;
}
return self;
}
#pragma mark -
#pragma mark Getters / Setters
/*!
Returns the current rect of the popover
@return CPRect represeting the frame of the popover
*/
- (CPRect)positionningRect
{
if (!_attachedWindow || ![_attachedWindow isVisible])
return nil;
return [_attachedWindow frame];
}
/*! Sets the frame of the popover
@param aRect the desired frame
*/
- (void)setPositionningRect:(CPRect)aRect
{
if (!_attachedWindow || ![_attachedWindow isVisible])
return;
[_attachedWindow setFrame:aRect];
}
/*!
Returns the size of the popover's view
@return CPSize represeting the size of the popover's view
*/
- (CPRect)contentSize
{
if (!_attachedWindow || ![_attachedWindow isVisible])
return nil;
return [[_contentViewController view] frameSize];
}
/*!
Sets the size of of the popover's view
@param aSize the desired size
*/
- (void)setContentSize:(CPSize)aSize
{
[[_contentViewController view] setFrameSize:aSize];
}
/*!
Indicates if CPPopover is visible
@returns YES if visible
*/
- (BOOL)shown
{
if (!_attachedWindow)
return NO;
return [_attachedWindow isVisible];
}
/*!
Set the behaviour of the CPPopover. It can be
- CPPopoverBehaviorTransient: the popover will be close if another control outside the popover become the responder
- CPPopoverBehaviorApplicationDefined: (DEFAULT) the application is responsible for closing the popover
@param aBehaviour the desired behaviour
*/
- (void)setBehaviour:(int)aBehaviour
{
if (_behavior == aBehaviour)
return;
_behavior = aBehaviour;
_needsCompute = YES;
}
- (void)setDelegate:(id)aDelegate
{
if (_delegate === aDelegate)
return;
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(popoverWillShow:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_willShow_;
if ([_delegate respondsToSelector:@selector(popoverDidShow:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_didShow_;
if ([_delegate respondsToSelector:@selector(popoverShouldClose:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_shouldClose_;
if ([_delegate respondsToSelector:@selector(popoverWillClose:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_willClose_;
if ([_delegate respondsToSelector:@selector(popoverDidClose:)])
_implementedDelegateMethods |= CPPopoverDelegate_popover_didClose_;
}
#pragma mark -
#pragma mark Positionning
/*!
Show the popover
@param positioningRect if set, the popover will be positionned to a random rect relative to the window
@param positioningView if set, the popover will be positioned relative to this view
@param preferredEdge: CPRectEdge representing the prefered positionning.
*/
- (void)showRelativeToRect:(CPRect)positioningRect ofView:(CPView)positioningView preferredEdge:(CPRectEdge)preferredEdge
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willShow_)
[_delegate popoverWillShow:self];
if (!_contentViewController)
[CPException raise:CPInternalInconsistencyException reason:@"contentViewController must not be nil"];
if (_needsCompute)
{
var styleMask = (_behavior == CPPopoverBehaviorTransient) ? CPClosableOnBlurWindowMask : nil;
_attachedWindow = [[_CPAttachedWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:styleMask];
}
[_attachedWindow setAppearance:_appearance];
[_attachedWindow setAnimates:_animates];
[_attachedWindow setMovableByWindowBackground:NO];
[_attachedWindow setFrame:[_attachedWindow frameRectForContentRect:[[_contentViewController view] frame]]];
[_attachedWindow setContentView:[_contentViewController view]];
if (positioningRect)
[_attachedWindow positionRelativeToRect:positioningRect preferredEdge:preferredEdge];
else if (positioningView)
[_attachedWindow positionRelativeToView:positioningView preferredEdge:preferredEdge];
else
[CPException raise:CPInvalidArgumentException reason:@"you must set positioningRect or positioningRect"];
if (_implementedDelegateMethods & CPPopoverDelegate_popover_didShow_)
[_delegate popoverDidShow:self];
}
/*!
Closes the popover
*/
- (void)close
{
if (_implementedDelegateMethods & CPPopoverDelegate_popover_shouldClose_)
if (![_delegate popoverShouldClose:self])
return;
if (_implementedDelegateMethods & CPPopoverDelegate_popover_willClose_)
[_delegate popoverWillClose:self];
[_attachedWindow close];
if (_implementedDelegateMethods & CPPopoverDelegate_popover_didClose_)
[_delegate popoverDidClose:self];
}
#pragma mark -
#pragma mark Action
/*!
Close the popover
@param aSender the sender of the action
*/
- (IBAction)performClose:(id)aSender
{
[self close];
}
@end
@implementation CPPopover (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_needsCompute = [aCoder decodeIntForKey:@"_needsCompute"];
_appearance = [aCoder decodeIntForKey:@"_appearance"];
_animates = [aCoder decodeBoolForKey:@"_animates"];
_contentViewController = [aCoder decodeObjectForKey:@"_contentViewController"];
[self setDelegate:[aCoder decodeObjectForKey:@"_delegate"]];
[self setBehaviour:[aCoder decodeIntForKey:@"_behavior"]];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeInt:_behavior forKey:@"_behavior"];
[aCoder encodeInt:_appearance forKey:@"_appearance"];
[aCoder encodeBool:_needsCompute forKey:@"_needsCompute"];
[aCoder encodeObject:_contentViewController forKey:@"_contentViewController"];
[aCoder encodeObject:_delegate forKey:@"_delegate"];
[aCoder encodeObject:_animates forKey:@"_animates"];
}
@end
+27 -27
View File
@@ -187,7 +187,7 @@ var itemsContext = "items",
[_slicesHolder addSubview:_dropLineView];
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPRuleEditorItemPBoardType,nil]];
[_boundArrayOwner addObserver:self forKeyPath:_boundArrayKeyPath options:CPKeyValueObservingOptionOld|CPKeyValueObservingOptionNew context:boundArrayContext];
[_boundArrayOwner addObserver:self forKeyPath:_boundArrayKeyPath options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:boundArrayContext];
}
/*! @endcond */
@@ -408,7 +408,7 @@ var itemsContext = "items",
if (![stringsFilename hasSuffix:@".strings"])
stringsFilename = stringsFilename + @".strings";
var path = [[CPBundle mainBundle] pathForResource:stringsFilename];
if (path !=nil)
if (path != nil)
[_standardLocalizer loadContentOfURL:[CPURL URLWithString:path]];
}
}
@@ -626,7 +626,7 @@ TODO: implement
{
var slice = _slices[count],
rowIndex = [slice rowIndex],
contains = [indexes containsIndex:rowIndex];
contains = [indexes containsIndex:rowIndex],
shouldSelect = (contains && !(extend && [slice _isSelected]));
if (contains)
@@ -1153,8 +1153,8 @@ TODO: implement
if (!_nestingModeDidChange)
return _nestingMode;
var a = (_nestingMode == CPRuleEditorNestingModeCompound || _nestingMode == CPRuleEditorNestingModeSimple);
var b = ([self rowTypeForRow:0] == CPRuleEditorRowTypeCompound);
var a = (_nestingMode == CPRuleEditorNestingModeCompound || _nestingMode == CPRuleEditorNestingModeSimple),
b = ([self rowTypeForRow:0] == CPRuleEditorRowTypeCompound);
if (a == b)
return _nestingMode;
@@ -1241,10 +1241,10 @@ TODO: implement
while (current_index != CPNotFound)
{
var parentIndex = [self parentRowForRow:current_index];
var parentIndex = [self parentRowForRow:current_index],
subrowsIndexes = [self subrowIndexesForRow:parentIndex];
var subrowsIndexes = [self subrowIndexesForRow:parentIndex];
if ([subrowsIndexes count]==1)
if ([subrowsIndexes count] == 1)
{
if (parentIndex != -1)
return [CPIndexSet indexSetWithIndex:0];
@@ -1432,7 +1432,7 @@ TODO: implement
- (void)_startObservingRowObjectsRecursively:(CPArray)rowObjects
{
[_boundArrayOwner addObserver:self forKeyPath:_boundArrayKeyPath options:CPKeyValueObservingOptionOld|CPKeyValueObservingOptionNew context:boundArrayContext];
[_boundArrayOwner addObserver:self forKeyPath:_boundArrayKeyPath options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:boundArrayContext];
var count = [rowObjects count];
@@ -1440,9 +1440,9 @@ TODO: implement
{
var rowObject = [rowObjects objectAtIndex:i];
[rowObject addObserver:self forKeyPath:_itemsKeyPath options:CPKeyValueObservingOptionOld|CPKeyValueObservingOptionNew context:itemsContext];
[rowObject addObserver:self forKeyPath:_valuesKeyPath options:CPKeyValueObservingOptionOld|CPKeyValueObservingOptionNew context:valuesContext];
[rowObject addObserver:self forKeyPath:_subrowsArrayKeyPath options:CPKeyValueObservingOptionOld|CPKeyValueObservingOptionNew context:subrowsContext];
[rowObject addObserver:self forKeyPath:_itemsKeyPath options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:itemsContext];
[rowObject addObserver:self forKeyPath:_valuesKeyPath options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:valuesContext];
[rowObject addObserver:self forKeyPath:_subrowsArrayKeyPath options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:subrowsContext];
var subrows = [self _subrowObjectsOfObject:rowObject];
if ([subrows count] > 0)
@@ -1604,13 +1604,14 @@ TODO: implement
break;
}
var replaceCount = (deltaCount == 0) ? maxCount : maxCount - minusCount;
var startIndex = parentCacheIndex + changeStartIndex + 1;
var replaceCount = (deltaCount == 0) ? maxCount : maxCount - minusCount,
startIndex = parentCacheIndex + changeStartIndex + 1;
if (deltaCount <= 0)
{
var removeIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startIndex, replaceCount)];
var removeSlices = [_slices objectsAtIndexes:removeIndexes];
var removeIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startIndex, replaceCount)],
removeSlices = [_slices objectsAtIndexes:removeIndexes];
[removeSlices makeObjectsPerformSelector:@selector(removeFromSuperview)];
[_slices removeObjectsAtIndexes:removeIndexes];
}
@@ -1694,7 +1695,7 @@ TODO: implement
var boundRows = [_boundArrayOwner valueForKey:_boundArrayKeyPath];
[_boundArrayOwner addObserver:self forKeyPath:_boundArrayKeyPath options:CPKeyValueObservingOptionOld|CPKeyValueObservingOptionNew context:boundArrayContext];
[_boundArrayOwner addObserver:self forKeyPath:_boundArrayKeyPath options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:boundArrayContext];
if ([boundRows isKindOfClass:[CPArray class]] && [boundRows count] > 0)
[_boundArrayOwner setValue:boundRows forKey:_boundArrayKeyPath];
@@ -1890,7 +1891,7 @@ TODO: implement
{
var current_index = [indexes firstIndex];
while (current_index !=CPNotFound)
while (current_index != CPNotFound)
{
var subindexes = [self subrowIndexesForRow:index];
[self _updateSliceIndentationAtIndex:current_index toIndentation:indentation + 1 withIndexSet:subindexes];
@@ -2152,7 +2153,7 @@ TODO: implement
- (CPDragOperation)draggingUpdated:(id <CPDraggingInfo>)sender
{
var point = [self convertPoint:[sender draggingLocation] fromView:nil],
y = point.y + _sliceHeight /2,
y = point.y + _sliceHeight / 2,
indexOfDropLine = FLOOR(y / _sliceHeight),
numberOfRows = [self numberOfRows];
@@ -2198,11 +2199,10 @@ TODO: implement
removeIndex;
var rowObjects = [_rowCache valueForKey:@"rowObject"],
index = [_draggingRows lastIndex];
var parentRowIndex = [self parentRowForRow:index]; // first index of draggingrows
var parentRowObject = (parentRowIndex == -1) ? _boundArrayOwner : [[self _rowCacheForIndex:parentRowIndex] rowObject];
var insertIndex = _subviewIndexOfDropLine;
index = [_draggingRows lastIndex],
parentRowIndex = [self parentRowForRow:index], // first index of draggingrows
parentRowObject = (parentRowIndex == -1) ? _boundArrayOwner : [[self _rowCacheForIndex:parentRowIndex] rowObject],
insertIndex = _subviewIndexOfDropLine;
while (index != CPNotFound)
{
@@ -2570,9 +2570,9 @@ var dropSeparatorColor = [CPColor colorWithHexString:@"4886ca"];
- (int)valueType
{
var result = 0;
var result = 0,
isString = [self isKindOfClass:[CPString class]];
var isString = [self isKindOfClass:[CPString class]];
if ( !isString )
{
var isView = [self isKindOfClass:[CPView class]];
@@ -2593,4 +2593,4 @@ var dropSeparatorColor = [CPColor colorWithHexString:@"4886ca"];
}
@end
/*! @endcond */
/*! @endcond */
+75 -2
View File
@@ -34,6 +34,12 @@
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;
@implementation CPScrollView : CPView
{
CPClipView _contentView;
@@ -41,6 +47,9 @@
CPView _cornerView;
CPView _bottomCornerView;
id _delegate;
CPTimer _scrollTimer;
BOOL _hasVerticalScroller;
BOOL _hasHorizontalScroller;
BOOL _autohidesScrollers;
@@ -48,7 +57,8 @@
CPScroller _verticalScroller;
CPScroller _horizontalScroller;
int _recursionCount;
CPInteger _recursionCount;
CPInteger _implementedDelegateMethods;
float _verticalLineScroll;
float _verticalPageScroll;
@@ -86,7 +96,6 @@
_borderType = CPNoBorder;
_contentView = [[CPClipView alloc] initWithFrame:[self _insetBounds]];
[self addSubview:_contentView];
_headerClipView = [[CPClipView alloc] init];
@@ -97,11 +106,37 @@
[self setHasVerticalScroller:YES];
[self setHasHorizontalScroller:YES];
_delegate = nil;
_scrollTimer = nil;
_implementedDelegateMethods = 0;
}
return self;
}
- (id)delegate
{
return _delegate;
}
- (void)setDelegate:(id)aDelegate
{
if (aDelegate === _delegate)
return;
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if (_delegate === nil)
return;
if ([_delegate respondsToSelector:@selector(scrollViewWillScroll:)])
_implementedDelegateMethods |= CPScrollViewDelegate_scrollViewWillScroll_;
if ([_delegate respondsToSelector:@selector(scrollViewDidScroll:)])
_implementedDelegateMethods |= CPScrollViewDelegate_scrollViewDidScroll_;
}
// Calculating Layout
+ (CGSize)contentSizeForFrameSize:(CGSize)frameSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
@@ -655,6 +690,8 @@
default: contentBounds.origin.y = ROUND(value * (_CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentBounds)));
}
[self _sendDelegateMessages];
[_contentView scrollToPoint:contentBounds.origin];
}
@@ -685,6 +722,8 @@
default: contentBounds.origin.x = ROUND(value * (_CGRectGetWidth(documentFrame) - _CGRectGetWidth(contentBounds)));
}
[self _sendDelegateMessages];
[_contentView scrollToPoint:contentBounds.origin];
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
}
@@ -962,6 +1001,8 @@
extraX = contentBounds.origin.x - constrainedOrigin.x,
extraY = contentBounds.origin.y - constrainedOrigin.y;
[self _sendDelegateMessages];
[_contentView scrollToPoint:constrainedOrigin];
[_headerClipView scrollToPoint:CGPointMake(constrainedOrigin.x, 0.0)];
@@ -1029,6 +1070,35 @@
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0)];
}
- (void)_sendDelegateMessages
{
if (_implementedDelegateMethods == 0)
return;
if (!_scrollTimer)
{
[self _scrollViewWillScroll];
_scrollTimer = [CPTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(_scrollViewDidScroll) userInfo:nil repeats:YES];
}
else
[_scrollTimer setFireDate:[CPDate dateWithTimeIntervalSinceNow:TIMER_INTERVAL]];
}
- (void)_scrollViewWillScroll
{
if (_implementedDelegateMethods & CPScrollViewDelegate_scrollViewWillScroll_)
[_delegate scrollViewWillScroll:self];
}
- (void)_scrollViewDidScroll
{
[_scrollTimer invalidate];
_scrollTimer = nil;
if (_implementedDelegateMethods & CPScrollViewDelegate_scrollViewDidScroll_)
[_delegate scrollViewDidScroll:self];
}
@end
var CPScrollViewContentViewKey = @"CPScrollViewContentView",
@@ -1079,6 +1149,9 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
_cornerView = [aCoder decodeObjectForKey:CPScrollViewCornerViewKey];
_bottomCornerView = [aCoder decodeObjectForKey:CPScrollViewBottomCornerViewKey];
_delegate = nil;
_scrollTimer = nil;
_implementedDelegateMethods = 0;
// Due to the anything goes nature of decoding, our subviews may not exist yet, so layout at the end of the run loop when we're sure everything is in a correct state.
[[CPRunLoop currentRunLoop] performSelector:@selector(_updateCornerAndHeaderView) target:self argument:_contentView order:0 modes:[CPDefaultRunLoopMode]];
}
+3 -2
View File
@@ -457,8 +457,9 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
}
[CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
[self sendAction:[self action] to:[self target]];
if (type === CPLeftMouseDragged)
[self sendAction:[self action] to:[self target]];
}
/*!
+14
View File
@@ -45,6 +45,7 @@ CPSoundPlayBackStatePause = 2;
CPString _name @accessors(property=name);
id _delegate @accessors(property=delegate);
BOOL _playRequestBeforeLoad;
HTMLAudioElement _audioTag;
int _loadStatus;
int _playBackStatus;
@@ -61,6 +62,7 @@ CPSoundPlayBackStatePause = 2;
_loops = NO;
_audioTag = document.createElement("audio");
_audioTag.preload = YES;
_playRequestBeforeLoad = NO;
_audioTag.addEventListener("canplay", function()
{
@@ -138,6 +140,12 @@ CPSoundPlayBackStatePause = 2;
- (void)_soundDidload
{
_loadStatus = CPSoundLoadStateCanBePlayed;
if (_playRequestBeforeLoad)
{
_playRequestBeforeLoad = NO;
[self play];
}
}
/*! @ignore
@@ -167,6 +175,12 @@ CPSoundPlayBackStatePause = 2;
*/
- (BOOL)play
{
if (_loadStatus === CPSoundLoadStateLoading)
{
_playRequestBeforeLoad = YES;
return YES;
}
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|| (_playBackStatus === CPSoundPlayBackStatePlay))
return NO;
+3 -3
View File
@@ -3545,16 +3545,16 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
}
var exposedRows = [self _unboundedRowsInRect:aRect],
firstRow = FLOOR(exposedRows.location / colorCount) * colorCount,
lastRow = CPMaxRange(exposedRows),
colorIndex = 0,
groupRowRects = [],
row = exposedRows.location;
groupRowRects = [];
//loop through each color so we only draw once for each color
while (colorIndex < colorCount)
{
CGContextBeginPath(context);
for (var row = colorIndex; row <= lastRow; row += colorCount)
for (var row = firstRow + colorIndex; row <= lastRow; row += colorCount)
{
// if it's not a group row draw it otherwise we draw it later
if (![_groupRows containsIndex:row])
+37 -27
View File
@@ -169,8 +169,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
+ (id)themeAttributes
{
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), [CPNull null]]
forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]];
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), _CGInsetMake(3.0, 3.0, 3.0, 3.0), [CPNull null]]
forKeys:[@"bezel-inset", @"content-inset", @"border-inset", @"bezel-color"]];
}
/* @ignore */
@@ -507,31 +507,32 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
var contentRect = [self contentRectForBounds:[self bounds]],
verticalAlign = [self currentValueForThemeAttribute:"vertical-alignment"];
verticalAlign = [self currentValueForThemeAttribute:"vertical-alignment"],
lineHeight = [font defaultLineHeightForFont];
switch (verticalAlign)
{
case CPTopVerticalTextAlignment:
var topPoint = (_CGRectGetMinY(contentRect) + 1) + "px"; // for the same reason we have a -1 for the left, we also have a + 1 here
var topPoint = _CGRectGetMinY(contentRect) + "px"; // for the same reason we have a -1 for the left, we also have a + 1 here
break;
case CPCenterVerticalTextAlignment:
var topPoint = (_CGRectGetMidY(contentRect) - (font._lineHeight / 2) + 1) + "px";
var topPoint = (_CGRectGetMidY(contentRect) - (lineHeight / 2)) + "px";
break;
case CPBottomVerticalTextAlignment:
var topPoint = (_CGRectGetMaxY(contentRect) - font._lineHeight) + "px";
var topPoint = (_CGRectGetMaxY(contentRect) - lineHeight) + "px";
break;
default:
var topPoint = (_CGRectGetMinY(contentRect) + 1) + "px";
var topPoint = _CGRectGetMinY(contentRect) + "px";
break;
}
element.style.top = topPoint;
element.style.left = (_CGRectGetMinX(contentRect) - 1) + "px"; // why -1?
element.style.width = _CGRectGetWidth(contentRect) + "px";
element.style.height = font._lineHeight + "px"; // private ivar for the line height of the DOM text at this particular size
element.style.height = lineHeight + "px";
_DOMElement.appendChild(element);
@@ -625,15 +626,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
#endif
// post CPControlTextDidEndEditingNotification
if (_isEditing)
{
_isEditing = NO;
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
_isEditing = NO;
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
if ([self sendsActionOnEndEditing])
[self sendAction:[self action] to:[self target]];
}
if ([self sendsActionOnEndEditing])
[self sendAction:[self action] to:[self target]];
[self textDidBlur:[CPNotification notificationWithName:CPTextFieldDidBlurNotification object:self userInfo:nil]];
@@ -776,14 +773,21 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
{
if ([self _valueIsValid:_stringValue])
{
// If _isEditing == YES then the target action can also be called via
// resignFirstResponder, and it is possible that the target action
// itself will change this textfield's responder status, so start by
// setting the _isEditing flag to NO to prevent the target action being
// called twice (once below and once from resignFirstResponder).
if (_isEditing)
{
_isEditing = NO;
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
}
// If there is no target action, or the sendAction call returns
// success.
if (![self action] || [self sendAction:[self action] to:[self target]])
{
if (_isEditing)
{
_isEditing = NO;
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
}
[self selectAll:nil];
}
}
@@ -1017,7 +1021,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
maxSize = [self currentValueForThemeAttribute:@"max-size"],
lineBreakMode = [self lineBreakMode],
text = (_stringValue || @" "),
textSize = _CGSizeMakeCopy(frameSize),
textSize = CGSizeMakeCopy(frameSize),
font = [self currentValueForThemeAttribute:@"font"];
textSize.width -= contentInset.left + contentInset.right;
@@ -1287,10 +1291,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (CGRect)contentRectForBounds:(CGRect)bounds
{
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
if (!contentInset)
return bounds;
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
contentInset = [self currentValueForThemeAttribute:@"content-inset"];
bounds.origin.x += contentInset.left;
bounds.origin.y += contentInset.top;
@@ -1315,6 +1317,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return bounds;
}
- (CGInset)borderInset
{
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
borderInset = [self currentValueForThemeAttribute:@"border-inset"];
return CGInsetUnion(bezelInset, borderInset);
}
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
{
if (aName === "bezel-view")
+153 -66
View File
@@ -93,65 +93,69 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
return "tokenfield";
}
- (id)initWithFrame:(CPRect)frame
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
_selectedRange = CPMakeRange(0, 0);
_tokenScrollView = [[CPScrollView alloc] initWithFrame:CGRectMakeZero()];
[_tokenScrollView setHasHorizontalScroller:NO];
[_tokenScrollView setHasVerticalScroller:NO];
[_tokenScrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
var contentView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
[contentView setAutoresizingMask:CPViewWidthSizable];
[_tokenScrollView setDocumentView:contentView];
[self addSubview:_tokenScrollView];
_tokenIndex = 0;
_cachedCompletions = [];
_completionDelay = [CPTokenField defaultCompletionDelay];
_tokenizingCharacterSet = [[self class] defaultTokenizingCharacterSet];
_autocompleteContainer = [[CPView alloc] initWithFrame:CPRectMake(0.0, 0.0, frame.size.width, 92.0)];
[_autocompleteContainer setBackgroundColor:[_CPMenuWindow backgroundColorForBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle]];
_autocompleteScrollView = [[CPScrollView alloc] initWithFrame:CPRectMake(1.0, 1.0, frame.size.width - 2.0, 90.0)];
[_autocompleteScrollView setAutohidesScrollers:YES];
[_autocompleteScrollView setHasHorizontalScroller:NO];
[_autocompleteContainer addSubview:_autocompleteScrollView];
_autocompleteView = [[CPTableView alloc] initWithFrame:CPRectMakeZero()];
var tableColumn = [[CPTableColumn alloc] initWithIdentifier:CPTokenFieldTableColumnIdentifier];
[tableColumn setResizingMask:CPTableColumnAutoresizingMask];
[_autocompleteView addTableColumn:tableColumn];
[_autocompleteView setDataSource:self];
[_autocompleteView setDelegate:self];
[_autocompleteView setAllowsMultipleSelection:NO];
[_autocompleteView setHeaderView:nil];
[_autocompleteView setCornerView:nil];
[_autocompleteView setRowHeight:30.0];
[_autocompleteView setGridStyleMask:CPTableViewSolidHorizontalGridLineMask];
[_autocompleteView setBackgroundColor:[CPColor clearColor]];
[_autocompleteView setGridColor:[CPColor colorWithRed:242.0 / 255.0 green:243.0 / 255.0 blue:245.0 / 255.0 alpha:1.0]];
[_autocompleteScrollView setDocumentView:_autocompleteView];
[self setBezeled:YES];
[self _init];
[self setObjectValue:[]];
[self setNeedsLayout];
}
return self;
}
- (void)_init
{
var frame = [self frame];
_tokenScrollView = [[CPScrollView alloc] initWithFrame:_CGRectMakeZero()];
[_tokenScrollView setHasHorizontalScroller:NO];
[_tokenScrollView setHasVerticalScroller:NO];
[_tokenScrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
var contentView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
[contentView setAutoresizingMask:CPViewWidthSizable];
[_tokenScrollView setDocumentView:contentView];
[self addSubview:_tokenScrollView];
_cachedCompletions = [];
_autocompleteContainer = [[CPView alloc] initWithFrame:_CGRectMake(0.0, 0.0, frame.size.width, 92.0)];
[_autocompleteContainer setBackgroundColor:[_CPMenuWindow backgroundColorForBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle]];
_autocompleteScrollView = [[CPScrollView alloc] initWithFrame:_CGRectMake(1.0, 1.0, frame.size.width - 2.0, 90.0)];
[_autocompleteScrollView setAutohidesScrollers:YES];
[_autocompleteScrollView setHasHorizontalScroller:NO];
[_autocompleteContainer addSubview:_autocompleteScrollView];
_autocompleteView = [[CPTableView alloc] initWithFrame:_CGRectMakeZero()];
var tableColumn = [[CPTableColumn alloc] initWithIdentifier:CPTokenFieldTableColumnIdentifier];
[tableColumn setResizingMask:CPTableColumnAutoresizingMask];
[_autocompleteView addTableColumn:tableColumn];
[_autocompleteView setDataSource:self];
[_autocompleteView setDelegate:self];
[_autocompleteView setAllowsMultipleSelection:NO];
[_autocompleteView setHeaderView:nil];
[_autocompleteView setCornerView:nil];
[_autocompleteView setRowHeight:30.0];
[_autocompleteView setGridStyleMask:CPTableViewSolidHorizontalGridLineMask];
[_autocompleteView setBackgroundColor:[CPColor clearColor]];
[_autocompleteView setGridColor:[CPColor colorWithRed:242.0 / 255.0 green:243.0 / 255.0 blue:245.0 / 255.0 alpha:1.0]];
[_autocompleteScrollView setDocumentView:_autocompleteView];
}
// ===============
// = CONVENIENCE =
// ===============
@@ -159,12 +163,16 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
{
var indexOfSelectedItem = 0;
_cachedCompletions = [self tokenField:self completionsForSubstring:[self _inputElement].value indexOfToken:_tokenIndex indexOfSelectedItem:indexOfSelectedItem];
#if PLATFORM(DOM)
_cachedCompletions = [self tokenField:self completionsForSubstring:[self _inputElement].value indexOfToken:0 indexOfSelectedItem:indexOfSelectedItem];
#endif
[_autocompleteView selectRowIndexes:[CPIndexSet indexSetWithIndex:indexOfSelectedItem] byExtendingSelection:NO];
[_autocompleteView reloadData];
}
#if PLATFORM(DOM)
- (void)_autocompleteWithDOMEvent:(JSObject)DOMEvent
{
if (!_cachedCompletions || ![self hasThemeState:CPThemeStateAutoCompleting])
@@ -202,10 +210,25 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
if (shouldRemoveLastObject)
[objectValue removeObjectAtIndex:_selectedRange.location];
[objectValue insertObject:token atIndex:_selectedRange.location];
// Give the delegate a chance to confirm, replace or add to the list of tokens being added.
var delegateApprovedObjects = [self tokenField:self shouldAddObjects:[CPArray arrayWithObject:token] atIndex:_selectedRange.location],
delegateApprovedObjectsCount = [delegateApprovedObjects count];
if (delegateApprovedObjects)
{
for (var i = 0; i < delegateApprovedObjectsCount; i++)
{
[objectValue insertObject:[delegateApprovedObjects objectAtIndex:i] atIndex:_selectedRange.location + i];
}
}
// Put the cursor after the last inserted token.
var location = _selectedRange.location;
[self setObjectValue:objectValue];
_selectedRange = CPMakeRange(location + 1, 0);
if (delegateApprovedObjectsCount)
location += delegateApprovedObjectsCount;
_selectedRange = CPMakeRange(location, 0);
[self _inputElement].value = @"";
[self setNeedsLayout];
@@ -218,6 +241,8 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
[self _autocompleteWithDOMEvent:nil];
}
#endif
- (void)_selectToken:(_CPTokenFieldToken)token byExtendingSelection:(BOOL)extend
{
var indexOfToken = [[self _tokens] indexOfObject:token];
@@ -368,10 +393,10 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
[self unsetThemeState:CPThemeStateEditing];
[self _autocomplete];
#if PLATFORM(DOM)
[self _autocomplete];
var element = [self _inputElement];
CPTokenFieldInputResigning = YES;
@@ -654,7 +679,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
rowRect = [autocompleteView rectOfRow:index],
owner = CPTokenFieldInputOwner;
if (rowRect && !CPRectContainsRect([clipView bounds], rowRect))
if (rowRect && !CGRectContainsRect([clipView bounds], rowRect))
[clipView scrollToPoint:[autocompleteView rectOfRow:index].origin];
if (aDOMEvent.keyCode === CPReturnKeyCode || aDOMEvent.keyCode === CPTabKeyCode)
@@ -744,7 +769,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
else if (aDOMEvent.keyCode === CPDeleteKeyCode)
{
// Highlight the previous token if backspace was pressed in an empty input element or re-show the completions view
if (CPTokenFieldDOMInputElement.value == @"")
if (CPTokenFieldDOMInputElement.value.length === 0)
{
[self _hideCompletions];
@@ -864,6 +889,15 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
}
#endif
// In the context of a token field, it is empty when there are no tokens
- (void)_updatePlaceholderState
{
if (([[self _tokens] count] === 0) && ![self hasThemeState:CPThemeStateEditing])
[self setThemeState:CPTextFieldStatePlaceholder];
else
[self unsetThemeState:CPTextFieldStatePlaceholder];
}
// - (void)setTokenStyle: (NSTokenStyle) style;
// - (NSTokenStyle)tokenStyle;
//
@@ -946,9 +980,9 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
// Manually sizeToFit because CPTableView's sizeToFit doesn't work properly
[_autocompleteContainer setHidden:NO];
var frameOrigin = [self convertPoint:[self bounds].origin toView:[_autocompleteContainer superview]];
[_autocompleteContainer setFrameOrigin:CPPointMake(frameOrigin.x, frameOrigin.y + frame.size.height)];
[_autocompleteContainer setFrameSize:CPSizeMake(CPRectGetWidth([self bounds]), 92.0)];
[_autocompleteScrollView setFrameSize:CPSizeMake([_autocompleteContainer frame].size.width - 2.0, 90.0)];
[_autocompleteContainer setFrameOrigin:_CGPointMake(frameOrigin.x, frameOrigin.y + frame.size.height)];
[_autocompleteContainer setFrameSize:_CGSizeMake(_CGRectGetWidth([self bounds]), 92.0)];
[_autocompleteScrollView setFrameSize:_CGSizeMake([_autocompleteContainer frame].size.width - 2.0, 90.0)];
}
else
[_autocompleteContainer setHidden:YES];
@@ -958,11 +992,11 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
return;
// Move each token into the right position.
var contentRect = CGRectMakeCopy([contentView bounds]),
var contentRect = _CGRectMakeCopy([contentView bounds]),
contentOrigin = contentRect.origin,
contentSize = contentRect.size,
offset = CPPointMake(contentOrigin.x, contentOrigin.y),
spaceBetweenTokens = CPSizeMake(2.0, 2.0),
offset = _CGPointMake(contentOrigin.x, contentOrigin.y),
spaceBetweenTokens = _CGSizeMake(2.0, 2.0),
isEditing = [[self window] firstResponder] == self,
tokenToken = [_CPTokenFieldToken new];
@@ -973,7 +1007,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
var fitAndFrame = function(width, height)
{
var r = CGRectMake(0, 0, width, height);
var r = _CGRectMake(0, 0, width, height);
if (offset.x + width >= contentSize.width && offset.x > contentOrigin.x)
{
@@ -986,7 +1020,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
// Make sure the frame fits.
if (CGRectGetHeight([contentView bounds]) < offset.y + height)
[contentView setFrame:CGRectMake(0, 0, CGRectGetWidth([_tokenScrollView bounds]), offset.y + height)];
[contentView setFrame:_CGRectMake(0, 0, CGRectGetWidth([_tokenScrollView bounds]), offset.y + height)];
offset.x += width + spaceBetweenTokens.width;
@@ -995,6 +1029,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
var placeEditor = function(useRemainingWidth)
{
#if PLATFORM(DOM)
var element = [self _inputElement],
textWidth = 1;
@@ -1004,6 +1039,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
// without clipping. Since different fonts might have different sizes of "X" this
// solution is not ideal, but it works.
textWidth = [(element.value || @"") + "X" sizeWithFont:[self font]].width;
if (useRemainingWidth)
textWidth = MAX(contentSize.width - offset.x - 1, textWidth);
}
@@ -1018,6 +1054,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
// When editing, always scroll to the cursor.
if (_selectedRange.length == 0)
[[_tokenScrollView documentView] scrollRectToVisible:inputFrame];
#endif
}
for (var i = 0, count = [tokens count]; i < count; i++)
@@ -1044,6 +1081,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
if (isEditing && CPMaxRange(_selectedRange) >= [tokens count])
placeEditor(true);
#if PLATFORM(DOM)
// Hide the editor if there are selected tokens, but still keep it active
// so we can continue using our standard keyboard handling events.
if (isEditing && _selectedRange.length)
@@ -1051,24 +1089,27 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
[self _inputElement].style.left = "-10000px";
[self _inputElement].focus();
}
#endif
// Trim off any excess height downwards.
if (CGRectGetHeight([contentView bounds]) > offset.y + tokenHeight)
[contentView setFrame:CGRectMake(0, 0, CGRectGetWidth([_tokenScrollView bounds]), offset.y + tokenHeight)];
[contentView setFrame:_CGRectMake(0, 0, CGRectGetWidth([_tokenScrollView bounds]), offset.y + tokenHeight)];
if (_shouldScrollTo !== CPScrollDestinationNone)
{
// Only carry out the scroll if the cursor isn't visible.
if (!(isEditing && _selectedRange.length == 0))
{
var scrollToToken = _shouldScrollTo;
if (scrollToToken === CPScrollDestinationLeft)
scrollToToken = tokens[_selectedRange.location]
else if (scrollToToken === CPScrollDestinationRight)
scrollToToken = tokens[MAX(0, CPMaxRange(_selectedRange) - 1)];
[self _scrollTokenViewToVisible:scrollToToken];
}
_shouldScrollTo = CPScrollDestinationNone;
}
}
@@ -1123,7 +1164,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
{
if ([[self delegate] respondsToSelector:@selector(tokenField:completionsForSubstring:indexOfToken:indexOfSelectedItem:)])
{
return [[self delegate] tokenField:tokenField completionsForSubstring:substring indexOfToken:_tokenIndex indexOfSelectedItem:selectedIndex];
return [[self delegate] tokenField:tokenField completionsForSubstring:substring indexOfToken:tokenIndex indexOfSelectedItem:selectedIndex];
}
return [];
@@ -1149,7 +1190,19 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
// // return an array of represented objects you want to add.
// // If you want to reject the add, return an empty array.
// // returning nil will cause an error.
// - (NSArray *)tokenField:(NSTokenField *)tokenField shouldAddObjects:(NSArray *)tokens atIndex:(NSUInteger)index;
- (CPArray)tokenField:(CPTokenField)tokenField shouldAddObjects:(CPArray)tokens atIndex:(int)index
{
var delegate = [self delegate];
if ([delegate respondsToSelector:@selector(tokenField:shouldAddObjects:atIndex:)])
{
var approvedObjects = [delegate tokenField:tokenField shouldAddObjects:tokens atIndex:index];
if (approvedObjects !== nil)
return approvedObjects;
}
return tokens;
}
//
// // If you return nil or don't implement these delegate methods, we will assume
// // editing string = display string = represented object
@@ -1184,11 +1237,11 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
return "tokenfield-token";
}
- (id)initWithFrame:(CPRect)frame
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
_deleteButton = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:CPRectMakeZero()];
_deleteButton = [[_CPTokenFieldTokenCloseButton alloc] initWithFrame:_CGRectMakeZero()];
[self addSubview:_deleteButton];
[self setEditable:NO];
@@ -1249,7 +1302,7 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
buttonOffset = [_deleteButton currentValueForThemeAttribute:@"offset"],
buttonSize = [_deleteButton currentValueForThemeAttribute:@"min-size"];
[_deleteButton setFrame:CPRectMake(CPRectGetMaxX(frame) - buttonOffset.x, CPRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)];
[_deleteButton setFrame:_CGRectMake(_CGRectGetMaxX(frame) - buttonOffset.x, _CGRectGetMinY(frame) + buttonOffset.y, buttonSize.width, buttonSize.height)];
}
}
@@ -1292,3 +1345,37 @@ var CPThemeStateAutoCompleting = @"CPThemeStateAutoCompleting",
}
@end
var CPTokenFieldTokenizingCharacterSetKey = "CPTokenFieldTokenizingCharacterSetKey",
CPTokenFieldCompletionDelayKey = "CPTokenFieldCompletionDelay";
@implementation CPTokenField (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_tokenizingCharacterSet = [aCoder decodeObjectForKey:CPTokenFieldTokenizingCharacterSetKey] || [[self class] defaultTokenizingCharacterSet];
_completionDelay = [aCoder decodeDoubleForKey:CPTokenFieldCompletionDelayKey] || [[self class] defaultCompletionDelay];
[self _init];
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeInt:_tokenizingCharacterSet forKey:CPTokenFieldTokenizingCharacterSetKey];
[aCoder encodeDouble:_completionDelay forKey:CPTokenFieldCompletionDelayKey];
}
@end
+1 -1
View File
@@ -1015,7 +1015,7 @@ var TOP_MARGIN = 5.0,
_labelSize = [_labelField frame].size;
_minSize = CGSizeMake(MAX(_labelSize.width, minSize.width), _labelSize.height + minSize.height + LABEL_MARGIN + TOP_MARGIN);
_maxSize = CGSizeMake(MIN(_labelSize.width, maxSize.width), 100000000.0);
_maxSize = CGSizeMake(MAX(_labelSize.width, maxSize.width), 100000000.0);
[_toolbar tile];
}
+8 -7
View File
@@ -111,7 +111,8 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
view = [self _targetView:dictionary],
startFrame = [self _startFrame:dictionary],
endFrame = [self _endFrame:dictionary],
differenceFrame = _CGRectMakeZero();
differenceFrame = _CGRectMakeZero(),
value = [super currentValue];
differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x;
differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y;
@@ -119,19 +120,19 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
differenceFrame.size.height = endFrame.size.height - startFrame.size.height;
var intermediateFrame = _CGRectMakeZero();
intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * progress;
intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * progress;
intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * progress;
intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * progress;
intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * value;
intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * value;
intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * value;
intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * value;
[view setFrame:intermediateFrame];
// Update the view's alpha value
var effect = [self _effect:dictionary];
if (effect === CPViewAnimationFadeInEffect)
[view setAlphaValue:1.0 * progress];
[view setAlphaValue:1.0 * value];
else if (effect === CPViewAnimationFadeOutEffect)
[view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * progress];
[view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * value];
if (progress === 1.0)
[self _targetView:view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
+64 -12
View File
@@ -426,6 +426,7 @@ CPTexturedBackgroundWindowMask
// Create a generic content view.
[self setContentView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
[self setInitialFirstResponder:[self contentView]];
_firstResponder = self;
@@ -480,6 +481,7 @@ CPTexturedBackgroundWindowMask
[self close];
_platformWindow = aPlatformWindow;
[_platformWindow _setTitle:_title window:self];
if (wasVisible)
[self orderFront:self];
@@ -949,6 +951,12 @@ CPTexturedBackgroundWindowMask
var bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_frame), CGRectGetHeight(_frame));
// During init the initial first responder is set to the contentView
// if it hasn't changed in the mean time we need to update that reference
// to the new contentView
if ([self initialFirstResponder] === _contentView)
[self setInitialFirstResponder:aView];
_contentView = aView;
[_contentView setFrame:[self contentRectForFrameRect:bounds]];
@@ -1273,17 +1281,17 @@ CPTexturedBackgroundWindowMask
- (BOOL)acceptsFirstResponder
{
return YES;
return NO;
}
- (id)initialFirstResponder
- (CPView)initialFirstResponder
{
return _initialFirstResponder;
}
- (void)setInitialFirstResponder:(id)aResponder
- (void)setInitialFirstResponder:(CPView)aView
{
_initialFirstResponder = aResponder;
_initialFirstResponder = aView;
}
/*!
@@ -1359,6 +1367,7 @@ CPTexturedBackgroundWindowMask
_title = aTitle;
[_windowView setTitle:aTitle];
[_platformWindow _setTitle:_title window:self];
[self _synchronizeMenuBarTitleWithWindowTitle];
}
@@ -2485,30 +2494,70 @@ CPTexturedBackgroundWindowMask
- (void)selectNextKeyView:(id)sender
{
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
[self recalculateKeyViewLoop];
var nextValidKeyView = nil;
if ([_firstResponder isKindOfClass:[CPView class]])
[self selectKeyViewFollowingView:_firstResponder];
nextValidKeyView = [_firstResponder nextValidKeyView];
if (!nextValidKeyView)
{
var initialFirstResponder = [self initialFirstResponder];
if ([initialFirstResponder acceptsFirstResponder])
nextValidKeyView = initialFirstResponder;
else
nextValidKeyView = [initialFirstResponder nextValidKeyView];
}
[self makeFirstResponder:nextValidKeyView];
}
- (void)selectPreviousKeyView:(id)sender
{
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
[self recalculateKeyViewLoop];
var previousValidKeyView = nil;
if ([_firstResponder isKindOfClass:[CPView class]])
[self selectKeyViewPrecedingView:_firstResponder];
previousValidKeyView = [_firstResponder previousValidKeyView];
if (!previousValidKeyView)
{
var initialFirstResponder = [self initialFirstResponder];
if ([initialFirstResponder acceptsFirstResponder])
previousValidKeyView = initialFirstResponder;
else
previousValidKeyView = [initialFirstResponder previousValidKeyView];
}
[self makeFirstResponder:previousValidKeyView];
}
- (void)selectKeyViewFollowingView:(CPView)aView
{
if (_keyViewLoopIsDirty)
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
[self recalculateKeyViewLoop];
[self makeFirstResponder:[aView nextValidKeyView]];
var nextValidKeyView = [aView nextValidKeyView];
if ([nextValidKeyView isKindOfClass:[CPView class]])
[self makeFirstResponder:nextValidKeyView];
}
- (void)selectKeyViewPrecedingView:(CPView)aView
{
if (_keyViewLoopIsDirty)
if (_keyViewLoopIsDirty && [self autorecalculatesKeyViewLoop])
[self recalculateKeyViewLoop];
[self makeFirstResponder:[aView previousValidKeyView]];
var previousValidKeyView = [aView previousValidKeyView];
if ([previousValidKeyView isKindOfClass:[CPView class]])
[self makeFirstResponder:previousValidKeyView];
}
/*!
@@ -2596,9 +2645,11 @@ CPTexturedBackgroundWindowMask
var allViews = function(aWindow)
{
var views = [[aWindow contentView] subviews],
index = 0;
var views = [CPArray arrayWithObject:[aWindow contentView]];
[views addObjectsFromArray:[[aWindow contentView] subviews]];
var index = 0;
for (; index < views.length; ++index)
views = views.concat([views[index] subviews]);
@@ -2928,5 +2979,6 @@ CPCustomWindowShadowStyle = 3;
@import "_CPHUDWindowView.j"
@import "_CPBorderlessWindowView.j"
@import "_CPBorderlessBridgeWindowView.j"
@import "_CPAttachedWindowView.j"
@import "CPDragServer.j"
@import "CPView.j"
+300
View File
@@ -0,0 +1,300 @@
/*
* _CPAttachedWindowView.j
* AppKit
*
* Created by Antoine Mercadal
* Copyright 2011 <primalmotion@archipelproject.org>
*
* 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 "_CPWindowView.j"
/*!
@ignore
A custom CPWindowView that manage border and cursor
*/
@implementation _CPAttachedWindowView : _CPWindowView
{
BOOL _mouseDownPressed @accessors(getter=isMouseDownPressed, setter=setMouseDownPressed:);
float _arrowOffsetX @accessors(property=arrowOffsetX);
float _arrowOffsetY @accessors(property=arrowOffsetY);
int _appearance @accessors(property=appearance);
unsigned _preferredEdge @accessors(property=preferredEdge);
CPSize _cursorSize;
}
/*!
Compute the contentView frame from a given window frame
@param aFrameRect the window frame
*/
- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
{
var contentRect = CGRectMakeCopy(aFrameRect);
// @todo change border art and remove this pixel perfect adaptation
// return CGRectInset(contentRect, 20, 20);
contentRect.origin.x += 18;
contentRect.origin.y += 17;
contentRect.size.width -= 35;
contentRect.size.height -= 37;
return contentRect;
}
/*!
Compute the window frame from a given contentView frame
@param aContentRect the contentView frame
*/
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
{
var frameRect = CGRectMakeCopy(aContentRect);
// @todo change border art and remove this pixel perfect adaptation
//return CGRectOffset(frameRect, 20, 20);
frameRect.origin.x -= 18;
frameRect.origin.y -= 17;
frameRect.size.width += 35;
frameRect.size.height += 37;
return frameRect;
}
/*!
Initialize the _CPWindowView
*/
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
{
if (self = [super initWithFrame:aFrame styleMask:aStyleMask])
{
var bundle = [CPBundle bundleForClass:[self class]];
_arrowOffsetX = 0.0;
_arrowOffsetY = 0.0;
// @TODO: make this themable
_useGlowingEffect = YES;
_appearance = CPPopoverAppearanceMinimal;
_cursorSize = CPSizeMake(15, 10);
}
return self;
}
/*!
Hide the cursor
*/
- (void)hideCursor
{
_cursorSize = CPSizeMakeZero();
[self setNeedsDisplay:YES];
}
/*!
Show the cursor
*/
- (void)showCursor
{
_cursorSize = CPSizeMake(15, 10);
[self setNeedsDisplay:YES];
_mouseDownPressed = NO;
}
/*!
Draw the view
*/
- (void)drawRect:(CGRect)aRect
{
[super drawRect:aRect];
var context = [[CPGraphicsContext currentContext] graphicsPort],
radius = 5,
arrowWidth = _cursorSize.width,
arrowHeight = _cursorSize.height,
strokeWidth = 1,
strokeColor,
shadowColor = [[CPColor blackColor] colorWithAlphaComponent:.2],
shadowSize = CGSizeMake(0, 7),
shadowBlur = 15,
gradient;
if (_appearance == CPPopoverAppearanceMinimal)
{
gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [(254.0 / 255), (254.0 / 255), (254.0 / 255), 0.93,
(231.0 / 255), (231.0 / 255), (231.0 / 255), 0.93], [0,1], 2);
strokeColor = [CPColor colorWithHexString:@"B8B8B8"];
}
else
{
gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [(38.0 / 255), (38.0 / 255), (38.0 / 255), 0.93,
(18.0 / 255), (18.0 / 255), (18.0 / 255), 0.93], [0,1], 2);
strokeColor = [CPColor colorWithHexString:@"222222"];
}
// fix rect to take care of stroke and shadow
aRect.origin.x += strokeWidth + shadowBlur;
aRect.origin.y += strokeWidth + (shadowBlur + shadowSize.height / 2);
aRect.size.width -= (strokeWidth * 2) + (shadowBlur * 2);
aRect.size.height -= (strokeWidth * 2) + (shadowBlur * 2 + shadowSize.height);
CGContextSetStrokeColor(context, strokeColor);
CGContextSetLineWidth(context, strokeWidth);
CGContextBeginPath(context);
CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor);
CGContextDrawLinearGradient(context, gradient, CGPointMake(CPRectGetMidX(aRect), 0.0), CGPointMake(CPRectGetMidX(aRect), aRect.size.height), 0);
var xMin = _CGRectGetMinX(aRect),
xMax = _CGRectGetMaxX(aRect),
yMin = _CGRectGetMinY(aRect),
yMax = _CGRectGetMaxY(aRect);
// draw!
switch (_preferredEdge)
{
case CPMinXEdge:
// origin ne
CGContextMoveToPoint(context, xMin + radius, yMin);
// ne
CGContextAddLineToPoint(context, xMax - radius, yMin);
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
// arrow CPMinXEdge
CGContextAddLineToPoint(context, xMax, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY - (arrowHeight - 2));
CGContextAddLineToPoint(context, aRect.size.width + arrowHeight + aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY);
CGContextAddLineToPoint(context, aRect.size.width + aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2 + (arrowWidth / 2)) + aRect.origin.y + _arrowOffsetY);
// se
CGContextAddLineToPoint(context, xMax, yMax - radius);
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
// sw
CGContextAddLineToPoint(context, xMin + radius, yMax);
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
// nw
CGContextAddLineToPoint(context, xMin, yMin + radius);
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
break;
case CPMaxXEdge:
// origin ne
CGContextMoveToPoint(context, xMin + radius, yMin);
// ne
CGContextAddLineToPoint(context, xMax - radius, yMin);
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
// se
CGContextAddLineToPoint(context, xMax, yMax - radius);
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
// sw
CGContextAddLineToPoint(context, xMin + radius, yMax);
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
// arrow CPMaxXEdge
CGContextAddLineToPoint(context, xMin, (aRect.size.height / 2 + (arrowWidth / 2) + aRect.origin.y + _arrowOffsetY));
CGContextAddLineToPoint(context, aRect.origin.x - arrowHeight + _arrowOffsetX, (aRect.size.height / 2) + aRect.origin.y + _arrowOffsetY);
CGContextAddLineToPoint(context, aRect.origin.x + _arrowOffsetX, (aRect.size.height / 2 - (arrowWidth / 2) + aRect.origin.y + _arrowOffsetY));
// nw
CGContextAddLineToPoint(context, xMin, yMin + radius);
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
break;
case CPMaxYEdge:
// origin nw
CGContextMoveToPoint(context, xMin, yMin + yMin);
// nw
CGContextAddLineToPoint(context, xMin, yMin + radius);
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
// arrow CPMaxYEdge
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX - (arrowWidth / 2), yMin);
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX, aRect.origin.y - arrowHeight + _arrowOffsetY);
CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX, aRect.origin.y + _arrowOffsetY);
// ne
CGContextAddLineToPoint(context, xMax - radius, yMin);
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
// se
CGContextAddLineToPoint(context, xMax, yMax - radius);
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
// sw
CGContextAddLineToPoint(context, xMin + radius, yMax);
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
break;
case CPMinYEdge:
// origin nw
CGContextMoveToPoint(context, xMin, yMin + yMin);
// nw
CGContextAddLineToPoint(context, xMin, yMin + radius);
CGContextAddCurveToPoint(context, xMin, yMin + radius, xMin, yMin, xMin + radius, yMin);
// ne
CGContextAddLineToPoint(context, xMax - radius, yMin);
CGContextAddCurveToPoint(context, xMax - radius, yMin, xMax, yMin, xMax, yMin + radius);
// se
CGContextAddLineToPoint(context, xMax, yMax - radius);
CGContextAddCurveToPoint(context, xMax, yMax - radius, xMax, yMax, xMax - radius, yMax);
// arrow CPMinYEdge
CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX , yMax);
CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x + _arrowOffsetX, aRect.size.height + aRect.origin.y + arrowHeight + _arrowOffsetY);
CGContextAddLineToPoint(context, (aRect.size.width / 2) - (arrowWidth / 2) + aRect.origin.x + _arrowOffsetX, aRect.size.height + aRect.origin.y + _arrowOffsetY);
// sw
CGContextAddLineToPoint(context, xMin + radius, yMax);
CGContextAddCurveToPoint(context, xMin + radius, yMax, xMin, yMax, xMin, yMax - radius);
break;
default:
// no computed edge means standard rounded rect
CGContextAddPath(context, CGPathWithRoundedRectangleInRect(aRect, radius, radius, YES, YES, YES, YES));
}
CGContextClosePath(context);
//Draw it
CGContextStrokePath(context);
CGContextFillPath(context);
}
- (void)mouseDown:(CPEvent)anEvent
{
_mouseDownPressed = YES;
[super mouseDown:anEvent];
}
- (void)mouseUp:(CPEvent)anEvent
{
_mouseDownPressed = NO;
[super mouseUp:anEvent];
}
@end
+26
View File
@@ -267,6 +267,32 @@ function CGPointFromEvent(anEvent)
return _CGPointMake(anEvent.clientX, anEvent.clientY);
}
/*!
Combines two insets by adding their individual elements and returns the result.
@group CGInset
*/
function CGInsetUnion(lhsInset, rhsInset)
{
return _CGInsetMake(lhsInset.top + rhsInset.top,
lhsInset.right + rhsInset.right,
lhsInset.bottom + rhsInset.bottom,
lhsInset.left + rhsInset.left);
}
/*!
Subtract one inset from another by subtracting their individual elements and returns the result.
@group CGInset
*/
function CGInsetDifference(lhsInset, rhsInset)
{
return _CGInsetMake(lhsInset.top - rhsInset.top,
lhsInset.right - rhsInset.right,
lhsInset.bottom - rhsInset.bottom,
lhsInset.left - rhsInset.left);
}
function CGInsetFromString(aString)
{
var numbers = aString.substr(1, aString.length - 2).split(',');
+16
View File
@@ -32,6 +32,7 @@ var PrimaryPlatformWindow = NULL;
CPInteger _level;
BOOL _hasShadow;
unsigned _shadowStyle;
CPString _title;
#if PLATFORM(DOM)
DOMWindow _DOMWindow;
@@ -258,6 +259,21 @@ var PrimaryPlatformWindow = NULL;
return [CPPlatform isBrowser];
}
- (void)_setTitle:(CPString)aTitle window:(CPWindow)aWindow
{
_title = aTitle;
#if PLATFORM(DOM)
if (_DOMWindow && _DOMWindow.document && (aWindow === [CPApp mainWindow]))
_DOMWindow.document.title = _title;
#endif
}
- (CPString)title
{
return _title;
}
@end
#if PLATFORM(BROWSER)
+3 -1
View File
@@ -534,13 +534,15 @@ var ModifierKeyCodes = [
if (_DOMWindow)
return _DOMWindow.focus();
_DOMWindow = window.open("", "_blank", "menubar=no,location=no,resizable=yes,scrollbars=no,status=no,left=" + _CGRectGetMinX(_contentRect) + ",top=" + _CGRectGetMinY(_contentRect) + ",width=" + _CGRectGetWidth(_contentRect) + ",height=" + _CGRectGetHeight(_contentRect));
_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));
[PlatformWindows addObject:self];
// FIXME: cpSetFrame?
_DOMWindow.document.write("<!DOCTYPE html><html lang='en'><head></head><body style='background-color:transparent;'></body></html>");
_DOMWindow.document.close();
if (self != [CPPlatformWindow primaryPlatformWindow])
_DOMWindow.document.title = _title;
if (![CPPlatform isBrowser])
{
Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 B

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 757 B

After

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 B

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1004 B

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 991 B

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1004 B

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 991 B

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 B

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 B

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 991 B

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1009 B

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1007 B

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 203 B

+156 -51
View File
@@ -762,46 +762,47 @@ var themedButtonValues = nil,
bezelColor = PatternColor(
[
["textfield-bezel-square-0.png", 3.0, 4.0],
["textfield-bezel-square-1.png", 1.0, 4.0],
["textfield-bezel-square-2.png", 3.0, 4.0],
["textfield-bezel-square-3.png", 3.0, 1.0],
["textfield-bezel-square-0.png", 6.0, 6.0],
["textfield-bezel-square-1.png", 1.0, 6.0],
["textfield-bezel-square-2.png", 6.0, 6.0],
["textfield-bezel-square-3.png", 6.0, 1.0],
["textfield-bezel-square-4.png", 1.0, 1.0],
["textfield-bezel-square-5.png", 3.0, 1.0],
["textfield-bezel-square-6.png", 3.0, 4.0],
["textfield-bezel-square-7.png", 1.0, 4.0],
["textfield-bezel-square-8.png", 3.0, 4.0]
["textfield-bezel-square-5.png", 6.0, 1.0],
["textfield-bezel-square-6.png", 6.0, 6.0],
["textfield-bezel-square-7.png", 1.0, 6.0],
["textfield-bezel-square-8.png", 6.0, 6.0]
]),
bezelFocusedColor = PatternColor(
[
["textfield-bezel-square-focused-0.png", 7.0, 7.0],
["textfield-bezel-square-focused-1.png", 1.0, 7.0],
["textfield-bezel-square-focused-2.png", 7.0, 7.0],
["textfield-bezel-square-focused-3.png", 7.0, 1.0],
["textfield-bezel-square-focused-0.png", 6.0, 6.0],
["textfield-bezel-square-focused-1.png", 1.0, 6.0],
["textfield-bezel-square-focused-2.png", 6.0, 6.0],
["textfield-bezel-square-focused-3.png", 6.0, 1.0],
["textfield-bezel-square-focused-4.png", 1.0, 1.0],
["textfield-bezel-square-focused-5.png", 7.0, 1.0],
["textfield-bezel-square-focused-6.png", 7.0, 7.0],
["textfield-bezel-square-focused-7.png", 1.0, 7.0],
["textfield-bezel-square-focused-8.png", 7.0, 7.0]
["textfield-bezel-square-focused-5.png", 6.0, 1.0],
["textfield-bezel-square-focused-6.png", 6.0, 6.0],
["textfield-bezel-square-focused-7.png", 1.0, 6.0],
["textfield-bezel-square-focused-8.png", 6.0, 6.0]
]),
bezelDisabledColor = PatternColor(
[
["textfield-bezel-square-disabled-0.png", 3.0, 4.0],
["textfield-bezel-square-disabled-1.png", 1.0, 4.0],
["textfield-bezel-square-disabled-2.png", 3.0, 4.0],
["textfield-bezel-square-disabled-3.png", 3.0, 1.0],
["textfield-bezel-square-disabled-0.png", 6.0, 6.0],
["textfield-bezel-square-disabled-1.png", 1.0, 6.0],
["textfield-bezel-square-disabled-2.png", 6.0, 6.0],
["textfield-bezel-square-disabled-3.png", 6.0, 1.0],
["textfield-bezel-square-disabled-4.png", 1.0, 1.0],
["textfield-bezel-square-disabled-5.png", 3.0, 1.0],
["textfield-bezel-square-disabled-6.png", 3.0, 4.0],
["textfield-bezel-square-disabled-7.png", 1.0, 4.0],
["textfield-bezel-square-disabled-8.png", 3.0, 4.0]
["textfield-bezel-square-disabled-5.png", 6.0, 1.0],
["textfield-bezel-square-disabled-6.png", 6.0, 6.0],
["textfield-bezel-square-disabled-7.png", 1.0, 6.0],
["textfield-bezel-square-disabled-8.png", 6.0, 6.0]
]),
textDisabledColor = [CPColor colorWithCalibratedWhite:0.60 alpha:1.0],
placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0];
// Global for reuse by CPTokenField.
// Global for reuse by subclasses
themedTextFieldValues =
[
[@"vertical-alignment", CPTopVerticalTextAlignment, CPThemeStateBezeled],
@@ -810,30 +811,38 @@ var themedButtonValues = nil,
[@"bezel-color", bezelDisabledColor, CPThemeStateBezeled | CPThemeStateDisabled],
[@"font", [CPFont systemFontOfSize:12.0], CPThemeStateBezeled],
[@"content-inset", CGInsetMake(7.0, 7.0, 6.0, 8.0), CPThemeStateBezeled],
[@"content-inset", CGInsetMake(6.0, 7.0, 6.0, 8.0), CPThemeStateBezeled | CPThemeStateEditing],
[@"bezel-inset", CGInsetMake(3.0, 4.0, 3.0, 4.0), CPThemeStateBezeled],
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled | CPThemeStateEditing],
// no border
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0)],
[@"content-inset", CGInsetMake(2.0, 2.0, 2.0, 2.0)], // as defined in [CPTextField +themeAttributes]
// with border
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled],
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled | CPThemeStateEditing],
[@"border-inset", CGInsetMake(3.0, 3.0, 3.0, 3.0), CPThemeStateBezeled],
[@"content-inset", CGInsetMake(8.0, 8.0, 7.0, 8.0), CPThemeStateBezeled],
[@"content-inset", CGInsetMake(8.0, 8.0, 7.0, 8.0), CPThemeStateBezeled | CPThemeStateEditing],
[@"text-color", textDisabledColor, CPThemeStateBezeled | CPThemeStateDisabled],
[@"text-color", placeholderColor, CPTextFieldStatePlaceholder],
[@"text-color", placeholderColor, CPTextFieldStatePlaceholder | CPThemeStateDisabled],
[@"line-break-mode", CPLineBreakByTruncatingTail, CPThemeStateTableDataView],
[@"vertical-alignment", CPCenterVerticalTextAlignment, CPThemeStateTableDataView],
[@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 5.0), CPThemeStateTableDataView],
[@"content-inset", CGInsetMake(0.0, 0.0, 0.0, 4.0), CPThemeStateTableDataView],
[@"text-color", [CPColor colorWithCalibratedWhite:51.0 / 255.0 alpha:1.0], CPThemeStateTableDataView],
[@"text-color", [CPColor whiteColor], CPThemeStateTableDataView | CPThemeStateSelectedTableDataView],
[@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateTableDataView | CPThemeStateSelectedTableDataView],
[@"text-color", [CPColor blackColor], CPThemeStateTableDataView | CPThemeStateEditing],
[@"content-inset", CGInsetMake(7.0, 7.0, 5.0, 8.0), CPThemeStateTableDataView | CPThemeStateEditing],
[@"content-inset", CGInsetMake(8.0, 8.0, 7.0, 4.0), CPThemeStateTableDataView | CPThemeStateEditing],
[@"font", [CPFont systemFontOfSize:12.0], CPThemeStateTableDataView | CPThemeStateEditing],
[@"bezel-inset", CGInsetMake(-2.0, -2.0, -2.0, -2.0), CPThemeStateTableDataView | CPThemeStateEditing],
[@"bezel-inset", CGInsetMake(-1.0, -1.0, -1.0, -1.0), CPThemeStateTableDataView | CPThemeStateEditing],
[@"text-color", [CPColor colorWithCalibratedWhite:125.0 / 255.0 alpha:1.0], CPThemeStateTableDataView | CPThemeStateGroupRow],
[@"text-color", [CPColor colorWithCalibratedWhite:1.0 alpha:1.0], CPThemeStateTableDataView | CPThemeStateGroupRow | CPThemeStateSelectedTableDataView],
[@"text-shadow-color", [CPColor whiteColor], CPThemeStateTableDataView | CPThemeStateGroupRow],
[@"text-shadow-offset", CGSizeMake(0,1), CPThemeStateTableDataView | CPThemeStateGroupRow],
[@"text-shadow-color", [CPColor colorWithCalibratedWhite:0.0 alpha:0.6], CPThemeStateTableDataView | CPThemeStateGroupRow | CPThemeStateSelectedTableDataView],
[@"text-shadow-color", [CPColor colorWithCalibratedWhite:0.0 alpha:0.6], CPThemeStateTableDataView | CPThemeStateGroupRow | CPThemeStateSelectedTableDataView],
[@"font", [CPFont boldSystemFontOfSize:12.0], CPThemeStateTableDataView | CPThemeStateGroupRow]
];
@@ -853,35 +862,45 @@ var themedButtonValues = nil,
var textfield = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 30.0)],
bezelColor = PatternColor(
[
["textfield-bezel-rounded-left.png", 13.0, 22.0],
["textfield-bezel-rounded-center.png", 1.0, 22.0],
["textfield-bezel-rounded-right.png", 13.0, 22.0]
["textfield-bezel-rounded-left.png", 15.0, 30.0],
["textfield-bezel-rounded-center.png", 1.0, 30.0],
["textfield-bezel-rounded-right.png", 15.0, 30.0]
],
PatternIsHorizontal),
bezelFocusedColor = PatternColor(
[
["textfield-bezel-rounded-focused-left.png", 17.0, 30.0],
["textfield-bezel-rounded-focused-left.png", 15.0, 30.0],
["textfield-bezel-rounded-focused-center.png", 1.0, 30.0],
["textfield-bezel-rounded-focused-right.png", 17.0, 30.0]
["textfield-bezel-rounded-focused-right.png", 15.0, 30.0]
],
PatternIsHorizontal),
bezelDisabledColor = PatternColor(
[
["textfield-bezel-rounded-disabled-left.png", 15.0, 30.0],
["textfield-bezel-rounded-disabled-center.png", 1.0, 30.0],
["textfield-bezel-rounded-disabled-right.png", 15.0, 30.0]
],
PatternIsHorizontal),
placeholderColor = [CPColor colorWithCalibratedRed:189.0 / 255.0 green:199.0 / 255.0 blue:211.0 / 255.0 alpha:1.0];
// Global for reuse by CPSearchField
// Global for reuse by subclasses
themedRoundedTextFieldValues =
[
[@"bezel-color", bezelColor, CPTextFieldStateRounded | CPThemeStateBezeled],
[@"bezel-color", bezelFocusedColor, CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing],
[@"bezel-color", bezelDisabledColor, CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateDisabled],
[@"font", [CPFont systemFontOfSize:12.0]],
[@"content-inset", CGInsetMake(8.0, 14.0, 6.0, 14.0), CPTextFieldStateRounded | CPThemeStateBezeled],
[@"content-inset", CGInsetMake(7.0, 14.0, 6.0, 14.0), CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing],
[@"bezel-inset", CGInsetMake(4.0, 4.0, 4.0, 4.0), CPTextFieldStateRounded | CPThemeStateBezeled],
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPTextFieldStateRounded | CPThemeStateBezeled],
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing],
// There is one extra empty pixel at the bottom for size compatibility with an earlier version.
[@"content-inset", CGInsetMake(8.0, 15.0, 8.0, 15.0), CPTextFieldStateRounded | CPThemeStateBezeled],
[@"content-inset", CGInsetMake(8.0, 15.0, 8.0, 15.0), CPTextFieldStateRounded | CPThemeStateBezeled | CPThemeStateEditing],
[@"text-color", placeholderColor, CPTextFieldStateRounded | CPTextFieldStatePlaceholder],
[@"min-size", CGSizeMake(0.0, 30.0), CPTextFieldStateRounded | CPThemeStateBezeled],
@@ -914,11 +933,17 @@ var themedButtonValues = nil,
overrides =
[
[@"content-inset", CGInsetMake(8.0, 0.0, 4.0, 0.0)],
// Placeholder is displayed as regular text, not tokens; requires a different inset.
[@"content-inset", CGInsetMake(9.0, 0.0, 5.0, 2.0), CPTextFieldStatePlaceholder],
[@"content-inset", CGInsetMake(6.0, 5.0, 5.0, 6.0), CPThemeStateBezeled],
[@"content-inset", CGInsetMake(9.0, 7.0, 6.0, 8.0), CPThemeStateBezeled | CPTextFieldStatePlaceholder],
// Non-bezeled token field with tokens
[@"content-inset", CGInsetMake(5.0, 8.0, 4.0, 8.0)],
// Non-bezeled token field with no tokens
[@"content-inset", CGInsetMake(7.0, 8.0, 6.0, 8.0), CPTextFieldStatePlaceholder],
// Bezeled token field with tokens
[@"content-inset", CGInsetMake(6.0, 8.0, 5.0, 8.0), CPThemeStateBezeled],
// Bezeled token field with no tokens
[@"content-inset", CGInsetMake(8.0, 8.0, 7.0, 8.0), CPThemeStateBezeled | CPTextFieldStatePlaceholder],
];
[self registerThemeValues:overrides forView:tokenfield inherit:themedTextFieldValues];
@@ -958,13 +983,13 @@ var themedButtonValues = nil,
[@"text-color", textHighlightedColor, CPThemeStateHighlighted],
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBezeled],
[@"content-inset", CGInsetMake(1.0, 24.0, 2.0, 16.0), CPThemeStateBezeled],
[@"content-inset", CGInsetMake(1.0, 23.0, 3.0, 15.0), CPThemeStateBezeled],
// Minimum height == maximum height since tokens are fixed height.
[@"min-size", CGSizeMake(0.0, 19.0)],
[@"max-size", CGSizeMake(-1.0, 19.0)],
[@"vertical-alignment", CPCenterTextAlignment],
[@"vertical-alignment", CPCenterTextAlignment]
];
[self registerThemeValues:themeValues forView:token];
@@ -990,7 +1015,7 @@ var themedButtonValues = nil,
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered],
[@"bezel-inset", CGInsetMake(0.0, 0.0, 0.0, 0.0), CPThemeStateBordered | CPThemeStateHighlighted],
[@"offset", CGPointMake(18, 6), CPThemeStateBordered]
[@"offset", CGPointMake(17, 6), CPThemeStateBordered]
];
[self registerThemeValues:themeValues forView:button];
@@ -998,6 +1023,86 @@ var themedButtonValues = nil,
return button;
}
+ (CPComboBox)themedComboBox
{
var combo = [[CPComboBox alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 29.0)],
bezelColor = PatternColor(
[
["combobox-bezel-left.png", 6.0, 29.0],
["combobox-bezel-center.png", 1.0, 29.0],
["combobox-bezel-right.png", 24.0, 29.0]
],
PatternIsHorizontal),
bezelFocusedColor = PatternColor(
[
["combobox-bezel-focused-left.png", 6.0, 29.0],
["combobox-bezel-focused-center.png", 1.0, 29.0],
["combobox-bezel-focused-right.png", 24.0, 29.0]
],
PatternIsHorizontal),
bezelDisabledColor = PatternColor(
[
["combobox-bezel-disabled-left.png", 6.0, 29.0],
["combobox-bezel-disabled-center.png", 1.0, 29.0],
["combobox-bezel-disabled-right.png", 24.0, 29.0]
],
PatternIsHorizontal),
bezelNoBorderColor = PatternColor(
[
["combobox-bezel-no-border-left.png", 6.0, 29.0],
["combobox-bezel-no-border-center.png", 1.0, 29.0],
["combobox-bezel-no-border-right.png", 24.0, 29.0]
],
PatternIsHorizontal),
bezelNoBorderFocusedColor = PatternColor(
[
["combobox-bezel-no-border-focused-left.png", 6.0, 29.0],
["combobox-bezel-no-border-focused-center.png", 1.0, 29.0],
["combobox-bezel-no-border-focused-right.png", 24.0, 29.0]
],
PatternIsHorizontal),
bezelNoBorderDisabledColor = PatternColor(
[
["combobox-bezel-no-border-disabled-left.png", 6.0, 29.0],
["combobox-bezel-no-border-disabled-center.png", 1.0, 29.0],
["combobox-bezel-no-border-disabled-right.png", 24.0, 29.0]
],
PatternIsHorizontal),
overrides =
[
[@"bezel-color", bezelColor, CPThemeStateBezeled | CPComboBoxStateButtonBordered],
[@"bezel-color", bezelFocusedColor, CPThemeStateBezeled | CPComboBoxStateButtonBordered | CPThemeStateEditing],
[@"bezel-color", bezelDisabledColor, CPThemeStateBezeled | CPComboBoxStateButtonBordered | CPThemeStateDisabled],
[@"bezel-color", bezelNoBorderColor, CPThemeStateBezeled],
[@"bezel-color", bezelNoBorderFocusedColor, CPThemeStateBezeled | CPThemeStateEditing],
[@"bezel-color", bezelNoBorderDisabledColor, 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],
[@"content-inset", CGInsetMake(8.0, 24.0, 7.0, 8.0), CPThemeStateBezeled],
[@"content-inset", CGInsetMake(8.0, 24.0, 7.0, 8.0), CPThemeStateBezeled | CPThemeStateEditing],
[@"popup-button-size", CGSizeMake(21.0, 23.0), CPThemeStateBezeled | CPComboBoxStateButtonBordered],
[@"popup-button-size", CGSizeMake(17.0, 23.0), CPThemeStateBezeled],
// Because combo box uses a three-part bezel, the height is fixed
[@"min-size", CGSizeMake(0, 29.0)],
[@"max-size", CGSizeMake(-1, 29.0)]
];
[self registerThemeValues:overrides forView:combo inherit:themedTextFieldValues];
return combo;
}
+ (CPRadioButton)themedRadioButton
{
var button = [CPRadio radioWithTitle:@"Hello Friend!"],
+501
View File
@@ -0,0 +1,501 @@
/*
* _CPToolTipWindowView.j
* AppKit
*
* Created by Antoine Mercadal
* Copyright 2011 <primalmotion@archipelproject.org>
*
* 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/CPObject.j>
@import "CPButton.j"
@import "CPWindow.j"
CPClosableOnBlurWindowMask = 1 << 4;
CPPopoverAppearanceMinimal = 0;
CPPopoverAppearanceHUD = 1;
/*!
@ignore
This is a simple attached window like the one that pops up
when you double click on a meeting in iCal
*/
@implementation _CPAttachedWindow : CPWindow
{
BOOL _animates @accessors(property=animates);
id _targetView @accessors(property=targetView);
int _appearance @accessors(getter=appearance);
BOOL _closeOnBlur;
BOOL _isClosed;
BOOL _shouldPerformAnimation;
CPButton _closeButton;
float _animationDuration;
}
/*!
override default windowView class loader
@param aStyleMask the window mask
@return the windowView class
*/
#pragma mark -
#pragma mark Class methods
+ (Class)_windowViewClassForStyleMask:(unsigned)aStyleMask
{
return _CPAttachedWindowView;
}
#pragma mark -
#pragma mark Initialization
/*!
Create and init a _CPAttachedWindow with given size of and view
@param aSize the size of the attached window
@param aView the target view
@return ready to use _CPAttachedWindow
*/
+ (id)attachedWindowWithSize:(CGSize)aSize forView:(CPView)aView
{
return [_CPAttachedWindow attachedWindowWithSize:aSize forView:aView styleMask:nil];
}
/*!
Create and init a _CPAttachedWindow with given size of and view
@param aSize the size of the attached window
@param aView the target view
@return ready to use _CPAttachedWindow
@param styleMask the window style mask (combine CPClosableWindowMask and CPClosableOnBlurWindowMask)
*/
+ (id)attachedWindowWithSize:(CGSize)aSize forView:(CPView)aView styleMask:(int)aMask
{
var attachedWindow = [[_CPAttachedWindow alloc] initWithContentRect:CPRectMake(0.0, 0.0, aSize.width, aSize.height) styleMask:aMask];
[attachedWindow attachToView:aView];
return attachedWindow;
}
/*!
Create and init a _CPAttachedWindow with given frame
@param aFrame the frame of the attached window
@return ready to use _CPAttachedWindow
*/
- (id)initWithContentRect:(CGRect)aFrame
{
self = [self initWithContentRect:aFrame styleMask:nil]
return self;
}
/*!
Create and init a _CPAttachedWindow with given frame
@param aFrame the frame of the attached window
@param styleMask the window style mask (combine CPClosableWindowMask and CPClosableOnBlurWindowMask)
@return ready to use _CPAttachedWindow
*/
- (id)initWithContentRect:(CGRect)aFrame styleMask:(unsigned)aStyleMask
{
if (self = [super initWithContentRect:aFrame styleMask:aStyleMask])
{
_animates = YES;
_animates = YES;
_animationDuration = 150;
_closeOnBlur = (aStyleMask & CPClosableOnBlurWindowMask);
_isClosed = NO;
_shouldPerformAnimation = _animates;
[self setLevel:CPStatusWindowLevel];
[self setMovableByWindowBackground:YES];
[self setHasShadow:NO];
_DOMElement.style.WebkitBackfaceVisibility = "hidden";
_DOMElement.style.WebkitTransitionProperty = "-webkit-transform, opacity";
_DOMElement.style.WebkitTransitionDuration = _animationDuration + "ms";
[_windowView setNeedsDisplay:YES];
}
return self;
}
#pragma mark -
#pragma mark Getters / Setters
- (void)setAppearance:(int)anAppearance
{
if (_appearance == anAppearance)
return;
[_windowView setAppearance:anAppearance];
}
#pragma mark -
#pragma mark Observer
/*!
Update the _CPAttachedWindow frame if a resize event is observed
*/
- (void)observeValueForKeyPath:(CPString)aPath ofObject:(id)anObject change:(CPDictionary)theChange context:(void)aContext
{
if ([aPath isEqual:@"frame"])
{
// @TODO: not recompute everything, just compute the move offset
var g = [_windowView preferredEdge];
[self positionRelativeToView:_targetView preferredEdge:g];
}
}
#pragma mark -
#pragma mark Notification handlers
- (void)_attachedWindowDidMove:(CPNotification)aNotification
{
if ([_windowView isMouseDownPressed])
{
[_targetView removeObserver:self forKeyPath:@"frame"];
[_windowView hideCursor];
[self setLevel:CPNormalWindowLevel];
[_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)];
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPWindowDidMoveNotification object:self];
}
}
#pragma mark -
#pragma mark Utilities
- (CPPoint)computeOrigin:(CPView)aView preferredEdge:(int)anEdge
{
var frameView = [aView frame],
currentView = aView,
origin = [aView frameOrigin],
lastView;
// if somebody succeed to use the conversion function of CPView
// to get this working, please do.
while (currentView = [currentView superview])
{
origin.x += [currentView frameOrigin].x;
origin.y += [currentView frameOrigin].y;
lastView = currentView;
}
origin.x += [[lastView window] frame].origin.x;
origin.y += [[lastView window] frame].origin.y;
// take care of the scrolling point
if ([aView enclosingScrollView])
{
var offsetPoint = [[[aView enclosingScrollView] contentView] boundsOrigin];
origin.x -= offsetPoint.x;
origin.y -= offsetPoint.y;
}
return [self computeOriginFromRect:CPRectMake(origin.x, origin.y, CPRectGetWidth(frameView), CPRectGetHeight(frameView)) preferredEdge:anEdge];
}
- (CPPoint)computeOriginFromRect:(CPRect)aRect preferredEdge:(int)anEdge
{
var nativeRect = [[[CPApp mainWindow] platformWindow] nativeContentRect],
originLeft = CPPointCreateCopy(aRect.origin),
originRight = CPPointCreateCopy(aRect.origin),
originTop = CPPointCreateCopy(aRect.origin),
originBottom = CPPointCreateCopy(aRect.origin);
// CPMaxXEdge
originRight.x += aRect.size.width;
originRight.y += (aRect.size.height / 2.0) - (CPRectGetHeight([self frame]) / 2.0)
// CPMinXEdge
originLeft.x -= CPRectGetWidth([self frame]);
originLeft.y += (aRect.size.height / 2.0) - (CPRectGetHeight([self frame]) / 2.0)
// CPMaxYEdge
originBottom.x += aRect.size.width / 2.0 - CPRectGetWidth([self frame]) / 2.0;
originBottom.y += aRect.size.height;
// CPMinYEdge
originTop.x += aRect.size.width / 2.0 - CPRectGetWidth([self frame]) / 2.0;
originTop.y -= CPRectGetHeight([self frame]);
var requestedEdge = (anEdge !== nil) ? anEdge : CPMaxXEdge,
requestedOrigin;
switch (requestedEdge)
{
case CPMaxXEdge:
requestedOrigin = originRight;
break;
case CPMinXEdge:
requestedOrigin = originLeft;
break;
case CPMinYEdge:
requestedOrigin = originTop;
break;
case CPMaxYEdge:
requestedOrigin = originBottom;
break;
}
var origins = [requestedOrigin, originRight, originLeft, originTop, originBottom],
edges = [requestedEdge, CPMaxXEdge, CPMinXEdge, CPMinYEdge, CPMaxYEdge];
for (var i = 0; i < origins.length; i++)
{
var o = origins[i],
g = edges[i];
[_windowView setArrowOffsetX:0];
[_windowView setArrowOffsetY:0];
[_windowView setPreferredEdge:g];
if (o.x < 0)
{
[_windowView setArrowOffsetX:o.x];
o.x = 0;
}
if (o.x + CPRectGetWidth([self frame]) > nativeRect.size.width)
{
[_windowView setArrowOffsetX:(o.x + CPRectGetWidth([self frame]) - nativeRect.size.width)];
o.x = nativeRect.size.width - CPRectGetWidth([self frame]);
}
if (o.y < 0)
{
[_windowView setArrowOffsetY:o.y];
o.y = 0;
}
if (o.y + CPRectGetHeight([self frame]) > nativeRect.size.height)
{
[_windowView setArrowOffsetY:(CPRectGetHeight([self frame]) + o.y - nativeRect.size.height)];
o.y = nativeRect.size.height - CPRectGetHeight([self frame]);
}
switch (g)
{
case CPMaxXEdge:
if (o.x >= (aRect.origin.x + aRect.size.width))
return o;
break;
case CPMinXEdge:
if ((o.x + _frame.size.width) <= aRect.origin.x)
return o;
break;
case CPMaxYEdge:
if (o.y >= (aRect.origin.y + aRect.size.height))
return o;
break;
case CPMinYEdge:
if ((o.y + _frame.size.height) <= aRect.origin.y)
return o;
break;
}
}
[_windowView setPreferredEdge:nil];
return requestedOrigin;
}
/*!
Compute the frame needed to be placed to the given view
and position the attached window according to this view (edge will be automatic)
@param aView the view where _CPAttachedWindow must be attached
*/
- (void)positionRelativeToView:(CPView)aView
{
[self positionRelativeToView:aView preferredEdge:nil];
}
/*!
Compute the frame needed to be placed to the given view
and position the attached window according to this view
@param aView the view where _CPAttachedWindow must be attached
@param anEdge the preferd edge to use
*/
- (void)positionRelativeToView:(CPView)aView preferredEdge:(int)anEdge
{
var point = [self computeOrigin:aView preferredEdge:anEdge];
[self setFrameOrigin:point];
[_windowView showCursor];
[self setLevel:CPStatusWindowLevel];
[_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)];
[_windowView setNeedsDisplay:YES];
[self makeKeyAndOrderFront:nil];
_targetView = aView;
[_targetView addObserver:self forKeyPath:@"frame" options:nil context:nil];
}
/*!
Position the _CPAttachedWindow to a random point
@param aPoint the point where the _CPAttachedWindow will be attached
*/
- (void)positionRelativeToRect:(CPRect)aRect
{
[self positionRelativeToRect:aRect preferredEdge:nil]
}
/*!
Position the _CPAttachedWindow to a random point
@param aPoint the point where the _CPAttachedWindow will be attached
@param anEdge the prefered edge
*/
- (void)positionRelativeToRect:(CPRect)aRect preferredEdge:(int)anEdge
{
var point = [self computeOriginFromRect:aRect preferredEdge:anEdge];
[self setFrameOrigin:point];
[_windowView showCursor];
[self setLevel:CPStatusWindowLevel];
[_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)];
[_windowView setNeedsDisplay:YES];
[self makeKeyAndOrderFront:nil];
}
#pragma mark -
#pragma mark Actions
/*!
Closes the _CPAttachedWindow
@param sender the sender of the action
*/
- (IBAction)close:(id)aSender
{
[self close];
}
#pragma mark -
#pragma mark Overrides
/*!
Called when the window is loowing focus and close the window if CPClosableOnBlurWindowMask is setted
*/
- (void)resignMainWindow
{
if (_closeOnBlur && !_isClosed)
{
// set a close flag to avoid infinite loop
_isClosed = YES;
[self close];
if (_delegate && [_delegate respondsToSelector:@selector(didAttachedWindowClose:)])
[_delegate didAttachedWindowClose:self];
}
}
/*!
Order front the window as usual and add listener for CPWindowDidMoveNotification
@param sender the sender of the action
*/
- (IBAction)orderFront:(is)aSender
{
[super orderFront:aSender];
var tranformOrigin = "50% 100%";
switch ([_windowView preferredEdge])
{
case CPMaxYEdge:
var posX = 50 + (([_windowView arrowOffsetX] * 100) / _frame.size.width);
tranformOrigin = posX + "% 0%"; // 50 0
break;
case CPMinYEdge:
var posX = 50 + (([_windowView arrowOffsetX] * 100) / _frame.size.width);
tranformOrigin = posX + "% 100%"; // 50 100
break;
case CPMinXEdge:
var posY = 50 + (([_windowView arrowOffsetY] * 100) / _frame.size.height);
tranformOrigin = "100% " + posY + "%"; // 100 50
break;
case CPMaxXEdge:
var posY = 50 + (([_windowView arrowOffsetY] * 100) / _frame.size.height);
tranformOrigin = "0% "+ posY + "%"; // 0 50
break;
}
// @TODO: implement for FF
if (_animates && _shouldPerformAnimation && typeof(_DOMElement.style.WebkitTransform) != "undefined")
{
_DOMElement.style.opacity = 0;
_DOMElement.style.WebkitTransform = "scale(0)";
_DOMElement.style.WebkitTransformOrigin = tranformOrigin;
window.setTimeout(function(){
_DOMElement.style.height = _frame.size.height + @"px";
_DOMElement.style.width = _frame.size.width + @"px";
_DOMElement.style.opacity = 1;
_DOMElement.style.WebkitTransform = "scale(1.1)";
var transitionEndFunction = function(){
_DOMElement.style.WebkitTransform = "scale(1)";
_DOMElement.removeEventListener("webkitTransitionEnd", transitionEndFunction, YES);
};
_DOMElement.addEventListener("webkitTransitionEnd", transitionEndFunction, YES)
},0);
}
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_attachedWindowDidMove:) name:CPWindowDidMoveNotification object:self];
_shouldPerformAnimation = NO;
_isClosed = NO;
}
/*!
Close the windo with animation
*/
- (void)close
{
if (_animates && typeof(_DOMElement.style.WebkitTransform) != "undefined")
{
_DOMElement.style.opacity = 0;
var transitionEndFunction = function(){
[super close];
_DOMElement.removeEventListener("webkitTransitionEnd", transitionEndFunction, YES);
};
_DOMElement.addEventListener("webkitTransitionEnd", transitionEndFunction, YES);
}
else
[super close];
[_targetView removeObserver:self forKeyPath:@"frame"];
_shouldPerformAnimation = _animates;
if (_delegate && [_delegate respondsToSelector:@selector(didAttachedWindowClose:)])
[_delegate didAttachedWindowClose:self];
}
@end
+2
View File
@@ -294,6 +294,7 @@ var _CPimageAndTextViewFrameSizeChangedFlag = 1 << 0,
var textFrame = CGRectMakeZero();
#if PLATFORM(DOM)
if (_DOMTextElement)
{
var textStyle = _DOMTextElement.style;
@@ -306,6 +307,7 @@ var _CPimageAndTextViewFrameSizeChangedFlag = 1 << 0,
textFrame.size.width += _textShadowOffset.width;
textFrame.size.height += _textShadowOffset.height;
}
#endif
return textFrame;
}
+838
View File
@@ -0,0 +1,838 @@
/*
* _CPPopUpList.j
* AppKit
*
* Created by Aparajita Fishman.
* Copyright (c) 2011, Intalio, 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 "CPTableView.j"
@import "_CPPopUpListDataSource.j"
/*!
Notification sent when the list is about to pop up. \c object is the _CPPopUpList.
*/
_CPPopUpListWillPopUpNotification = @"_CPPopUpListWillPopUpNotification";
/*!
Notification sent when the list is about to be dismissed. \c object is the _CPPopUpList.
*/
_CPPopUpListWillDismissNotification = @"_CPPopUpListWillDismissNotification";
/*!
Notification sent when the list is dismissed. \c object is the _CPPopUpList.
*/
_CPPopUpListDidDismissNotification = @"_CPPopUpListDidDismissNotification";
/*!
Notification sent when the selection of the list changes. \c object is the _CPPopUpList.
*/
_CPPopUpListDidDismissNotification = @"_CPPopUpListDidDismissNotification";
/*!
Notification sent when the list is dismissed. \c object is the _CPPopUpList.
*/
_CPPopUpListDidDismissNotification = @"_CPPopUpListDidDismissNotification";
/*!
Notification sent by when an item is selected. \c object is the _CPPopUpList.
When this is received the list has already been dismissed and the dismiss notification has been sent.
*/
_CPPopUpListItemWasClickedNotification = @"_CPPopUpListItemWasClickedNotification";
/*!
@ignore
The minimum number of items that must be visible below the related field.
If less than this number would be completely visible, and there is room for this many complete items
above the field, the list is displayed above.
*/
var ListMinimumItems = 3;
/*! @ignore */
var ListColumnIdentifier = @"1";
/*!
This class is a controller for a panel that can pop up and display a scrollable list of items in a CPTableView.
It is used by CPComboBox to display the list of choices.
This class requires a data source which must conform to the interface of _CPPopUpListDataSource.
Objects of this class send the following notifications:
_CPPopUpListWillPopUpNotification
_CPPopUpListWillDismissNotification
_CPPopUpListDidDismissNotification
_CPPopUpListItemWasClickedNotification
*/
@implementation _CPPopUpList : CPObject
{
_CPPopUpListDataSource _dataSource;
BOOL _itemWasClicked;
BOOL _listWasClicked;
int _listWidth;
_CPPopUpPanel _panel;
CPScrollView _scrollView;
_CPPopUpTableView _tableView;
CPTableColumn _tableColumn;
}
#pragma mark Creating and Displaying a List
/*!
Creates a pop up list of choices that will display in a scrollable CPTableView.
@param aDataSource A subclass of _CPPopUpListDataSource
*/
- (id)initWithDataSource:(_CPPopUpListDataSource)aDataSource
{
self = [super init];
if (self)
{
[self setDataSource:aDataSource];
_itemWasClicked = NO;
_listWasClicked = NO;
_listWidth = 0;
_tableView = [self makeTableView];
// Start with a default size, we will resize it later
frame = CGRectMake(0, 0, 200, 200);
_tableColumn = [[CPTableColumn alloc] initWithIdentifier:ListColumnIdentifier];
[_tableColumn setWidth:CGRectGetWidth(frame) - [CPScroller scrollerWidth]];
[_tableColumn setResizingMask:CPTableColumnAutoresizingMask];
[_tableView addTableColumn:_tableColumn];
_scrollView = [self makeScrollViewWithFrame:CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame))];
[_scrollView setDocumentView:_tableView];
// This has to be done after setDocumentView so that the table knows which scroll view to update
[_tableView setHeaderView:nil];
_panel = [self makeListPanelWithFrame:frame];
[[_panel contentView] addSubview:_scrollView];
[_panel setInitialFirstResponder:_tableView];
if ([_dataSource numberOfItemsInList:self] > 0)
[_tableView selectRowIndexes:[CPIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
else
[_tableView setEnabled:NO];
[_scrollView scrollToBeginningOfDocument:nil];
}
return self;
}
/*! @ignore */
- (CPPanel)makeListPanelWithFrame:(CGRect)aFrame
{
var panel = [[_CPPopUpPanel alloc] initWithContentRect:aFrame styleMask:CPBorderlessWindowMask];
[panel setTitle:@""];
[panel setFloatingPanel:YES];
[panel setBecomesKeyOnlyIfNeeded:YES];
[panel setHasShadow:YES];
[panel setShadowStyle:CPMenuWindowShadowStyle];
[panel setDelegate:self];
return panel;
}
/*! @ignore */
- (_CPPopUpTableView)makeTableView
{
[self removeTableViewObservers];
var table = [[_CPPopUpTableView alloc] initWithFrame:CGRectMakeZero()];
[table setDelegate:self];
[table setDataSource:self];
[table setColumnAutoresizingStyle:CPTableViewLastColumnOnlyAutoresizingStyle];
[table setUsesAlternatingRowBackgroundColors:NO];
[table setAllowsMultipleSelection:NO];
[table setIntercellSpacing:CGSizeMake(3, 2)];
[table setTarget:self];
[table setDoubleAction:@selector(tableViewClickAction:)];
[table setAction:@selector(tableViewClickAction:)];
[table setRowHeight:[self rowHeightForTableView:table]];
return table;
}
/*! @ignore */
- (void)removeTableViewObservers
{
if (_tableView)
{
var defaultCenter = [CPNotificationCenter defaultCenter];
[defaultCenter removeObserver:self name:CPTableViewSelectionIsChangingNotification object:_tableView];
[defaultCenter removeObserver:self name:CPTableViewSelectionDidChangeNotification object:_tableView];
}
}
/*! @ignore */
- (CPScrollView)makeScrollViewWithFrame:(CGRect)aFrame
{
var scroll = [[CPScrollView alloc] initWithFrame:aFrame];
[scroll setBorderType:CPLineBorder];
[scroll setAutohidesScrollers:NO];
[scroll setHasVerticalScroller:YES];
[scroll setHasHorizontalScroller:NO];
[scroll setLineScroll:[_tableView rowHeight]];
[scroll setVerticalPageScroll:0.0];
return scroll;
}
/*!
Pop up the list if it is not already visible.
If it is not visible, a _CPPopUpListWillPopUpNotification will be sent.
@param aRect A rect (in \c aView coordinates) to display relative to
@param aView The view whose coordinate system \c aRect is in
@param offset How far to offset the list from \c aRect
*/
- (void)popUpRelativeToRect:(CGRect)aRect view:(CPView)aView offset:(int)offset
{
if ([_panel isVisible])
return;
var rowRect = [_tableView rectOfRow:[self numberOfRowsInTableView:_tableView] - 1],
frame = CGRectMake(0, 0, MAX(_listWidth, CGRectGetWidth(aRect)), CGRectGetMaxY(rowRect));
// Place the frame relative to aRect and constrain it to the screen bounds
frame = [self constrain:frame relativeToRect:aRect view:aView offset:offset];
[_panel setFrame:frame];
[_scrollView setFrameSize:CGSizeMakeCopy(frame.size)];
[_tableView setEnabled:[_dataSource numberOfItemsInList:self] > 0];
[self scrollItemAtIndexToTop:[_tableView selectedRow]];
[self listWillPopUp];
[_panel orderFront:nil];
}
#pragma mark Setting Display Attributes
/*!
Returns the desired width of the list.
*/
- (int)listWidth
{
return _listWidth;
}
/*!
Sets the desired width of the list for the next call to \ref showListForfield:relativeTo:.
Note that the actual display width may be larger if the given width is less than the width of the associated
field.
*/
- (void)setListWidth:(int)width
{
_listWidth = width;
}
- (void)setFont:(CPFont)aFont
{
var oldDataView = [_tableColumn dataView],
newDataView = [CPTextField new];
[newDataView setFont:aFont];
[newDataView setAlignment:[oldDataView alignment]];
[_tableColumn setDataView:newDataView];
// Force the data view cache to flush
[_tableView reloadData];
}
- (void)setAlignment:(CPTextAlignment)alignment
{
var oldDataView = [_tableColumn dataView],
newDataView = [CPTextField new];
[newDataView setAlignment:alignment];
[newDataView setFont:[oldDataView font]];
[_tableColumn setDataView:newDataView];
// Force the data view cache to flush
[_tableView reloadData];
}
/*!
Returns whether the list is currently visible.
*/
- (BOOL)isVisible
{
return [_panel isVisible];
}
/*!
Returns the desired row height for the table view.
Subclasses should override this if they want something other than the default.
*/
- (int)rowHeightForTableView:(CPTableView)aTableView
{
return [aTableView rowHeight];
}
/*!
Returns the table view used by the list.
*/
- (CPTableView)tableView
{
return _tableView;
}
/*!
Returns the single table column used by the list.
*/
- (CPTableColumn)tableColumn
{
return _tableColumn;
}
/*!
Returns the scroll view used by the list.
*/
- (CPScrollView)scrollView
{
return _scrollView;
}
/*!
Returns the panel in which the list appears.
*/
- (CPPanel)panel
{
return _panel;
}
#pragma mark Setting a Data Source
- (void)setDataSource:(_CPPopUpListDataSource)aDataSource
{
if (_dataSource === aDataSource)
return;
if (![_CPPopUpListDataSource protocolIsImplementedByObject:aDataSource])
{
CPLog.warn("Illegal %s data source (%s). Must implement the methods in _CPPopUpListDataSource.", [self className], [aDataSource description]);
}
else
_dataSource = aDataSource;
}
- (_CPPopUpListDataSource)dataSource
{
return _dataSource;
}
#pragma mark Manipulating the Selection
/*!
Select the next item in the list if there one. If there is currently no selected item,
the first item is selected. Returns YES if the selection changed.
*/
- (BOOL)selectNextItem
{
if (![_tableView isEnabled])
return NO;
var row = [_tableView selectedRow];
if (row < ([_dataSource numberOfItemsInList:self] - 1))
return [self selectRow:++row];
else
return NO;
}
/*!
Select the previous item in the list. If there is currently no selected item,
nothing happens. Returns YES if the selection changed.
*/
- (BOOL)selectPreviousItem
{
if (![_tableView isEnabled])
return NO;
var row = [_tableView selectedRow];
if (row > 0)
return [self selectRow:--row];
else
return NO;
}
/*!
Returns the selected object value. If no value is selected,
returns nil.
*/
- (id)selectedObjectValue
{
var row = [_tableView selectedRow];
return (row >= 0) ? [_dataSource list:self objectValueForItemAtIndex:row] : nil;
}
/*!
Returns the selected value as a single-line string. If no value is selected,
returns nil.
*/
- (CPString)selectedStringValue
{
var value = [self selectedObjectValue];
return value !== nil ? [_dataSource list:self stringValueForObjectValue:value] : nil;
}
/*!
Returns the last selected row in the list. If no row has been selected, returns -1.
*/
- (int)selectedRow
{
return [_tableView selectedRow];
}
/*!
Selects a row and scrolls it to be visible. Returns YES if the selection actually changed.
*/
- (BOOL)selectRow:(int)row
{
if (row === [_tableView selectedRow])
return NO;
var validRow = (row >= 0 && row < [self numberOfRowsInTableView:_tableView]),
indexes = validRow ? [CPIndexSet indexSetWithIndex:row] : [CPIndexSet indexSet];
[_tableView selectRowIndexes:indexes byExtendingSelection:NO];
if (validRow)
{
[_tableView scrollRowToVisible:row];
return YES;
}
else
return NO;
}
#pragma mark Manipulating the Displayed List
/*!
Scroll the list down one page.
*/
- (void)scrollPageDown
{
[_scrollView scrollPageDown:nil];
}
/*!
Scroll the list up one page.
*/
- (void)scrollPageUp
{
[_scrollView scrollPageUp:nil];
}
/*!
Scroll to the top of the list.
*/
- (void)scrollToTop
{
[_scrollView scrollToBeginningOfDocument:nil];
}
/*!
Scroll to the bottom of the list.
*/
- (void)scrollToBottom
{
[_scrollView scrollToEndOfDocument:nil];
}
- (void)scrollItemAtIndexToTop:(int)row
{
var rect = [_tableView rectOfRow:row];
[[_tableView superview] scrollToPoint:rect.origin];
}
/*!
Close the list if it is currently visible. If it is visible,
a CPComboBoxWillDismissNotification will be sent. If the
list is being closed after an item was clicked, the close
is delayed slightly so the user can briefly see the clicked row
get highlighted.
*/
- (void)close
{
if (![_panel isVisible])
return;
if ([self listWasClicked])
{
[self setListWasClicked:NO];
// Wait until we get through the run loop and delay a little
// so the user can briefly see the clicked row get highlighted.
if ([self itemWasClicked])
{
[self setItemWasClicked:NO];
[CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(closeListAfterItemClick) userInfo:nil repeats:NO];
return;
}
}
[[CPNotificationCenter defaultCenter] postNotificationName:_CPPopUpListWillDismissNotification object:self];
[_panel close];
[[CPNotificationCenter defaultCenter] postNotificationName:_CPPopUpListDidDismissNotification object:self];
}
/*!
Close the list after an item was clicked.
*/
- (void)closeListAfterItemClick
{
[self close];
[[CPNotificationCenter defaultCenter] postNotificationName:_CPPopUpListItemWasClickedNotification object:self];
}
#pragma mark Handling Events
/*!
Handles standard key equivalents for moving the selection
and selecting an item. This method should be called by
the -performKeyEquivalent method of the field that is
controlling the list.
*/
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
{
var key = [anEvent charactersIgnoringModifiers];
switch (key)
{
case CPDownArrowFunctionKey:
if ([self isVisible])
{
[self selectNextItem];
return YES;
}
break;
case CPUpArrowFunctionKey:
if ([self isVisible])
{
[self selectPreviousItem];
return YES;
}
break;
case CPEscapeFunctionKey:
if ([self isVisible])
{
[self close];
return YES;
}
break;
case CPPageUpFunctionKey:
if ([self isVisible])
{
[self scrollPageUp];
return YES;
}
break;
case CPPageDownFunctionKey:
if ([self isVisible])
{
[self scrollPageDown];
return YES;
}
break;
case CPHomeFunctionKey:
if ([self isVisible])
{
[self scrollToTop];
return YES;
}
break;
case CPEndFunctionKey:
if ([self isVisible])
{
[self scrollToBottom];
return YES;
}
break;
}
return NO;
}
/*!
Returns whether an item in the list was clicked since it was opened.
If there are no items, \ref itemWasClicked will always return NO.
*/
- (BOOL)itemWasClicked
{
return _itemWasClicked && ([_dataSource numberOfItemsInList:self] > 0);
}
/*!
Sets whether an item in the list was clicked since it was opened.
If there are no items, \ref itemWasClicked will always return NO.
Subclasses will usually want to set this in the mouseDown:
of the control.
*/
- (void)setItemWasClicked:(BOOL)flag
{
_itemWasClicked = ([_dataSource numberOfItemsInList:self] > 0) && flag;
}
/*!
Returns whether any view in the list was clicked since it was opened.
If there are no items, \ref listWasClicked will always return NO.
*/
- (BOOL)listWasClicked
{
return _listWasClicked && ([_dataSource numberOfItemsInList:self] > 0);
}
/*!
Sets whether any view in the list was clicked since it was opened.
If there are no items, \ref listWasClicked will always return NO.
Subclasses will usually want to use a subclass of CPPanel and override
sendEvent: to set this flag when the event type is CPLeftMouseDown
or CPRightMouseDown. This is distinct from \ref itemWasClicked because,
for example, a scroller in the list may be clicked without clicking an
item in the list.
*/
- (void)setListWasClicked:(BOOL)flag
{
_listWasClicked = ([_dataSource numberOfItemsInList:self] > 0) && flag;
}
/*!
Returns whether a controlling view should resign. This should be called
from the controlling view's resignFirstResponder method.
*/
- (BOOL)controllingViewShouldResign
{
if ([self listWasClicked])
{
/*
If an item was not clicked (probably the scrollbar), clear the click flag so that future
clicks outside the list will allow it to close.
*/
if ([self listWasClicked] && ![self itemWasClicked])
[self setListWasClicked:NO];
return NO;
}
else
return YES;
}
#pragma mark Internal Helpers
/*! @ignore */
- (void)listWillPopUp
{
[[CPNotificationCenter defaultCenter] postNotificationName:_CPPopUpListWillPopUpNotification object:self];
}
/*!
Return a frame in platform window base coordinates such that the list, when displayed, will show at least ListMinimumItems
items completely on screen. Normally the list should be displayed below \c aRect, but if there is not room
for at least ListMinimumItems items, an attempt should be made to display that many
items above \c aRect. If the minimum cannot be displayed on top, whichever direction can display more items
is chosen.
@ignore
*/
- (CGRect)constrain:(CGRect)aFrame relativeToRect:(CGRect)aRect view:(CPView)aView offset:(int)offset
{
// Convert from the view's coordinate system to the coordinate system of the primary platform window
var baseOrigin = [aView convertPointToBase:aRect.origin],
windowOrigin = [[aView window] convertBaseToPlatformWindow:baseOrigin],
rowHeight = [self rowHeightForTableView:_tableView] + [_tableView intercellSpacing].height,
// Be sure to clip the number of displayed rows to what the field wants
numberOfRows = MIN([self numberOfRowsInTableView:_tableView], [_dataSource numberOfVisibleItemsInList:self]),
// Add 2 to height for border
frame = CGRectMake(windowOrigin.x, windowOrigin.y + CGRectGetHeight(aRect) + offset, MAX(_listWidth, CGRectGetWidth(aFrame)), (rowHeight * numberOfRows) + 2),
// Get the bottom coordinate of the frame and the platform window
bottomFrame = CGRectMakeCopy(frame),
bottom = CGRectGetMaxY(bottomFrame),
viewRect = [[CPPlatformWindow primaryPlatformWindow] visibleFrame],
visibleBottom = CGRectGetMaxY(viewRect),
bottomVisibleRows = numberOfRows;
// Make sure it will fit in the screen. If not, reduce the number of items till we reach the minimum.
while (bottom > visibleBottom && bottomVisibleRows >= ListMinimumItems)
{
bottom -= rowHeight;
bottomFrame.size.height -= rowHeight;
--bottomVisibleRows;
}
if (bottom >= visibleBottom || bottomVisibleRows < ListMinimumItems)
{
// The minimum number of items will not fit, try above
var topFrame = CGRectMakeCopy(frame);
topFrame.origin.y = windowOrigin.y - offset - CGRectGetHeight(topFrame);
var visibleTop = CGRectGetMinY(viewRect),
topVisibleRows = numberOfRows;
while (topFrame.origin.y <= visibleTop && topVisibleRows >= ListMinimumItems)
{
topFrame.origin.y += rowHeight;
topFrame.size.height -= rowHeight;
--topVisibleRows;
}
// If there is room on the top or it can display more than at the bottom, show it there
if ((topFrame.origin.y > visibleTop && topVisibleRows >= ListMinimumItems) || topVisibleRows > bottomVisibleRows)
frame = topFrame;
else
frame = bottomFrame;
}
else
frame = bottomFrame;
return frame;
}
- (void)tableViewClickAction:(id)sender
{
[self close];
}
@end
var _CPPopUpListDataSourceKey = @"_CPPopUpListDataSourceKey",
_CPPopUpListListWidthKey = @"_CPPopUpListListWidthKey",
_CPPopUpListListPanelKey = @"_CPPopUpListListPanelKey",
_CPPopUpListScrollViewKey = @"_CPPopUpListScrollViewKey",
_CPPopUpListTableViewKey = @"_CPPopUpListTableViewKey";
@implementation _CPPopUpList (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_listWasClicked = NO;
_itemWasClicked = NO;
_dataSource = [aCoder decodeObjectForKey:_CPPopUpListDataSourceKey];
_listWidth = [aCoder decodeIntForKey:_CPPopUpListListWidthKey];
_panel = [aCoder decodeObjectForKey:_CPPopUpListListPanelKey];
_scrollView = [aCoder decodeObjectForKey:_CPPopUpListScrollViewKey];
_tableView = [aCoder decodeObjectForKey:_CPPopUpListTableViewKey];
_tableColumn = [_tableView tableColumnWithIdentifier:ListColumnIdentifier];
[_scrollView setDocumentView:_tableView];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_dataSource forKey:_CPPopUpListDataSourceKey];
[aCoder encodeObject:_listWidth forKey:_CPPopUpListListWidthKey];
[aCoder encodeObject:_panel forKey:_CPPopUpListListPanelKey];
[aCoder encodeObject:_scrollView forKey:_CPPopUpListScrollViewKey];
[aCoder encodeObject:_tableView forKey:_CPPopUpListTableViewKey];
}
@end
@implementation _CPPopUpList (CPTableViewDataSource)
- (int)numberOfRowsInTableView:(id)aTableView
{
return MAX([_dataSource numberOfItemsInList:self], 1);
}
- (id)tableView:(id)aTableView objectValueForTableColumn:(CPTableColumn)aColumn row:(int)aRow
{
return [_dataSource list:self displayValueForObjectValue:[_dataSource list:self objectValueForItemAtIndex:aRow]];
}
@end
@implementation _CPPopUpTableView : CPTableView
- (void)mouseDown:(CPEvent)anEvent
{
if (![self isEnabled])
return;
[[self delegate] setItemWasClicked:YES];
[super mouseDown:anEvent];
}
/*
We always want the autocomplete to have focus
*/
- (BOOL)acceptsFirstResponder
{
return NO;
}
/*!
Return the column used for the list.
*/
- (CPTableColumn)listColumn
{
return _tableColumn;
}
@end
@implementation _CPPopUpPanel : CPPanel
- (void)sendEvent:(CPEvent)anEvent
{
var type = [anEvent type];
if (type === CPLeftMouseDown || type === CPRightMouseDown)
[[self delegate] setListWasClicked:YES];
return [super sendEvent:anEvent];
}
@end
+98
View File
@@ -0,0 +1,98 @@
/*
* _CPPopUpListDataSource.j
* AppKit
*
* Created by Aparajita Fishman.
* Copyright (c) 2011, Intalio, 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
*/
/*!
This abstract base class defines the methods that delegates of _CPPopUpList must implement.
You may either subclass this class to use the default implementation of
list:objectValueForItemAtIndex: and list:displayValueForObjectValue: or use your
own class and define these methods yourself.
*/
@implementation _CPPopUpListDataSource : CPObject
/*!
Returns whether the given object conforms to the minimum protocol defined by this class.
*/
+ (BOOL)protocolIsImplementedByObject:(id)anObject
{
return (anObject &&
[anObject respondsToSelector:@selector(numberOfItemsInList:)] &&
[anObject respondsToSelector:@selector(numberOfVisibleItemsInList:)] &&
[anObject respondsToSelector:@selector(list:objectValueForItemAtIndex:)] &&
[anObject respondsToSelector:@selector(list:displayValueForObjectValue:)] &&
[anObject respondsToSelector:@selector(list:stringValueForObjectValue:)]);
}
/*!
Returns the number of items managed by the list.
*/
- (int)numberOfItemsInList:(_CPPopUpList)aList
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
Returns the number of items to display at one time.
*/
- (int)numberOfVisibleItemsInList:(_CPPopUpList)aList
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
Returns the data for a given row index.
*/
- (id)list:(_CPPopUpList)aList objectValueForItemAtIndex:(int)index
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
Returns a value to display for a single row in the list. Subclasses should override
this if the table data needs to be converted or formatted in some way to be displayed.
If your data source use a data representation other than CPStrings, you must override
this method and return the appropriate data when there are no search results.
If the _CPPopUpList's table uses a custom data view, this method should return a value suitable
for sending to the setObjectValue: method of the data view.
@param aValue Data for the given row
@return A value to be displayed in the list
*/
- (id)list:(_CPPopUpList)aList displayValueForObjectValue:(id)aValue
{
return aValue || @"";
}
/*!
Returns a single-line string representation for an object value. Subclasses should override
this if the object data is not convertible to a simple single-line string.
@param aValue Table data to be converted to a string
@return A value to be displayed in the autocomplete field
*/
- (CPString)list:(_CPPopUpList)aList stringValueForObjectValue:(id)aValue
{
return String(aValue);
}
@end
+3 -3
View File
@@ -2,7 +2,7 @@ require File.join(File.dirname(__FILE__), "repositories.rb")
require File.join(File.dirname(__FILE__), "dependencies.rb")
# Keep this structure to allow the build system to update version numbers.
VERSION_NUMBER = "1.0.0.006"
VERSION_NUMBER = "1.0.0.021"
# Shorten expressions
@@ -14,7 +14,7 @@ desc "Cappuccino Distribution"
define "cappuccino" do
project.version = VERSION_NUMBER
project.group = "com.intalio.cloud.cappuccino"
file(_("target/patch")).enhance do
mkdir_p "target/patch"
end
@@ -44,7 +44,7 @@ BASH
package(:jar).with :manifest => read_m
#package(:jar).include _("target/patch"), :as => "patch"
package(:jar).include _("target/Frameworks"), :as => "META-INF/resources/Frameworks"
#do not include those: they are at best not read harmful.
#package(:bundle).include _("build.properties"), :as => "build.properties"
#package(:bundle).meta_inf << file('META-INF/web-fragment.xml')
+109 -20
View File
@@ -131,7 +131,7 @@
_replaceManySEL = sel_getName(@"replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_replaceManySEL])
_replace = [_proxyObject methodForSelector:_replaceManySEL];
_replaceMany = [_proxyObject methodForSelector:_replaceManySEL];
_objectAtIndexSEL = sel_getName(@"objectIn" + capitalizedKey + "AtIndex:");
if ([_proxyObject respondsToSelector:_objectAtIndexSEL])
@@ -233,10 +233,25 @@
- (id)objectAtIndex:(unsigned)anIndex
{
if (_objectAtIndex)
return _objectAtIndex(_proxyObject, _objectAtIndexSEL, anIndex);
return [[self objectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]] firstObject];
}
return [[self _representedObject] objectAtIndex:anIndex];
- (CPArray)objectsAtIndexes:(CPIndexSet)theIndexes
{
if (_objectsAtIndexes)
return _objectsAtIndexes(_proxyObject, _objectsAtIndexesSEL, theIndexes);
if (_objectAtIndex)
{
var index = CPNotFound,
objects = [];
while ((index = [theIndexes indexGreaterThanIndex:index]) !== CPNotFound)
objects.push(_objectAtIndex(_proxyObject, _objectAtIndexSEL, index));
return objects;
}
return [[self _representedObject] objectsAtIndexes:theIndexes];
}
- (void)addObject:(id)anObject
@@ -288,37 +303,111 @@
[self removeObject:anObject inRange:CPMakeRange(0, [self count])];
}
- (void)removeLastObject
- (void)removeObjectsInArray:(CPArray)theObjects
{
if (_removeMany)
{
var indexes = [CPIndexSet indexSet],
index = [theObjects count];
while (index--)
[indexes addIndex:[self indexOfObject:[theObjects objectAtIndex:index]]];
_removeMany(_proxyObject, _removeManySEL, indexes);
}
else if (_remove)
{
var index = [theObjects count];
while (index--)
_remove(_proxyObject, _removeSEL, [self indexOfObject:[theObjects objectAtIndex:index]]);
}
else
{
var target = [[self _representedObject] copy];
[target removeObjectsInArray:theObjects];
[self _setRepresentedObject:target];
}
}
- (void)removeObject:(id)theObject inRange:(CPRange)theRange
{
if (_remove)
return _remove(_proxyObject, _removeSEL, [self count] - 1);
_remove(_proxyObject, _removeSEL, [self indexOfObject:theObject inRange:theRange]);
else if (_removeMany)
{
var index = [self indexOfObject:theObject inRange:theRange];
_removeMany(_proxyObject, _removeManySEL, [CPIndexSet indexSetWithIndex:index]);
}
else
{
var index;
var target = [[self _representedObject] copy];
while ((index = [self indexOfObject:theObject inRange:theRange]) !== CPNotFound)
{
[self removeObjectAtIndex:index];
theRange = CPIntersectionRange(CPMakeRange(index, length - index), theRange);
}
}
}
[target removeLastObject];
[self _setRepresentedObject:target];
- (void)removeLastObject
{
[self removeObjectsAtIndexes:[CPIndexSet indexSetWithIndex:[self count] - 1]];
}
- (void)removeObjectAtIndex:(unsigned)anIndex
{
if (_remove)
return _remove(_proxyObject, _removeSEL, anIndex);
[self removeObjectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]];
}
var target = [[self _representedObject] copy];
- (void)removeObjectsAtIndexes:(CPIndexSet)theIndexes
{
if (_removeMany)
_removeMany(_proxyObject, _removeManySEL, theIndexes);
else if (_remove)
{
var index = [theIndexes lastIndex];
[target removeObjectAtIndex:anIndex];
[self _setRepresentedObject:target];
while (index !== CPNotFound)
{
_remove(_proxyObject, _removeSEL, index)
index = [theIndexes indexLessThanIndex:index];
}
}
else
{
var target = [[self _representedObject] copy];
[target removeObjectsAtIndexes:theIndexes];
[self _setRepresentedObject:target];
}
}
- (void)replaceObjectAtIndex:(unsigned)anIndex withObject:(id)anObject
{
if (_replace)
return _replace(_proxyObject, _replaceSEL, anIndex, anObject);
[self replaceObjectsAtIndexes:[CPIndexSet indexSetWithIndex:anIndex] withObjects:[anObject]]
}
var target = [[self _representedObject] copy];
- (void)replaceObjectsAtIndexes:(CPIndexSet)theIndexes withObjects:(CPArray)theObjects
{
if (_replaceMany)
return _replaceMany(_proxyObject, _replaceManySEL, theIndexes, theObjects);
else if (_replace)
{
var i = 0,
index = [theIndexes firstIndex];
[target replaceObjectAtIndex:anIndex withObject:anObject];
[self _setRepresentedObject:target];
while (index !== CPNotFound)
{
_replace(_proxyObject, _replaceSEL, index, [theObjects objectAtIndex:i++]);
index = [theIndexes indexGreaterThanIndex:index];
}
}
else
{
var target = [[self _representedObject] copy];
[target replaceObjectsAtIndexes:theIndexes withObjects:theObjects];
[self _setRepresentedObject:target];
}
}
@end
@@ -362,7 +451,7 @@
- (id)valueForKeyPath:(CPString)aKeyPath
{
if (aKeyPath.indexOf("@") === 0)
if (aKeyPath.charAt(0) === "@")
{
var dotIndex = aKeyPath.indexOf("."),
operator,
+22 -5
View File
@@ -156,8 +156,7 @@ var concat = Array.prototype.concat,
- (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects
{
if (!aSelector)
[CPException raise:CPInvalidArgumentException
reason:"makeObjectsPerformSelector:withObjects: 'aSelector' can't be nil"];
_CPRaiseInvalidArgumentException(self, _cmd, 'attempt to pass a nil selector');
var index = 0,
count = self.length;
@@ -198,7 +197,7 @@ var concat = Array.prototype.concat,
- (CPArray)subarrayWithRange:(CPRange)aRange
{
if (aRange.location < 0 || CPMaxRange(aRange) > self.length)
[CPException raise:CPRangeException reason:"subarrayWithRange: aRange out of bounds"];
[CPException raise:CPRangeException reason:_cmd + " aRange out of bounds"];
return slice.call(self, aRange.location, CPMaxRange(aRange));
}
@@ -208,13 +207,19 @@ var concat = Array.prototype.concat,
return join.call(self, aString);
}
- (void)insertObject:(id)anObject atIndex:(CPUInteger)anIndex
- (void)insertObject:(id)anObject atIndex:(int)anIndex
{
if (anIndex > self.length || anIndex < 0)
_CPRaiseRangeException(self, _cmd, anIndex, self.length);
splice.call(self, anIndex, 0, anObject);
}
- (void)removeObjectAtIndex:(CPUInteger)anIndex
- (void)removeObjectAtIndex:(int)anIndex
{
if (anIndex >= self.length || anIndex < 0)
_CPRaiseRangeException(self, _cmd, anIndex, self.length);
splice.call(self, anIndex, 1);
}
@@ -235,16 +240,28 @@ var concat = Array.prototype.concat,
- (void)removeObjectsInRange:(CPRange)aRange
{
if (aRange.location < 0 || CPMaxRange(aRange) > self.length)
[CPException raise:CPRangeException reason:_cmd + " aRange out of bounds"];
splice.call(self, aRange.location, aRange.length);
}
- (void)replaceObjectAtIndex:(int)anIndex withObject:(id)anObject
{
if (anIndex >= self.length || anIndex < 0)
_CPRaiseRangeException(self, _cmd, anIndex, self.length);
self[anIndex] = anObject;
}
- (void)replaceObjectsInRange:(CPRange)aRange withObjectsFromArray:(CPArray)anArray range:(CPRange)otherRange
{
if (aRange.location < 0 || CPMaxRange(aRange) > self.length)
[CPException raise:CPRangeException reason:_cmd + " aRange out of bounds"];
if (otherRange && (otherRange.location < 0 || CPMaxRange(otherRange) > anArray.length))
[CPException raise:CPRangeException reason:_cmd + " otherRange out of bounds"];
if (otherRange && (otherRange.location !== 0 || otherRange.length !== [anArray count]))
anArray = [anArray subarrayWithRange:otherRange];
+63
View File
@@ -174,6 +174,27 @@ var _builtInCharacterSets = {};
@end
var CPCharacterSetInvertedKey = @"CPCharacterSetInvertedKey";
@implementation CPCharacterSet (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super init])
{
_inverted = [aCoder decodeBoolForKey:CPCharacterSetInvertedKey];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeBool:_inverted forKey:CPCharacterSetInvertedKey];
}
@end
// A character set that stores a list of ranges of
// acceptable characters.
@implementation _CPRangeCharacterSet : CPCharacterSet
@@ -342,6 +363,48 @@ var _builtInCharacterSets = {};
}
}
- (BOOL)isEqual:(CPCharacterSet)aCharacterSet
{
if (self === aCharacterSet)
return YES;
if (!aCharacterSet || ![aCharacterSet isKindOfClass:[self class]])
return NO;
return [self _isEqualToStringContentCharacterSet:aCharacterSet];
}
- (BOOL)_isEqualToStringContentCharacterSet:(_CPStringContentCharacterSet)aCharacterSet
{
if (!aCharacterSet)
return NO;
return _string == aCharacterSet._string && _inverted == aCharacterSet._inverted;
}
@end
var _CPStringContentCharacterSetStringKey = @"_CPStringContentCharacterSetStringKey";
@implementation _CPStringContentCharacterSet (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super initWithCoder:aCoder])
{
_string = [aCoder decodeObjectForKey:_CPStringContentCharacterSetStringKey]
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:_string forKey:_CPStringContentCharacterSetStringKey];
}
@end
_CPCharacterSetTrimAtBeginning = 1 << 1;
+11 -4
View File
@@ -176,11 +176,18 @@
for (; i < count; i++)
{
var thisValue = value[i];
if (thisValue.constructor === Object)
newValue.push([CPDictionary dictionaryWithJSObject:thisValue recursively:YES]);
if (thisValue === null)
{
newValue.push([CPNull null]);
}
else
newValue.push(thisValue);
{
if (thisValue.constructor === Object)
newValue.push([CPDictionary dictionaryWithJSObject:thisValue recursively:YES]);
else
newValue.push(thisValue);
}
}
value = newValue;
+27 -1
View File
@@ -228,6 +228,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
id _targetObject;
Class _nativeClass;
CPDictionary _changesForKey;
CPDictionary _nestingForKey;
Object _observersForKey;
int _observersForKeyLength;
CPSet _replacedKeys;
@@ -251,6 +252,7 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
_nativeClass = [aTarget class];
_observersForKey = {};
_changesForKey = {};
_nestingForKey = {};
_observersForKeyLength = 0;
[self _replaceClass];
@@ -715,6 +717,18 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
if (isBefore)
{
if (changes)
{
// "willChange:X" nesting.
var level = _nestingForKey[aKey];
if (!level)
[CPException raise:CPInternalInconsistencyException reason:@"_changesForKey without _nestingForKey"];
_nestingForKey[aKey] = level + 1;
// Only notify on the first willChange..., silently note any following nested calls.
return;
}
_nestingForKey[aKey] = 1;
changes = changeOptions;
var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
@@ -772,9 +786,19 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
}
else
{
if (!changes)
var level = _nestingForKey[aKey];
if (!changes || !level)
[CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
_nestingForKey[aKey] = level - 1;
if (level - 1 > 0)
{
// willChange... was called multiple times. Only fire observation notifications when
// didChange... has been called an equal number of times.
return;
}
delete _nestingForKey[aKey];
[changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey];
var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
@@ -812,6 +836,8 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOpti
[changes setObject:newValue forKey:CPKeyValueChangeNewKey];
}
delete _changesForKey[aKey];
}
var observers = [_observersForKey[aKey] allValues],
+7 -2
View File
@@ -306,12 +306,17 @@ var CPStringRegexSpecialCharacters = [
}
/*!
Creates a substring from the beginning of the receiver to the specified index.
@param anIndex the last index of the receiver to use for the substring (inclusive)
Creates a substring of characters from the receiver, starting at the beginning and up to
the given index.
@param anIndex the index of the receiver where the substring should end (non inclusive)
@return the substring
*/
- (CPString)substringToIndex:(unsigned)anIndex
{
if (anIndex > length)
[CPException raise:CPRangeException reason:"index out of bounds"];
return substring(0, anIndex);
}
+43 -11
View File
@@ -59,7 +59,7 @@ task ("sudo-install", ["CommonJS"], function()
if (OS.system(["sudo", "tusk", "install", "--force", $BUILD_CJS_OBJECTIVE_J, $BUILD_CJS_CAPPUCCINO]))
{
// Attempt a hackish work-around for sudo compiled with the --with-secure-path option
sudo("tusk install --force " + $BUILD_CJS_OBJECTIVE_J + " " + $BUILD_CJS_CAPPUCCINO)
sudo("tusk install --force " + $BUILD_CJS_OBJECTIVE_J + " " + $BUILD_CJS_CAPPUCCINO);
}
});
@@ -71,10 +71,38 @@ task ("install-symlinks", function()
task ("install-debug-symlinks", function()
{
SYSTEM.env["CONFIG"] = "Debug";
SYSTEM.env.CONFIG = "Debug";
spawnJake("install-symlinks");
});
task ("clean-sprites", function()
{
var f = new FileList(FILE.join(SYSTEM.env.CAPP_BUILD, "**/dataURLs.txt")),
paths = f.items();
f = new FileList(FILE.join(SYSTEM.env.CAPP_BUILD, "**/MHTML*.txt"));
paths = paths.concat(f.items());
paths.forEach(function(path)
{
FILE.remove(path);
});
});
task ("clobber-theme", function()
{
var f = new FileList(FILE.join(SYSTEM.env.CAPP_BUILD, "**/Aristo.blend")),
paths = f.items();
f = new FileList(FILE.join(SYSTEM.env.CAPP_BUILD, "Aristo.build"));
paths = paths.concat(f.items());
paths.forEach(function(path)
{
rm_rf(path);
});
});
// Documentation
$DOCUMENTATION_BUILD = FILE.join($BUILD_DIR, "Documentation");
@@ -160,6 +188,10 @@ function generateDocs(/* boolean */ noFrame)
rm_rf($DOCUMENTATION_BUILD);
mv("debug.txt", FILE.join("Documentation", "debug.txt"));
mv("Documentation", $DOCUMENTATION_BUILD);
// There is a bug in doxygen 1.7.x preventing loading correctly the custom CSS
// So let's do it manually
cp(FILE.join(documentationDir, "doxygen.css"), FILE.join($DOCUMENTATION_BUILD, "html", "doxygen.css"));
}
}
@@ -247,30 +279,30 @@ task ("demos", function()
if (key)
return this._plist.valueForKey(key);
return this._plist;
}
};
Demo.prototype.name = function()
{
return this.plist("CPBundleName");
}
};
Demo.prototype.path = function()
{
return this._path;
}
};
Demo.prototype.excluded = function()
{
return !!this.plist("CPDemoExcluded");
}
};
Demo.prototype.toString = function()
{
return this.name();
}
};
FILE.glob(FILE.join(demosDir, "demos", "**/Info.plist")).map(function(demoPath){
return new Demo(FILE.dirname(demoPath))
return new Demo(FILE.dirname(demoPath));
}).filter(function(demo){
return !demo.excluded();
}).forEach(function(demo)
@@ -315,7 +347,7 @@ task("push-cappuccino", function() {
pushPackage(
$BUILD_CJS_CAPPUCCINO,
"git@github.com:280north/cappuccino-package.git",
SYSTEM.env["PACKAGE_BRANCH"]
SYSTEM.env.PACKAGE_BRANCH
);
});
@@ -323,7 +355,7 @@ task("push-objective-j", function() {
pushPackage(
$BUILD_CJS_OBJECTIVE_J,
"git@github.com:280north/objective-j-package.git",
SYSTEM.env["PACKAGE_BRANCH"]
SYSTEM.env.PACKAGE_BRANCH
);
});
@@ -361,7 +393,7 @@ function pushPackage(path, remote, branch)
var pkg = JSON.parse(packagePath.join("package.json").read({ charset : "UTF-8" }));
stream.print(" Version: " + colorize(pkg["version"], "purple"));
stream.print(" Version: " + colorize(pkg.version, "purple"));
stream.print(" Revision: " + colorize(pkg["cappuccino-revision"], "purple"));
stream.print(" Timestamp: " + colorize(pkg["cappuccino-timestamp"], "purple"));
+74 -1
View File
@@ -464,6 +464,45 @@
[self assert:1 equals:[observations count] message:@"exactly 1 notification for insertObject x 2 (clearsFilterPredicate YES)"];
}
/**
Replicate the situation found in the Bindings manual test where one array controller's
contents depend on the selection of another.
*/
- (void)testObservationBetweenBoundControllers
{
var companies = [Companies new],
companiesController = [CPArrayController new],
employeesController = [CPArrayController new];
[companiesController bind:@"contentArray" toObject:companies withKeyPath:@"items" options:nil];
[employeesController bind:@"contentArray" toObject:companiesController withKeyPath:@"selection.employees" options:nil];
[companiesController addObserver:self forKeyPath:@"selection" options:0 context:@"companies.selection"];
[companiesController addObserver:self forKeyPath:@"selectionIndexes" options:0 context:@"companies.selectionIndexes"];
[companiesController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:@"companies.arrangedObjects"];
[employeesController addObserver:self forKeyPath:@"selectionIndexes" options:0 context:@"employees.selectionIndexes"];
[employeesController addObserver:self forKeyPath:@"selection" options:0 context:@"employees.selection"];
[employeesController addObserver:self forKeyPath:@"arrangedObjects" options:0 context:@"employees.arrangedObjects"];
observations = [];
[companiesController setSelectionIndexes:[CPIndexSet indexSetWithIndex:1]];
//for (var i = 0; i < [observations count]; i++)
// CPLog.error(observations[i].context);
// There should be exactly one observation of each kind, and they should be in the right
// order.
[self assert:5 equals:[observations count]];
[self assert:@"companies.selectionIndexes" equals:observations[0].context];
[self assert:@"employees.arrangedObjects" equals:observations[1].context];
[self assert:@"employees.selectionIndexes" equals:observations[2].context];
[self assert:@"employees.selection" equals:observations[3].context];
// This observation registers after the employees update because we are later in the
// observation queue than the binding.
[self assert:@"companies.selection" equals:observations[4].context];
}
- (void)testCompoundKeyPaths
{
var departmentNameField = [[CPTextField alloc] init];
@@ -524,6 +563,18 @@
[self assert:[1, 2, 1] equals:[ac arrangedObjects] message:"only one copy of 1 removed + the right copy should be removed"];
}
- (void)testSetOutOfBoundsSelectedIndexes
{
var ac = [CPArrayController new],
contentArray = [1, 2, 3],
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 4)];
[ac setContent:contentArray];
[ac setSelectionIndexes:indexes];
[self assert:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 3)] equals:[ac selectionIndexes]];
[self assert:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 4)] equals:indexes];
}
- (void)observeValueForKeyPath:keyPath
ofObject:anActivity
change:change
@@ -534,6 +585,7 @@
[observations addObject:{
keyPath: keyPath,
context: context,
oldValue: [change valueForKey:CPKeyValueChangeOldKey],
newValue: [change valueForKey:CPKeyValueChangeNewKey]
}];
@@ -620,4 +672,25 @@
return self;
}
@end
@end
@implementation Companies : CPObject
{
CPMutableArray items @accessors;
}
- (id)init
{
self = [super init];
if (self)
{
items = [CPMutableArray array];
[items addObject:[CPDictionary dictionaryWithObjectsAndKeys:@"Spacely Sprockets", @"name", [CPArray arrayWithObjects:@"Tom", @"Dick", @"Harry"], @"employees"]];
[items addObject:[CPDictionary dictionaryWithObjectsAndKeys:@"Cogswell Cogs", @"name", [CPArray arrayWithObjects:@"Jane", @"Mary", @"Vic"], @"employees"]];
}
return self;
}
@end
+20
View File
@@ -0,0 +1,20 @@
@import <AppKit/CPTokenField.j>
@implementation CPTokenFieldTest : OJTestCase
{
}
- (void)testArchiving
{
var tokenField = [CPTokenField new];
[tokenField setCompletionDelay:5.0];
[tokenField setTokenizingCharacterSet:[CPCharacterSet characterSetWithCharactersInString:@","]];
var archived = [CPKeyedArchiver archivedDataWithRootObject:tokenField],
unarchived = [CPKeyedUnarchiver unarchiveObjectWithData:archived];
[self assert:[unarchived completionDelay] equals:5.0];
[self assert:[unarchived tokenizingCharacterSet] equals:[CPCharacterSet characterSetWithCharactersInString:@","]];
}
@end
+98 -5
View File
@@ -5,15 +5,108 @@
@implementation CPWindowTest : OJTestCase
{
CPWindow theWindow;
CPWindow _window @accessors(property=window);
}
-(void)testCanAllocWindow
- (void)setUp
{
theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(0,0,200,150)
styleMask:CPWindowNotSizable];
_window = [[CPWindow alloc] initWithContentRect:CGRectMake(0.0, 0.0, 1024.0, 768.0)
styleMask:CPWindowNotSizable];
}
[self assertTrue:!!theWindow];
- (void)testCanAllocWindow
{
[self assertTrue:!![self window]];
}
- (void)testThatContentViewIsInitialFirstResponder
{
var contentView = [[self window] contentView];
[self assert:contentView
equals:[[self window] initialFirstResponder]
message:@"The window's content view must be the initial first responder"];
}
- (void)testKeyViewLoop
{
var contentView = [[self window] contentView],
textField = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:100.0];
[textField setFrame:CGRectMake(100.0, 100.0, 100.0, 26.0)];
[contentView addSubview:textField];
[[self window] recalculateKeyViewLoop];
var nextTextField = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:100];
[nextTextField setFrame:CGRectMake(100.0, 200.0, 100.0, 26.0)];
[contentView addSubview:nextTextField];
[[self window] recalculateKeyViewLoop];
// Test nextKeyView
[self assert:textField
equals:[contentView nextKeyView]
message:@"textField should be the next key view of contentView"];
[self assert:nextTextField
equals:[textField nextKeyView]
message:@"nextTextField should be the next key view of textField"];
[self assert:contentView
equals:[nextTextField nextKeyView]
message:@"contentView should be the next key view of nextTextField"];
// Test previousKeyView
[self assert:contentView
equals:[textField previousKeyView]
message:@"contentView should be the previous key view of textField"];
[self assert:textField
equals:[nextTextField previousKeyView]
message:@"textField should be the previous key view of nextTextField"];
[self assert:nextTextField
equals:[contentView previousKeyView]
message:@"nextTextField should be the previous key view of contentView"];
}
- (void)testValiKeyViewLoop
{
var contentView = [[self window] contentView],
textField = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:100.0];
[textField setFrame:CGRectMake(100.0, 100.0, 100.0, 26.0)];
[contentView addSubview:textField];
[[self window] recalculateKeyViewLoop];
var nextTextField = [CPTextField textFieldWithStringValue:@"" placeholder:@"" width:100];
[nextTextField setFrame:CGRectMake(100.0, 200.0, 100.0, 26.0)];
[contentView addSubview:nextTextField];
[[self window] recalculateKeyViewLoop];
// Test nextValidKeyView
[self assert:textField
equals:[contentView nextValidKeyView]
message:@"textField should be the next valid key view of contentView"];
[self assert:nextTextField
equals:[textField nextValidKeyView]
message:@"nextTextField should be the next valid key view of textField"];
[self assert:textField
equals:[nextTextField nextValidKeyView]
message:@"textField should be the next key valid view of nextTextField"];
// Test previousValidKeyView
[self assert:nextTextField
equals:[textField previousValidKeyView]
message:@"nextTextField should be the previous valid key view of textField"];
[self assert:textField
equals:[nextTextField previousValidKeyView]
message:@"textField should be the previous key valid view of nextTextField"];
[self assert:nextTextField
equals:[contentView previousValidKeyView]
message:@"nextTextField should be the previous valid key view of contentView"];
}
@end
+39
View File
@@ -0,0 +1,39 @@
@import <Foundation/CPCharacterSet.j>
@implementation CPCharacterSetTest : OJTestCase
- (void)testEquals
{
var charSetA = [CPCharacterSet characterSetWithCharactersInString:"abc"],
charSetB = [CPCharacterSet characterSetWithCharactersInString:"abc"],
charSetC = [CPCharacterSet characterSetWithCharactersInString:""];
[self assertFalse:[charSetA isEqual:[CPNull null]]];
[self assertFalse:[charSetA isEqual:charSetC]];
[self assertTrue:[charSetA isEqual:charSetA]];
[self assertTrue:[charSetA isEqual:charSetB]];
}
- (void)testArchiving
{
var charSet = [CPCharacterSet characterSetWithCharactersInString:"abc"],
archived = [CPKeyedArchiver archivedDataWithRootObject:charSet],
unarchived = [CPKeyedUnarchiver unarchiveObjectWithData:archived];
[self assertTrue:[unarchived characterIsMember:'a']];
[self assertTrue:[unarchived characterIsMember:'b']];
[self assertTrue:[unarchived characterIsMember:'c']];
[self assertFalse:[unarchived characterIsMember:'d']];
var invertedSet = [charSet invertedSet];
archived = [CPKeyedArchiver archivedDataWithRootObject:invertedSet];
unarchived = [CPKeyedUnarchiver unarchiveObjectWithData:archived];
[self assertFalse:[unarchived characterIsMember:'a'] message:"in unarchived inverted set 'a' is not a member"];
[self assertFalse:[unarchived characterIsMember:'b'] message:"in unarchived inverted set 'b' is not a member"];
[self assertFalse:[unarchived characterIsMember:'c'] message:"in unarchived inverted set 'c' is not a member"];
[self assertTrue:[unarchived characterIsMember:'d'] message:"in unarchived inverted set 'd' is a member"];
}
@end
+396
View File
@@ -0,0 +1,396 @@
var COUNTER;
@implementation CPKVCArrayTest : OJTestCase
{
TestObject _object @accessors(property=object);
ImplementedTestObject _implementedObject @accessors(property=implementedObject);
}
- (void)setUp
{
_object = [[TestObject alloc] init];
_implementedObject = [[ImplementedTestObject alloc] init];
COUNTER = 0;
}
- (void)_patchSelector:(SEL)theSelector
{
var method_dtable = [[self object] class].method_dtable,
method_list = [[self object] class].method_list,
selectorsToRemove = [@selector(countOfValues), @selector(objectInValuesAtIndex),
@selector(objectInValuesAtIndex:), @selector(valuesAtIndexes:),
@selector(insertObject:inValuesAtIndex:), @selector(insertValues:atIndexes:),
@selector(removeObjectFromValuesAtIndex:), @selector(removeValuesAtIndexes:),
@selector(removeObjectFromValuesAtIndex:),
@selector(replaceObjectInValuesAtIndex:withObject:),
@selector(replaceValuesAtIndexes:withValues:)],
selectorIndex = [selectorsToRemove count];
while (selectorIndex--)
{
var selector = [selectorsToRemove objectAtIndex:selectorIndex],
implementation = method_dtable[selector];
if (!implementation)
continue;
delete method_dtable[selector]
[method_list removeObject:implementation];
}
var method = class_getInstanceMethod([[self implementedObject] class], theSelector),
implementation = method_getImplementation(method);
class_addMethod([[self object] class], theSelector, implementation);
}
- (void)testUsesCountOfKey
{
[self _patchSelector:@selector(countOfValues)];
var count = [[[self object] mutableArrayValueForKey:@"values"] count];
[self assert:10 equals:count message:@"countOfValues should return 10"];
[self assert:1 equals:COUNTER message:@"countOfValues should have been called once"]
}
- (void)testObjectAtIndexUsesObjectInKeyAtIndex
{
[self _patchSelector:@selector(objectInValuesAtIndex:)];
var values = [[self object] mutableArrayValueForKey:@"values"],
object = [values objectAtIndex:0];
[self assert:0 equals:object]
[self assert:1 equals:COUNTER message:@"objectInValuesAtIndex: should have been called once"];
}
- (void)testObjectsAtIndexesUsesObjectInKeyAtIndex
{
[self _patchSelector:@selector(objectInValuesAtIndex:)];
var values = [[self object] mutableArrayValueForKey:@"values"],
objects = [values objectsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]];
[self assert:[0, 1] equals:objects]
[self assert:2 equals:COUNTER message:@"objectInValuesAtIndex: should have been called once"];
}
- (void)testObjectAtIndexUsesKeyAtIndexes
{
[self _patchSelector:@selector(valuesAtIndexes:)];
var values = [[self object] mutableArrayValueForKey:@"values"],
object = [values objectAtIndex:0];
[self assert:0 equals:object]
[self assert:1 equals:COUNTER message:@"valuesAtIndexes: should have been called once"];
}
- (void)testObjectsAtIndexesUsesKeyAtIndexes
{
[self _patchSelector:@selector(valuesAtIndexes:)];
var values = [[self object] mutableArrayValueForKey:@"values"],
objects = [values objectsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)]];
[self assert:[0, 1] equals:objects]
[self assert:1 equals:COUNTER message:@"valuesAtIndexes: should have been called once"];
}
- (void)testInsertObjectAtIndexUsesInsertKeyAtIndex
{
[self _patchSelector:@selector(insertObject:inValuesAtIndex:)];
[[[self object] mutableArrayValueForKey:@"values"] insertObject:11 atIndex:10];
[self assert:11 equals:[[[self object] values] objectAtIndex:10]];
[self assert:1 equals:COUNTER message:@"insertObject:inValuesAtIndex: should have been called once"];
}
- (void)testInsertObjectsAtIndexesUsesInsertKeyAtIndex
{
[self _patchSelector:@selector(insertObject:inValuesAtIndex:)];
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 2)];
[[[self object] mutableArrayValueForKey:@"values"] insertObjects:[11, 12] atIndexes:indexes];
[self assert:[11, 12] equals:[[[self object] values] objectsAtIndexes:indexes]];
[self assert:2
equals:COUNTER
message:@"insertValues:atIndexes: should have been called once for each object"];
}
- (void)testInsertObjectAtIndexUsesInsertKeyAtIndexes
{
[self _patchSelector:@selector(insertValues:atIndexes:)];
[[[self object] mutableArrayValueForKey:@"values"] insertObject:11 atIndex:10];
[self assert:11 equals:[[[self object] values] objectAtIndex:10]];
[self assert:1 equals:COUNTER message:@"insertValues:atIndexes: should have been called once"];
}
- (void)testInsertObjectsAtIndexesUsesInsertKeyAtIndexes
{
[self _patchSelector:@selector(insertValues:atIndexes:)];
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(10, 2)];
[[[self object] mutableArrayValueForKey:@"values"] insertObjects:[11, 12] atIndexes:indexes];
[self assert:[11, 12] equals:[[[self object] values] objectsAtIndexes:indexes]];
[self assert:1
equals:COUNTER
message:@"insertValues:atIndexes: should have been called once for each object"];
}
- (void)testRemoveObjectAtIndexUsesRemoveObjectFromKeyAtIndex
{
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
[[[self object] mutableArrayValueForKey:@"values"] removeObjectAtIndex:0];
[self assert:[1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
[self assert:1 equals:COUNTER message:@"removeObjectFromValuesAtIndex: should have been called once"];
}
- (void)testRemoveObjectAtIndexesUsesRemoveObjectFromKeyAtIndex
{
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, 2)];
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsAtIndexes:indexes];
[self assert:[2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
[self assert:2 equals:COUNTER message:@"removeValuesAtIndex: should have been called once for each object"];
}
- (void)testRemoveObjectsAtIndexesUsesRemoveKeyAtIndex
{
[self _patchSelector:@selector(removeValuesAtIndexes:)];
[[[self object] mutableArrayValueForKey:@"values"] removeObjectAtIndex:0];
[self assert:[1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
[self assert:1
equals:COUNTER
message:@"removeValuesAtIndexes: should have been once for each object"];
}
- (void)testRemoveObjectsAtIndexesUsesRemoveKeyAtIndexes
{
[self _patchSelector:@selector(removeValuesAtIndexes:)];
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(3, 2)];
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsAtIndexes:indexes];
[self assert:[0, 1, 2, 5, 6, 7, 8, 9] equals:[[self object] values]];
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
}
- (void)testRemoveObjectUsesRemoveKeyAtIndex
{
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
[[[self object] mutableArrayValueForKey:@"values"] removeObject:1];
[self assert:[0, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
[self assert:1 equals:COUNTER message:"removeObjectFromValuesAtIndex: should have been called once"]
}
- (void)testeRemoveObjectUsesRemoveKeyAtIndexes
{
[self _patchSelector:@selector(removeValuesAtIndexes:)];
[[[self object] mutableArrayValueForKey:@"values"] removeObject:5];
[self assert:[0, 1, 2, 3, 4, 6, 7, 8, 9] equals:[[self object] values]];
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
}
- (void)testRemoveObjectsInArrayUsesRemoveKeyAtIndex
{
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsInArray:[3, 6]];
[self assert:[0, 1, 2, 4, 5, 7, 8, 9] equals:[[self object] values]];
[self assert:2 equals:COUNTER message:"removeObjectFromValuesAtIndex: should have been called once"]
}
- (void)testRemoveObjectsInArrayUsesRemoveKeyAtIndexes
{
[self _patchSelector:@selector(removeValuesAtIndexes:)];
[[[self object] mutableArrayValueForKey:@"values"] removeObjectsInArray:[0, 9]];
[self assert:[1, 2, 3, 4, 5, 6, 7, 8] equals:[[self object] values]];
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
}
- (void)testRemoveLastObjectUsesRemoveKeyAtIndex
{
[self _patchSelector:@selector(removeObjectFromValuesAtIndex:)];
[[[self object] mutableArrayValueForKey:@"values"] removeLastObject];
[self assert:[0, 1, 2, 3, 4, 5, 6, 7, 8] equals:[[self object] values]];
[self assert:1 equals:COUNTER message:"removeObjectFromValuesAtIndex: should have been called once"]
}
- (void)testRemoveLastObjectUsesRemoveKeyAtIndexes
{
[self _patchSelector:@selector(removeValuesAtIndexes:)];
[[[self object] mutableArrayValueForKey:@"values"] removeLastObject];
[self assert:[0, 1, 2, 3, 4, 5, 6, 7, 8] equals:[[self object] values]];
[self assert:1 equals:COUNTER message:@"removeValuesAtIndexes: should have been called once"];
}
- (void)testReplaceObjectAtIndexWithObjectUsesReplaceObjectInKeyAtIndexWithObject
{
[self _patchSelector:@selector(replaceObjectInValuesAtIndex:withObject:)];
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectAtIndex:0 withObject:1];
[self assert:[1, 1, 2, 3, 4, 5, 6, 7, 8, 9] equals:[[self object] values]];
[self assert:1
equals:COUNTER
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
}
- (void)testReplaceObjectsAtIndexesWithObjectsUsesReplaceObjectInKeyAtIndexWithObject
{
[self _patchSelector:@selector(replaceObjectInValuesAtIndex:withObject:)];
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(5, 3)];
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectsAtIndexes:indexes withObjects:[1, 2, 3]];
[self assert:[0, 1, 2, 3, 4, 1, 2, 3, 8, 9] equals:[[self object] values]];
[self assert:3
equals:COUNTER
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
}
- (void)testReplaceObjectAtIndexWithObjectUsesReplaceKeyAtIndexesWithKeys
{
[self _patchSelector:@selector(replaceValuesAtIndexes:withValues:)];
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectAtIndex:5 withObject:6];
[self assert:[0, 1, 2, 3, 4, 6, 6, 7, 8, 9] equals:[[self object] values]];
[self assert:1
equals:COUNTER
message:@"replaceObjectInValuesAtIndex:withObject: should have been called once"];
}
- (void)testReplaceObjectsAtIndexesWithObjectsUsesReplaceKeyAtIndexesWithKeys
{
[self _patchSelector:@selector(replaceValuesAtIndexes:withValues:)];
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(6, 2)];
[[[self object] mutableArrayValueForKey:@"values"] replaceObjectsAtIndexes:indexes withObjects:[7, 8]];
[self assert:[0, 1, 2, 3, 4, 5, 7, 8, 8, 9] equals:[[self object] values]];
[self assert:1
equals:COUNTER
message:@"replaceObjectInValuesAtIndexes:withValues: should have been called once"];
}
@end
@implementation TestObject : CPObject
{
CPArray _values @accessors(property=values);
}
- (id)init
{
if (self = [super init])
{
_values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
}
return self;
}
@end
@implementation ImplementedTestObject : TestObject
{
}
- (int)countOfValues
{
// CPLog.warn(@"countOfValues");
COUNTER += 1;
return [[self values] count];
}
- (id)objectInValuesAtIndex:(int)theIndex
{
// CPLog.warn(@"objectInValuesAtIndex: %@", theIndex);
COUNTER += 1;
return [[self values] objectAtIndex:theIndex];
}
- (CPArray)valuesAtIndexes:(CPIndexSet)theIndexes
{
// CPLog.warn(@"valuesAtIndexes: %@", theIndexes);
COUNTER += 1;
return [[self values] objectsAtIndexes:theIndexes];
}
- (void)insertObject:(id)theObject inValuesAtIndex:(int)theIndex
{
// CPLog.warn(@"insertObject: %@ inValuesAtIndex: %@", theObject, theIndex);
COUNTER += 1;
[[self values] insertObject:theObject atIndex:theIndex];
}
- (void)insertValues:(CPArray)theObjects atIndexes:(CPIndexSet)theIndexes
{
// CPLog.warn(@"insertValues: %@ atIndexes: %@", theObjects, theIndexes);
COUNTER += 1;
[[self values] insertObjects:theObjects atIndexes:theIndexes];
}
- (void)removeObjectFromValuesAtIndex:(int)theIndex
{
// CPLog.warn(@"removeObjectFromValuesAtIndex: %@", theIndex);
COUNTER += 1;
[[self values] removeObjectAtIndex:theIndex];
}
- (void)removeValuesAtIndexes:(CPIndexSet)theIndexes
{
// CPLog.warn(@"removeValuesAtIndexes: %@", theIndexes);
COUNTER += 1;
[[self values] removeObjectsAtIndexes:theIndexes];
}
- (void)replaceObjectInValuesAtIndex:(int)theIndex withObject:(id)theObject
{
// CPLog.warn(@"replaceObjectInValuesAtIndex: %@ withObject: %@", theIndex, theObject);
COUNTER += 1;
[[self values] replaceObjectAtIndex:theIndex withObject:theObject];
}
- (void)replaceValuesAtIndexes:(CPIndexSet)theIndexes withValues:(id)theObjects
{
// CPLog.warn(@"replaceValuesAtIndexes: %@ withValues: %@", theIndexes, theObjects);
COUNTER += 1;
[[self values] replaceObjectsAtIndexes:theIndexes withObjects:theObjects];
}
@end
+12
View File
@@ -128,6 +128,18 @@
[self assert:"abcd" equals:["abcd" substringToIndex:4]];
[self assert:"abc" equals:["abcd" substringToIndex:3]];
[self assert:"" equals:["abcd" substringToIndex:0]];
var sawException = false;
try
{
[@"abcd" substringToIndex:5];
}
catch (anException)
{
sawException = true;
[self assert:CPRangeException equals:[anException name]];
}
[self assertTrue:sawException message:"expected CPRangeException"];
}
- (void)testBoolValue

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