Compare commits

...
2775 Commits
Author SHA1 Message Date
David RichardsonandGitHub f56855010f Merge pull request #3310 from enquora/manual-tests-audit
Manual tests audit
2026-09-08 19:28:28 -06:00
David Richardson b80713c1e5 Remove temporary test suite migration tooling
Deletes the Python migration script and shell restoration utilities used to transition the Tests/Manual directory to the @objj/jake toolchain.

The subsidiary Jakefile migration is complete.

The scripts are removed from the active tree to prevent repository clutter, while their implementation details and exact transformation logic remain permanently verifiable in the git history.
2026-09-08 19:25:21 -06:00
David Richardson 93a1eea08b Add missing copyright and license headers 2026-09-08 19:22:50 -06:00
David Richardson 7393607a52 Add batch compile tasks to Jakefile 2026-09-08 19:21:58 -06:00
David Richardson 53784f0e10 Migrate subsidiary test Jakefiles to @objj/jake toolchain
Applies the transformation script across all applications in the Tests/Manual directory to eliminate Narwhal-era dependencies and establish compatibility with the modern Node-based runner.

* Removes explicit `require("jake")` calls in favor of the injected JAKE global.
* Replaces legacy `cappuccino/jake` application instantiation with the `CAPPUCCINO.Jake.applicationtask.app` hook.
* Substitutes archaic `system`, `file`, and `os` API calls with native Node `child_process`, `fs`, and `path` modules.
* Corrects package paths for nativehost deployment targets.
2026-09-08 19:08:43 -06:00
David Richardson 900448528a Add temporary automation scripts for test suite migration
Commit the Python migration script and the shell restoration loop used to batch-process the transition of legacy test Jakefiles to the modern Node toolchain.

These scripts provide an auditable record of the automated transformations applied to the test suite and will be removed in a subsequent commit - once the migration is complete.
2026-09-08 19:05:20 -06:00
David Richardson e04e3d9ba5 Normallize naming of ScalingTest2
All test names must use Test suffix.
2026-09-08 18:48:55 -06:00
David Richardson 5eaede0622 Refactor subsidiary Jakefile migration script for @objj/jake compatibility
Updates the transformation script to target the modern Node-based Cappuccino toolchain, removing Narwhal-era APIs from test configurations.

* Replaces legacy `require("jake")` with the implicitly injected `JAKE` global.
* Routes application task instantiation through the modern `CAPPUCCINO.Jake.applicationtask.app` hook.
* Substitutes archaic `OS`, `FILE`, and `system` module calls with native Node `fs`, `path`, and `child_process` equivalents.
* Corrects package pathing for `nativehost` references.
2026-09-08 18:45:44 -06:00
David Richardson 2c06bf566f Refactor Tests/Manual orchestration
Removes recursive JAKE.subjake calls from the root Jakefile, eliminating silent error masking and path evaluation failures during test suite compilation. Replace subjake with POSIX find.

* Replaces Node-based directory traversal with POSIX find and -execdir to guarantee correct working directory context and propagate standard output.
* Restricts the `clean` task strictly to the removal of build artifacts.
* Introduces a `clobber` task to remove Frameworks symlinks alongside build artifacts, restoring the test directories to a pristine state.
2026-09-08 18:02:47 -06:00
David Richardson 4784a1a102 Normalize application naming in ./Tests/Manual
Ensure:
* CamelCase, ending in Test
* No hyphens, spaces, underscores or other non-alpha characters

Updated application directory names.
Update Jakefiles.
2026-09-08 17:47:30 -06:00
David RichardsonandGitHub e3cb2cd6db Merge pull request #3309 from cappuccino/manual-tests-xib-cleanup
Update test application menu titles in MainMenu.xib
2026-09-03 16:41:23 -06:00
David Richardson dfe14a0f97 Update test application menu titles in MainMenu.xib
* Replace generic 'NewApplication' placeholder strings with specific product names.

* Extract target names from Jakefile and assert parity with Info.plist CPBundleName.

* Improve visual grounding during manual test suite auditing.
2026-09-03 16:31:50 -06:00
David RichardsonandGitHub aa6fce3297 Merge pull request #3308 from enquora/manual-tests-upgrade-xibs-and-cibs
Upgrade legacy XIB documents and regenerate CIB test assets
2026-09-03 14:26:59 -06:00
David Richardson 06da3164fc Upgrade legacy XIB documents and regenerate CIB test assets
* Execute batch structural upgrade of all XIB files within ./Tests/Manual to modern XML DOM format using ibtool (Xcode 26.3) to resolve imminent Xcode 27 parsing failures.

* Add regenerate-test-cibs.py script to recursively compile the modernized XIB documents into CIB artifacts within Tests/Manual/ using the local nib2cib toolchain.

* Commit the updated CIB artifacts to maintain CI and test execution parity.

*Include batch execution bash and Python scripts with LGPL-2 licensing to serve as reference migration templates for the community.
2026-09-03 14:16:53 -06:00
David RichardsonandGitHub 9cacb134c7 Merge pull request #3307 from enquora/manual-tests-navigation-viewer
Manual tests navigation viewer
2026-09-03 12:36:11 -06:00
David Richardson 17e5d32621 rename Jakefile migration script
prefix with wip- to indicate it is not yet stable.
2026-09-03 12:19:20 -06:00
David Richardson f5b305317f Improve colour scheme
Match navigation bar/masthead to https://cappuccino.dev
Highlight table row on hover
Increase visual distinction of test buttons using colour
2026-09-03 11:26:10 -06:00
David Richardson 9677609340 Improve index.html presentation
Add logotype as self-contained data URI
Improve fonts for legibility
Improve table styling
2026-09-03 10:47:29 -06:00
David Richardson ab8e47fd01 Scripts to generate and serve index HTML file for all tests
Node.js, requires no additional interpreter or dependencies.
Generates a single index.html file at root which displays launch links for all tests.
2026-09-03 09:45:37 -06:00
David Richardson a2181c88d7 Improve migrate-jakefiles.py script
Move to ./Tests/Manual
Hard-code root path to ./
Exclude sibling Jakefile
2026-09-03 09:24:53 -06:00
David Richardson 1a9ff9ffb7 Provision framework dependencies for manual integration tests
Previously, the Tests/Manual integration suite lacked an automated mechanism to provision the required Frameworks directories, leaving test applications unexecutable or reliant on stale, manually copied dependencies.

This commit introduces a Jake task to automatically provision a shared, symlinked Frameworks cache for all manual test applications, ensuring dependencies remain synchronized with the current framework build. Additionally, it adds documentation detailing the architecture and execution of the manual test suite.
2026-09-02 18:32:11 -06:00
David Richardson cc4b0e2540 Jakefile Narwhal => Node migration script, initial effort 2026-09-01 20:03:32 -06:00
David RichardsonandGitHub 87e0a6fe34 Merge pull request #3304 from cappuccino/ToolsTest-disable
Disable ToolsTest unit
2026-08-31 10:13:32 -06:00
David Richardson 45d9f10a89 Disable ToolsTest unit
Unable to modify test so that it is successful both locally and on GitHub.
Tests Node hosted tooling which is deprecated and will not receive further support.

Expedient solution is to simply disable the test.
2026-08-31 10:11:39 -06:00
David RichardsonandGitHub 93937e8b1b Merge pull request #3303 from enquora/Address-failing-array-tests
test: disable unasserted performance benchmarks in CPArray suites
2026-08-31 09:52:15 -06:00
David Richardson bdc2b7cd67 test: disable unasserted performance benchmarks in CPArray suites
Prefix unasserted performance tests with 'disabled_' in CPArrayControllerTest
and CPArrayPerformanceTest to eliminate wall-clock timing noise from standard
CI runs.

Add a dummy assertion to CPArrayPerformanceTest to maintain runner
compatibility without executing obsolete micro-benchmarks.
2026-08-31 09:49:44 -06:00
David RichardsonandGitHub b6ba250f82 Merge pull request #3301 from enquora/_CPAutocompleteMenu-fix-cold-build-warning
Fix undeclared _clientView instance variable in CPRulerView
2026-08-28 20:58:43 -06:00
David Richardson 4a5a25bb65 Fix undeclared _clientView instance variable in CPRulerView
Declare _clientView within the CPRulerView instance variable block.

This resolves a cold build compilation failure where the Objective-J parser flags assignments to the undeclared variable as illegal global variable creations.
2026-08-28 20:57:03 -06:00
David RichardsonandGitHub bc1889c7d4 Merge pull request #3300 from enquora/Update-GitHub-Actions
ci: Restrict Node.js test matrix to version 24.x
2026-08-28 20:39:02 -06:00
David Richardson 5471c38905 ci: Restrict Node.js test matrix to version 24.x
Modify GitHub Actions workflows to execute strictly against Node.js 24.x.
* Reduced the execution matrix in BuildAndTest.yml to remove deprecated and redundant runtimes (versions 20 through 23).
* Explicitly pinned the setup-node step in main-branch-publish-manual-tests.yaml to version 24.x.

This eliminates unnecessary CI resource consumption. Node 24 is the active LTS, and exhaustive version testing is no longer warranted for the legacy infrastructure given the transition to the native Go toolchain.
2026-08-28 20:36:45 -06:00
David RichardsonandGitHub 9866dd49bb Merge pull request #3299 from enquora/CPTimeZoneTest-fix
Fixes: CPTimeZoneTest, CPTimeZone, and CPDate
2026-08-28 20:01:10 -06:00
David Richardson a6969bc756 Fix CPDate string initialization to respect explicit time zone offsets
The initWithString: method previously initialized the underlying JavaScript Date object using the local-time constructor (new Date(year, month, day)).
This caused the resulting absolute time to be corrupted by the host engine's local time zone configuration, effectively ignoring the explicit time zone offset provided in the initialization string.

The implementation was a fundamental violation of the canonical contract, as established by NSDate and to be followed by CPDate.

This commit refactors the parsing logic to use Date.UTC(), guaranteeing that the parsed components are evaluated independently of the host environment.

Additionally, it eliminates a redundant RegExp allocation during pattern matching and updates the API documentation to explicitly state that offset resolution is decoupled from both CPTimeZone and the host's local time rules.
2026-08-28 19:59:11 -06:00
David Richardson 23dff002a4 Fix CPTimeZone initialization and bridge prototype exceptions
The object returned by the host engine's Intl.supportedValuesOf("timeZone") lacks standard JavaScript Array prototype methods (e.g., slice, indexOf) when crossing the runtime bridge.

This previously caused exceptions in testKnownTimeZoneNamesUsesIntlWhenAvailable.

This commit resolves the exceptions by utilizing primitive JavaScript property access and zero-indexed iteration to construct a local array. This architecture safely isolates the class from bridge prototype limitations and CPArray dependencies.

Additionally, it explicitly appends the legacy aliases "GMT" and "UTC" during the manual array construction. Engines adhering to canonical IANA identifiers omit these aliases, which caused localTimeZone initialization to abort in UTC-bound CI environments.

Guaranteeing their presence resolves the null evaluation failure in testSecondsFromGMTForDate.
2026-08-28 19:46:46 -06:00
David Richardson 03b875697e Fix CPTimeZone initialization failure in UTC environments
Intl.supportedValuesOf("timeZone") returns canonical IANA identifiers, omitting legacy aliases such as "GMT" and "UTC". This causes localTimeZone to evaluate to nil when the host engine operates in a UTC time zone, as abbreviationDictionary statically maps these aliases.

We explicitly append "GMT" and "UTC" to the dynamic knownTimeZoneNames array. This ensures invariant preservation for static dictionary lookups, guarantees successful object initialization across all UTC-bound environments, and resolves the evaluation in testSecondsFromGMTForDate on CI runners.
2026-08-28 19:22:56 -06:00
David Richardson d717e07d5c Add offset-based fallback for local-zone abbreviation resolution
* Fix a GitHub CI failure on a UTC-configured runner. Name-based resolution previously found no candidate, and abbreviationForDate returned no value.

* Add a fallback mechanism that uses the system's current UTC offset when a name match fails.

* Ensure correct abbreviation resolution, regardless of the zone name string that the runtime reports.
2026-08-28 19:07:32 -06:00
David Richardson c39f03823a Replace date-string parsing in CPTimeZone with Intl API
* Update abbreviationForDate and _abbreviationForNameAndDate to read the short time zone name directly from Intl.DateTimeFormat(...).formatToParts(). This replaces regular expression parsing of date strings.

 * Update knownTimeZoneNames to use Intl.supportedValuesOf('timeZone') to retrieve the complete IANA time zone set. The method falls back to the legacy 48-city list if Intl is unavailable.

* Replace duplicate regular expression logic in testInitWithNameRespectsDaylightSaving with a direct Intl call.

* Add pinning tests for the recently corrected offsets and the unchanged MSD value.

* Add a feature-detected test for the knownTimeZoneNames upgrade.

* Add two disabled tests to document unresolved gaps: non-English localizedName outputs and ambiguous reverse offset lookups.

* The full IANA-identity redesign as a separate task. Abbreviation-keyed lookups, colliding offsets, and English-only localizedName limitations remain unresolved in this interim fix.
2026-08-28 14:19:46 -06:00
David Richardson f3a04f23f8 Fix incorrect UTC offsets in CPTimeZone abbreviation table
* Correct timeDifferenceFromUTC values for MDT, WAT, NZDT, NZST, and WIT.

* Update MSK to the current constant UTC+3 offset. Russia abolished Daylight Saving Time in 2014.

* Leave MSD unchanged. Add an inline comment explaining that Moscow Summer Time no longer exists and assigning a new offset would fabricate data.

* Track future work to replace this static table with Intl-backed lookup from browsers’ internal values.

This future lookup will include a protocol seam for:
* runtimes without complete timezone data.
* developers who cannot tolerate non-current information from browser update latency or have other individual need.
2026-08-28 13:59:05 -06:00
David RichardsonandGitHub 0089785713 Merge pull request #3281 from enquora/CPTreeNode-improve-tests
Improve and extend CPTreeNode tests

Structural gaps still exist in CPTreeNode, covered as comments in CPTreeNodeTest.
Issue to be created for these.
2026-08-28 13:38:57 -06:00
David Richardson 3a140b07fe Fix child-node index arithmetic and expand CPTreeNode test coverage
CPTreeNode.j:
* Correct index calculations in insertObject:inChildNodesAtIndex: and replaceObjectInChildNodesAtIndex:withObject:. Nodes no longer misplace during same-parent or cross-parent moves.
 * Update _removeChildNode: to call removeObjectFromChildNodesAtIndex:. This ensures detaches notify childNodes observers on the original parent.

CPTreeNodeTest.j:
* Add test coverage for same-parent replacement, cycle rejection on replace-path, KVO during cross-parent moves, mutableChildNodes proxy operations, insertion bounds, non-recursive sorting, isLeaf, and descendantNodeAtIndexPath: edge cases.
* Disable same-parent childNodes KVO test. CPKeyValueObserving.j coalesces nested change notifications on the same key, which drops the inner removal event.
* Disable parentNode KVO test. No setParentNode: method exists to instrument.
* Add inline comments to both disabled tests explaining these mechanisms.
2026-08-28 13:35:27 -06:00
David Richardson 74039c799a Merge branch 'main' into CPTreeNode-improve-tests 2026-08-27 22:05:19 -06:00
David RichardsonandGitHub b3e2ed1071 Merge pull request #3297 from enquora/CPException-fix-missing
Fix undefined function call in CPArray range/argument exceptions
2026-08-27 22:04:25 -06:00
David Richardson 1f8e75388d Fix undefined function call in CPArray range/argument exceptions
Foundation/CPException.j: _CPRaiseRangeException and _CPRaiseInvalidArgumentException called METHOD_CALL_STRING(), undefined anywhere.

Every out-of-bounds CPArray access threw an
uncatchable JS ReferenceError instead of CPRangeException/
CPInvalidArgumentException.
Fixed to call the existing _CPMethodCallString(anObject, aSelector).

Tests/Foundation/CPArrayTest.j, CPMutableArrayTest.j: the defect
was invisible because test_objectAtIndex_ and test_objectsAtIndexes_
checked only that something was thrown, not what.
Added exception-identity checks, and fixed ConcreteArray/ConcreteMutableArray's
objectAtIndex: overrides, which threw a plain string and would have
failed the new checks.
2026-08-27 21:57:37 -06:00
David Richardson ee0c6fdf2d CPTreeNodeTest: fix child-move index arithmetic; correct a flawed test expectation
CPTreeNode: same-parent child moves no longer misplace the moved node — this corrects a defect in the class itself, properly exposed by the test.

Separately, the unit test's expectation for a root node's index path was itself wrong.
Corrected to match the class's existing, contract-correct behavior.

Test unit now runs cleanly.
2026-08-27 16:54:31 -06:00
David Richardson 54b7d24081 Merge branch 'main' into CPTreeNode-improve-tests 2026-08-27 16:02:08 -06:00
daboe01andGitHub bf76c7d2c7 Merge pull request #3241 from daboe01/setBoundsSize-fix
Fixed: scale accumulation in scaleUnitSquareToSize: when setting bounds size
2026-08-27 22:37:45 +02:00
daboe01andGitHub bad82ee587 Merge pull request #3296 from daboe01/pageunloadfix2
fix: replace legacy `unload` event listener cleanup with `pagehide`
2026-08-27 19:40:57 +02:00
daboe01 16beb1a86c fix: replace legacy unload event listener cleanup with pagehide 2026-08-27 19:28:31 +02:00
David Richardson 690b79f113 Revert "Merge pull request #3260 from enquora/modernize-CALayer"
This reverts commit 8134b6dff7, reversing
changes made to 7083f7b268.
2026-08-27 11:20:41 -06:00
David Richardson 4d13785119 Revert "Merge pull request #3257 from enquora/modernize-CABackingStore"
This reverts commit 5bdac195fc, reversing
changes made to 8134b6dff7.
2026-08-27 11:20:30 -06:00
David Richardson 4f29b77d65 Revert "Merge pull request #3261 from enquora/modernize-CPPlatformpasteboard"
This reverts commit 10351c3d4a, reversing
changes made to 5bdac195fc.
2026-08-27 11:20:20 -06:00
David Richardson be08bed01e Revert "Merge pull request #3256 from enquora/modernize-cgcontext-canvas"
This reverts commit e107e2d804, reversing
changes made to 10351c3d4a.
2026-08-27 11:19:56 -06:00
daboe01andGitHub e107e2d804 Merge pull request #3256 from enquora/modernize-cgcontext-canvas
Modernize CGContextCanvas.j: Remove CPP macros and legacy fallbacks
2026-08-27 10:06:21 +02:00
daboe01andGitHub 10351c3d4a Merge pull request #3261 from enquora/modernize-CPPlatformpasteboard
Refactor CPPlatformPasteboard: Remove CPP macros and legacy IE fallbacks
2026-08-27 10:00:33 +02:00
daboe01 67b4798caa formatting 2026-08-26 20:57:53 +02:00
daboe01 301e940236 formatting 2026-08-26 20:56:38 +02:00
daboe01andGitHub 5bdac195fc Merge pull request #3257 from enquora/modernize-CABackingStore
Refactor CABackingStore: Eliminate preprocessor directives and legacy…
2026-08-26 20:52:08 +02:00
daboe01andGitHub 8134b6dff7 Merge pull request #3260 from enquora/modernize-CALayer
Modernize CALayer, remove macro defined access in favour of ivar
2026-08-26 20:50:47 +02:00
daboe01andGitHub 7083f7b268 Merge pull request #3226 from daboe01/CPLanguageModel-and-friends
New: CPSystemLanguageModel and CPLanguageModelSession
2026-08-26 20:48:59 +02:00
daboe01andGitHub 72d2b22088 Merge pull request #3278 from daboe01/disable-googletranslator
new: prevent google translator in cappuccino
2026-08-26 20:47:40 +02:00
daboe01andGitHub acf410776d Merge pull request #3279 from daboe01/whitespace-cleanup
fixed: usage of tab character instead of 4 spaces
2026-08-26 20:46:52 +02:00
daboe01andGitHub 82f82190a1 Merge pull request #3294 from daboe01/right-contraint
new: constrain tab stop marker movement between preceding text and adjacent tab stops
2026-08-26 19:18:04 +02:00
daboe01 f85ac1ea10 new: constrain tab stop marker movement between preceding text and adjacent tab stops 2026-08-26 19:16:12 +02:00
daboe01andGitHub b6a245c852 Merge pull request #3293 from daboe01/tabstop-contraint
new: constrain tab stop marker dragging to preceding text boundary
2026-08-26 19:05:09 +02:00
daboe01 874792fa6e new: constrain tab stop marker dragging to preceding text boundary 2026-08-26 19:01:32 +02:00
daboe01andGitHub 378f7bbda1 Merge pull request #3292 from daboe01/tabstop-fix
fixed: resolve tab stop skipping and dragging positioning
2026-08-26 17:59:09 +02:00
daboe01 ca13595e37 formatting 2026-08-26 17:58:39 +02:00
daboe01 22a43bcfc5 fixed: resolve tab stop skipping and dragging positioning 2026-08-26 17:56:48 +02:00
daboe01andGitHub 4c54e6cd31 Merge pull request #3291 from daboe01/rulerviewfix
fixed: indentation paddle dragging and clientView binding
2026-08-26 17:19:43 +02:00
daboe01 9ffa4decd4 fixed: indentation paddle dragging and clientView binding 2026-08-26 17:06:34 +02:00
daboe01andGitHub 389ad61251 Merge pull request #3290 from daboe01/treecontroller-selection-fix
fixed: fix selection bindings and correct KVO dependencies in CPTreeController
2026-08-25 22:36:07 +02:00
daboe01 d8dd2e8636 improved: manual test for treecontroller 2026-08-25 22:18:37 +02:00
daboe01 8d1429d063 proxy fix 2026-08-25 21:09:51 +02:00
daboe01 d1b5c08272 fixed: KVO observer was missing 2026-08-25 21:03:07 +02:00
daboe01 6ef3cf776f new: selection tests 2026-08-25 20:39:34 +02:00
daboe01 0a6f2e569a fixed: fix selection bindings and correct KVO dependencies in CPTreeController 2026-08-25 20:35:09 +02:00
David Richardson ae409d05c1 Convert tabs to spaces 2026-08-21 09:21:51 -06:00
David Richardson 6b732da749 Merge branch 'main' into CPTreeNode-improve-tests 2026-08-21 08:54:19 -06:00
David RichardsonandGitHub 45741365f4 Merge pull request #3289 from enquora/IvarTest-fix-test-warnings
Include inline comment for IvarTest wrt warning
2026-08-20 22:02:46 -06:00
David Richardson 2c86863258 Include inline comment for IvarTest wrt warning
In this one case, a warning is the expected output and is the only viable way to test the case.
See inline comment at top of IvarTest.j for explanation.
2026-08-20 22:00:53 -06:00
David RichardsonandGitHub 8994806789 Merge pull request #3288 from enquora/CPSetTest-fix-test-warning
Fix static analysis warnings for unused iteration variables in Founda…
2026-08-20 21:23:40 -06:00
David Richardson e51f0df1a6 Fix static analysis warnings for unused iteration variables in Foundation tests
The `for...of` syntax tests in `CPSetTest` and `CPDictionaryTest` iterate over empty collections. This leaves the bound variables unread, which triggers the static analyzer and fails the zero-warning CI policy.

Standard Javascript idioms for handling blank identifiers (such as `_` or pure evaluation via `void`) are either unsupported or cause AST collisions within the Node.js parser. The variables are now explicitly evaluated using native Objective-J message sends. This registers a read operation for the analyzer, preserves the legacy parser's structural expectations, and ensures the runtime state remains pristine.
2026-08-20 21:21:56 -06:00
David RichardsonandGitHub a8a0c64a2a Merge pull request #3287 from enquora/_CPFontPanelPreviewView-address-build-warning
Clear _CPFontPanelPreviewView build warning
2026-08-20 20:15:40 -06:00
David Richardson 5449561fe2 Clear _CPFontPanelPreviewView build warning
CPFontPanel.j used _CPFontPanelPreviewView as an ivar type and instantiated it before its own @implementation, later in the same file, with no forward declaration. Added @class _CPFontPanelPreviewView, alongside the file's existing forward declarations.

This removes all build warnings.
2026-08-20 20:14:14 -06:00
David RichardsonandGitHub 073fae648e Merge pull request #3286 from enquora/CPTabStopType-clear-build-warning
Remove CPTabStopType build warning
2026-08-20 20:07:33 -06:00
David Richardson 6121caf75d Remove CPTabStopType build warning
CPParagraphStyle.j defined CPTabStopType's four constants directly, with a comment noting they were "missing" but no @typedef for the type itself.

_CPRTFParser.j uses CPTabStopType as an ivar type and imports CPParagraphStyle.j directly, so the fix belongs in the defining file.

Added the missing @typedef CPTabStopType, same pattern as CPRulerOrientation.
2026-08-20 20:05:38 -06:00
David RichardsonandGitHub 253d6669eb Merge pull request #3285 from enquora/CPRulerOrientation-address-build-warning
Address CPRulerOrientation build warning
2026-08-20 19:59:14 -06:00
David Richardson 533a1d46eb Address CPRulerOrientation build warning
CPRulerView.j declared CPRulerOrientation's constants directly,
with only a comment claiming it was a typedef.

No @typedef directive existed, so the type was unknown wherever used as an ivar or parameter type.

Added the missing @typedef CPRulerOrientation, matching the pattern already used for CPTextAlignment in CPText.j.
2026-08-20 19:57:35 -06:00
David Richardson c0b4fbfeb1 Merge branch 'main' into CPTreeNode-improve-tests 2026-08-20 16:31:42 -06:00
David RichardsonandGitHub f6d96bbf17 Merge pull request #3284 from enquora/CPTextTab-resolve-build-warnings
Add missing CPTextTab forward declaration in CPRulerView
2026-08-20 16:30:02 -06:00
David Richardson 52658b636a Add missing CPTextTab forward declaration in CPRulerView
CPRulerView.j referenced CPTextTab (isKindOfClass: and alloc/init) without a declaration.

Added @class CPTextTab; to resolve the symbol reference. A forward declaration was utilized rather than importing CPParagraphStyle.j to minimize dependency coupling, despite the absence of a circular import risk.

Resolves build warning.
2026-08-20 16:28:02 -06:00
David RichardsonandGitHub c32994af66 Merge pull request #3283 from enquora/_CPTableTextAttachment-resolve-build-warning
Fix build warnings in _CPTableTextAttachment
2026-08-20 15:35:15 -06:00
David Richardson a1ac821678 Fix build warnings in _CPTableTextAttachment
Replaced the CPTextView.j import with forward declarations for CPTextView and CPTextContainer (the latter previously reached via the removed import).

The previous import graph created a circular dependency (_CPTableTextAttachment.j -> CPTextView.j -> _CPRTFParser.j / _CPRTFProducer.j -> _CPTableTextAttachment.j).

This caused the legacy compiler to emit a class registration warning during clean builds, as the class was unregistered at the time of resolution. Incremental builds masked this warning via cached symbol tables.

Using forward declarations breaks the cycle and ensures warning-free clean and incremental builds.
2026-08-20 15:31:45 -06:00
David RichardsonandGitHub 548ed836a0 Merge pull request #3282 from enquora/CPScrollView-address-build-warnings
Remove CPScrollView build warning
2026-08-19 20:28:51 -06:00
David Richardson 78a0ebab6e Remove CPScrollView build warning
CPRulerView.j did not import CPScrollView.
A direct import would create a cycle, since CPScrollView.j already imports CPRulerView.j.
Added a forward declaration instead.
2026-08-19 20:23:33 -06:00
David Richardson fad987641b Improve and extend CPTreeNode tests 2026-08-19 11:41:39 -06:00
David RichardsonandGitHub 3aa053e7a8 Merge pull request #3280 from enquora/CPOutlineView-build-warnings-cleanup
Fix CPOutlineView build warnings and audit+improve CPTreeNode
2026-08-19 11:05:17 -06:00
David Richardson 7aebf371e9 Back out CPTreeNodeTest for addition under separate PR
GitHub CI uses existing CPTreeNodeTest and fails when new tests are included.

New tests must be added as a separate PR, independently of the changes in CPTreeNode.j
2026-08-19 10:31:15 -06:00
David Richardson ac8ce3eec3 Fix CPOutlineView build warnings and audit+improve CPTreeNode
CPOutlineView.j used CPTreeNode before AppKit.j imported it. This
triggered build warnings. This commit adds an import.

- Add @import "CPTreeNode.j" to CPOutlineView.j.

While addressing this warning, CPTreeNode was audited and found to have an algorithmic complexity regression and multiple structural inconsistencies in the KVO support code.

The test suite for CPTreeNode was Spartan in the extreme.

All of these have been addressed - see inline notes for details.
CPTreeNode is subject to further audit.
2026-08-19 10:11:20 -06:00
daboe01 567ec55875 fixed: usage of tab character instead of 4 spaces 2026-08-18 19:46:22 +02:00
daboe01 59569dcb34 new: prevent google translator in cappuccino 2026-08-17 18:20:09 +02:00
David RichardsonandGitHub 3b09a5bde6 Merge pull request #3277 from enquora/CPStackView-build-warnings-cleanup
Fix CPStackView compile errors and add minimal correctness fixes
2026-08-14 20:12:35 -06:00
David Richardson 01f7f9b601 Fix CPStackView compile errors and add minimal correctness fixes
CPStackView.j emited warnings during builds and tests which constituted
either real errors or indeterminate state.

These included four types that did not exist: CPUserInterfaceLayoutOrientation, CPLayoutAttribute,
CPEdgeInsets, and CPMapTable.

The missing types are now present.

- Add local typedefs for CPUserInterfaceLayoutOrientation and
  CPLayoutAttribute. Numeric values match the equivalent Cocoa
  constants.
- Add CPEdgeInsets as an alias for the existing CGInset struct.
  Add CPEdgeInsetsMake and CPEdgeInsetsEqualToEdgeInsets.
- Import Foundation/CPMapTable.j. CPMapTable already exists in
  Foundation.

This commit also fixes two logic errors found during review.

- arrangedSubviews did not match the true view order after
  insert or remove. Add _rebuildArrangedSubviews and call it
  from every method that changes view order.
- Trailing-gravity layout added one extra spacing gap past the
  last view. This shifted the returned layout boundary. Spacing
  is now added before each view, not after.

This commit adds one new property.

- Add a distribution property with storage and accessors. It
  has no effect on layout yet. This makes the property honest:
  before this change, the type existed but no property did.

The manual test, ./Tests/Manual/CPStackViewTest, used a Narwhal-era
Jakefile and would not build. This has been updated to reflect
usage under the Node toolchain.

This commit adds CPStackView.j to AppKit.j. The file was never
imported. Only the build script's wildcard file list included
it.

This commit adds a header comment to CPStackView.j. The comment
states that the class is a placeholder. It lists known
limitations. It states that a constraint-solver based
replacement is planned.

Why: CPStackView must compile and pass tests before capp-build
can use this tree as a build reference, and a class with unstated
limits invites misuse.
2026-08-14 20:09:17 -06:00
David RichardsonandGitHub 907ff374e6 Merge pull request #3273 from enquora/add-property-copy-test
Add test for @accessors(copy)
2026-08-13 16:51:51 -06:00
David Richardson a786418e19 Add test for @accessors(copy)
@accessors(copy) had no test in ./Tests.
The compiler's copy-accessor setter behaviour was unverified.

Add CopyAccessorTest.j to Tests/Objective-J. It asserts three
properties of the generated setter:
- the stored value is not identical to the assigned object
- the stored value is unaffected by later mutation of the original
- the stored value is equal in content to the original at assignment

This is added both as a matter of good coverage for the existing toolchain and so that new tooling has a reference for a piece of foundational behaviour.
2026-08-13 16:42:57 -06:00
daboe01andGitHub 6448f7ea12 Merge pull request #3272 from daboe01/pageunloadfix
replace unload event listener with pagehide
2026-08-13 14:16:09 +02:00
daboe01 ce2803f354 replace unload event listener with pagehide and add DOM safety checks 2026-08-12 16:58:12 +02:00
David RichardsonandGitHub 7e3d22b10e Merge pull request #3270 from enquora/CPMapTable-fix-build-warning
Fix legacy compiler warnings in CPMapTable
2026-08-12 07:50:43 -06:00
David Richardson 84dab55993 Fix legacy compiler warnings in CPMapTable
Using ES6 destructuring in the `for...of` loop declaration
(`var [key, value] of _map.entries()`) causes the legacy
Objective-J compiler to emit "uninitialized global variable"
warnings for `key` and `value`.

This raises concerns about variable scoping and generates
unacceptable noise in the CI pipeline. To resolve the warnings,
the loop has been restructured to use standard array indexing
inside the loop body.

A TODO has been added to revert to ES6 destructuring once the
legacy compiler is retired.
2026-08-11 13:04:05 -06:00
David RichardsonandGitHub 60a143eb7c Merge pull request #3268 from cappuccino/readme-aristo3-testing
Update README to reflect testing branch for the Aristo3 theme
2026-08-10 20:27:02 -06:00
David Richardson aa961ff518 Update README to reflect testing branch for the Aristo3 theme 2026-08-10 20:17:30 -06:00
David RichardsonandGitHub bbdc7e1fe1 Merge pull request #3266 from enquora/modernize-isNumeric-macro
Remove _IS_NUMERIC macro in favour of concrete function CPIsNumeric()

- Create Foundation/_CPFoundationUtilities.j to house global low-level utilities.
- Implement CPIsNumeric as a concrete JavaScript function, preserving the exact legacy logic.
- Import _CPFoundationUtilities.j in the Foundation.j umbrella manifest.
- Replace _IS_NUMERIC macro calls at all call sites with CPIsNumeric().
2026-08-05 13:50:00 -06:00
David Richardson b62721d79a Remove _IS_NUMERIC macro in favor of concrete funtion CPIsNumeric()
- Create Foundation/_CPFoundationUtilities.j to house global low-level utilities.
- Implement CPIsNumeric as a concrete JavaScript function, preserving the exact legacy logic.
- Import _CPFoundationUtilities.j in the Foundation.j umbrella manifest.
- Replace _IS_NUMERIC macro calls at all call sites with CPIsNumeric().
2026-08-05 13:47:28 -06:00
David RichardsonandGitHub 450db2678a Merge pull request #3265 from enquora/modernize-CPNumberFormatter-remove-cpp-directives
Foundation: remove preprocessor macro from CPNumberFormatter.j
2026-08-04 19:48:17 -06:00
David Richardson 02a5434e4f Foundation: remove preprocessor macro from CPNumberFormatter.j
Replace SET_NEEDS_NUMBER_HANDLER_UPDATE() with inline statements.
Add a comment at each of the 6 call sites to state the reason.
No behavior change.
2026-08-04 19:46:09 -06:00
David RichardsonandGitHub 51b10e2bc4 Merge pull request #3258 from enquora/modernize-CPTimer
Refactor CPTimer: Remove CPP dependencies and mark legacy anti-patterns
2026-08-04 19:09:46 -06:00
David RichardsonandGitHub 8cb3d7771c Merge pull request #3264 from enquora/modernize-_CPArray-cpp
refactor(foundation): expand placeholder redirection macro in CPArray
2026-08-04 18:42:58 -06:00
David Richardson 4ef40ff151 refactor(foundation): expand placeholder redirection macro in CPArray
Remove the pre-processor dependency on FORWARD_TO_CONCRETE_CLASS by  inlining the class cluster instantiation routing logic across all designated initializers to support the new Go-based toolchain architecture.
2026-08-04 18:40:57 -06:00
David RichardsonandGitHub d7e5c2ccac Merge pull request #3259 from enquora/modernize-CPNumber
Refactor CPNumber: Remove CPP macros and modernize syntax
2026-08-04 18:11:59 -06:00
David Richardson f414fae60a Replace tabs with spaces
Tab indentation inadvertenly applied previously.
2026-08-04 18:09:27 -06:00
David RichardsonandGitHub a6c57b7128 Merge pull request #3263 from enquora/modernize-CPException-inline-method-call-string
Refactor METHOD_CALL_STRING macro to Javascript function

Replace the unhygienic METHOD_CALL_STRING() C pre-processor macro 
in CPException.j with a standard JavaScript helper function, 
_CPMethodCallString().

Add a `// MARK: - Exception Utilities` navigation landmark. 

This removes a legacy text-substitution dependency, enabling 
single-pass AST generation for the native compilation pipeline.
2026-08-04 17:52:30 -06:00
David Richardson cbf1d35f24 Refactor METHOD_CALL_STRING macro to Javascript function
Replace the unhygienic METHOD_CALL_STRING() C pre-processor macro
in CPException.j with a standard JavaScript helper function,
_CPMethodCallString().

Add a `// MARK: - Exception Utilities` navigation landmark.

This removes a legacy text-substitution dependency, enabling
single-pass AST generation for the platform-native compilation pipeline.
2026-08-04 17:47:44 -06:00
David Richardson ecf36f12cb docs: Establish v1.5.0 baseline and direct legacy users to 1.4.0
Merge Aristo3 theme and make it default.
Update README to reflect transition to Cappuccino 2.
Change README extension from .markdown to .md for universal editor support.
2026-08-04 15:00:41 -06:00
daboe01 fa79df4309 Merge remote-tracking branch 'upstream/main' 2026-07-25 19:22:16 +02:00
David Richardson c97839905c Refactor CPPlatformPasteboard: Remove CPP macros and legacy IE fallbacks
As part of the ongoing technical debt cleanup, this commit modernizes
CPPlatformPasteboard.j by removing its reliance on the C-preprocessor
and stripping obsolete Internet Explorer compatibility code.

Key changes:
- Inlined the `SUPPRESS_CAPPUCCINO_CUT_FOR_EVENT` and `SUPPRESS_CAPPUCCINO_PASTE_FOR_EVENT`
  macros directly to standard property assignments.
- Removed the IE `srcElement` fallback in `hasEditableTarget`.
- Removed the IE `attachEvent`/`detachEvent` branch in `createDOMElements`,
  keeping only the standard `addEventListener` path.
- Replaced `var` with `const` and `let` for proper block scoping and immutability.
- Retained the `#if PLATFORM(DOM)` guards to protect the Node.js CI during
  the phased modernization effort.

All changes are annotated with `// MODIFICATION:` comments for clear
review tracking.
2026-07-23 17:22:36 -06:00
David Richardson d34149b236 Modernize CALayer, remove macro defined access in favour of ivar 2026-07-23 17:02:28 -06:00
David Richardson d9d3e5d613 Refactor CPNumber: Remove CPP macros and modernize syntax
Key changes:
- Removed the `CAST_TO_INT` macro and replaced it with native ES6
  `Math.trunc()` for float truncation.
- Replaced `var` with `let` for proper block scoping.
- Simplified boolean coercion in `boolValue` using the `!!` operator.
- Replaced the hostile `throw new Error` in `descriptionWithLocale:`
  with a safe fallback to `self.toString()`.
- Added `FIXME` comments to explicitly document architectural
  anti-patterns, including the global mutable state in `CPNumberUIDs`
  and the unimplemented `decimalValue` method.
2026-07-23 16:37:06 -06:00
David Richardson a745af8ead Refactor CPTimer: Remove CPP dependencies and mark legacy anti-patterns
As part of the ongoing technical debt cleanup, this commit modernizes
CPTimer.j by removing its reliance on the C-preprocessor and updating
syntax to modern JavaScript standards.

Key changes:
- Replaced `#define CPTimerDefaultTimeInterval` with a native `const`.
- Replaced `var` with `const`/`let` for proper block scoping and immutability.
- Replaced the build-time `PLATFORM(DOM)` guard with a runtime
  `typeof(window) !== 'undefined'` check, ensuring Foundation does not
  depend on build-time platform flags.
- Added `FIXME` comments to explicitly document architectural anti-patterns:
  - The invasive global override of `window.setTimeout` and `window.setInterval`.
  - The use of `new Function` for string evaluation (CSP violation).
  - The questionable exposure of `CPTimerDefaultTimeInterval` as a fallback.
2026-07-23 15:26:31 -06:00
David Richardson 6e0d2f97cc Refactor CABackingStore: Eliminate preprocessor directives and legacy compatibility
* Remove PIXEL macro and replace with native inline string concatenation.
* Remove CPHTMLCanvasFeature branching and obsolete CGBitmapGraphicsContextCreate fallback.
* Convert var declarations to block-scoped const.
* Standardize global function assignments to explicit C-style declarations.
* Remove unused CPCompatibility.j import.
2026-07-23 14:48:08 -06:00
David Richardson e288ea3690 Modernize CGContextCanvas.j: Remove CPP macros and legacy fallbacks
As part of the ongoing effort to remove the C-preprocessor (CPP) from
the new Lisette compiler pipeline, this commit refactors CGContextCanvas.j
to use native JavaScript and HTML5 Canvas APIs directly.

Key changes:
- Removed all `#define` macros that wrapped standard Canvas API calls
  (e.g., `_CGContextMoveToPointCanvas`). Direct method invocations are
  now used, eliminating the preprocessor pass and simplifying AST generation.
- Removed the `CPJavaScriptCanvasTransformFeature` runtime check and its
  complex mathematical fallbacks (`scale_rotate`, `rotate_scale`, `eigen`).
  Modern target environments universally support native canvas transformations.
- Removed obsolete vendor-prefixed line dash properties (`webkitLineDash`,
  `mozDash`) in favor of the standard `setLineDash`.
- Replaced `var` with `const`/`let` for proper block scoping and immutability.
- Replaced the `ROUND` macro with standard `Math.round`.

These changes strip away 2008-era browser compatibility hacks and micro-optimizations, resulting in cleaner, more maintainable, and strictly browser-targeted code.
2026-07-21 16:54:35 -06:00
David RichardsonandGitHub 87c1727ea0 Merge pull request #3255 from enquora/modernize-pragma-mark
Replace objc directive with native Javascript version // MARK: to facilitate elimination of pre-processing pass.
2026-07-21 16:16:00 -06:00
David Richardson 1a9a42ff54 Replace #pragma mark with // MARK:
Replace objc directive with native Javascript version // MARK: to facilitate elimination of preproccsing pass.
2026-07-21 15:15:53 -06:00
David RichardsonandGitHub 5592056351 Merge pull request #3254 from enquora/cleanup-formatting
Cleanup formatting in CPSet class cluster.
Align variable declarations.
Convert Xcode landmarks from c preprocessor pragmas to objj-native // MARK:
2026-07-21 11:11:53 -06:00
David Richardson 98bfd87446 Convert Xcode code landmarks from Objective-C to native Javascript
While objc `#pragma mark` works in objj files, it requires a preprocessor to handle the directive.

// MARK: is the fully functional and correct Objective-J variant.
2026-07-21 11:05:11 -06:00
David Richardson 3f63be7290 Align variable declarations 2026-07-21 10:56:38 -06:00
daboe01andGitHub b23d0cb8eb Merge pull request #3251 from daboe01/ruleeditor-drag-fix
fixed: CPRuleEditor: crash when dragging nested compound rows
2026-07-11 14:24:54 +02:00
daboe01andGitHub 0da009d1b9 Merge pull request #3246 from daboe01/markdown-manual-test
new: manual test for CPMarkdownParser
2026-07-11 13:51:37 +02:00
daboe01 b1fade86fe formatting 2026-07-11 13:51:04 +02:00
daboe01 04bad947e3 fixed: CPRuleEditor: crash when dragging nested compound rows 2026-07-10 16:14:29 +02:00
David RichardsonandGitHub f2cdb08e76 Merge pull request #3249 from enquora/resolve-duplicate-category-CPSet
Deconstruct _CPSet monolithic layout and relocate KVO proxies
2026-07-08 18:37:24 -06:00
David Richardson da3b0b5e21 fix(Foundation): implement valueForKey: for CPSet KVC compliance
Resolves testKVCCountSetOperators failure in CPSetTest. The previous implementation lacked an override for valueForKey:, causing the runtime to fall back to the default CPObject behavior, which failed to recognize KVC collection operators (e.g., @count).

Delegating valueForKey: to valueForKeyPath: ensures that CPSet correctly routes KVC operations through the existing collection operator logic.
2026-07-08 18:28:49 -06:00
David Richardson a240d312d0 Deconstruct _CPSet monolithic layout and relocate KVO proxies
•	Refactor @implementation _CPKVCSet and CPObject (CPSetKVO) from _CPSet.j to the tail of CPMutableSet.j.
•	Fix legacy compiler parse failures by ensuring the CPMutableSet superclass symbol is fully resolved within the translation unit prior to proxy evaluation.
•	Maintain strict DAG compliance across the modern toolchain pipeline topology.
•	Embed explicit #pragma mark navigation landmarks and enforce Allman brace layout to improve human structural ergonomics.
2026-07-08 17:35:33 -06:00
David RichardsonandGitHub bd48e2055e Merge pull request #3248 from enquora/foundation-resolve-duplicate-methods
Remove duplicate CPIndexSet -isEqual
2026-07-08 10:18:41 -06:00
David Richardson 42c212df64 Remove duplicate CPIndexSet -isEqual
Originally added in error, not realizing another author had just applied and committed the same fix.

Javascript-based tooling unable to catch this.
2026-07-08 10:16:25 -06:00
David RichardsonandGitHub 59e811328f Merge pull request #3247 from enquora/remove-import-cycles-foundation
Break Foundation import cycles for strict graph sorting
2026-07-08 09:16:16 -06:00
David Richardson 317c1a1475 Break Foundation import cycles for strict graph sorting
Disentangle circular dependencies Foundation framework primitives using
forward declarations.
2026-07-08 08:58:53 -06:00
daboe01 a93e67662b new: manual test for CPMarkdownParser 2026-06-26 19:42:42 +02:00
daboe01 14379b1be9 Merge remote-tracking branch 'upstream/main' 2026-06-24 16:53:48 +02:00
daboe01andGitHub 2e8767509e Merge pull request #3245 from daboe01/textview-selection-fix
new: Prefer e.data to capture the exact inserted character(s) from ke…
2026-06-24 16:53:22 +02:00
daboe01 429dfd7467 new: Prefer e.data to capture the exact inserted character(s) from key entry 2026-06-24 16:50:05 +02:00
daboe01 71a7b54edd Merge remote-tracking branch 'upstream/main' 2026-06-21 17:36:04 +02:00
daboe01andGitHub ad05fbe97b Merge pull request #3244 from daboe01/textview-stalenes-fix
Fixed: CPLayoutManager false-positive layout rescue on zero-length-delta edits
2026-06-21 17:35:30 +02:00
daboe01 0fed962b37 Fixed: CPLayoutManager false-positive layout rescue on zero-length-delta edits 2026-06-21 17:30:18 +02:00
daboe01 ee36796180 Merge remote-tracking branch 'upstream/main' 2026-06-20 17:52:48 +02:00
daboe01 cff2fd8ed9 optimized version 2026-06-20 17:51:11 +02:00
daboe01 344cbd0660 formatting 2026-06-20 15:01:35 +02:00
daboe01 e6e676c91e more safety checks 2026-06-20 15:00:21 +02:00
daboe01 5fd2f49266 safety checks 2026-06-20 14:55:27 +02:00
daboe01 10fb3e6d31 Fixed: scale accumulation in scaleUnitSquareToSize: when setting bounds size 2026-06-20 14:35:15 +02:00
daboe01andGitHub 535315485a Merge pull request #3235 from daboe01/paste-as-plain-text
New: standard key binding for pasteAsPlainText:
2026-06-20 14:10:49 +02:00
daboe01andGitHub 88146a5389 Merge pull request #3240 from daboe01/baseline-fix
Fixed: baseline alignment in _CPLineFragment to prevent overlapping text descenders
2026-06-20 12:36:57 +02:00
daboe01 c8789c5450 formatting 2026-06-20 12:32:10 +02:00
daboe01 766035f4b7 Fixed: baseline alignment in _CPLineFragment to prevent overlapping text descenders 2026-06-20 12:06:24 +02:00
daboe01andGitHub a90b504dc7 Merge pull request #3239 from daboe01/more-cptextview-fixes
new: heightTracksTextView support in CPTextContainer + rtf fixes
2026-06-20 10:42:26 +02:00
daboe01 26b2280fc2 fixed: color spill issue 2026-06-20 10:40:46 +02:00
daboe01 15bbca8d77 fixed: whitespace issue 2026-06-20 10:36:07 +02:00
daboe01 319bcdaba9 fixed: table roundtrip issue 2026-06-19 22:56:38 +02:00
daboe01 89445f94a2 fixed: whitespace before tab 2026-06-19 22:26:52 +02:00
daboe01 beb4aef424 new: heightTracksTextView support in CPTextContainer 2026-06-19 09:47:39 +02:00
daboe01andGitHub ef680c080d Merge pull request #3238 from daboe01/baseline-fixes
New: implement baseline offsets and superscript/subscript support
2026-06-19 08:52:06 +02:00
daboe01 c187f5de1d fixed: rtf roundtrip with baseline stuff 2026-06-19 08:48:17 +02:00
daboe01 d40a151e05 new: colorpanel sync 2026-06-19 07:18:52 +02:00
daboe01 e67c141921 new: color panel 2026-06-18 22:21:50 +02:00
daboe01 de2ffd3aad New: implement baseline offsets and superscript/subscript support 2026-06-18 19:08:45 +02:00
daboe01 4c8fd58471 fixed: leaky abstraction 2026-06-17 19:22:36 +02:00
daboe01 873a102872 New: standard key binding for pasteAsPlainText: 2026-06-17 18:24:31 +02:00
daboe01 6dc11de431 Revert "New: standard key binding for pasteAsPlainText:"
This reverts commit 9731c4b394.
2026-06-17 18:21:26 +02:00
daboe01 672f25ce27 New: standard key binding for pasteAsPlainText:
This reverts commit 9731c4b394.
2026-06-17 18:20:39 +02:00
daboe01 9731c4b394 New: standard key binding for pasteAsPlainText: 2026-06-17 18:18:39 +02:00
daboe01 b2c5955e7d Merge remote-tracking branch 'upstream/main' into CPLanguageModel-and-friends 2026-06-17 16:21:24 +02:00
daboe01andGitHub bc0380e1cb Merge pull request #3233 from daboe01/fixed-Unable-to-preventDefault-inside-passive-event
new: register wheel event listeners as non-passive
2026-06-17 16:20:37 +02:00
daboe01 1223cfc965 new: register wheel event listeners as non-passive 2026-06-17 16:10:39 +02:00
daboe01 2f63a5fd10 formatting 2026-06-17 08:25:24 +02:00
daboe01 b827310c75 Merge remote-tracking branch 'upstream/main' into CPLanguageModel-and-friends 2026-06-17 08:09:34 +02:00
daboe01andGitHub 361a4203da Merge pull request #3232 from daboe01/table-in-textview-fixes
fixed: table sizing
2026-06-17 08:09:17 +02:00
daboe01 8de928d905 formatting 2026-06-17 08:07:26 +02:00
daboe01 5a3d6e30cb fixed: table sizing 2026-06-17 07:58:33 +02:00
daboe01 227eda9d92 new: spinner in manual test 2026-06-17 07:57:17 +02:00
daboe01 9fe0df74b3 formatting 2026-06-16 20:20:25 +02:00
daboe01 381c1880c2 cleanup 2026-06-16 20:15:34 +02:00
daboe01 5439e5d473 Merge remote-tracking branch 'upstream/main' into CPLanguageModel-and-friends 2026-06-16 19:48:16 +02:00
daboe01andGitHub 50bac0a470 Merge pull request #3230 from daboe01/setfontfix
Fixed: CPInvalidArgumentException in CPTextField -setFont: when handling composite theme states
2026-06-16 19:45:49 +02:00
daboe01 cfff603508 Fixed: CPInvalidArgumentException in CPTextField -setFont: when handling composite theme states 2026-06-16 19:43:45 +02:00
daboe01andGitHub 894a60ad45 Merge pull request #3229 from daboe01/rtf-producer-table-interface
NEW: add table support and other improvements to CPTextView
2026-06-16 19:28:45 +02:00
daboe01andGitHub acbe5885f1 Merge branch 'main' into rtf-producer-table-interface 2026-06-16 19:24:10 +02:00
daboe01 a981b8b04d improved: indentation markers 2026-06-16 19:17:44 +02:00
daboe01 d75c0ac452 improved: tab management 2026-06-16 17:28:03 +02:00
daboe01 da061aac1d improved: table generation 2026-06-16 10:30:00 +02:00
daboe01 77433a3a1c fixed: scrolling glitch 2026-06-16 07:16:14 +02:00
daboe01 4aaa32833a fixed: table roundtrip 2026-06-15 22:33:03 +02:00
daboe01 2a0cebb851 improved: rtf roundtrip 2026-06-15 19:42:53 +02:00
daboe01 1b23bb8a67 new: CPMarkdownParser 2026-06-15 16:43:58 +02:00
daboe01 5dc66c5b4e fixed: roundtrip 2026-06-14 22:56:23 +02:00
daboe01 49b1800027 improved: table generation 2026-06-14 22:42:33 +02:00
daboe01 1cad83aba8 initial display of the table 2026-06-14 21:06:51 +02:00
daboe01andGitHub e1f5238063 Merge pull request #3228 from daboe01/textview-dom-fix
removed: dead code
2026-06-13 22:47:30 +02:00
daboe01 5897d76c04 fixed: imports 2026-06-13 22:38:36 +02:00
daboe01 a0fc7723fc new: table support 2026-06-13 22:36:09 +02:00
daboe01andGitHub ec4a1d1724 Merge pull request #3225 from daboe01/textfield-setfontfix
fixed: ensure custom fonts are respected in all states, styles, and CIBs
2026-06-13 22:27:11 +02:00
daboe01 6139216146 new: RTFProducer PROTOCOL METHOD ADDITIONS 2026-06-13 19:53:39 +02:00
daboe01 861c504c41 NEW: add native table support via dynamic protocol detection 2026-06-13 19:47:24 +02:00
daboe01 c9df6e4d25 removed: dead code 2026-06-13 19:18:12 +02:00
daboe01 c9d3ccdf0d fixed: top was not applied to style 2026-06-13 18:42:34 +02:00
daboe01 2c275b279b new: markdown support in manual test 2026-06-13 18:13:51 +02:00
daboe01 44f6e0fded cosmetic fixes 2026-06-13 16:38:40 +02:00
daboe01 e5278fd17e new: run loop management 2026-06-13 16:30:16 +02:00
daboe01 1bd3c93717 formatting 2026-06-13 16:22:34 +02:00
daboe01 afc6691f07 formatting 2026-06-13 16:02:17 +02:00
daboe01 8c41c7b8ac fixed: import semantics 2026-06-13 15:54:07 +02:00
daboe01 5445d42eba Merge remote-tracking branch 'upstream/main' into CPLanguageModel-and-friends 2026-06-13 15:47:01 +02:00
daboe01andGitHub d88f2ef7c0 Merge pull request #3227 from daboe01/cptextsizefix
new: Clear the cached visible rect so it gets recalculated after size…
2026-06-13 15:46:28 +02:00
daboe01 14b5a67bf5 new: Clear the cached visible rect so it gets recalculated after size changes 2026-06-13 15:42:48 +02:00
daboe01 8e04b172ce simplification of demo 2026-06-13 15:34:21 +02:00
daboe01 5fff34be91 new: CPLanguageModel and manual test 2026-06-13 14:50:38 +02:00
daboe01andGitHub 210133d9e2 Merge pull request #3224 from daboe01/formatting-/-documentation-CPRuleEditor
new: Documentation for CPRuleEditor I8N stuff
2026-06-05 17:55:09 +02:00
daboe01 524b1df4c9 fix font issue 2026-06-05 17:38:04 +02:00
daboe01 9115949169 new: Documentation for CPRuleEditor I8N stuff 2026-06-04 21:39:02 +02:00
daboe01andGitHub 0b3aa6aac2 Merge pull request #3221 from daboe01/Fixed-CPRuleEditor-word-by-word-translation-issue
Fixed: CPRuleEditor word-by-word translation issue (#1834)
2026-06-04 14:12:08 +02:00
daboe01 e03e0c6fc4 fixed: exception after deleting the last row 2026-06-04 08:55:32 +02:00
daboe01 613a2722be new: dynamic mutilanguage demo 2026-06-04 08:15:52 +02:00
daboe01 e9b9d9702e fixed: localizer issues 2026-06-03 21:19:09 +02:00
daboe01 750c123d1e fixed: redraw issues 2026-06-03 19:19:36 +02:00
daboe01 5e0fc1cbb5 fixes + manual test 2026-06-02 22:20:44 +02:00
daboe01 015f027cf2 Merge remote-tracking branch 'upstream/main' 2026-05-30 15:12:31 +02:00
daboe01andGitHub c3d9dead19 Merge pull request #3192 from daboe01/fixed-CPControlTextDidBeginEditingNotification-is-not-correctly-triggered
Fixed: timing of CPControlTextDidBeginEditingNotification (#1941)
2026-05-30 15:09:34 +02:00
daboe01andGitHub b296f17d2b Merge pull request #3168 from daboe01/new-cprulerview
new: CPRulerview and friends
2026-05-30 15:08:56 +02:00
daboe01 22fdc971c7 improved: marker icons 2026-05-30 10:11:00 +02:00
daboe01 291be04dc3 improved: rtf parser 2026-05-30 09:40:25 +02:00
daboe01 5c7215c455 new: apply ruler to current paragraph in case of no selection 2026-05-30 09:25:26 +02:00
daboe01 2a83dcb64a fixed: adding new tabstops 2026-05-30 09:08:56 +02:00
daboe01 979118a86f restored: tabstop-fix 2026-05-30 08:55:25 +02:00
daboe01 4fbd50e81f revert: textTabForWidth-"fix" 2026-05-30 08:44:43 +02:00
daboe01 3b1c1c0886 fixed: tab could not be entered using the keyboard 2026-05-29 22:08:00 +02:00
daboe01 dbc8bfc7f3 fixed: pasting tab characters 2026-05-29 21:28:16 +02:00
daboe01 10bb6723f3 fixed: missing global symbols 2026-05-29 20:02:13 +02:00
daboe01 c58a5b520e fixed: tab copypaste 2026-05-29 18:19:03 +02:00
daboe01 7b6cb51f4d fixed: format menu 2026-05-29 11:45:17 +02:00
daboe01 c704880e13 improved: tabstop handles 2026-05-29 11:41:39 +02:00
daboe01 41c422ed03 new: visual feedback when "tearing off" 2026-05-29 10:55:22 +02:00
daboe01 c94ed00f41 fixed: basic ruler view support 2026-05-29 10:46:10 +02:00
daboe01 1d93cf2b1a new: cpscrollview fixes 2026-05-28 21:31:26 +02:00
daboe01 420f3a538d Update AppController.j 2026-05-28 21:23:01 +02:00
daboe01 453e8dbd2e Merge remote-tracking branch 'upstream/main' into new-cprulerview 2026-05-28 21:22:55 +02:00
daboe01 c6549867bd fixed: missing global variable 2026-05-28 21:13:42 +02:00
daboe01 3aecb3ea98 fixed: missing import 2026-05-28 21:11:51 +02:00
daboe01 2819546716 fixed: missing import 2026-05-28 21:07:43 +02:00
daboe01 7e0f03a6fc fixed: CI error 2026-05-28 21:03:31 +02:00
daboe01 6e36fc0b20 formatting 2026-05-28 17:51:10 +02:00
daboe01 e856605a09 _CPRuleEditorViewSliceRow integration 2026-05-28 17:38:14 +02:00
daboe01 d7315773e1 fixed: syntax issues 2026-05-28 17:15:41 +02:00
daboe01 a27aaf8bd0 Fixed: CPRuleEditor word-by-word translation issue (#1834) 2026-05-28 17:11:29 +02:00
daboe01andGitHub c0b875de5d Merge pull request #3220 from daboe01/rtf-parser-fix
Update _CPRTFParser.j
2026-05-25 06:59:44 +02:00
daboe01 141eb7b98e fixed: wrong characters in output 2026-05-25 06:56:32 +02:00
daboe01 6099311cbd Update _CPRTFParser.j 2026-05-25 06:48:27 +02:00
daboe01andGitHub dcb06fbc4f Fix formatting issues in README.markdown 2026-05-23 13:51:50 +02:00
daboe01andGitHub 8258c8ef0b Revise Cappuccino usage description in README 2026-05-23 13:51:25 +02:00
daboe01andGitHub 9312c96154 Clarify Cappuccino's purpose in README 2026-05-23 09:05:07 +02:00
daboe01andGitHub c27b48d27a Update documentation links in README 2026-05-23 08:46:29 +02:00
daboe01andGitHub f3039fb77c Merge pull request #3219 from daboe01/cptextview-speed-fix
CPLayoutManager: Fix O(N^2) layout bottleneck when loading large text documents
2026-05-21 09:49:12 +02:00
daboe01 abe159eceb CPLayoutManager: Fix O(N^2) layout bottleneck when loading large text documents 2026-05-21 09:44:54 +02:00
daboe01andGitHub ffb6b3e6fe Merge pull request #3218 from daboe01/cptextview-fixes
Fixed: rare layout issue after inserting multiple newlines and text layout performance improvement
2026-05-19 18:52:34 +02:00
daboe01 7f25de0fad improved: kerning safe optimization 2026-05-17 21:30:11 +02:00
daboe01 d6c5d8b4f2 formatting 2026-05-17 16:47:12 +02:00
daboe01 78e1ea43c8 new: layouting speedup in CPTextView 2026-05-17 15:58:16 +02:00
daboe01andGitHub 9d24c855e2 Update project status in README
Removed outdated information and clarified project status.
2026-05-06 19:55:15 +02:00
daboe01andGitHub 872af9ce2a Revise Cappuccino introduction and project status
Reorganized and condensed introductory information about Cappuccino, emphasizing its purpose and benefits for building desktop-class applications.
2026-05-06 19:54:43 +02:00
daboe01andGitHub 18b2a668c5 Merge pull request #3213 from daboe01/double-action-tableview
new: doubleClickTarget and doubleClickArgument for CPTableView
2026-04-09 22:32:00 +02:00
daboe01andGitHub 72be296713 Merge pull request #3214 from daboe01/fixed-tableview-column-removal-issue
Fixed: CPTableView bug where removing a column breaks internal state
2026-04-09 22:30:59 +02:00
daboe01 b620991770 Merge remote-tracking branch 'upstream/main' 2026-04-08 15:26:31 +02:00
daboe01andGitHub 732fc2d1e1 Merge pull request #3215 from daboe01/cptreecontroller-fixes
Fixed: CPTreeController selection bug for asynchronously loaded nodes
2026-04-08 15:26:17 +02:00
daboe01 79a9ae31d3 Fixed: CPTreeController selection bug for asynchronously loaded nodes 2026-04-08 15:23:45 +02:00
daboe01 e0e16eeca5 formatting 2026-04-08 15:18:46 +02:00
daboe01 a784435c2b formatting 2026-04-08 15:17:46 +02:00
daboe01 57987b6f67 Fixed: CPTableView bug where removing a column breaks internal state 2026-04-06 17:26:38 +02:00
daboe01 480917eeab new: doubleClickTarget and doubleClickArgument for CPTableView 2026-04-06 13:15:55 +02:00
daboe01andGitHub 013e7bdf7e Merge pull request #3210 from daboe01/cpwebview-resize-fix
Fixed: incorrect CPWebView sizing after animation
2026-04-06 12:08:35 +02:00
daboe01andGitHub c4032ecd1d Merge pull request #3212 from daboe01/tooltip-tableview-fix
New: toolTips for CPTableView and CPOutlineView
2026-04-06 11:59:14 +02:00
daboe01 2ea11eb5ab formatting 2026-04-06 11:58:00 +02:00
daboe01 7fad67484c new: manual tests 2026-04-06 11:56:23 +02:00
daboe01 f34cbbdf29 New: toolTips for CPTableView and CPOutlineView 2026-04-04 15:00:53 +02:00
daboe01 1c0cc2acef Merge remote-tracking branch 'upstream/main' 2026-04-03 19:01:23 +02:00
daboe01andGitHub 14a4b04d9b Merge pull request #3211 from daboe01/fix-colorpanel-issues
fixed: colorpanel issue
2026-04-03 19:00:47 +02:00
daboe01 8aee2a511f fixed: colorpanel issue 2026-04-03 19:00:09 +02:00
daboe01 65cb048b1b Fixed: incorrect CPWebView sizing after animation 2026-04-03 17:40:45 +02:00
daboe01 cf10b826d7 Merge remote-tracking branch 'upstream/main' 2026-04-03 16:01:18 +02:00
daboe01andGitHub b7ac5272d0 Merge pull request #3193 from daboe01/fixed-right-buttons-in-buttonbar
New: support for right-aligned buttons in CPButtonBar (#745)
2026-04-03 16:00:37 +02:00
daboe01 dc893f7ca3 Merge remote-tracking branch 'upstream/main' 2026-04-03 15:55:56 +02:00
daboe01andGitHub 206b1d4a05 Merge pull request #3209 from daboe01/cppredicateeditor-nil-value-fix
new: clear CPPredicateEditor rows when objectValue is set to nil
2026-04-03 15:33:10 +02:00
daboe01 0bd83b6635 new: clear CPPredicateEditor rows when objectValue is set to nil 2026-04-03 15:20:59 +02:00
daboe01 4dcfe992de Merge remote-tracking branch 'upstream/main' 2026-04-03 15:16:40 +02:00
daboe01andGitHub ef9d6662cb Merge pull request #3208 from daboe01/new-_CPKVOCollectionOperatorObserver
New:  support collection operators on child key paths
2026-04-03 14:56:29 +02:00
daboe01 0e5f3b1697 fixed caching 2026-04-03 13:49:33 +02:00
daboe01 7fbea7913a new: manual test 2026-04-03 13:39:42 +02:00
daboe01 4b81001330 new: testCollectionOperatorKeyPath 2026-04-03 13:33:06 +02:00
daboe01 043029cf41 new: _CPKVOCollectionOperatorObserver 2026-04-03 13:30:13 +02:00
daboe01 81988f1ec7 Merge remote-tracking branch 'upstream/main' 2026-03-25 19:34:03 +01:00
daboe01andGitHub 9ecc739e8d Merge pull request #3186 from daboe01/colorpanel-fixes
Fix CPColorWell drag-and-drop and responder chain integration
2026-03-25 19:33:27 +01:00
daboe01andGitHub 3d5f4eec18 Merge pull request #3207 from daboe01/cplocalefix
fixed: locale inference
2026-03-24 21:36:02 +01:00
daboe01 c7e89d3018 formatting 2026-03-24 21:34:39 +01:00
daboe01 63e3f6c3fc fixed single region of FF 2026-03-24 21:33:24 +01:00
daboe01 d39a583127 fixed: locale inference 2026-03-24 21:18:43 +01:00
daboe01 f568427dc2 Merge remote-tracking branch 'upstream/main' 2026-03-15 17:44:59 +01:00
daboe01andGitHub 6e486bb30b Merge pull request #3205 from daboe01/fixed-missing-dom-protection
removed: erroneous code spillover
2026-03-15 17:40:54 +01:00
daboe01 5e297dd293 removed: erroneous code spillover 2026-03-15 17:39:33 +01:00
daboe01 63cbb04acc Merge remote-tracking branch 'upstream/main' 2026-03-15 17:09:10 +01:00
daboe01andGitHub 6f55c7bbba Merge pull request #3204 from daboe01/fixed-outlineview-animation-race-condition
fixed: racecondition in row expansion animation
2026-03-15 17:08:49 +01:00
daboe01 ea4adf1039 fixed: racecondition in row expansion animation 2026-03-15 17:08:21 +01:00
daboe01 3ff7818d58 Merge remote-tracking branch 'upstream/main' 2026-03-15 11:37:00 +01:00
daboe01andGitHub 3e4ebe8e47 Merge pull request #3203 from daboe01/fixed-treeviewbindings
fixed: treeview binding issues
2026-03-15 08:58:21 +01:00
daboe01 b77a374f48 formatting 2026-03-15 08:57:06 +01:00
daboe01 43936f640c formatting 2026-03-15 08:53:04 +01:00
daboe01 6671d371e9 fixed: treeview binding issues 2026-03-15 08:51:03 +01:00
daboe01 4fd99d2c52 fixed: tokenfield CPControlTextDidBeginEditingNotification notification 2026-03-14 19:25:50 +01:00
daboe01andGitHub 6563e730a0 Merge pull request #3202 from daboe01/textview-caret-blinking-fix
fixed: caret is invisible at the end of the line
2026-03-14 13:02:28 +01:00
daboe01 b39c3e8d4b fixed: caret is invisible at the end of the line 2026-03-14 13:01:58 +01:00
daboe01andGitHub 9f092a3c2e Merge pull request #3201 from daboe01/cptexctview-smartpaste-fix
fixed: smartpaste crash
2026-03-13 16:04:15 +01:00
daboe01 5b820b270c fixed: smartpaste crash 2026-03-13 16:03:30 +01:00
daboe01 da2c89daf3 Revert "stop tracking build artifacts"
This reverts commit ea425ae4c4.
2026-03-11 06:39:06 +01:00
daboe01 ea425ae4c4 stop tracking build artifacts 2026-03-11 06:35:24 +01:00
daboe01 37a6e8fde5 Merge remote-tracking branch 'upstream/main' 2026-03-11 06:29:40 +01:00
daboe01andGitHub 0274d9fee7 Merge pull request #3199 from daboe01/fixed-textsizing-via-canvas-not-functional-anymore
fixed: canvas text sizing is not functional anymore
2026-03-11 06:29:08 +01:00
daboe01 f223644640 fixed: canvas text sizing is not functional anymore 2026-03-11 06:26:10 +01:00
daboe01andGitHub 0aa4607d8e Merge pull request #3191 from daboe01/fixed-CPTextField-called-error-method-called-3-times
Fixed: multiple didFailToFormatString delegate calls in CPTextField (#1546)
2026-03-08 21:28:57 +01:00
daboe01andGitHub 37b68bb166 Merge pull request #3194 from daboe01/fixed-CPTokenField-autocomplete-does-not-work-when-placed-in-toolbar
fixed: CPTokenField autocomplete when placed in a CPToolbar
2026-03-08 21:27:59 +01:00
daboe01andGitHub ae4eef5d16 Merge pull request #3156 from daboe01/cpoutlineview-delegate-fix
fixed: CPOutlineView should be its own tableView delegate and datasource
2026-03-08 21:20:09 +01:00
daboe01 fdba5f55c5 fixed> syntax error 2026-03-08 21:16:54 +01:00
daboe01 bfeaabc038 Merge remote-tracking branch 'upstream/main' into cpoutlineview-delegate-fix 2026-03-08 21:13:47 +01:00
daboe01andGitHub a32b2d5f73 Merge pull request #3197 from daboe01/outlineview-fixes
removed: dead code
2026-03-08 21:09:20 +01:00
daboe01 9f4db09bec removed: dead code 2026-03-08 21:07:08 +01:00
daboe01 58fe3d3d27 Merge remote-tracking branch 'upstream/main' 2026-03-08 21:03:35 +01:00
daboe01andGitHub 28303a7893 Merge pull request #3196 from daboe01/new-CPTreeController
New: CPTreeController
2026-03-08 19:56:44 +01:00
daboe01 031e5e7ca3 fixed: selection issues 2026-03-08 19:52:58 +01:00
daboe01 c70079ae71 improved: bindings to CPOutlineView 2026-03-08 18:03:52 +01:00
daboe01 8ac2b94400 refac 2026-03-07 21:48:10 +01:00
daboe01 b1f2fb44a8 new: CPOutlineView bindings support 2026-03-07 21:43:41 +01:00
daboe01 cd4c3cffe0 fixed: test cases 2026-03-07 20:05:50 +01:00
daboe01 98c01b88e2 fixed various binding issues 2026-03-07 20:00:09 +01:00
daboe01 4970a2742d fixed: recursive tree building 2026-03-07 19:47:31 +01:00
daboe01 10d18e35ed formatting 2026-03-07 19:28:04 +01:00
daboe01 12960f8160 new: testcase 2026-03-07 19:27:01 +01:00
daboe01 622d738b44 new: CPTreeController 2026-03-07 19:25:32 +01:00
daboe01 4e5c0aed67 formatting 2026-03-07 17:29:49 +01:00
daboe01 96d3ad2c47 fixed: CPTokenField autocomplete when placed in a CPToolbar 2026-03-07 17:11:25 +01:00
daboe01 c519784ecc New: support for right-aligned buttons in CPButtonBar (#745) 2026-03-07 16:17:36 +01:00
daboe01 d0d7033f63 Fixed: timing of CPControlTextDidBeginEditingNotification (#1941) 2026-03-07 14:53:44 +01:00
daboe01 9aaa565657 fixed: accidental code spillover 2026-03-07 14:40:34 +01:00
daboe01 50e98d1335 Merge remote-tracking branch 'upstream/main' 2026-03-07 14:37:32 +01:00
daboe01 837df80b64 fixed: accidental code spill over 2026-03-07 14:33:31 +01:00
daboe01 f7a27cf905 fixed: delegate method was called more than once by textfield 2026-03-07 14:26:51 +01:00
daboe01andGitHub da35d6b6ed Merge pull request #3190 from daboe01/textview-copypaste-fix
Use setTimeout to prevent flickering on copy/paste
2026-03-03 18:19:59 +01:00
daboe01 fca30325e4 Use setTimeout to prevent flickering on copy/paste 2026-03-03 18:18:32 +01:00
daboe01 8f48d01c33 removed: enabled/disabled drawing emulation via alpha 2026-02-28 19:37:59 +01:00
daboe01 89d345dfdc fixed: added missing isActive 2026-02-28 19:35:51 +01:00
daboe01 1bbf87e2cb fixed: testcases 2026-02-28 18:26:47 +01:00
daboe01 ec9c658565 fixed: acceptsFirstResponder was uncoditionally true 2026-02-28 17:07:02 +01:00
daboe01 8db24fcaf6 new: testcases from enquora 2026-02-28 16:46:34 +01:00
daboe01 bce326de9d fixed: Colorwell did not reflect enabled state visually 2026-02-28 15:57:19 +01:00
daboe01andGitHub 676ddc4601 Merge pull request #3189 from daboe01/fixed-key-duplication-on-alt
fixed: key duplication in textview on ALT
2026-02-23 14:25:19 +01:00
daboe01 19cceb4dd9 fixed: key duplication in textview on ALT 2026-02-23 14:24:09 +01:00
daboe01andGitHub f31ac7cf21 Merge pull request #3188 from daboe01/windowcontenviewfix
Fixed: CPWindow setContentView: overlapping the toolbar
2026-02-22 16:30:25 +01:00
daboe01 802c644d94 Fixed: CPWindow setContentView: overlapping the toolbar 2026-02-22 14:54:46 +01:00
daboe01andGitHub 749909b23c Merge pull request #3187 from daboe01/fixed_keyequivalent_dom_bubbling_issue
fixed: menu key support conflicting with browser native menu keys
2026-02-22 11:32:33 +01:00
daboe01 df594ae48e fixed: copy/paste issue 2026-02-21 18:48:19 +01:00
daboe01 980e2f2c91 fixed: dom protection was missing 2026-02-21 12:08:47 +01:00
daboe01 ea47767bb8 fixed: menu key support conflicting with browser native menu keys 2026-02-21 11:51:25 +01:00
daboe01 0b83c20648 fixed: life update 2026-02-15 19:53:17 +01:00
daboe01 0f9cadf8d3 fixed: life update 2026-02-15 19:53:00 +01:00
daboe01 c82e5750f0 fixed: the first version was not functional 2026-02-15 18:28:20 +01:00
daboe01 2ff5eb39eb Fix CPColorWell drag-and-drop and responder chain integration 2026-02-15 12:32:13 +01:00
daboe01andGitHub e216f0a355 Merge pull request #3184 from daboe01/fixed-wrong-event-factory-cover-method
fixed: faulty event factory covermethod
2026-02-06 16:17:17 +01:00
daboe01 0e86264837 fixed: faulty event factory covermethod 2026-02-06 16:16:08 +01:00
daboe01andGitHub 57d0750f3c Revise README for updated application demos and tutorials
Updated links and descriptions in the README to reflect current resources.
2026-02-01 21:02:42 +01:00
daboe01andGitHub 3be864e50f Merge pull request #3180 from daboe01/cptextview-changefont-fix
fixed: crash when changing typing attributes without selection
2026-02-01 19:43:50 +01:00
daboe01 d583403195 fixed: crash when changing typing attributes without selection 2026-02-01 19:43:17 +01:00
daboe01andGitHub 946362f0d1 Merge pull request #3179 from daboe01/calendarstepper-fix
fixed: prevent timezone shift from reverting month navigation in _CPD…
2026-02-01 19:30:54 +01:00
daboe01 7ee4003840 fixed: prevent timezone shift from reverting month navigation in _CPDatePickerCalendar 2026-02-01 19:29:59 +01:00
daboe01andGitHub f2bebcb2e6 Merge pull request #3178 from daboe01/improved-font-panel
new: font preview in fontpanel
2026-01-31 21:17:15 +01:00
daboe01 e413f3a9fd fixed: column sizing 2026-01-31 21:12:35 +01:00
daboe01 0c4d33dccc new: font preview in fontpanel 2026-01-31 18:21:41 +01:00
daboe01andGitHub 4af8fcc008 Merge pull request #3177 from daboe01/menushortcut-textviewfix
fixed: keystrokes from menu shortcuts are inserted into textviews
2026-01-30 22:41:47 +01:00
daboe01 6800472725 fixed: keystrokes from menu shortcuts are inserted into textviews 2026-01-30 22:40:35 +01:00
daboe01andGitHub e6b9b9cfbd Merge pull request #3176 from daboe01/fixed-cpdatepicker-relied-on-deprecated-keycode-property
Fixed cpdatepicker relied on deprecated keycode property
2026-01-29 19:14:37 +01:00
daboe01 9dfaf293f2 Removed: hardcoded KeyCodes as they are unreliable 2026-01-29 19:12:17 +01:00
daboe01 fffcc85486 fixed: cpdatepicker relied on deprecated keycode property 2026-01-29 19:06:35 +01:00
daboe01andGitHub b349b23b81 Merge pull request #3175 from daboe01/fontmanager-font-trait-fix
fixed: addFontTrait
2026-01-28 21:41:36 +01:00
daboe01 886a4a422f fixed: addFontTrait 2026-01-28 21:41:15 +01:00
daboe01andGitHub d8dd3d0660 Merge pull request #3174 from daboe01/new-texview-format-action-methods
new: CPTextView action methods for format menu
2026-01-28 21:24:12 +01:00
daboe01 f19901c214 new: action methods for format menu 2026-01-28 21:23:03 +01:00
daboe01andGitHub e198fbdc40 Merge pull request #3122 from daboe01/fixed-keycode-management-in-cptextview
fixed: Keyboard-event handling is based on the deprecated charCode
2026-01-28 20:57:07 +01:00
daboe01andGitHub 3dd97c0b49 Merge pull request #3135 from daboe01/ipad-double-tap-support
improved: double click emulation by double tapping
2026-01-28 20:56:34 +01:00
daboe01andGitHub b93aa732d2 Merge pull request #3173 from daboe01/fixed-datepicker-nextkeyview-loop
fixed: keyview loop trap
2026-01-28 19:39:04 +01:00
daboe01 16d8780812 fixed: keyview loop trap 2026-01-28 19:38:29 +01:00
daboe01andGitHub febd1260cd Merge pull request #3172 from daboe01/cptext-fix
fixed: acceptsFirstMouse was missing
2026-01-28 19:24:57 +01:00
daboe01 45162a3fcb fixed: acceptsFirstMouse was missing 2026-01-28 19:24:29 +01:00
daboe01andGitHub b598e80c58 Merge pull request #3171 from daboe01/textual-picker-key-navi-fix
Fixed: CPDatePicker tab navigation when elements are hidden
2026-01-28 07:28:01 +01:00
daboe01 41648b8605 Fixed: CPDatePicker tab navigation when elements are hidden 2026-01-28 07:26:02 +01:00
daboe01andGitHub 6b285cd7f0 Merge pull request #3170 from daboe01/fontpanel-fixes
fixed: fontpanel was crashing
2026-01-27 20:04:15 +01:00
daboe01 a5e9d65852 fixed: fontpanel was crashing 2026-01-27 20:03:05 +01:00
daboe01andGitHub 94195237d9 Merge pull request #3169 from daboe01/calayer-fixes
fixed: group animation semantics and test
2026-01-26 07:18:24 +01:00
daboe01 b8f63166cd fixed: group animation semantics and test 2026-01-26 07:18:02 +01:00
daboe01andGitHub fc91326a5f Merge pull request #3147 from daboe01/new-caanimationgroup
New: CAAnimationGroup and implement timer-based animations in CALayer
2026-01-25 19:34:34 +01:00
daboe01 0fc2ed7081 new: manual test 2026-01-25 19:33:33 +01:00
daboe01 8433d4a1d1 new: support for kCAMediaTimingFunctionEaseInEaseOut 2026-01-25 17:25:17 +01:00
daboe01andGitHub 2615577173 Fix Kitchen Sink demo link in README
Updated the link for the Kitchen Sink demo in the README.
2026-01-19 07:50:18 +01:00
daboe01 5aa315e190 fixed: syntax errors 2026-01-16 17:17:00 +01:00
daboe01 d180a25f2e new: CPRulerview and friends 2026-01-16 17:08:50 +01:00
daboe01andGitHub 8d61a5fbe1 Correct release year in project status
Update project status release year from 2025 to 2026.
2025-12-31 09:18:30 +01:00
daboe01 b2cd64b956 Revert "fixed: test-cases"
This reverts commit 53488c0adb.
2025-12-28 21:15:13 +01:00
daboe01 53488c0adb fixed: test-cases 2025-12-28 21:14:29 +01:00
daboe01andGitHub ca9b8a78b2 Merge pull request #3167 from michaelbach/main
Fix #3166: zero width/height for svg images
2025-12-27 22:15:15 +01:00
Michael BachandGitHub c05b900fe6 improve formatting of fox 2025-12-27 21:58:00 +01:00
Michael BachandGitHub 907450a63d Fix zero width/height for svg images 2025-12-27 21:38:14 +01:00
daboe01andGitHub 6b1e308dab Merge pull request #3157 from daboe01/fixed-does-not-properly-transform-the-DOMElements-bounds-if-the-view-has-a-non-zero-origin
Fixed: [CPView addSubview:] was ignoring bounds origin for DOM positioning
2025-12-26 12:34:21 +01:00
daboe01andGitHub e28df7d7d7 Merge pull request #3164 from daboe01/cpmenu-sticky-fix
Cpmenu top level item sticky selection fix
2025-12-26 12:21:47 +01:00
daboe01andGitHub 84d3a820f2 Merge pull request #3152 from daboe01/new-menu-icon-placement-issue-fix
new: Left-align menu bar icon and title
2025-12-26 12:16:01 +01:00
daboe01 dda325b51b fixed: wrong target for bounds 2025-12-26 12:09:15 +01:00
daboe01 f0e29069a9 fixed: sticky main menu selection issue 2025-12-26 12:06:18 +01:00
daboe01andGitHub 4730c23a97 Merge pull request #3154 from daboe01/Fixed-CPMenu-items-appearing-on-the-wrong-side-of-the-menu-bar
Fixed: CPMenu items appearing on the wrong side of the menu bar
2025-12-26 11:56:24 +01:00
daboe01andGitHub 593d098cc1 Merge pull request #3153 from daboe01/fixed-Main-menus-with-no-items-appear-in-the-menu-bar
new: Hide main menu items that do not have submenus
2025-12-26 11:51:52 +01:00
daboe01andGitHub 2e15f351a9 Merge branch 'main' into fixed-Main-menus-with-no-items-appear-in-the-menu-bar 2025-12-26 11:48:12 +01:00
daboe01 14a195592a new manual test 2025-12-26 11:43:54 +01:00
daboe01andGitHub 7c81471a3a Merge pull request #3149 from daboe01/new-CPMenu-Item-Change-Reflection
fixed: Update CPMenu immediately when a CPMenuItem changes
2025-12-26 11:36:42 +01:00
daboe01 42ea1a5972 new: manual test 2025-12-26 11:34:40 +01:00
daboe01andGitHub 1da107c404 Merge pull request #3160 from daboe01/new-CPStackView
new: CPStackView
2025-12-26 11:21:14 +01:00
daboe01 b2ada8a76f formatting 2025-12-26 11:16:46 +01:00
daboe01 5ac90c163a new: manual test 2025-12-26 11:10:44 +01:00
daboe01andGitHub 53e7d14e1f Merge pull request #3161 from daboe01/fixed-CPRuleEditor-did-not-resize-the-rhs-view-after-the-ruleeditor-has-been-shrunken
Fixed: CPRuleEditor rows did not resize after being shrunken
2025-12-26 11:01:50 +01:00
daboe01andGitHub 186e2e6130 Merge pull request #3150 from daboe01/fixed-when-performing-key-equivalent-for-a-MenuItem,-the-top-most-menu-item-should-highlight
new: Highlight top-level menu item when performing key equivalent
2025-12-26 10:53:18 +01:00
daboe01andGitHub fefa603b45 Merge pull request #3158 from daboe01/fixed-CPDocument-sends-windowWillClose-message-twice
fixed: CPDocument sends windowWillClose: message twice
2025-12-26 10:44:27 +01:00
daboe01 99cf8e6387 improved logic 2025-12-26 10:42:41 +01:00
daboe01andGitHub 53b4fe4fdc Merge pull request #3162 from daboe01/fixed-Toolbar-items-show-now-visual-feedback-when-clicked
Fixed: Toolbar items show now visual feedback when clicked
2025-12-26 10:21:34 +01:00
daboe01andGitHub 6773df15b9 Merge pull request #3163 from daboe01/new-slideback-animation-after-failed-drag
new: slideback animation after failed drag
2025-12-26 10:12:32 +01:00
daboe01 bc64d147a8 fixed: used wrong approach 2025-12-25 19:56:15 +01:00
daboe01 569f93549e new: slideback animation after failed drag
new: slideback animation after failed drag
2025-12-25 17:23:54 +01:00
daboe01andGitHub 2045d1eaeb Add cappuccino bin directory to .gitignore 2025-12-09 17:56:47 +01:00
daboe01 a2595093f8 Fixed: Toolbar items show now visual feedback when clicked 2025-12-07 21:23:07 +01:00
daboe01 9a9c9b8481 Fixed: CPRuleEditor rows did not resize after being shrunken 2025-12-07 19:46:11 +01:00
daboe01 99ec1af301 new: CPStackView 2025-12-07 18:14:52 +01:00
daboe01 f3fe384df1 fixed: CPDocument sends windowWillClose: message twice 2025-12-07 11:28:53 +01:00
daboe01 c7f3dd5b0f Fixed: [CPView addSubview:] was ignoring bounds origin for DOM positioning 2025-12-07 11:21:11 +01:00
daboe01 547be633b6 fixed: CPOutlineView should be its own tableView delegate and datasource 2025-12-07 09:18:36 +01:00
daboe01 a88002861b Fixed: CPMenu items appearing on the wrong side of the menu bar 2025-12-06 19:48:18 +01:00
daboe01 395022d652 new: Hide main menu items that do not have submenus 2025-12-06 19:39:53 +01:00
daboe01 dd24de5daf new: Left-align menu bar icon and title 2025-12-06 19:32:52 +01:00
daboe01 3c02cf5358 new: Highlight top-level menu item when performing key equivalent 2025-12-06 19:15:59 +01:00
daboe01 bf0f0ab509 fixed: Update CPMenu immediately when a CPMenuItem changes 2025-12-06 19:09:55 +01:00
daboe01 3d0639b6dc fixed: use requestAnimationFrame instead of setTimeout 2025-11-21 08:18:41 +01:00
daboe01 ed50f3b654 add test 2025-11-20 22:07:44 +01:00
daboe01 25d186bf5c New: CAAnimationGroup and implement timer-based animations in CALayer 2025-11-20 19:58:24 +01:00
daboe01andGitHub e02107102c Merge pull request #3142 from daboe01/new-CPSplitViewController
new: CPSplitViewController and CPSplitViewItem
2025-11-19 19:19:50 +01:00
daboe01andGitHub 1376466939 Merge pull request #3136 from daboe01/fixed-CAKeyframeAnimation-missing-properties-and-methods
fixed: CAKeyframeAnimation missing properties and methods
2025-11-19 19:18:05 +01:00
daboe01andGitHub f2c05c4841 Merge pull request #3141 from daboe01/fixed-scrollview-missing-notifications
fixed: CPScrollView did not send some notifications
2025-11-19 19:16:29 +01:00
daboe01andGitHub 17136d23f9 NEW: for..of iterator for CPDictionary (#3113) 2025-09-25 07:27:17 +02:00
daboe01 812f69f4a7 fixed: missing splitView:effectiveRect:forDrawnRect:ofDividerAtIndex: 2025-09-24 14:20:37 +02:00
Martin CarlbergandGitHub 7f286129de Create main-branch-publish-manual-tests.yaml 2025-09-17 16:21:12 +02:00
Martin CarlbergandGitHub b92459ba38 Merge pull request #3121 from daboe01/new-CPView-is-missing-rotateByAngle-method
Fixed: CPView was missing rotateByAngle: method
2025-09-12 12:56:13 +02:00
daboe01 51e9737114 improved: documentation 2025-09-11 18:26:53 +02:00
Martin CarlbergandGitHub 66051b066f Updated to use latest version of the runtime 2025-09-09 13:00:57 +02:00
daboe01 a3cb43bad8 fixed: missing api issues 2025-09-06 08:58:59 +02:00
Martin CarlbergandGitHub ba4f0f7e4d Merge pull request #3140 from daboe01/new-CPMapTable
New: CPMapTable.j
2025-09-05 11:42:10 +02:00
Martin CarlbergandGitHub ada5887deb Node version 25 and 26 are not yet available on GitHub 2025-09-05 11:39:01 +02:00
Martin CarlbergandGitHub af148b5008 Tests run with newer Node versions. Older is removed 2025-09-05 11:33:35 +02:00
daboe01 205b645de8 fixed: import statement for CPViewController 2025-09-04 12:14:06 +03:00
daboe01 f1f0703619 fixed: imports 2025-09-04 12:11:03 +03:00
daboe01 dc23fe49af new: unit tests 2025-09-04 11:54:06 +03:00
daboe01 44de0334b7 improved: documentation 2025-09-04 11:49:59 +03:00
daboe01 de96e0c22c fixed: missing import 2025-09-03 15:57:42 +03:00
daboe01 2f5593390e new: CPSplitViewController and CPSplitViewItem 2025-09-03 15:54:40 +03:00
daboe01 0ef1239283 fixed: CPScrollView did not send some notifications 2025-09-03 15:21:44 +03:00
daboe01 258dd7ba38 fixed: several css issues 2025-09-03 13:30:47 +03:00
daboe01 3e38560620 fixed: testcase 2025-09-03 11:09:22 +03:00
daboe01 1826a14afe improved: testcase 2025-09-03 10:39:11 +03:00
daboe01 e46fc1dc77 fixed: blue box was invisible 2025-09-03 08:24:19 +03:00
daboe01 debb43c1ca fixed: timing functions 2025-09-02 18:25:35 +03:00
daboe01 da8bf0abe1 fixed: property issues 2025-09-02 18:12:04 +03:00
daboe01 92b7583f9f fixed: SVGString 2025-09-02 17:38:10 +03:00
daboe01 8950c4f0e3 fixed: variable scope issues 2025-09-02 17:25:32 +03:00
daboe01 9442f6b7d7 new: manual tests 2025-09-02 17:04:51 +03:00
daboe01 c14e08633d fixed: testcase 2025-09-02 15:51:40 +03:00
daboe01 dfbf07b10c fixed: test suite 2025-09-02 15:44:39 +03:00
daboe01 b2b29c928f fixed: testcases 2025-09-02 15:41:34 +03:00
daboe01 c56448a29c fixed: keyEnumerator 2025-09-02 15:38:00 +03:00
daboe01 42a00edbd8 fixed: objectEnumerator 2025-09-02 15:35:53 +03:00
daboe01 18bb6daf10 fixed: nil and operator issues 2025-09-02 15:34:02 +03:00
daboe01 799cb189c9 new: test cases 2025-09-02 15:28:14 +03:00
daboe01 1112e658d4 removed: dealloc 2025-09-02 15:22:31 +03:00
daboe01 23cdee2f22 new: CPMapTable.j 2025-08-29 21:39:04 +03:00
daboe01 13688507dc new: restore old CPEvent factory method for backwards compatibility 2025-08-07 10:01:03 +02:00
daboe01 568d0faef6 fixed: missing parameter to CPEvent initializer 2025-08-06 17:19:14 +02:00
daboe01 41035a5b10 new: Implement missing CAKeyframeAnimation properties 2025-08-05 19:27:45 +02:00
daboe01 4cb0a87c68 fixed: syntax error 2025-08-03 22:26:09 +02:00
daboe01 cab5683cd9 fixed: CAKeyframeAnimation missing properties and methods 2025-08-03 22:22:42 +02:00
daboe01andGitHub 4d20007a9b Merge pull request #3133 from daboe01/collectionview-erroneous-offset-before-the-first-row
fixed: CPCollectionview displayed an erroneous offset before the first row of content
2025-08-02 16:55:25 +02:00
daboe01 56eb096903 fixed: accidental delete 2025-07-19 14:44:38 +02:00
daboe01 c077863e7a fixed: accidental changes 2025-07-19 14:29:46 +02:00
daboe01 49e5cb64ad Revert "removed: unwanted files"
This reverts commit 7e99e3ca20.
2025-07-19 14:23:52 +02:00
daboe01 7e99e3ca20 removed: unwanted files 2025-07-19 14:21:10 +02:00
daboe01 0b4d156b5b fixed: keyboard issues on ipad 2025-07-19 14:19:46 +02:00
daboe01 76ba6a4e64 fixed: ipad issue with backspace 2025-07-19 13:53:28 +02:00
daboe01 46bab97b49 fixed: backspace issue on ipad 2025-07-19 13:51:58 +02:00
daboe01 2a32934f86 fixed: ipad backspace issue 2025-07-19 09:40:14 +02:00
daboe01 bda5594f0c fixed: ipad softkeyboard trigger 2025-07-18 22:05:12 +02:00
daboe01 134688ef5c improved: double click emulation by double tapping 2025-07-18 21:48:09 +02:00
daboe01 e3fd7a295c fixed: ipad keyboard does not show 2025-07-17 19:41:48 +02:00
daboe01andGitHub c2d07b7b06 Merge pull request #3120 from daboe01/nil-in-cpviewcontroller-fix
fixed: subview's window setting did not happen BEFORE _setSuperview
2025-07-17 19:27:36 +02:00
daboe01 d9f31e8543 Revert "new: UIBuilder demo application"
This reverts commit 38f523b3c2.
2025-07-15 18:55:23 +02:00
daboe01 38f523b3c2 new: UIBuilder demo application 2025-07-15 18:54:09 +02:00
daboe01andGitHub 69a353d969 Update README.markdown 2025-07-15 07:21:35 +02:00
daboe01andGitHub 945e70c0ff Update README.markdown 2025-07-14 19:35:15 +02:00
daboe01andGitHub 277e9bee7b new: link to example app 2025-07-14 19:34:01 +02:00
daboe01andGitHub 95614d567c Update README.markdown 2025-07-14 19:14:55 +02:00
daboe01andGitHub 6a488af1ca Update README.markdown demo app 2025-07-14 19:14:17 +02:00
daboe01 a0f9cb5183 formatting 2025-07-13 19:55:34 +02:00
daboe01 1a74b4c692 fixed: CPCollectionview displayed an erroneous offset before the first row of content 2025-07-13 19:53:33 +02:00
daboe01andGitHub 746311c6cf Merge pull request #3124 from daboe01/fixed-touch-scrolling-had-no-momentum-support
fixed: touch scrolling did not support momentum
2025-07-09 22:23:42 +02:00
daboe01andGitHub 876aaa483e Merge pull request #3132 from daboe01/fixed-submenu-placement-issue
fixed: CPMenu Submenu Placement Issue
2025-07-09 22:22:28 +02:00
daboe01 570c31f248 fixed: richtext copypaste 2025-07-06 14:04:35 +02:00
daboe01 b3695ed710 formatting 2025-07-06 12:48:31 +02:00
daboe01 e9dddaad20 new: manual test 2025-07-06 10:58:19 +02:00
daboe01 2b254a6f16 fixed: CPMenu Submenu Placement Issue 2025-07-05 19:01:57 +02:00
daboe01andGitHub e9022d045c Merge pull request #3123 from daboe01/new-viewcontroller-responder-chain-issue
fixed: CPViewController doesn't participate fully in responder chain
2025-07-05 16:41:23 +02:00
daboe01 c70ea739ec fixed: unittest was overambitious 2025-07-05 16:39:10 +02:00
daboe01 85da0164ba fixed: designated initialiser was not used 2025-07-05 16:32:22 +02:00
daboe01 89b262103f Revert "fixed: _view was assigned bypassing the accessors"
This reverts commit cfcf8bc567.
2025-07-05 16:28:16 +02:00
daboe01 2c5744be57 formatting 2025-07-05 16:25:00 +02:00
daboe01 cfcf8bc567 fixed: _view was assigned bypassing the accessors 2025-07-05 16:21:19 +02:00
daboe01 d1acff5b44 improved setNextResponder assignment 2025-07-05 16:13:50 +02:00
daboe01 2add37983d new: unittest for responderchain 2025-07-05 15:56:44 +02:00
daboe01 ce00fdaae8 fixed: copy-paste-issues 2025-07-05 15:37:32 +02:00
daboe01 2fe29d4a26 Update .gitignore 2025-07-05 13:36:14 +02:00
daboe01andGitHub fc582d8125 Merge pull request #3131 from daboe01/fixed-CPTimeZone-initWithName-does-not-return-the-timeZone-with-the-good-daylight-saving-
fixed: CPTimeZone initWithName: does not return the timeZone with the good daylight saving
2025-07-05 13:16:56 +02:00
daboe01andGitHub c41307dc37 Merge branch 'main' into fixed-CPTimeZone-initWithName-does-not-return-the-timeZone-with-the-good-daylight-saving- 2025-07-05 12:58:01 +02:00
daboe01 27280f6da1 Update CPMenuTest.j 2025-07-05 12:35:43 +02:00
daboe01 ce7fec170a Update CPEventTest.j 2025-07-05 12:33:21 +02:00
daboe01 98ef6fd902 Update CPEventTest.j 2025-07-05 12:30:58 +02:00
daboe01 1c4d9b1d8f Update CPButtonTest.j 2025-07-05 12:29:11 +02:00
daboe01 b8b988c10b Update CPResponderTest.j 2025-07-05 09:04:26 +02:00
daboe01 0eb87f85b4 fixed: accidentally removed files 2025-07-05 09:01:12 +02:00
daboe01 a0136e2144 removed accidentally committed stuff 2025-07-05 08:58:57 +02:00
daboe01 d12770c65d fixed: testcase for keyevent 2025-07-05 08:57:29 +02:00
daboe01 b0186bf4f2 fixed: smart copy/paste 2025-07-05 08:49:08 +02:00
daboe01 9e15700f93 cleanup stuff 2025-07-03 22:10:57 +02:00
daboe01 6b2494ede4 fixed: arrow navigation 2025-07-02 19:54:52 +02:00
daboe01 266965f506 improved: native input manager 2025-07-02 07:50:46 +02:00
daboe01 1226f5120c improved: deadkey management 2025-07-01 22:55:53 +02:00
daboe01 f4ac0630b8 formatting 2025-07-01 22:25:20 +02:00
daboe01 b3cb22caef more modernisations 2025-07-01 22:16:33 +02:00
daboe01 b1d96dfdee improved reliance of key 2025-07-01 21:53:53 +02:00
daboe01 25732f2f52 fixed: second half of the fix was missing 2025-07-01 21:42:58 +02:00
daboe01 5ecdaafadf fixed: arrow keys did not work 2025-07-01 20:23:29 +02:00
daboe01 223bbc86e1 fixed: arrow keys did not work 2025-07-01 19:42:12 +02:00
daboe01 0867e718d1 Revert "fixed: arrow keys did not work"
This reverts commit c814b5daed.
2025-07-01 19:40:47 +02:00
daboe01 c814b5daed fixed: arrow keys did not work 2025-07-01 19:36:02 +02:00
daboe01andGitHub 0e8ee2f7d5 Merge pull request #3130 from daboe01/fixed-NSDateFormatter-unable-to-parse-dates-in-Javascript-Date.toISOString-format
fixed: NSDateFormatter was unable to parse dates in Javascript Date.toISOString format
2025-07-01 16:21:30 +02:00
daboe01andGitHub 4b22d67f42 Merge pull request #3117 from daboe01/null-marker-versus-nil-issue
fixed: CPObjectController returns 'null marker' instead of nil
2025-07-01 16:19:58 +02:00
daboe01andGitHub edf0a61653 Merge pull request #3119 from daboe01/CPTimeZone-fix
fixed: time zone methods return nil in Chrome and Firefox
2025-07-01 16:19:14 +02:00
daboe01andGitHub e50ede815c Merge pull request #3128 from daboe01/fixed-missing-documentation-of-CPOutlineView
fixed: missing documentation of CPOutlineViewDataSource protocol
2025-07-01 16:15:58 +02:00
daboe01andGitHub 6d2888e1c6 Merge pull request #3129 from daboe01/textfieldsizefix
fixed: nonsense addition accidentally turning the size into a string
2025-07-01 16:14:17 +02:00
daboe01 b6461ba4fb fixed: tests 2025-06-29 20:28:43 +02:00
daboe01 25708554fd fixed: typo 2025-06-29 20:22:38 +02:00
daboe01 74f204e6fa fixed: tests and implementation 2025-06-29 20:20:58 +02:00
daboe01 263ec36d80 fixed: test 2025-06-29 19:42:12 +02:00
daboe01 bb6b738fc7 fixed: CPTimeZone initWithName: does not return the timeZone with the good daylight saving 2025-06-29 19:34:56 +02:00
daboe01 fb1c2732ac fixed test 2025-06-29 18:00:19 +02:00
daboe01 1fb42fc119 fixed: NSDateFormatter was unable to parse dates in Javascript Date.toISOString format 2025-06-29 17:51:08 +02:00
daboe01 23f9170b90 fixed: nonsense addition accidentally turning the size into a string 2025-06-29 17:18:42 +02:00
daboe01 7be2005fae fixed: wrong documentation 2025-06-29 15:50:05 +02:00
daboe01 afadec517b fixed: missing documentation of CPOutlineViewDataSource protocol 2025-06-29 15:20:23 +02:00
daboe01 da5a12c938 formatting 2025-06-26 17:54:05 +02:00
daboe01 a9aa15d63f fixed CALayer class name 2025-06-26 17:52:11 +02:00
daboe01 e6d2d840e9 fixed: CALayer autocreate 2025-06-26 17:51:26 +02:00
daboe01 4963127c31 formatting 2025-06-26 17:49:36 +02:00
daboe01 62c7ac891f fixed: manual test 2025-06-26 17:43:51 +02:00
daboe01 e4b594f716 fixed: manual test 2025-06-26 17:42:03 +02:00
daboe01 0835f18721 new: manual test 2025-06-26 10:01:37 +02:00
daboe01 8231cbc55c fixed: wrong core animation symbol names 2025-06-26 08:01:45 +02:00
daboe01andGitHub b63cde48c6 Merge pull request #3111 from daboe01/CPImage_Drawing
new: CPImage method drawAtPoint:fromRect: operation: fraction:
2025-06-25 19:26:00 +02:00
daboe01andGitHub 30150178db Merge pull request #3116 from daboe01/doxygen-fix
fixed: make docs was not working in node version
2025-06-25 19:25:20 +02:00
daboe01andGitHub 61eebfcf46 Merge pull request #3118 from daboe01/fixed-capp-gen--l-issue
fixed: node fs issues in Generate.j
2025-06-25 19:24:22 +02:00
daboe01andGitHub 40154ed86e Update README.markdown 2025-06-25 10:58:27 +02:00
daboe01andGitHub 23605ff2b0 Merge pull request #3115 from daboe01/better-readme
Better readme
2025-06-25 10:56:42 +02:00
daboe01 964eb00ce1 formatting 2025-06-25 10:54:50 +02:00
daboe01 930c1e874c reordering faq 2025-06-25 10:54:08 +02:00
daboe01 1d96b09129 switch to passive voice 2025-06-25 10:53:41 +02:00
daboe01 9c6ce6d200 fixed: missing _momentumScrollStep 2025-06-23 09:40:27 +02:00
daboe01 a1aad41a6b fixed: missing ivars 2025-06-23 09:30:52 +02:00
daboe01 38cb3cb1a2 fixed: touch scrolling did not support momentum 2025-06-23 09:19:02 +02:00
daboe01 751e1d27a6 Update CPPlatformWindow+DOMKeys.j 2025-06-23 09:02:28 +02:00
daboe01 36d4bd32b5 Update CPPlatformWindow+DOM.j 2025-06-23 08:58:12 +02:00
daboe01 d835cf0c00 fixed: CPViewController doesn't participate fully in responder chain 2025-06-23 07:32:49 +02:00
daboe01 6ce85bae7e fixed: Keyboard-event handling is based on the deprecated charCode 2025-06-23 07:21:08 +02:00
daboe01 7d1df21b0e removed unused variable 2025-06-22 22:57:59 +02:00
daboe01 bbb87b4970 removed: wrong code 2025-06-22 22:56:35 +02:00
daboe01 14d9bb795d fixed: wrong code 2025-06-22 22:55:41 +02:00
daboe01 2eb1d1bfb9 fixed: CPView was missing rotateByAngle: method 2025-06-22 22:51:26 +02:00
daboe01 bae78b5055 formatting 2025-06-22 21:52:57 +02:00
daboe01 c0705448cd new: testcase 2025-06-22 21:47:26 +02:00
daboe01 e0a8fa3fde formatting 2025-06-22 21:34:19 +02:00
daboe01 64f4b0fc13 fixed: accidental removal of commented out code 2025-06-22 21:32:48 +02:00
daboe01 c28a05e968 fixed logic 2025-06-22 21:28:37 +02:00
daboe01 170d6ac2e7 fixed wrong logic 2025-06-22 21:19:03 +02:00
daboe01 64cc36a1ac new logic 2025-06-22 21:06:47 +02:00
daboe01 af61bcb93f fixed: wrong logic 2025-06-22 20:45:55 +02:00
daboe01 1f24e89c1b fixed wrong logic 2025-06-22 20:35:11 +02:00
daboe01 b12aee53d3 fixed: subview's window setting mus happen BEFORE _setSuperview 2025-06-22 19:47:19 +02:00
daboe01 feb8ea0316 new: test case for the new timezone abbreviation logic 2025-06-22 19:33:13 +02:00
daboe01 db51d5ca54 fixed: time zone methods return nil in Chrome and Firefox 2025-06-22 19:27:58 +02:00
daboe01 01e9f9181d fixed: node issues 2025-06-22 16:59:54 +02:00
daboe01 3a9db70426 fixed: CPObjectController returns 'null marker' instead of nil 2025-06-22 11:04:46 +02:00
daboe01 c7e850b95f fixed: accidentally removed markdown install instructions 2025-06-21 18:09:19 +02:00
daboe01 89b3fb003c formatting 2025-06-21 17:29:31 +02:00
daboe01 6abb8234fb fixed: docsetutil is nowadays missing from xcode 2025-06-21 17:26:46 +02:00
daboe01 4395663cc9 fixed: doc directories are spilled everywhere 2025-06-21 16:56:57 +02:00
daboe01 aeb982c616 formatting 2025-06-21 16:32:11 +02:00
daboe01 3fd26e681b fixed: make docs was not working in node version 2025-06-21 16:29:31 +02:00
daboe01 8bcf446187 fixed: cappuccino-cookbook was missing 2025-06-21 14:50:02 +02:00
daboe01 42d51dc808 Merge branch 'better-readme' of https://github.com/daboe01/cappuccino into better-readme 2025-06-21 13:16:12 +02:00
daboe01 b05aa3dd32 improved readme 2025-06-21 11:24:03 +02:00
daboe01 98a539657c fixed: type CGBlendMode missing 2025-06-19 09:15:08 +02:00
daboe01andGitHub 85f7125f22 New: for..of iteration for CPSet (#3114) 2025-06-18 10:22:41 +02:00
daboe01 2f8dd8c088 new: drawAtPoint:(CPPoint)point fromRect:(CPRect)fromRect operation:(CPCompositingOperation)op fraction:(CGFloat)delta 2025-06-11 14:14:30 +02:00
Martin CarlbergandGitHub 86d595b086 Fixed: Make the isa property on classes not enumerable. (#3100)
As we have a tool-free bridges between many classes and JavaScript types by setting the isa property on the type, for example CPArray and Array. Using the for...in enumeration the isa property will be included in the loop if the property is enumerable (this is the default behavior). This commit set the property as not enumerable.

Also added test case for check that the isa property on classes are not enumerable using a for...in loop.
2025-05-14 10:09:01 +02:00
daboe01andGitHub 2f563fb870 New: give nil values the chance to be transformed to true via CPNegateBoolean (#3103) 2025-04-28 15:40:57 +02:00
daboe01andGitHub c78a18dc3e Update README.markdown 2025-04-02 17:03:14 +02:00
daboe01andGitHub 0fa636dd79 Update README.markdown 2025-04-02 17:01:01 +02:00
Martin CarlbergandGitHub 7fc9fa70c1 Fixed: Github Action build with Node 22 and 23 too 2025-03-27 15:36:14 +01:00
daboe01andGitHub 9109cf3b54 Fixed: changing background color in CPTextView did not redraw properly (#3107) 2025-03-26 13:31:05 +01:00
David RichardsonandGitHub 50076efe1e Merge pull request #3106 from enquora/dark-mode-cleanup
Set default project details view (no project selected) to default colours
2025-01-27 09:45:48 -07:00
David Richardson 097785fb93 Set default project details view, when no project selected, to default colours
Remove hard-coded colours to allow dark mode support.
2025-01-27 09:41:58 -07:00
daboe01andGitHub 7e07deeb23 Update README for ECMA Script 2024 2025-01-14 15:46:42 +01:00
daboe01andGitHub e8c0c680ca Update README.markdown 2024-12-30 15:39:53 +01:00
daboe01andGitHub d9ee7b23c2 Update README.markdown 2024-12-30 15:36:34 +01:00
daboe01andGitHub 03339e3c5d Merge pull request #3102 from cappuccino/narwhal
merge of PR #1372 and #2018 into main
2024-12-30 14:05:09 +01:00
daboe01andGitHub c58c5554f9 Merge pull request #2018 from vidinoti/patch-1
Changing a CALayer in a menu callback doesn't trigger re-draw
2024-12-30 13:57:56 +01:00
daboe01andGitHub 16ae7b7e74 Merge pull request #1372 from kjamieson/scroll-fixes
If target view of a drag and drop is a CPTableView, set inset bounds based on the row height of the table
2024-12-30 13:51:00 +01:00
David RichardsonandGitHub 6d3fec88ec Merge pull request #3101 from enquora/dark-mode-cleanup
Dark mode cleanup
2024-11-13 10:48:25 -07:00
David Richardson 4b74e7da31 Increment patch component of version number to reflect dark mode support changes 2024-11-13 10:41:13 -07:00
David Richardson fa70ffc918 Clean up control colours for background and text to fully support dark mode 2024-11-13 10:34:04 -07:00
David RichardsonandGitHub fd177f4b9a Merge pull request #3072 from enquora/remove-narwhal-artifacts
Remove narwhal-specific artifacts
2024-10-17 10:01:08 -06:00
daboe01andGitHub 08c666a53f Merge pull request #3097 from cappuccino/remove-unused-local-variable-charCode
removed: unused local variable charCode
2024-10-07 19:44:13 +02:00
daboe01andGitHub 668567d6e2 Merge pull request #3094 from enquora/editorconfig
Add editorconfig file
2024-09-27 13:36:07 +02:00
David Richardson 09f92407ec Cleanup
Make settings universal, across all file types.
2024-09-26 11:08:33 -06:00
daboe01andGitHub 9dc12c0142 Merge pull request #3091 from daboe01/cptextviewcopypastedocu
added documentation to the copy paste limitation
2024-09-22 13:03:52 +02:00
David RichardsonandGitHub 2fd821ca00 Merge pull request #3096 from enquora/fix-menubar-icon-visibility
Change rendering mode for menubar inactive icon from ‘Render As’ to ‘Template Image’
2024-09-21 12:58:57 -06:00
daboe01andGitHub 03195b66bc removed: unused local variable charCode 2024-09-21 19:12:18 +02:00
David Richardson a3d906165a Change menubar inactive icon ‘Render As’ mode to ‘Template Image’
Allows macOS to automatically manage blending mode and color of menubar icon.
Modern macOS renders the menubar with translucency, making icons with some fixed colors difficult to see with some wallpapers.
This was particularly pronounced with ‘status-icon-inactive’.
Changing render mode to ‘Template Image’ allows the OS to adjust icon color and blending mode dynamically — to suit current combination of Light/Dark mode and wallpaper color.
2024-09-15 16:03:23 -06:00
David Richardson 4c1ce50dd2 Add editorconfig file
Enforce/guide compliance with Cappuccino project coding standards.
Most modern editors supported directly or via plugins.
2024-08-29 19:20:16 -06:00
daboe01 b638242e6f added documentation to the copy paste limitation 2024-08-04 15:15:34 +02:00
daboe01andGitHub 2349aacc3e Update README.markdown 2024-07-21 21:34:53 +02:00
daboe01andGitHub 1be7bbe0dc Update README.markdown 2024-07-21 21:32:22 +02:00
daboe01andGitHub ea332d5492 Update README.markdown 2024-07-21 21:30:54 +02:00
daboe01andGitHub aedede7381 Update README.markdown 2024-07-21 21:22:52 +02:00
daboe01andGitHub d4cd46c331 Update README.markdown
stringWithFormat returns autoreleased instances iirc
2024-07-21 21:22:05 +02:00
daboe01andGitHub 62450c3bb1 Update README.markdown 2024-07-21 21:19:16 +02:00
daboe01andGitHub bafba81f56 Update README.markdown 2024-07-21 21:18:07 +02:00
daboe01andGitHub 651b445c75 Update README.markdown 2024-07-21 21:17:38 +02:00
daboe01andGitHub 5adc3c6a99 Update README.markdown 2024-07-21 21:17:04 +02:00
daboe01andGitHub 4914ff8205 Update README.markdown 2024-07-21 21:14:49 +02:00
daboe01andGitHub 783205c731 Update README.markdown 2024-07-21 19:32:24 +02:00
daboe01andGitHub 00ab2d1f3a Update README.markdown 2024-07-21 19:28:21 +02:00
daboe01andGitHub 98dcbdd87a Update README.markdown 2024-07-21 19:24:58 +02:00
daboe01andGitHub 4a936a6eef README.markdown: reduced redundant introduction 2024-07-21 19:10:41 +02:00
daboe01andGitHub ef7ee1b699 Merge pull request #3089 from enquora/README-refinements
Readme refinements
2024-07-21 19:06:48 +02:00
daboe01andGitHub 4e70c1fb10 Merge branch 'main' into README-refinements 2024-07-21 19:02:52 +02:00
daboe01andGitHub 21d4fa5176 Update README.markdown 2024-07-21 13:35:07 +02:00
daboe01andGitHub f16b2e8243 Update README.markdown 2024-07-21 13:32:34 +02:00
daboe01andGitHub 4e550a22b0 fixed: markdown syntax errors 2024-07-21 13:30:34 +02:00
David Richardson c2404d3c67 Reduce references to Cocoa, annotate remaining ones to indicate it is a registered trademark 2024-07-20 09:50:18 -06:00
David Richardson f1a5467d04 Refine and re-order benefits section 2024-07-20 09:19:56 -06:00
David Richardson 512486f2e8 Increase priority/visibility of toolchain stability as a benefit 2024-07-20 08:49:52 -06:00
daboe01andGitHub f1d95fa883 Merge pull request #3087 from enquora/README-refinements
README refinements
2024-07-20 16:48:52 +02:00
David Richardson 31cf9b8527 Add missing statement terminator in code example 2024-07-20 08:43:10 -06:00
David Richardson e0e8e8c6cc Clean up porting example gone wrong, further edits to increase brevity 2024-07-20 08:39:03 -06:00
daboe01andGitHub 6653690d0c Merge pull request #3086 from daboe01/segmentedcontrol-theme-fix
fixed: 1xp glitch with CPSegmentedControl in safari
2024-07-20 13:42:08 +02:00
David Richardson e63f59d33e Update source code installation to reflex new jake dist task 2024-07-19 16:08:20 -06:00
David Richardson 3702c18df5 README refinements
General refinements.
Move npm quickstart instructions to a more prominent position.
Add licensing FAQ.
Add FAQ for porting code from Objective-C to Objective-J
2024-07-19 16:05:05 -06:00
daboe01 dfe215f34c fixed: 1xp glitch with CPSegmentedControl in safari 2024-07-19 06:36:58 +02:00
daboe01andGitHub 621a559585 Update README.markdown 2024-07-18 10:07:25 +02:00
daboe01andGitHub 61317eff71 Update README.markdown (swift vs. objective j) 2024-07-18 10:06:37 +02:00
daboe01andGitHub b314a951da Update README.markdown 2024-07-17 12:31:51 +02:00
daboe01andGitHub 1a1c105c71 Update README.markdown 2024-07-17 07:38:24 +02:00
daboe01andGitHub 3f8ee35ddb Update README.markdown 2024-07-17 07:27:03 +02:00
daboe01andGitHub d2ea74ea14 Update README.markdown 2024-07-17 06:41:35 +02:00
daboe01andGitHub 8e1fb771e0 Update README.markdown 2024-07-17 06:35:36 +02:00
daboe01andGitHub bbe5c82700 Merge pull request #3083 from michaelbach/patch-2
fixied: bindings for case  'only stop at tick marks'
2024-07-16 19:13:32 +02:00
daboe01andGitHub 95e3ff3349 wrap fix in if (_allowsTickMarkValuesOnly) 2024-07-16 19:12:04 +02:00
daboe01andGitHub 00c3b2083b formatting 2024-07-16 19:08:58 +02:00
daboe01andGitHub 8549858951 Merge pull request #3084 from cappuccino/narwhal
fixed: selection and backspace are recently broken in CPTokenField
2024-07-16 19:07:18 +02:00
daboe01andGitHub 4730c3f4b5 Update README.markdown 2024-07-16 19:06:38 +02:00
daboe01andGitHub 19ba66fd9d Merge branch 'main' into narwhal 2024-07-16 19:04:08 +02:00
daboe01andGitHub 589c26192d Merge pull request #2894 from daboe01/tokenfield-fr-fix
fixed: selection and backspace are recently broken in CPTokenField
2024-07-16 19:01:25 +02:00
David RichardsonandGitHub 8471aeea1b Merge pull request #3082 from michaelbach/patch-1
Update README.markdown
2024-07-16 09:43:32 -06:00
Michael BachandGitHub 58c39c14a5 Update CPSlider.j (fixing bindings for case 'only stop at tick marks'
This fixes bindings to also work when 'only stop at tick marks' is selected; formerly the code would crash silently with `setObjectValue`.
2024-07-16 16:15:57 +02:00
Michael BachandGitHub 2cfe45950b Update README.markdown
mistype (LTS → Node.js), typography (- → –) and slight presentation change (bullet list).
2024-07-16 10:22:18 +02:00
daboe01andGitHub 688671db6a Merge pull request #3081 from enquora/update-README
Modernize project README
2024-07-16 07:23:38 +02:00
David Richardson 1d2e50dfa2 FAQ section added 2024-07-15 13:20:39 -06:00
David Richardson 7a70036c8f Further content edits
Link to description of message passing languages.
Style and content edits.
2024-07-15 09:54:11 -06:00
David Richardson ab88a3b236 Add comment about loose-coupled nature of message-passing architectures 2024-07-14 15:05:16 -06:00
David Richardson 266e14063e Modernize project README 2024-07-14 14:00:07 -06:00
David RichardsonandGitHub 3af0296779 Merge pull request #3080 from enquora/re-enable-capp_lint
Re-enable installation of capp_lint
2024-07-05 15:22:06 -06:00
David Richardson 5ff05503a4 Re-enable installation of capp_lint
Requires Python 2, but this is true for XcodeCapp also.
Installs to /usr/bin/local, but must be manually installed using install script in source directory.
2024-07-05 15:14:43 -06:00
David RichardsonandGitHub 13e6782cf1 Merge pull request #3077 from enquora/ibtool-fix-node
Update nib2cib's Converter.j to handle xib files with deployment targ…
2024-07-03 13:32:46 -06:00
David Richardson 24052e6369 Improve inline documentation, code formatting 2024-07-03 13:05:07 -06:00
David RichardsonandGitHub 9b17ce5524 Merge pull request #3078 from enquora/XcodeCapp-fix-deprecations
Xcode capp fix deprecations
2024-07-03 12:20:21 -06:00
David Richardson e4c2d62eb0 Clean up deprecation notices
Reduce debugging noise by removing deprecation notices.
2024-07-03 09:05:32 -06:00
David Richardson 27fa37a7e1 Update dataFromPropertyList usage to remove deprecation warning 2024-07-03 09:05:32 -06:00
David Richardson 987da2b812 Remove deprecation warning when reading settings plist 2024-07-03 09:05:32 -06:00
David Richardson d1611bae12 Update deprecated NSCenterTextAlignment usage to NSTextAlignmentCenter 2024-07-03 09:05:32 -06:00
David Richardson 96a3c4b9dd Remove extraneous 'SettingsView.cib' file
Not a part of XcodeCapp project, unclear why it was included.
2024-07-03 09:05:32 -06:00
David Richardson 8ef40dced5 Update 'About' dialog
Use property values in Info.plist rather than hard-coded values in dialog view.
2024-07-03 09:05:32 -06:00
David Richardson 4a6ddb901f Clean up alignment of assignment statements 2024-07-03 09:05:32 -06:00
David Richardson a5094e0acc Update nib2cib's Converter.j to handle xib files with deployment targets of Xcode 11 or later 2024-07-02 16:39:17 -06:00
David Richardson 966d9fe0d2 Remove bootstrap.sh and narwhal.local.conf
Used by/useful for Narwhal javascript engine - no longer needed.
2024-06-03 15:08:59 -06:00
daboe01andGitHub fffcca592d Merge pull request #3047 from daboe01/patch-8
Prepare README.markdown for the upcoming release
2024-06-02 21:19:22 +02:00
Martin CarlbergandGitHub 341584c032 Change reference to branch main instead of node 2024-05-24 12:49:10 +02:00
Martin Carlberg a1e178d0d7 Merge remote-tracking branch 'origin/narwhal' 2024-05-24 12:18:28 +02:00
Martin CarlbergandGitHub c59687b6e2 Fixed: Use latest version of actions as the old ones are deprecated 2024-04-10 11:34:50 +02:00
Martin CarlbergandGitHub c2bd9f26a7 Fixed: Github Action build with Node 20 and 21 too 2024-04-10 11:19:10 +02:00
David RichardsonandGitHub 6d78b9b6df Merge pull request #3065 from enquora/update-XcodeCapp-project-structure
Update xcode capp project structure
2024-03-20 10:15:51 -06:00
David Richardson 0669ce06f2 Update version check in XcodeCapp Jakefile
Match deployment version set in XcodeCapp project file - macOS 10.13.
2024-03-20 10:07:25 -06:00
David Richardson 767d9378e3 Move deployment target back from macOS 10.15 to 10.13,
the earliest version on-hand for testing.
2024-03-20 09:48:09 -06:00
David Richardson c2d62b7a13 Update Info.plist
Set Display Name property to 'XcodeCapp'.
Set App Category to 'Developer Tools'.
2024-03-20 09:25:16 -06:00
David Richardson 10e33298a9 Fixes to allow successful building under Xcode 15
Manually remove DSTROOT occurrences from project.pbxproj.
Their presence causes Xcode build process to believe a reference cycle exists - preventing compilation.

Update minimum installation target to macOS 10.15
Earlier targets require presence of 'libarclite' - no longer included with Xcode 15.
2024-03-20 09:23:11 -06:00
David RichardsonandGitHub ecc88112d5 Merge pull request #3063 from enquora/narwhal-jsc-sonoma 2024-03-19 08:11:08 -06:00
daboe01andGitHub b190063264 Merge pull request #3060 from daboe01/new-releaseWhenClosed-support
new: support for releasedWhenClosed in CPWindow
2024-03-19 15:04:44 +01:00
David RichardsonandGitHub 0c9c0c0239 Merge pull request #3064 from enquora/fix-host-os-version-check
Allow building XcodeCapp under any version greater than Sierra
2024-03-19 08:03:18 -06:00
David Richardson ae333ecb77 Allow building XcodeCapp under any version greater than Sierra
Individual new OS releases had support added individually until now.
2024-03-18 10:09:46 -06:00
David Richardson 72a85d11df Fix narwhal-jsc build under Sonoma
Remove newly-unsupported linker flag
2024-03-18 09:57:59 -06:00
Martin Carlberg f33ba954f5 Fixed: critical severity vulnerability in package xmldom 2024-01-09 14:28:15 +01:00
daboe01 f642330306 new: support for releasedWhenClosed in CPWindow 2023-12-23 19:08:38 +01:00
daboe01andGitHub ae1a39322e Merge pull request #3053 from daboe01/CPDatePickerUsabilityImprovement
fixed: poor user experience when entering dates that are too high for…
2023-12-21 20:00:53 +01:00
daboe01 29502d10eb formatting 2023-12-21 19:59:38 +01:00
daboe01andGitHub 37550b9fb0 Merge pull request #3048 from michaelbach/fix-typo-in-CPView-(_ephereralSubviews)
CPView.j, fix typo: _ephereralSubviews → _ephemeralSubviews
2023-12-21 19:24:11 +01:00
daboe01andGitHub efd2ed1960 Merge pull request #3055 from daboe01/cpstring+-containsString
fixed: CPString did not have containsString:
2023-12-21 19:19:49 +01:00
Martin Carlberg 150e44dcd5 Fixed: The objj2objcskeleton tool stop generating output if a SelectorLiteralExpression was used in the source file 2023-05-31 14:32:38 +02:00
daboe01 3a164c8080 fixed: CPString did not have containsString: 2023-03-05 21:37:50 +01:00
daboe01 fb9223c6c3 fixed: poor user experience when entering dates that are too high for the current month 2023-02-25 21:30:42 +01:00
Martin Carlberg 511ce4b07d Merge branch 'master' into node 2022-12-09 15:13:17 +01:00
Martin Carlberg aea3fb5f07 Merge remote-tracking branch 'origin/es2022' into node 2022-12-09 13:59:48 +01:00
Martin CarlbergandGitHub 5932a07eb2 Fixed: Make sure Jakefile both work for async and sync calls
The callback variable was not set if the call to initilize was synchronous.
It is now set before so both sync and async will work
2022-12-09 10:15:38 +01:00
Martin CarlbergandGitHub 2b07c4b662 Fixed: Make sure Jakefile both work for async and sync calls
The callback variable was not set if the call to initilize was synchronous.
It is now set before so both sync and async will work
2022-12-09 10:14:52 +01:00
Martin CarlbergandGitHub bc689b91dc Fixed: Added 'npm update' to use latest modules 2022-12-08 17:26:57 +01:00
Martin CarlbergandGitHub c6a42781e7 Fixed: Github Action build with Node 18 and 19 too 2022-12-08 14:25:01 +01:00
Martin Carlberg c92aedf433 Fixed: Quick fix for XcodeCapp so it will run without python2 scripts 2022-11-21 16:41:16 +01:00
cacaodevandMartin Carlberg 7857f92f19 FIXED: CPLikePredicateOperatorType was matching substrings
Fixes #2955
2022-11-21 16:33:56 +01:00
cacaodevandGitHub 9361e5d401 FIXED: CPLikePredicateOperatorType was matching substrings
Fixes #2955
2022-11-10 10:49:01 +01:00
Michael Bach 6c6f69d4e5 CPView.j, fix typo: _ephereralSubviews → _ephemeralSubviews
This typo occurs only once, line #3462 references the correct spelling.
2022-11-01 18:04:21 +01:00
daboe01andGitHub e836226174 Prepare README.markdown for the upcoming release 2022-11-01 17:32:37 +01:00
Martin CarlbergandGitHub ad1862a253 Update CPURLConnection.j
New: Async method for requesting data
2022-10-06 11:24:56 +02:00
Martin Carlberg 4af137894a Fixed: Npm lock file with latest versions 2022-09-01 14:02:40 +02:00
Martin Carlberg 55aca1fde9 Fixed: Make sure we compile with es2022 as ecmaVersion in the parser 2022-09-01 13:53:52 +02:00
Alfred Wärnsäter 53e1e936d9 Reordered includes 2022-08-26 15:08:58 +02:00
Alfred Wärnsäter ff1448fb54 Fixed Objective-J framework using latest transpiler and parser 2022-08-26 12:25:40 +02:00
Alfred Wärnsäter 95fb19f311 New version 2022-08-25 17:57:57 +02:00
Alfred Wärnsäter 46d869a62f Updated lock file 2022-08-25 15:46:09 +02:00
Alfred Wärnsäter 03140dbf64 Updated Terser and added acorn walk dependency 2022-08-25 15:45:23 +02:00
Alfred Wärnsäter 052c96e05a Changes for compatibility with ES2022 transpiler 2022-08-25 15:34:19 +02:00
Alfred Wärnsäter 9a88f9c324 Added ES2022 as compile option 2022-08-25 15:32:43 +02:00
Alfred Wärnsäter 747ea627c9 Removed debugger statement 2022-08-25 15:30:02 +02:00
daboe01andMartin Carlberg 89c0ab405a Fixed: å and Å did not work well on scandinavian keyboards in CPTextView (#3031) 2022-04-04 10:23:17 +02:00
daboe01andGitHub 04c2b98ae4 Fixed: å and Å did not work well on scandinavian keyboards in CPTextView (#3031) 2022-04-04 10:16:37 +02:00
Martin Carlberg 24b36cdd95 Fixed: Allow space in path to .xib/.cib file when running nib2cib 2022-03-29 10:39:04 +02:00
Martin Carlberg 3e83bd2363 Revert "Fixed: Before this PR, nib2cib failed if a color well or a scrollable text view was added in a CPBox (#2994)"
This reverts commit 6544aff88e.
2022-03-21 16:25:24 +01:00
Martin Carlberg 5c91a08eaf Revert "Fixed: Before this PR, nib2cib failed if a color well or a scrollable text view was added in a CPBox (#2994)"
This reverts commit 6544aff88e.
2022-03-21 16:24:17 +01:00
Martin Carlberg edfba15d6f Merge branch 'master' into node 2022-03-21 15:55:08 +01:00
Didier KorthoudtandGitHub 48e786e8b5 Fixed: This PR adapts views and custom views decoding to fix "not ready view" bugs (#2917) 2022-03-21 15:50:44 +01:00
daboe01andGitHub 1282f1ac0e Removed: Flash support (#2903) 2022-03-21 15:46:54 +01:00
KrisandGitHub ee2fb61fdd Fixed: Changed Cappuccino.doxygen EXCLUDE_PATTERNS to exclude nothing. (#2680)
Classes such as CPArray are no longer shown in the generated documentation

This should fix #1956
2022-03-21 15:42:46 +01:00
Martin CarlbergandGitHub 68a113e7ee Fixed: New Cocoa api that replace old deprecated for calculating content and frame size of scroll view (#3023)
Old deprecated api did not work as it was accessing ivars _horizontalScroller and _verticalScroller from class method.
New Cocoa api is added as described in Apple Developer Documentation.

Also added type CPScrollerStyle
2022-03-21 15:25:09 +01:00
Martin CarlbergandGitHub 3cf3b76446 Fixed: Correct init of CPTextView when running nib2cib (#3014) 2022-03-21 15:24:34 +01:00
Didier KorthoudtandGitHub d7d561d3a9 Fixed: Aristo3 - New _CPImageAndTextView & theme parameter (#2951) 2022-03-21 15:23:16 +01:00
David SiawandGitHub 73eeda3c09 Fixed: strange lines that appear in Google Chrome (#2863) 2022-03-21 15:14:14 +01:00
Didier KorthoudtandGitHub 3d514b5010 Fixed: Aristo3 - Various _CPImageAndTextView bugs (#2914) 2022-03-21 14:28:21 +01:00
Didier KorthoudtandGitHub 93fc8d29cc Fixed: Before this PR, imagesize would provide image size in points (that is, taking care of the image resolution) instead of pixels (#2965) 2022-03-21 14:12:51 +01:00
Didier KorthoudtandGitHub 145bf65542 Fixed: Before this PR, outlet connection decoding was done before views are ready (#2959) 2022-03-21 14:10:53 +01:00
daboe01andGitHub f21555bb6c Fixed: Width of text sizing was slightly off in chrome due to a Chrome bug (#2975)
This is a real bug in chrome. see https://bugs.chromium.org/p/chromium/issues/detail?id=1192834

Also fixed: ellipsis clipping in segmented controls
and
fixed: tooltip wrapping issues from fractional pixels
2022-03-21 14:08:45 +01:00
Martin CarlbergandGitHub be072a283f Fixed: Basic support for on/off/mixed state images for menuitems from the Interface Builder (#2991) 2022-03-21 13:38:39 +01:00
David RichardsonandGitHub 9e8d51c94d New: Add additional manual test for scaleUnitSquareToSize (#2979)
Currently failing to match behaviour of Objective-C equivalent.
2022-03-21 13:37:24 +01:00
Didier KorthoudtandGitHub b3051a5bd0 Fixed: Before this PR, CPBox followed the autoresizesSubviews flags while Cocoa NSBox always autoresizes its subviews, whatever the flag is set to. (#2992) 2022-03-21 13:35:22 +01:00
Didier KorthoudtandGitHub a523cfc381 Fixed: Before this PR, CGContextSetShadow wasn't initializing the shadow color (#2966) 2022-03-21 13:33:00 +01:00
Didier KorthoudtandGitHub 6544aff88e Fixed: Before this PR, nib2cib failed if a color well or a scrollable text view was added in a CPBox (#2994) 2022-03-21 13:29:31 +01:00
Michael BachandGitHub 7c1fdbc720 Fixed: Modernise "document.write" in «index.html» to "innerHTML" (#2998) 2022-03-21 13:26:11 +01:00
daboe01andGitHub bd84804e32 Fixed: Usage of eval instead of JSON.parse for parsing JSON (#3016)
Using eval for parsing JSON is considered bad practice and is also slower
2022-03-21 13:07:44 +01:00
David RichardsonandGitHub 1f2a257bab Fixed: CPBox - Suppress superfluous warnings and expand documentation (#2996)
Warnings about borderColor, borderWidth, cornerRadius and fillColor are suppressed when boxType is CPBoxSeparator.
They are not relevant and needlessly clutter verbose output when debugging and should be suppressed.
2022-03-21 13:06:18 +01:00
daboe01andGitHub b0684929e2 Fixed: key event issues of handling escape in recent firefox … (#3020) 2022-03-21 13:02:37 +01:00
daboe01andGitHub 22dc587a8f Fixed: caret was blinking in non-editable text in CPTextView (#3022) 2022-03-21 13:00:40 +01:00
daboe01andMartin Carlberg 622fcf740e Update README.markdown
make wording of system requirements more concise
2022-03-21 12:58:55 +01:00
cacaodevandGitHub 77b59c8a18 Merge pull request #3024 from cappuccino/revert-2963-compile_deep_structured_project_in_browser
Revert "New: Support to compile deep structured project in the web browser"
2022-03-17 21:10:03 +01:00
cacaodevandGitHub 462a18930b Revert "New: Support to compile deep structured project in the web browser" 2022-03-17 21:07:16 +01:00
cacaodevandGitHub fb07def5fa Merge pull request #3017 from daboe01/fixed-sheet-animation-click-issue
fixed: freeze when clicking during sheet animation
2022-03-05 18:12:27 +01:00
cacaodevandGitHub a03ee6726e Merge pull request #2963 from mrcarlberg/compile_deep_structured_project_in_browser
New: Support to compile deep structured project in the web browser
2022-03-05 18:11:08 +01:00
daboe01 1ef49ef290 formatting 2022-01-23 13:42:12 +01:00
daboe01 76d52e07d4 fixed: freeze when clicking during sheet animation
previously, the app froze when clicking somewhere during sheet open/close animations.
these adversarial clicks are simply ignored by this PR
2022-01-23 13:39:33 +01:00
Martin Carlberg 044b0bb0d2 Fixed: NibApplication template for 'capp' to use correct build dir 2022-01-04 14:51:43 +01:00
Martin Carlberg 472b59ef80 Fixed: Allow space in path to .xib/.cib file 2022-01-04 14:51:43 +01:00
Martin Carlberg 6b559ce283 1.2.2 2021-12-03 14:02:00 +01:00
Martin Carlberg c17b806fd5 1.2.1 2021-12-03 14:00:14 +01:00
Martin Carlberg 1dcea8fe4c Added: .npmignore for easier "npm publish" 2021-12-03 14:00:02 +01:00
Martin Carlberg d72a52b9ce Merge branch 'master' into node 2021-12-03 12:36:27 +01:00
Martin Carlberg 61e01b08e5 Fixed: Rename the README back to the original name 2021-12-03 12:29:37 +01:00
Didier KorthoudtandGitHub 37756a5853 Fixed: nib2cib: checks if there's a defined superview in resizeWithOldSuperviewSize (#2995) 2021-11-23 13:47:54 +01:00
David RichardsonandGitHub b66690e304 New: Implement missing indexPath method for CPTreeNode (#2999) 2021-11-23 13:45:10 +01:00
Martin CarlbergandGitHub ccc6007bab Fixed: Bezel state instance variable _bezelState must remove current state when changing to a new state (#3001)
If the bezel state was changed and some attribute was set like an "image". The image would not be found as there were two states active and it would not match.
2021-11-23 13:42:36 +01:00
David RichardsonandGitHub 3179d56879 Fixed: Include commented code to hide mainMenu (#3008)
Add commented-out code to capp gen NibApplication template.
The technique itself is easy to forget and not obvious to newcomers.
It is included in the standard capp gen application template
and added here for completeness.
2021-11-23 13:41:30 +01:00
Martin CarlbergandGitHub b631032857 Fixed: Node build badge can be clicked to actions 2021-11-09 12:43:32 +01:00
Martin CarlbergandGitHub 49574d1c54 Fixed: Node build badge can be clicked to actions 2021-11-09 12:41:01 +01:00
Martin CarlbergandGitHub b7d7368ce6 Fixed: Include the Node build status badge at top 2021-11-09 11:49:30 +01:00
Martin CarlbergandGitHub e221156c11 Fixed: Changed name on Build & Test GitHub action 2021-11-09 11:43:39 +01:00
Martin CarlbergandGitHub 8e346acdce Fixed: Changed travis build badge to Github 2021-11-09 11:34:14 +01:00
Martin CarlbergandGitHub 7c36fa3f56 Fixed: "capp gen" template for NApplication uses environment variable CAPP_BUILD 2021-11-09 10:14:37 +01:00
Martin CarlbergandGitHub 642e254522 Fixed: Use upper case ENV variable 2021-11-09 10:07:35 +01:00
Martin CarlbergandGitHub 72aa1b8416 Fixed: Better name on the Build and Test Action 2021-11-08 17:11:43 +01:00
Martin CarlbergandGitHub edb1ef7f98 Fixed: Remove action on Node version 12 and 14
We use npm package-lock file version 2 and it is not supported by version 12 and 14
2021-11-08 13:21:40 +01:00
Martin CarlbergandGitHub 92beaba1d6 Fixed: Add Node version 12, 14 and 17 in action 2021-11-08 13:12:37 +01:00
Martin CarlbergandGitHub 3b61b1c85d Fixed: Use node branch 2021-11-08 13:07:40 +01:00
Martin CarlbergandGitHub a529e752b3 Create BuildAndTest.yml 2021-11-08 13:05:26 +01:00
Martin Carlberg d9791673df Fixed: Make sure we can build on Linux. Tested on Ubuntu 20.04 2021-11-07 13:41:01 +00:00
David RichardsonandGitHub a49a20e2c2 Fixed: nib2cib under Xcode 13 (#3006)
Backwards-compatible changes to accommodate different ibtool output from Xcode 13 onwards
2021-10-19 08:31:54 +02:00
Martin CarlbergandGitHub 8570476cff New: Added link to install the new Node version 2021-10-06 12:18:51 +02:00
Martin Carlberg d0923efa1a Fixed: Updated package-lock file. 2021-10-06 09:26:15 +02:00
Martin Carlberg eab8fe2d64 Fixed: Divided 'jake install' into 'jake dist' and 'jake install'
'jake dist' will do everything that 'jake install' does but creating the global symlinks
for the 'dist' binaries at the 'npm prefix' path.
2021-10-05 11:34:25 +02:00
Martin Carlberg c9e8b2970b Fixed: Moved out nib2cib functions to correct scope so they can be accessed by catch 2021-10-04 12:51:12 +02:00
Martin Carlberg 1440c156b2 Fixed: Make the path to the Frameworks directory for each package 2021-10-01 19:32:53 +02:00
Martin Carlberg ddfa4445ae Fixed: Move the 'objj' tool to the right place 2021-10-01 13:11:57 +02:00
Martin Carlberg 1f501e6429 Fixed: Make sure environment variables are included when running test-only 2021-10-01 13:10:46 +02:00
Martin Carlberg 7969f118de Fixed: Build in project home directory if no environment variables are set
Earlier the build directory was placed in the parent directory
2021-10-01 08:45:58 +02:00
Martin Carlberg 706166dc5c Fixed: Make sure we use the built parts of Cappuccino when building parts that is dependent of former.
This is done by passing the environment variable 'OBJJ_OPT' when spawning a new jake task. The content
of OBJJ_OPT will be parsed as arguments when starting an Objective-J tool
2021-09-29 09:18:12 +02:00
Martin Carlberg 4a8667e0f3 Fixed: Exit with error code when test cases did fail 2021-09-28 15:31:01 +02:00
Martin Carlberg 094fbcc67c Fixed: Move Terser from a development dependency to a regular dependency 2021-09-22 09:37:22 +02:00
Martin Carlberg d34c47c167 Fixed: Use preset variable from common.jake for build directory 2021-09-21 20:49:05 +02:00
Martin Carlberg 7226330514 Fixed: Create soft link to node_modules in a simpler way 2021-09-21 20:31:29 +02:00
Martin Carlberg 852b2bd5d9 Fixed: Missing enquote of path when creating build directory and soft link to node_modules 2021-09-21 17:56:14 +02:00
Martin Carlberg 3d8ecfad47 Fixed: Use unique temp directory for each run with nib2cib 2021-09-21 16:04:10 +02:00
Martin Carlberg 9e6f1e0dc3 Fixed: Timezone abbreviation was not working with the latest changes on all setups.
This is still not 100% but looks like it works a little bit better
2021-09-21 15:38:51 +02:00
Martin Carlberg 4deb7a3f51 Merge branch 'node' of github.com:cappuccino/cappuccino into node 2021-09-21 12:54:00 +02:00
Martin Carlberg 5b0b030435 Fixed: Exclude the dist folder except cappuccino/bin and objective-j/bin 2021-09-21 12:52:48 +02:00
Martin Carlberg 4edec2df2e 1.2.0 2021-09-21 12:34:10 +02:00
Martin Carlberg e0596bdf75 Fixed: Added latest package.json files 2021-09-21 12:34:04 +02:00
Martin Carlberg 6430463b5d Fixed: Included the latest binaries into the dist folder. 2021-09-21 12:31:45 +02:00
Martin Carlberg d9e27e549f Fixed: Removed compiler warnings in test case 2021-09-21 11:11:57 +02:00
Martin Carlberg 9f14dac5d2 Fixed: Cleanup of the output from our test cases 2021-09-21 11:11:25 +02:00
Martin Carlberg d70199ca58 Fixed: Asynchronous and output changes from the new Terser compressor 2021-09-21 11:09:09 +02:00
Martin Carlberg 6b1cdbd19c Fixed: Removed compiler warnings from test cases 2021-09-21 10:58:03 +02:00
Martin Carlberg 5b99757a0d Fixed: The compiler might inline the 'message send' call so we do it manually in these test cases. 2021-09-21 10:56:10 +02:00
Martin Carlberg 6c49116c68 Fixed: Use date string that is not as old as the previous one.
JavaScript now handles time a lot better. Times from before around 1900 is different in different
parts of the world so we just changed this test case to be a bit more modern.
2021-09-21 10:55:16 +02:00
Martin Carlberg 68f0cf27df Fixed: Bundle test cases must run asynchronous as a bundle will also load asynchronous in Node.
Also removed some compilation warnings from the test cases.
2021-09-21 10:51:17 +02:00
Martin Carlberg 89757e35e6 Fixed: Class Swapper should convert NSView -> CPView.
This is needed when a plain NSView is use without a Custom class name as it will otherwise try to create a NSView.
Also the Class Swapper is only used when the 'Instantiation' is check for 'Prefer code' under
the 'Runtime Behavior' header in the Document tab in Xcode. If this is not checked the '_CPCibCustomView'
is used instead of the Class Swapper.
2021-09-21 10:46:18 +02:00
Martin Carlberg 24bf09b70c Fixed: Abbreviation for a timezone is not handled in JavaScript. This changes makes it better but not perfect. 2021-09-21 10:30:26 +02:00
Martin Carlberg 0ed2709124 Fixed: Better message when loading a cib file and the class swapper doesn't find a class 2021-09-21 10:26:33 +02:00
Martin Carlberg a835202db1 Fixed: Adjusted test cases for Node. XMLHttpRequest now demands a protocol. 2021-09-20 11:00:37 +02:00
Martin Carlberg 830fbbab26 Fixed: Removed old Rhino adjustments in test cases 2021-09-20 10:51:14 +02:00
Martin Carlberg 119e11cfe2 Fixed: Allow 'await' for loading a bundle with the method 'loadWithDelegate:' 2021-09-20 09:19:05 +02:00
Martin Carlberg 511e18b44f Fixed: Wait for the bundle load to finish with the new async bundle load function 2021-09-17 21:55:48 +02:00
Martin Carlberg 2f00d93b22 Fixed: Must check in Node if path exists when doing 'fs.lstatSync' as it will throw if not. 2021-09-17 10:39:11 +02:00
Martin Carlberg 973fa1b978 Fixed: Misspelled function name 2021-09-17 10:37:03 +02:00
Martin Carlberg 79f40d24ae Fixed: Replace soft link to the directory 'node_modules' when it is already there. 2021-09-17 09:39:37 +02:00
Martin Carlberg fdff92e5c8 Fixed: Ask Node for a temporary directory instead of using '/tmp' 2021-09-17 09:38:29 +02:00
Martin Carlberg 653dd07162 Fixed: Include the tool 'objj2objcskeleton' in the installed binaries 2021-09-15 17:38:24 +02:00
Martin Carlberg b7968ca4a7 Fixed: Make sure output from the tool 'ojtest' gets to the terminal.
Also suspend stack trace etc. when the tests fails with a try ... catch.
2021-09-15 17:37:04 +02:00
Martin Carlberg 2a0a6449e5 Fixed: Converting the test cases in ToolTest.j to Node 2021-09-15 17:33:21 +02:00
Martin Carlberg b7c04d7d16 Fixed: Converted the tool 'objj2objcskeleton' for Node 2021-09-15 17:32:31 +02:00
Martin Carlberg ab4c87e69f Fixed: Removed warnings and cleanup for test cases when converting to Node 2021-09-15 17:31:20 +02:00
Martin Carlberg 8257632706 Fixed: When creating a method the function name can't start with '0_' 2021-09-15 17:28:07 +02:00
Martin Carlberg 3f8821b43b Fixed: Adjust arguments for the tool objj
This has changed now when running on Node.
2021-09-15 17:26:40 +02:00
Martin Carlberg f6b8bc0c45 Fixed: Include source files when compiling ojtest 2021-09-15 17:25:36 +02:00
Martin Carlberg 0618fa9bb2 Fixed: Added protocol to data source and delegate for outline view 2021-09-15 17:24:31 +02:00
Martin Carlberg 2e5a483dce Fixed: Added examples how to add macros in the 'capp gen' templates index[-debug].html files 2021-09-13 09:38:02 +02:00
Martin Carlberg e608e0dbf3 Fixed: Partly adapted Tooltest for Node 2021-09-10 15:56:12 +02:00
Martin Carlberg a88e316a94 Fixed: Compile OJTest with source map for easier debugging 2021-09-10 15:55:40 +02:00
Martin Carlberg 51ffa256ca Fixed: Preprocessor Output test adapted for Node 2021-09-10 15:55:01 +02:00
Martin Carlberg 52bd654850 Fixed: The new script for the ojtest tool must be in the dist/cappuccino/bin directory 2021-09-04 09:15:24 +02:00
Martin Carlberg 0518d479d3 Fixed: Included ojtest as binary in the package.json file 2021-09-03 16:28:23 +02:00
Martin Carlberg a893f3f5c1 Fixed: The tool ojtest now works with the new Node version. 2021-09-03 14:31:11 +02:00
Martin Carlberg 9c2f37b43f Fixed: The Tool nib2cib will give information of used Resource directory. 2021-09-02 12:44:48 +02:00
Martin Carlberg 0289ba17f1 Fixed: Add test task for Framework template used by the tool capp gen
Plus some minor bugfixes
2021-09-02 11:23:32 +02:00
Martin Carlberg 5db94e1e52 Fixed: Cleanup of the Jakefile template for Application and Framework used by the tool "capp gen" 2021-09-01 16:28:55 +02:00
Martin Carlberg a4bc08c29e Fixed: Moved enquote function to utils package.
This function was originally in the Narwhal package OS
2021-09-01 16:27:16 +02:00
Martin Carlberg b638d8e904 Fixed: From around Xcode 12.5.1 ibtool starts to create a folder with two files.
We have to check for this and take action. We don't know of this is a temporary thing as Apple has no
information about this change.
2021-08-30 16:29:43 +02:00
Martin Carlberg 251ce7fdad Fixed: Info.plist file with just whitespaces is not treated as an empty file in Node. Trim it and check length. 2021-08-27 09:08:19 +02:00
Martin Carlberg a2337a2969 Fixed: Can't use generic array of numbers when writing to file with Node 2021-08-27 09:04:51 +02:00
Martin Carlberg 0c803e26ca Fixed: Must expand path to absolute to be able to get application name. Was translated to wrong Node function when converting from Narwhal. 2021-08-27 09:03:58 +02:00
Martin Carlberg ab25fadb1e Fixed: Function for imagesize used wrong and non declared argument 2021-08-27 09:02:26 +02:00
Martin Carlberg e697f4eca7 Fixed: Better debugging experience 2021-08-27 09:01:19 +02:00
Martin Carlberg 80158a8982 Fixed: Cleanup of fontinfo and imagesize Jakefile 2021-08-27 08:59:45 +02:00
Martin Carlberg 55bcfacf3b Fixed: Moved common function used in Jakefiles that was declared all over the place to a file.js in @objj/utils. Also some general cleanup. 2021-08-25 09:32:58 +02:00
Martin Carlberg 52b7a82b4d Fixed: Cleanup of Jakefiles for better Node adaption 2021-08-24 10:58:12 +02:00
Martin Carlberg 471e605871 Fixed: removed debug log message 2021-08-23 11:14:12 +02:00
Alfred Wärnsäter 5c635f17a7 require node 14 as minimum 2021-08-20 14:22:11 +02:00
Alfred Wärnsäter c27d604780 removed print statement 2021-08-20 09:29:59 +02:00
Alfred Wärnsäter ddaf575719 fixed --version option in nib2cib 2021-08-20 09:29:34 +02:00
Alfred Wärnsäter 0757f2ea80 corrected instructions for Node alpha 2021-08-20 09:28:11 +02:00
Alfred Wärnsäter 3f8dfb6516 removed debugger statement 2021-08-20 09:27:03 +02:00
Alfred Wärnsäter 45ee25adc2 removed print statement 2021-08-20 09:26:13 +02:00
Alfred Wärnsäter fcfb3d4083 added bin dir from dist 2021-08-19 17:31:21 +02:00
Alfred Wärnsäter 357f8bd684 1.1.1 2021-08-19 16:36:38 +02:00
Alfred Wärnsäter 173cd8b4ad bumped npm version 2021-08-19 16:36:27 +02:00
Alfred Wärnsäter d2ca5994c8 1.2.0 2021-08-19 16:35:39 +02:00
Alfred Wärnsäter fe04d177af added more instructions 2021-08-19 16:35:19 +02:00
Alfred Wärnsäter 2a6b18bc1b added more instructions 2021-08-19 16:33:15 +02:00
Alfred Wärnsäter 152cd272f6 updated readme for Node alpha version 2021-08-19 16:07:26 +02:00
Alfred Wärnsäter abbdc139e8 now using sudo only when necessary 2021-08-19 13:59:43 +02:00
Alfred Wärnsäter 7226019b6a use npm prefix for install path 2021-08-19 13:30:40 +02:00
Alfred Wärnsäter 78ea29bd48 added dist to to gitignore 2021-08-19 11:15:27 +02:00
Alfred Wärnsäter 5c90d1f769 added binary part of dist dir 2021-08-19 11:14:38 +02:00
Alfred Wärnsäter 2ecea40fdc lots of Node fixes 2021-08-19 10:56:13 +02:00
Martin Carlberg 8d9c11b6f2 Fixed: Make sure we return the result from an executed file in the callback 2021-08-18 15:56:36 +02:00
Martin Carlberg afd23e3457 Fixed: Added file translation dictionary for the Foundation framework
This allows the Foundation framework to be compiled while loading the Application in the Browser
2021-08-17 16:47:06 +02:00
Alfred Wärnsäter ddeaad74d0 added jake stuff to bundletask 2021-08-10 12:31:48 +02:00
Alfred Wärnsäter a82472f509 removed unused dependancies 2021-08-10 12:29:43 +02:00
Alfred Wärnsäter 4a43e376ed modified capp to work with node 2021-08-10 12:27:50 +02:00
Alfred Wärnsäter 9ab82e1e5e removed console.log statement 2021-08-10 12:24:20 +02:00
Didier KorthoudtandGitHub 4b28d0c38b Fixed: setTextColor on a CPTextField would not take visual effect before an event (#2993) 2021-08-06 12:17:07 +02:00
Alfred Wärnsäter 1924cd1822 removed fs-extra dependancy 2021-08-06 11:02:55 +02:00
Alfred Wärnsäter 730b2dd874 removed glob dependancy 2021-08-06 10:36:04 +02:00
Alfred Wärnsäter 1c2b383838 removed uuid dependancy 2021-08-06 09:43:49 +02:00
Alfred Wärnsäter 0b99f83d6f fixed bug that broke the build process when building twice 2021-08-05 17:53:45 +02:00
Alfred Wärnsäter dc5b3f48e8 fixed watch functionality in nib2cib 2021-08-05 16:22:36 +02:00
Alfred Wärnsäter d17671c99e added parsing of command line arguments 2021-08-04 15:19:10 +02:00
Alfred Wärnsäter b69259e83b swapped print to console.log 2021-08-04 15:16:41 +02:00
Alfred Wärnsäter f2c80dc815 added node_modules to .gitignore 2021-08-04 15:15:12 +02:00
Alfred Wärnsäter cbc281b173 added terser as compressor 2021-08-04 09:25:36 +02:00
Alfred Wärnsäter 472f859bd8 basic support for nib2cib 2021-08-03 13:39:54 +02:00
Alfred Wärnsäter e71f650650 removal of print and debugger statements + added some more color 2021-07-30 15:35:57 +02:00
Alfred Wärnsäter e64c9d9cd4 starting to port the capp tool to node 2021-07-30 14:09:11 +02:00
Alfred Wärnsäter 87a1c6621c ported nib2cib from narwhal to node, it now build using objj-jake 2021-07-29 13:20:30 +02:00
Didier KorthoudtandGitHub 09f49c4afa Fixed: CPButton should not have the same label text size whatever the control size (#2990)
Adding font size definitions to Aristo2.
2021-07-29 12:38:25 +02:00
eba1b2f999 Fixed: CPSearchField's bezel did not resize (#2987)
Co-authored-by: Didier Korthoudt <didier.korthoudt@icloud.com>
2021-07-29 11:39:42 +02:00
daboe01andMartin Carlberg 8e026279ac fixed: CPButton's images were lost when becoming disabled 2021-07-29 11:30:55 +02:00
Alfred Wärnsäter 3605e99914 minor bugfixes and clean up 2021-07-28 10:12:24 +02:00
Alfred Wärnsäter 5e9d479cef AppKit and Themes are now buildable 2021-07-27 08:59:05 +02:00
David RichardsonandMartin Carlberg fb20463449 Fix Java version check to cleanly identify both Oracle and OpenJDK variants
Set minimum supported version to 8 to simplify this check.
Java 8 is the last variant from Oracle to be free and unencumbered, and is classified LTS.
It is also available from OpenJDK.
2021-07-05 11:07:53 +02:00
Alfred Wärnsäter f5db73342f changed some code in blendtask.j to older javascript 2021-07-01 16:45:39 +02:00
Alfred Wärnsäter 9b2dc23197 finished porting blendtask.j 2021-07-01 16:30:30 +02:00
Alfred Wärnsäter 067052e1a8 ported blendtask.j to node api 2021-07-01 16:17:43 +02:00
Alfred Wärnsäter 441e5fbc05 ported nib2cib to node the node api 2021-07-01 16:00:32 +02:00
Alfred Wärnsäter 0a2aae3bc2 some restructuring of the repo, trying to fix problems when building with node 2021-06-30 10:13:52 +02:00
Alfred Wärnsäter a76c798500 Merge remote-tracking branch 'mrcarlberg/compile_deep_structured_project_in_browser' into tmp 2021-06-23 10:45:34 +02:00
Alfred Wärnsäter c238ebeef4 major changes, very much WIP, experimenting with async 2021-06-22 14:04:31 +02:00
Alfred Wärnsäter 206eddbdbd further removal of narwhal APIs 2021-06-16 15:43:19 +02:00
Alfred Wärnsäter 45549cdbcf removing narwhal api from jakefiles 2021-06-14 13:17:14 +02:00
cacaodevandGitHub e043b6a8c5 Merge pull request #2978 from daboe01/nib2cib-update
fixed: some cib files have been outdated
2021-05-22 10:48:30 +02:00
daboe01 677076b89d fixed: some cib files have been outdated 2021-04-01 18:04:06 +02:00
cacaodev 77c9b99f74 Manual test exposing issue #2970 2021-03-07 20:46:44 +01:00
cacaodevandGitHub c8cdda0c18 Merge pull request #2962 from mrcarlberg/cptextview_more_ib_options
Fixed: CPTextView now handles more options from Interface Builder
2021-03-07 08:48:50 +01:00
daboe01andGitHub 41cd73703e Fixed: sharedApplication was instantiating the wrong class in subclasses of CPApplication (#2968)
Previously, CPApplication was hard-coded. This PR makes sharedApplication
instantiating the CPApp of the correct class.
2021-01-19 09:03:37 +01:00
cacaodevandGitHub 607e84c052 Merge pull request #2964 from mrcarlberg/compiler_remove_unused
Fixed: Removed unused variables and statements in compiler
2020-12-23 07:36:29 +01:00
Martin Carlberg 8416390631 Fixed: Removed unused variables and statements in compiler 2020-12-22 20:16:30 +01:00
Martin Carlberg 6094d25cfb New: Support to compile deep structured project in the web browser
A Cappuccino project has always needed to have a flat file structure to
be able to compile from source in the web browser.

A new dictionary can be added to the project's Info.plist with the key
'CPFileTranslationDictionary'. It should contain all the path
translations in the project to allow the Browser to find out where in
the file structure the source files are. Check in the AppKit framework
for an example.

Also, a include list can be added in the Info.plist for the key
'CPCompileIncludeFileArray'. The file in the list will be included
by a '#include' statement for each file that is compiled in this project

A last new feature is to add macros in the 'OBJJ_COMPILER_FLAGS' global
variable. It is usually declared in the index.html file. It is done
with the format '-Dmacroname[=macrodefinition]'.

An updated Info.plist file in the AppKit framework will allow the AppKit
to compile from the source in the web browser. The following things is
needed to get this to work:

1. Replace the AppKit folder in your project's Frameworks folder with a
copy of the AppKit folder from the Cappuccino project.
2. Add a copy of the built Aristo2.blend folder to the AppKit's Resource
folder.
3. Add "-DPLATFORM_DOM", "-DPLATFORM_BROWSER" and optionally "-DDEBUG"
to the global variable 'OBJJ_COMPILER_FLAGS' in the project's index.html
file.

The app load time will increase (about 3 seconds on a 2018 MacBook Pro)
as the AppKit framework is compiled from source.

The Foundation framework is not yet tested but it might be working by
adding info about the file structure in the Info.plist file.
2020-12-21 11:14:36 +01:00
cacaodevandGitHub c80504efae Merge pull request #2960 from mrcarlberg/cptextview_exposedrect
Fixed: Prevent crash in CPTextView when _exposedRect is nil
2020-12-19 15:19:19 +01:00
cacaodevandGitHub 2589033523 Merge pull request #2961 from mrcarlberg/radio_multiple_value_bindings_test
New: Unit test to illustrate multiple value bindings on radio buttons
2020-12-19 15:18:27 +01:00
Martin Carlberg f74fed5ee0 Fixed: CPTextView now handles more options from Interface Builder
The new options are:
1. Resizable Horizontally
2. Resizable Vertically
3. Max Size

Also two options from the Text Container is recognized:
1. Tracks text view width
2. Tracks text view height

The last two are needed to allow the text view to recalculate
the size of its content if the frame size is changed.
2020-12-16 11:29:11 +01:00
Martin Carlberg 01e295084c New: Unit test to illustrate multiple value bindings on radio buttons 2020-12-14 13:11:24 +01:00
Martin Carlberg 9f923941a8 Fixed: Prevent crash when _exposedRect is nil 2020-12-14 12:47:34 +01:00
Didier KorthoudtandGitHub 3aba03eb28 Fixed: Missing call to super in NSTabView.j (#2956) 2020-11-22 11:01:14 +01:00
Didier KorthoudtandGitHub 1107195ab6 Fixed: Modern Cocoa behavior, new theme parameters, Aristo3 CSS theme ready for CPTabView (#2936) 2020-11-20 08:33:56 +01:00
Didier KorthoudtandGitHub b67384cdd1 Fixed: Running condition where the value is set before the radio button is associated in a radio group (#2952) 2020-11-20 08:25:03 +01:00
David RichardsonandGitHub 59603b550e Fixed: "Loading…" animation using modern layout (#2953)
Correct vertical alignment in all modern browsers. IE no longer a target.
2020-11-20 08:22:45 +01:00
David RichardsonandGitHub c52f714149 Fixed: Version check for XcodeCapp compatibility with Big Sur (#2954) 2020-11-20 08:20:19 +01:00
Didier KorthoudtandGitHub 35420732d1 Fixed: Modern Cocoa behavior and Aristo3 CSS theme ready for CPDatePicker (#2935)
Also, clock hands can be manipulated
2020-11-06 16:27:07 +01:00
David RichardsonandGitHub 7f48b84292 Fixed: Update template for NibApplication's xib and cib to modern format (#2946) 2020-11-06 08:31:22 +01:00
Didier KorthoudtandGitHub 5110c5706f Fixed: Compilation warnings (#2950) 2020-11-06 07:25:21 +01:00
Didier KorthoudtandGitHub 633511acda Fixed: Before this PR, placing a CPTableView in a CPBox would break nib2cib (#2949) 2020-10-31 17:56:13 +01:00
Didier KorthoudtandGitHub 75d0a69c02 Fixed: uncorrect CPStepper rendering (#2948) 2020-10-30 10:58:34 +01:00
Didier KorthoudtandGitHub e6fe9396f8 Fixed: Change default bezel style to CPRoundRectBezelStyle for CPButton (#2947)
Also removes unwanted comments
2020-10-27 11:01:18 +01:00
Didier KorthoudtandGitHub 5c4107835b New: Aristo3 improvements for CPSearchField (#2934)
New theme parameters
Animations
Modern Cocoa behavior
Aristo3 CSS theme ready
2020-10-23 09:05:33 +02:00
Didier KorthoudtandGitHub 57cc90f89f New: Provides more information on an error message in CPScroller (#2931) 2020-10-23 08:43:59 +02:00
Didier KorthoudtandGitHub 7e08e48a8a New: Adds a new theme parameter "menu-offset" to CPPopUpButton instead of hard coded value (#2923) 2020-10-23 08:42:00 +02:00
Didier KorthoudtandGitHub 87719f1161 Fixed: Adds the missing isCSSBased method for CPView (#2945) 2020-10-12 08:51:49 +02:00
Didier KorthoudtandGitHub 99d7c8d824 New: Modern Cocoa behavior for Menus (#2929)
More theme parameters
Modern Cocoa behavior
First menubar item can be displayed specifically
All 3 control sizes are now managed
2020-10-09 15:52:54 +02:00
Didier KorthoudtandGitHub 1caf4f99cf Fixed: Correct / complete theme states for themed values on CPStepper (#2930) 2020-10-09 14:39:38 +02:00
Didier KorthoudtandGitHub 78b3887e4a New: CSS shadow, new theme parameters and Aristo3 CSS theme ready for CPWindow (#2927) 2020-10-09 14:29:10 +02:00
Didier KorthoudtandGitHub bb064aacd5 New: The ability to control CPPopoverWindow background color (#2928)
This introduces a Cappuccino specific behavior that permits to control the background color of the popover by setting a background color on the content view.
2020-10-09 13:37:47 +02:00
Didier KorthoudtandGitHub d9e8321d9e Fixed: CPButton's now set their font using [self font] (#2943)
The corresponding adaptation in CPControl was missing in the previous PR
2020-10-09 13:05:51 +02:00
Didier KorthoudtandGitHub 5f6df95e30 New: Modern Cocoa behavior and methods, ticked sliders and Aristo3 CSS theme ready (#2926) 2020-10-09 11:47:18 +02:00
Didier KorthoudtandGitHub ef779c346e New: Support modern Cocoa behavior regarding radio buttons groups. (#2941) 2020-10-09 09:35:56 +02:00
Martin CarlbergandGitHub c1543d4a2f Fixed: Loading cached theme attributes will miss previous cached values if superclass is done before subclass (#2892) 2020-10-02 16:01:30 +02:00
Didier KorthoudtandGitHub 9145e27009 Fixed: Aristo3 CSS theme ready and replaces setTextFieldBackgroundColor by setBackgroundColor for CPTextField (#2925) 2020-10-02 14:47:50 +02:00
Didier KorthoudtandGitHub 299f1a9102 Fixed: Aristo3 ready and compatible with new button state management for CPRadio (#2922) 2020-10-02 14:03:25 +02:00
bedaltonandGitHub 6eab438778 Fixed: CPKeyedArchiver method call does not match selector tested (#2904)
Delegate methods described on lines 76 and 85 are not the selectors
checked on lines 305 and line 302, but are the methods actually called
on the delegate on lines 195 and 225. This fix updates the method
selectors checked on the delegate, to match those defined and called
within the CPKeyedArchiver class.

Fixes #2679
2020-10-02 13:52:42 +02:00
Didier KorthoudtandGitHub 2a623fbb8c Fixed: correct mask values used to determine image position for NSButton.j (#2906) 2020-10-02 13:49:11 +02:00
Didier KorthoudtandGitHub 7bde091f1b FIXED: when using a custom view with subviews, subviews were presented in the reversed order (#2913) 2020-10-02 13:31:45 +02:00
Didier KorthoudtandGitHub 412baf7872 Fixed: Aristo3 ready and modern Cocoa behavior for CPBox (#2924) 2020-10-02 13:28:06 +02:00
Didier KorthoudtandGitHub 010057d9e1 New: CPCheckBox Aristo3 ready and compatible with new button state management (#2921) 2020-10-02 13:05:59 +02:00
Didier KorthoudtandGitHub 6c7d8df565 Aristo3: CPButton (#2920) 2020-10-02 12:59:32 +02:00
Didier KorthoudtandGitHub 92598da06f New: themable-system-font (#2919) 2020-10-02 12:55:32 +02:00
Didier KorthoudtandGitHub 601d6f7c6c New: class method to BKThemeDescriptor that enables a view to inherit theme values of another view (#2916) 2020-10-02 12:31:22 +02:00
Didier KorthoudtandGitHub e13cebaa3a NEW: Material icon image (#2909) 2020-10-02 12:13:00 +02:00
Didier KorthoudtandGitHub 5724868c5a Fixed: restorePreviousCSSState method for CPColor must be a class method for internal reasons (#2912) 2020-10-02 10:58:20 +02:00
Didier KorthoudtandGitHub 03972f0aec Fixed: The classname for CSS theming on CPImage was not specified in the correct DOM element (#2908) 2020-10-02 10:55:55 +02:00
Didier KorthoudtandGitHub 6143ec4180 Fixed: Theme compilation problem because of lack of rgba capability of the compiler environment.(#2910) 2020-09-11 09:01:33 +02:00
Didier KorthoudtandGitHub c30464ee77 New: This adds the CSS dictionaries to the description of the color (#2911) 2020-09-11 08:55:10 +02:00
cacaodevandGitHub 1e1e6456cd Merge pull request #2902 from daboe01/test-attributedString-replacing
new: unittest for replaceOccurrencesOfString:withString:options:range:
2020-09-02 18:26:14 +02:00
cacaodevandGitHub 6c46bb4756 Merge pull request #2940 from didierkorthoudt/031-CPViewAnimator-fix
FIXED: This PR fixes correct parameter type in CPViewAnimator -setAlphaValue
2020-09-02 18:21:45 +02:00
Didier Korthoudt 5ac6b1d1f1 Update CPViewAnimator.j
This fixes parameter type.
2020-08-30 10:01:11 +02:00
daboe01 9c95c9c6e9 formatting 2020-07-13 20:37:52 +02:00
daboe01 4b052e74f1 test whether range restriction works 2020-07-05 16:06:04 +02:00
daboe01 bd6b8e4b7f fixed: comparison between wrong objects 2020-07-03 21:27:48 +02:00
daboe01 26e8432bb0 debugging 2020-07-03 19:55:51 +02:00
daboe01 05f9fd22fe formatting 2020-07-03 19:36:06 +02:00
daboe01 8f5bde2797 new: unittest for replaceOccurrencesOfString:withString:options:range: 2020-07-02 19:38:34 +02:00
daboe01andGitHub 77fe16faec Fixed: background color attribute was ignored by CPTextView (#2899) 2020-07-01 16:33:07 +02:00
Martin CarlbergandGitHub 38dd776049 Fixed: JavaScriptCore (WebKit) has a limit at 65536 on number of arguments in a function. (#2891)
The compiler will crash for really large Objective-J files as the array with code segments will hit this limit.
The solution is to append the array segment by segment.
2020-06-30 20:53:28 +02:00
daboe01andGitHub 7e3c4074e4 New: replaceOccurrencesOfString:(CPString)target withString:(CPString… (#2900)
New: replaceOccurrencesOfString:(CPString)target withString:(CPString)replacement options:(int)options range:(CSRange)searchRange
2020-06-30 20:52:34 +02:00
cacaodevandGitHub 89c8f8e80f Merge pull request #2895 from mrcarlberg/bad_merge
Fixed: Removed old unused line from a bad manual merge
2020-05-05 08:38:05 +02:00
Martin Carlberg 7397e681f4 Fixed: Removed old unused line from a bad manual merge 2020-05-04 13:42:37 +02:00
Didier KorthoudtandGitHub e7d5afe90e New: Added dynamic attributes set for CSS based themes (#2701) 2020-04-06 13:10:34 +02:00
Didier KorthoudtandGitHub 2d9d354a05 Fixed: When a CPSearchField predicate was bound specifying a predicate format using more than one time the value of the search field ($value), only the first occurence was correctly set, others were set to null (#2777) 2020-04-06 13:03:57 +02:00
Didier KorthoudtandGitHub 60a686d75c Fixed: _CPMenuItemStandardView didn't take in account the left-margin theme attribute (#2689) 2020-04-06 09:18:31 +02:00
Didier KorthoudtandGitHub 89b70843c7 Fixed: Nib2cib was using hard coded frame adjustment for CPBox (#2761) 2020-04-06 09:17:05 +02:00
daboe01 9f9044b164 fixed: selection and backspace are recently broken in CPTokenField
this bug was introduced by commit 26aab29
 26aab29 was meant to improve iOS experience but makes the tokenfield loose FR status prematurely
2020-03-22 11:59:24 +01:00
Didier KorthoudtandGitHub 617987f4a5 Fixed: CPTextField forced height to 29 instead of using theme value min-size (#2688) 2020-03-17 09:14:25 +01:00
daboe01andGitHub 5bb338d6ec New: CPTextField now supports the CPEditorRegistrationProtocol (#2169) 2020-03-11 10:30:00 +01:00
Didier KorthoudtandGitHub 8003d91c4a Fixed: [CPControl setTextColor:] was missing theme state attribute (#2828) 2020-03-11 10:20:04 +01:00
Didier KorthoudtandGitHub a4ef18ddab FIXED: Before this PR, only CPControl could use nib2cib-adjustment-frame. (#2831) 2020-03-11 10:17:14 +01:00
daboe01andGitHub 82e55b4761 Fixed: _glyphsFrames and _glyphsOffsets could be used uninitialized in CPTextView (#2886) 2020-03-03 17:04:52 +01:00
Didier KorthoudtandGitHub 3a51c386ca Fixed: Before this PR, a click in a not first key responder CPTextField would misplace the caret if text alignment was not CPLeftTextAlignment #2887
Adapts the calculation of where in the textfield text the mouse is clicked to take in account the text alignment
2020-03-03 17:02:42 +01:00
Didier KorthoudtandGitHub 9868519cc0 Fix: Before this PR, the method attributeBySettingValue ignored existing values #2888 2020-03-03 16:43:29 +01:00
Didier KorthoudtandGitHub 038c007915 FIXED: Before this PR, CPButton never declares that its label should be single line (#2889) 2020-03-03 13:44:07 +01:00
Didier KorthoudtandGitHub ba6350e24d NEW: CPSplitView modern Cocoa behavior implementation (#2876) 2020-02-21 14:32:53 +01:00
Martin CarlbergandGitHub 1c866931f3 New: The compiler will now create warnings for variables that are never read (#2871)
This new warning adds a lot of warnings in the Cappuccino frameworks. I have turned off
the warning in the Jakefiles when compiling most of the Cappuccino frameworks. But it is
on as default for any user projects.

So there is also a new feature to turn off/on warnings as a compiler flag.

These flags are (all flags are on as default):
-Wunused-but-set-variable                    Warning when a local variable is never read.
-Wshadow-ivar                                Warning when a local variable is shadowing an instance variable for the class.
-Wcreate-global-inside-function-or-method    Warning when creating a global variable inside a function or method.
-Wunknown-class-or-global                    Warning when a class or global variable is not known.
-Wunknown-ivar-type                          Warning when the type for an instance variable is not known.

To turn off a flag add a 'no-' prefix. Example: -Wno-unused-but-set-variable

For an example how to use it in a Jakefile please look at the Jakefiles in this commit.
2020-02-21 14:29:48 +01:00
cacaodevandGitHub b7aff28b5e Merge pull request #2884 from daboe01/documentation-for-attributedStringWithAttachment
fixed: documentation for attributedStringWithAttachment: was missing
2020-02-04 13:14:45 +01:00
cacaodevandGitHub f0897ffdf8 Merge pull request #2885 from mrcarlberg/predicate_not_equal_nil
Fixed: CPPredicate does never evaluate to true for 'something != nil'
2020-02-04 13:06:37 +01:00
Martin Carlberg f5937a6fad Fixed: CPComparisonPredicate does never evaluate to true for a predicate like 'something != nil'
An example:

var array = @[ @{@"Name": @"1"},
               @{@"Name": @"2", @"approveTime": @"Now"}
             ];

var predicate = [CPPredicate predicateWithFormat:@"approveTime != nil"];

CPLog(@"result: %@", [array filteredArrayUsingPredicate:predicate]); // result: <empty array>

If I run this in Cappuccino I get an empty array

If I run it in Cocoa on a Mac I get:

result: (
        {
        Name = 2;
        approveTime = Now;
    }
2020-01-31 14:24:29 +01:00
Martin CarlbergandGitHub 7dc77ed609 Fixed: Make sure we handle undefined when testing for nil values (#2862) 2020-01-31 13:43:58 +01:00
daboe01 d1bfe62a65 removed: obsolete fixme comment 2020-01-12 17:08:23 +01:00
daboe01 f7c8f24bff removed dead code 2020-01-11 15:13:30 +01:00
daboe01 055d63eeb5 formatting 2020-01-11 15:13:04 +01:00
daboe01 ef7f92ed15 fixed: documentation for attributedStringWithAttachment: was missing 2020-01-11 15:09:41 +01:00
daboe01andMartin Carlberg 8daa53dd3e Fixed: CPTextView keyboard issue in recent Firefox versions (#2874)
Wrong characters were sometimes emitted in keyboard events on Firefox
2020-01-07 12:34:39 +01:00
Martin CarlbergandGitHub 109003ae12 Fixed: Performance improvement on visibleRect when updating tracking areas (#2881)
Calculations of the visible rect can be a heavy operation as it depends of the superviews
visible rect. They have to be transformed and intersected together to create the result.
This calculation is a recursive operation that travels up the view hierarchy all the way
to the top.

Updating the tracking areas is also a recursive operation. But it travels down the view hierarchy
instead. It will calculate the new tracking area using the visible rect for the current view. For
each view it updates it will get the visible rect that has to travel all the way up the view
hierarchy to calculate it by transform and intersect them together. This results in that the visible
rect will be calculated for each view multible times. This makes it a very slow operation, specially
for deeper view hierarchies.

This fix will send along the visible rect for the superview when traveling down the
view hierarchy. This means that the visible rect does not need to travel up the view hierarchy
to be calculated. It will just need transform and intersect the provided rect for its superview
with the one from itself.
2020-01-06 12:48:40 +01:00
Martin CarlbergandGitHub 36f1abdbe6 Fixed: CPAppearance has much better performance now when views needs layout. (#2880)
This makes updating views much faster when they need layout. This will make resizing browser window and changing split views sizes much smoother.

The effectiveAppearance is inherited from the superview. The superviews appearance is passed along as it is recomputed down the view hierarchy.
Also the current appearance is saved on each view to make it possible to only change the theme states when the appearance is changed.
2020-01-06 12:35:38 +01:00
Martin CarlbergandGitHub 50519776e0 Fixed: Update Tracking areas only once when doing setFrame: (#2879)
This speed improvement is very notable when resizing a browser window of altering the size of split views.
The fix uses a variable on the window to control when to update the tracking areas.
If the view is not connected to a window the tracking areas will not be updated as they will
be when added to a window.
2020-01-06 12:32:35 +01:00
daboe01andMartin Carlberg 9e289574ff Fixed: attachment support was missing in CPTextView (#2872) 2019-12-19 09:08:33 +01:00
cacaodevandGitHub 2d96b06d4c Merge pull request #2878 from didierkorthoudt/CPButton-continous-bug
FIXED: Before this PR, disabled buttons that were continuous responded to mouse clicks
2019-12-17 13:27:06 +01:00
cacaodevandGitHub 0c706cfed2 Merge pull request #2866 from mrcarlberg/cpset_enumerator_use_stop_variable
Fixed: CPSet enumerateObjectsUsingBlock: will honor the stop variable
2019-12-16 11:27:37 +01:00
Didier Korthoudt 3f9e1c6c47 Continuous fix 2019-12-15 10:48:21 +01:00
jasperdebandMartin Carlberg 58c08cebd7 Fixed: Added underline support in CPTextView (#2875) 2019-12-06 09:12:31 +01:00
Martin CarlbergandGitHub fed0703ecf New: Allow lazy loading control for combined observer key paths (#2854)
This pull request adds a kvoValueForKey: method used by _CPKVOForwardingObserver for getting values.
The default action is to just return the valueForKey value.
Override this method if you want any other behavior.
It can be used to just return nil if you are implementing a lazy load behavior on a class.
It can allow a combined binding to observe this object without trigger any lazy loading.
2019-11-28 09:38:55 +01:00
Martin CarlbergandGitHub aaed1a934e Fixed: When in modal mode make sure a focus event will make the modal window the key window (#2867)
The default was to make the first window in the most background layer the key window if nothing else was used.
This does not work well if there is a modal window that should have all the attention.

This solution will first choose the modal window if in modal mode. If not the old variant is used.
2019-11-27 19:16:03 +01:00
Martin CarlbergandGitHub 11639af26e Fixed: Let the text field handle key commands when it is selectable. (#2868)
There was a problem introduced by pull request #2600 when trying to select and copy text in a selectable text field.
The problem only occurs when the text field is in a modal window.

It was caused by the modal run loop that is present as an eventListener on the CPApplication when in modal mode. It will
force a bail out from the sendEvent: method in CPApplication when trying to handle the event. As the check for key
equivalent is moved down by the #2600 pull request it will never be allowed to handle a copy command from the browser
menu or key equivalent.

The solution is to allow the text field to handle the event if it is selectable. This will also allow the text selection
to be altered from the keyboard with keys like shift-left/right/up/down arrow. Combinations like shift-option arrow also
work for whole word selection.
2019-11-26 09:20:47 +01:00
cacaodevandMartin Carlberg 623ad2b1dc Fixed: Previoulsly, clearsFilterPredicateOnInsertion was applied (removes filter predicate) when setting content or on content binding. (#2860)
With Tests: Foundation unit test.
Solves #2791
2019-11-12 14:02:35 +01:00
Brian M. PalmaandMartin Carlberg ac68fec6b1 FIXED: Add Dark Mode to XcodeCapp (#2856)
FIXED: Add Dark Mode to XcodeCapp

This commit also fixes an issue where two NSBox instances had a borderRadius of 100 (boxStatus and boxImport) in IB. The borderRadius values are now updated to be half the size of the height and width values, restoring the circle visual.

Update installation directory for XcodeCapp

- Replace $LOCAL_APPS_DIR with $USER_APPS_DIR
- Installation directory updated from /Applications to ~/Applications
2019-10-18 13:37:22 +02:00
Martin Carlberg dad49354aa Fixed: CPSet enumerateObjectsUsingBlock: will honor the stop variable
Test case also added.

Deprecated the very old behaviour that the block could return YES to abort the enumeration.
2019-10-18 13:01:04 +02:00
daboe01andMartin Carlberg ce4fa3c027 Fixed: Pressing capslock inserted whitespaces under certain conditions (#2857)
This was present in a CPTextView.

A press on caps lock should also result in a flagsChanged event.
An event should have the corresponding flag for caps lock in modifierFlags.
2019-09-10 08:34:07 +02:00
meelashandMartin Carlberg 02d1a12bf5 Fixed: Update nonworking link (#2859) 2019-09-02 09:23:17 +02:00
cacaodevandGitHub 78cbc3871e Merge pull request #2852 from mrcarlberg/msg_send_return_undefined_when_receiver_is_undefined
New: msgSend will return undefined instead of nil when the receiver is undefined.
2019-08-31 11:28:17 +02:00
daboe01andMartin Carlberg f9bb882ceb Formatting: make readme a litte more concise (#2858)
removed the wrong attribute 'demo' for our tutorials link
2019-08-26 09:24:58 +02:00
cacaodevandGitHub 87fc6bde1d Merge pull request #2853 from mrcarlberg/optimize_table_column_bindings_with_long_key_paths
Fixed: Optimization for bindings with longer key paths in table views.
2019-08-17 18:33:33 +02:00
Martin Carlberg f666367b5d Fixed: Optimization for bindings with longer key paths in table views.
This prevents a buildup of large arrays with a lot of values and then choose one row and throw away the rest. This is great for speed and a must for lazy loading.

Previous there was a optimization for this but it was only effective for short combined key path like 'a.b'. This pull request makes it effective for any kind of length of a combined key path like 'a.b.c.d...'.

This is done by returning the object at a row in the first found CPArray in the key path
that is divided in a first and second part.
The first part is never a combined key path. The second part can be a combined key path.
If this optimization is not done we will create an array with the valueForKeyPath value on each row and then pick
the wanted value for the row and throw away all the other rows. It is much more effective to first
pick the row and then do the valueForKeyPath on the rest of the key path.
2019-06-26 09:37:40 +02:00
Martin Carlberg 81c02f24c6 New: msgSend will return undefined instead of nil when the receiver is undefined.
The msgSend function has always returned nil if the receiver has been nil or undefined.
Example:

var a = undefined;
var b = [a someSelector]; // b = nil

The variable b is now nil.

This pull request adds the ability for the msgSend function to return undefined if the receiver is undefined. If the receiver is nil it still return nil.
The above example again:

var a = undefined;
var b = [a someSelector]; // b = undefined

The variable b is now undefined.

Background.
The use of nil in Objective-C corresponds to the use of null in C and C++ and stands for "no value". A fundamental function in Objective-C is when you send a message to a receiver that is nil the result is again a nil value. This can be very convenient as you don't need the check if a receiver is nil before sending a message to it.

Objective-J adds the ability the use class structures and message send functionality for Javascript in the same way as Objective-C adds this to C and C++. Javascript also has the value null and is handled in the same way for Objective-J. Javascript also has the value undefined that stands for "not yet defined". This can sometimes be confusing and many try to handle null and undefined as the same thing. This is almost what Objective-J and the Cappuccino frameworks always has been doing. They try to always check for both null and ``undefined and always return nil but never undefined. We can say that Objective-J understands undefined but will try to translate it to nil.

Why do we need this change.
Javascript has both null and undefined and Objective-J is a superset of Javascript. It is now very hard to use undefined in Objective-J code as it will always try to translate it to nil. There are some Javascript libraries that use both null and undefined values and they are very hard to use from Objective-J programs today.  Same if you decide to use nil and undefined as a value in your own Objective-J code. This small change will make undefined a full member of the Objective-J language. It should be as it is a superset of Javascript. The Cappuccino frameworks will still handle undefined but only return nil.

How will it impact.
Today most methods will never return undefined so this is a very limited problem. If any does this the main concern is if some code will only check for nil and not undefined.
Example:

var a = [someObject someSelector];
If (a === nil) a = 42;
...

If the variable someObject contains undefined the variable a will now be set to undefined instead of nil.  But the variable a will then never be set to 42.
The correct way is to check for both nil and undefined like this:

var a = [someObject someSelector];
If (a == nil) a = 42;
...
2019-06-24 17:36:15 +02:00
Didier KorthoudtandMartin Carlberg 1d01ed92d8 Fixed: CPSearchField, when no recent searches were done, the menu wasn’t showing, even if search categories were specified (#2846) 2019-06-13 16:30:53 +02:00
daboe01andMartin Carlberg 1a7ced34a7 Fixed: smart copy/paste cycle was leaving an additional whitespace on the LHS (#2776) 2019-06-13 16:17:52 +02:00
Martin CarlbergandGitHub 47ab94b5bc Fixed: If sourcemap is available include it when building with a nondebug version of the tools. (#2850)
The sourcemap was not included when building from command line with the release (the default) version of the tools.
2019-06-13 16:15:48 +02:00
Didier KorthoudtandMartin Carlberg bea0778acc Fixed: Before this PR, cell based table views that use specific font, reverted to standard font when selected/editing (#2845) 2019-06-13 16:14:48 +02:00
Didier KorthoudtandMartin Carlberg eeba5087b1 Fixed: Before this, under certain circumstances, CPControl didn't declare its tracking areas soon enough (#2827) 2019-06-13 16:12:52 +02:00
daboe01andMartin Carlberg 7bd8bf02f6 Fixed: Typo in function call to _CPDecimalSetZero and made the test case work properly (#2820) 2019-06-13 16:09:17 +02:00
Martin CarlbergandGitHub 378f87a634 Fixed: Temporary fix so finding the compiler can work both in the Narwhal and Node world (#2793) 2019-06-13 16:06:15 +02:00
Didier KorthoudtandMartin Carlberg 76dd5383ab Fixed: A «feature» of Xcode where IB sometimes stores NSFont values in the alternate image property. (#2844) 2019-06-13 16:04:59 +02:00
daboe01andMartin Carlberg 129ca185e4 Fixed: backspace triggered page back in FireFox (#2849) 2019-06-13 15:59:35 +02:00
daboe01andMartin Carlberg 1f4ef42625 Fixed: index.html and index-debug.html were outdated in the manual tests (#2848) 2019-06-13 15:58:34 +02:00
daboe01andMartin Carlberg eb55e4def6 Fixed: keyboard issues from FF66 alignmets to the other major browsers (#2847) 2019-05-24 12:57:57 +02:00
Alexandre WilhelmandGitHub acda834ad9 Merge pull request #2819 from daboe01/CPFontManager-duplicate-methods-removal
fixed: removed outdated duplicate method definitions in CPFontManager.j
2019-04-14 20:02:22 -07:00
Alexandre WilhelmandGitHub aba3e30d9c Merge pull request #2824 from daboe01/cpdocumentcontroller-return-type-fix
fixed: return type of openUntitledDocumentOfType:display:
2019-04-14 20:01:36 -07:00
Alexandre WilhelmandGitHub c9e55ff1dc Merge pull request #2825 from daboe01/CPTableHeaderView-return-type-fix
fixed: wrong return type of textField method in
2019-04-14 20:01:21 -07:00
Alexandre WilhelmandGitHub 013c5e9e58 Merge pull request #2826 from daboe01/cpproxy-return-type-fix
fixed: wrong return type of forward::
2019-04-14 20:01:07 -07:00
Didier Korthoudt 5e7f3dc24a Merge remote-tracking branch 'cappuccino/master' 2019-04-13 10:25:08 +02:00
daboe01 2ffa174900 fixed: wrong return type of forward:: 2019-04-08 14:42:23 +02:00
daboe01 26cbe17218 fixed: wrong return type of textField method in 2019-04-08 14:39:02 +02:00
daboe01 fc8f1dca4e fixed: return type of openUntitledDocumentOfType:display: 2019-04-08 14:32:06 +02:00
Alexandre WilhelmandGitHub 6b20a4f8f7 Merge pull request #2786 from didierkorthoudt/CPStepper-enabled-from-IB
FIXED: CPStepper was not receiving the enabled status from IB
2019-04-03 15:15:22 -07:00
Alexandre WilhelmandGitHub 091025a304 Merge pull request #2794 from mrcarlberg/initialize_missing_on_custom_root_class
Fixed: the class method +initialize was always sent even if the root class did not implement it.
2019-04-03 15:07:59 -07:00
Alexandre WilhelmandGitHub 1180789c0c Merge pull request #2823 from daboe01/CPBrowserDelegateMethodFix
fixed: CPBrowserDelegate_browser_imageValueForItem_ was defined twice
2019-04-03 14:37:33 -07:00
Alexandre WilhelmandGitHub ac056d4a06 Merge pull request #2822 from daboe01/cpcursor-fix
fixed: accidental creation of a global variable in CPCursor.j
2019-04-03 14:36:44 -07:00
Alexandre WilhelmandGitHub 7c196e098a Merge pull request #2821 from daboe01/CPGraphicsContext-typo-fix
fixed: typo in initWithGraphicsPort: declaration
2019-04-03 14:36:23 -07:00
daboe01 c612b13529 fixed: CPBrowserDelegate_browser_imageValueForItem_ was defined twice 2019-03-31 19:42:54 +02:00
daboe01 5f4c79f6c4 fixed: accidental creation of a global variable 2019-03-31 17:44:07 +02:00
daboe01 302cbf641e fixed: typo in initWithGraphicsPort: declaration 2019-03-31 17:37:44 +02:00
daboe01 4bc2e9ad44 fixed: removed outdated duplicate method definitions in CPFontManager.j 2019-03-31 17:24:03 +02:00
Alexandre WilhelmandGitHub 6853c4c212 Merge pull request #2779 from daboe01/textview-contextmenu
New: support contextmenus for CPTextView
2019-03-29 15:06:39 -07:00
Alexandre WilhelmandGitHub 800e15cfd0 Merge pull request #2803 from bedalton/fixed-return-type-CPPlatformPasteboard
Fixed: Return type CPPlatformPasteboard.j should return BOOL
2019-03-29 15:04:14 -07:00
Alexandre WilhelmandGitHub 873f184117 Merge pull request #2798 from bedalton/formatting-add-semi-colons-to-CPTextStorage
Formatting: Add semi-colons to CPTextStorage.j
2019-03-29 15:03:47 -07:00
Alexandre WilhelmandGitHub 3bae5cd5cb Merge pull request #2807 from bedalton/fixed-duplicate-method-in-cparraycontroller
Fixed: Duplicate method in CPArrayController
2019-03-29 15:03:26 -07:00
Alexandre WilhelmandGitHub 19300332e8 Merge pull request #2813 from bedalton/fixed-cpcontrol-misspelled-parameter-type
Fixed: CPControl misspelled parameter type
2019-03-29 15:02:12 -07:00
Alexandre WilhelmandGitHub 89d88c9644 Merge pull request #2811 from bedalton/formatting-remove-redundant-semi-colon-in-cptextview
Formatting: Remove redundant semi-colon in CPTextView
2019-03-29 15:01:52 -07:00
Alexandre WilhelmandGitHub 472014b1b1 Merge pull request #2812 from bedalton/fixed-cpwindowcontroller-returns-cpview
Fixed: Changed CPWindowController method return to CPView
2019-03-29 15:01:29 -07:00
Alexandre WilhelmandGitHub c094420428 Merge pull request #2810 from bedalton/fixed-cpscroller-return-style
Fixed: Change CPScroller's style method return type to int
2019-03-29 15:01:13 -07:00
Alexandre WilhelmandGitHub e948064063 Merge pull request #2809 from bedalton/fixed-cocolor-parameter-to-cpstring
Fixed: Changed CPColor parameter to CPString
2019-03-29 15:00:59 -07:00
Alexandre WilhelmandGitHub bdd3b7d2a5 Merge pull request #2808 from bedalton/fixed-cpbrowserdelegate-duplicate-method
Fixed: CPBrowserDelegate duplicate protocol method
2019-03-29 15:00:46 -07:00
Alexandre WilhelmandGitHub 404c37b54c Merge pull request #2800 from bedalton/fixed-mismatched-parameters-in-CPTextView
Fixed: mismatched parameters in CPTextView
2019-03-29 15:00:18 -07:00
Alexandre WilhelmandGitHub ac686a17fa Merge pull request #2799 from bedalton/fixed-mispelled-parameter-in-CPTextView
Fixed: Misspelled parameter in CPTextView.j
2019-03-29 14:59:55 -07:00
Alexandre WilhelmandGitHub bfc5068b5e Merge pull request #2796 from bedalton/formatting-remove-redundant-semi-colon-CPDatePicker
Formatting: Remove redundant semi-colon form CPDatePicker
2019-03-29 14:59:39 -07:00
Alexandre WilhelmandGitHub 649bee2330 Merge pull request #2806 from bedalton/fixed-cpbezier-path-nsinteger
Fixed: CPBezierPath using parameter type NSInteger
2019-03-29 14:59:18 -07:00
Alexandre WilhelmandGitHub 593587ea60 Merge pull request #2805 from bedalton/fixed-cpapplication-isactive-return-type
Fixed: CPApplication's isActive method return type
2019-03-29 14:59:02 -07:00
Alexandre WilhelmandGitHub 0ab81a7b32 Merge pull request #2804 from bedalton/fixed-_cpautocompletemenu-return-type
Fixed:  Changed void return to id in CPAutocompleteMenu.j
2019-03-29 14:58:50 -07:00
Alexandre WilhelmandGitHub 8bf937357c Merge pull request #2801 from bedalton/formatting-remove-semi-colon-_CPStandardWindowView
Formatting: Remove extraneous semi-colon _CPStandardWindowView.j
2019-03-29 14:58:16 -07:00
Alexandre WilhelmandGitHub 1e8b1fda81 Merge pull request #2797 from bedalton/fixed-return-type-_CPMenuBarWindow
Fixed: Return type in _CPMenuBarWindow
2019-03-29 14:57:43 -07:00
daniel cba9212150 Fixed: CPControl parameter type to CPVerticalTextAlignment
Parameter was set to type CPTextVerticalAlignment, but typedef was declared as CPVerticalTextAlignment. This commit fixes this mismatch
2019-03-29 11:58:51 -07:00
daniel a0a6c42227 Fixed: Change return in CPWindowController to CPView
In CPWindowController, method viewControllerContainerView, implies that it returns something, and it does. It returns the _viewControllerContainerView instance object, and return type should be changed from void to CPView
2019-03-28 15:19:59 -07:00
daniel c245d9de80 Formatting: Removed redundant semi-colon in CPTextField 2019-03-28 14:55:44 -07:00
daniel ca5cd8e09b Fixed: CPScroller's style method should have returned int
Despite returning an int and a comment stating it returns a value, CPScroller's style method had a return type of void.
2019-03-28 14:51:15 -07:00
daniel 34ac4cd23b Fixed: Changed CPColor parameter from string to CPString
Parameter type in CPColor was string, changed here to CPString, to make it more inline with other string parameter types.
2019-03-28 14:33:00 -07:00
daniel 8598fe8ad9 Revert "Fixed: Changed CPColor parameter type from string to CPString"
This reverts commit 06498672ab.
2019-03-28 14:32:00 -07:00
daniel 3699a31f28 Revert "Formatting: Removed extra semi-colon ThemeDescriptors.j"
This reverts commit 75cd5a75e1.
2019-03-28 14:31:17 -07:00
daniel 06498672ab Fixed: Changed CPColor parameter type from string to CPString
Parameter type in CPColor was string, changed here to CPString, to make it more inline with other string parameter types.
2019-03-28 14:29:50 -07:00
daniel cd3b92af7a Fixed:CPBrowserDelegate duplicate method
Method browser:imageValueForItem was defined twice in CPBrowserDelegate. Once on line 43, and the other on line 45. This removes the commit removed the second declaration
2019-03-28 14:26:08 -07:00
daniel e15cc29e2f Fixed: CPBezierPath used parameter types NSInteger
CPBezierPath has parameter type NSInteger. Assumed meant to be CPInteger
2019-03-28 14:19:09 -07:00
daniel dabeb066de Fixed: Duplicate method in CPArrayController.j
Method alwaysUsesMultipleValuesMarker is duplicated in class CPArrayController.j. Both methods have the same body and same selector, the only difference being their comments are slightly different. Both comments however communicate the same thing.
2019-03-28 14:11:28 -07:00
daniel c59e7cc456 Fixed: Changed CPApplication's isActive method return type to BOOL
Method has declared return type of void, despite the method returning a BOOL, and the name implying that it returns a BOOL
2019-03-28 14:08:00 -07:00
daniel 3c16bfef58 Fixed: Changed void return type to id in _CPAutocompleteMenu.j
tableView:objectValueForTableColumn:row: has return type set to void, despite returning an method call returning id. Similar methods also return id.
2019-03-28 14:01:31 -07:00
daniel ec24977d4e Revert "Fixed: changed void return type in _CPAutocompleMenu.j to id"
This reverts commit 10b4f57a0e.
2019-03-28 13:58:15 -07:00
daniel 10b4f57a0e Fixed: changed void return type in _CPAutocompleMenu.j to id
tableView:objectValueForTableColumn:row: has return type set to void, despite returning an method call returning id. Similar methods also return id.
2019-03-28 13:54:20 -07:00
daniel 75cd5a75e1 Formatting: Removed extra semi-colon ThemeDescriptors.j 2019-03-28 13:46:25 -07:00
daniel fb2d0ed170 Fixed: Return types in CPPlatformPasteboard.j should return BOOL
Method _mayRequireDOMPasteboardElementHack:flags had return type of void, despite returning a BOOL value, and also being annotated with a doc comment stating that it should return a BOOL value

Also some methods had return type "boolean", should possibly be BOOL
2019-03-28 13:42:39 -07:00
daniel d4e139eaf5 Fixed: Return type for method in CPPlatformPasteboard.j
Method return type is labeled as void despite returning a BOOL and the DOC comment stating that it should return a BOOL
2019-03-28 13:38:33 -07:00
daniel 49ef1276f6 Formatting: Removed extra semi-colon in _CPStandardWindowView.j 2019-03-28 13:32:56 -07:00
daniel 828788b643 Fixed: Mismatched parameter types in CPTextView
isBlinking had return type void, but returned a BOOL, and the method name implies that it returns a BOOL.

Some parameters were marked as integer, so were changed to int to coincide with most other names for integers.

setString had parameter type id, despite implying that parameter was a string, and the super class parameter being a string.
2019-03-28 13:21:34 -07:00
daniel 0ca8d7a258 Fixed: Mispelled DOMElement parameter in CPTextView.j 2019-03-28 13:12:30 -07:00
daniel 75de49d151 Formatting: Added potentially missing semi-colons in CPTextStorage 2019-03-28 13:08:19 -07:00
daniel d39e1c0c6e Fixed: Return type for font method should have been CPFont 2019-03-28 12:51:04 -07:00
daniel 8e7f201ca8 Formatting: Removed redundant semi-colon in CPDatePicker 2019-03-28 12:49:13 -07:00
daniel 85b2a9b0a0 Merge remote-tracking branch 'upstream/master' 2019-03-28 12:41:13 -07:00
Martin Carlberg d4e40faecc Fixed: the class method +initialize was always sent even if the root class did not implement it. Forwarding and Invocation worked also on the method.
To mimic Objective-C correctly the +initialize method should not be sent on a class hierarchy if it does not implement it.
Also the +initialize method should not allowed to be forwarded to another target with Invocation or any other forwarding mechanism.
Added test cases for this and cleaned up some test cases that succeeded for the wrong reason.

This change will not affect many projects as all classes inherit from CPObject.
2019-02-20 23:44:00 +01:00
Alexandre WilhelmandGitHub 0be913ab18 Merge pull request #2780 from daboe01/fixed-wrong-return-type-in-_CPCibClassSwapper
fixed: wrong return type in initializer of _CPCibClassSwapper
2019-02-06 16:58:44 -08:00
Alexandre WilhelmandGitHub bab30f3b61 Merge pull request #2781 from daboe01/fixed-wrong-return-type-in-shouldArchiveValueForKey-
fixed: wrong return type in shouldArchiveValueForKey:
2019-02-06 16:58:25 -08:00
Alexandre WilhelmandGitHub abb599c556 Merge pull request #2782 from daboe01/fixed-wrong-return-type-of-_menuWithName-
fixed: wrong return type of _menuWithName:
2019-02-06 16:57:59 -08:00
Alexandre WilhelmandGitHub 2b6c6e9ab3 Merge pull request #2783 from daboe01/fixed-wrong-return-type-of-backgroundColorForGlyphAtIndex-
fixed: wrong return type of backgroundColorForGlyphAtIndex:
2019-02-06 16:57:42 -08:00
Alexandre WilhelmandGitHub 0db0d744da Merge pull request #2784 from daboe01/fixed-wrong-return-type-in-isMovable
fixed: wrong return type in isMovable
2019-02-06 16:57:21 -08:00
Alexandre WilhelmandGitHub 6b9f2d29fc Merge pull request #2785 from daboe01/fixed-wrong-return-type-of-isMiniaturized
fixed: wrong return type of isMiniaturized
2019-02-06 16:56:49 -08:00
bedalton 2c141a5255 Revert "Fixed: CPMenuBarWindow did not return a value when expected"
This reverts commit 4f69a32700.
2019-01-04 17:24:14 -08:00
bedalton 4f69a32700 Fixed: CPMenuBarWindow did not return a value when expected
CPMenuBarWindows's font: method did not return a value when a CPFont was expected.
2019-01-04 17:24:05 -08:00
Didier Korthoudt 3d4c08ad20 CPStepper-enabled-from-IB 2018-10-27 12:01:57 +02:00
Didier Korthoudt 594d415976 Merge remote-tracking branch 'cappuccino/master' 2018-10-27 11:59:52 +02:00
daboe01 ae50fde4e1 fixed: wrong return type of isMiniaturized 2018-10-14 16:49:49 +02:00
daboe01 401461f1f0 fixed: wrong return type in isMovable 2018-10-14 13:36:53 +02:00
daboe01 676c6e106d fixed: wrong return type of backgroundColorForGlyphAtIndex: 2018-10-14 13:34:33 +02:00
daboe01 d0a066ccd3 fixed: wrong return type of _menuWithName: 2018-10-14 13:31:20 +02:00
daboe01 340e286277 fixed: wrong return type in shouldArchiveValueForKey: 2018-10-14 13:27:38 +02:00
daboe01 9211d41984 fixed: wrong return type in initializer of _CPCibClassSwapper 2018-10-14 13:24:03 +02:00
daboe01 686aa457d6 fixed: cut/copy did not autoenable 2018-10-07 21:22:37 +02:00
daboe01 1b1431f0d0 menu cleanup 2018-10-07 21:08:31 +02:00
daboe01 cbcb46597d fixed: syntax errors 2018-10-06 23:19:50 +02:00
daboe01 6644c85978 new: context menu support for CPTextView 2018-10-06 23:16:46 +02:00
Didier KorthoudtandMartin Carlberg dd781fd86b Fixed: Enabled state for CPSlider was not converted by nib2cib (#2775) 2018-09-17 09:11:36 +02:00
Didier Korthoudt 97209846ad Merge remote-tracking branch 'cappuccino/master' 2018-09-16 10:55:10 +02:00
Alexander Ljungberg 9744aa79b3 Fixed: doc generation crash due to bad UTF.
Not clear what's causing the problem, but the ± in CPDate.j get prefixed with some garbage bytes in the Doxygen output even that the input encoding looks fine.

The work-around for now is to just discard the bad bytes in the Python step. The output comes out correct (with the ± preserved).
2018-09-10 12:14:35 +01:00
Didier Korthoudt 801648fcfe Merge remote-tracking branch 'cappuccino/master' 2018-09-10 11:45:02 +02:00
Alexander Ljungberg 181fb9edb6 New: Dockerfile which helps test bootstrap itself. 2018-09-10 10:39:07 +01:00
Alexander Ljungberg 9ddbdfb80a Fixed: new rsync-base option always on.
Whether enabled or not, this new feature would try to rsync from the current folder.

Refs #2769.
2018-09-10 10:21:57 +01:00
Alexander Ljungberg 1cab88181d Fixed: XcodeCapp good build reporting failure.
After a recent change, a successful build of XcodeCapp actually resulted in the message "Unable to build  XcodeCapp."
2018-09-07 23:52:08 +01:00
Alexander Ljungberg 803cc6e402 New: bump XcodeCapp copyright year. 2018-09-07 23:49:20 +01:00
Alexander Ljungberg 2e0b9f5ea5 New: tweak flash view warning. 2018-09-07 23:16:31 +01:00
Alexander Ljungberg fe60311072 This is Cappuccino 1.0. 2018-09-06 18:38:52 +01:00
Alexander Ljungberg de4a1d1e93 Fixed: building the source link. 2018-09-03 14:54:48 +02:00
David RichardsonandMartin Carlberg de36a2f4d1 Fixed: Update XcodeCapp code and Jakefile to install properly and launch without crashing when built with Xcode 10/ macOSSDK10.14 (#2773)
Disable YRKSpinningProgressIndicator threaded animation (causing immediate crash on launch since Xcode 10 beta 6).
Simplify Jakefile build process to 'xcodebuild install' to avoid permissions problems when building (still requires sudo when using Cappuccino defaults).
Increment version number to 4.0.1 to reflect minor nature of change.
Update About window version number field to fully display and properly align longer version numbers.
2018-09-03 10:29:43 +02:00
David RichardsonandMartin Carlberg 57f6f3b318 New: Add --rsync-base option to bootstrap.sh (#2769)
Existing --copy-local option to use local cappuccino-base has following limitations which are cumbersome when updating bootstrap process
* local source must be a zip archive - which requires an extra step after making changes to source
* local zip archive is erased after install - which makes repetitive testing more difficult

This commit intended to allow testing of updates to cappuccino-base narwhal-jsc.
With macOS 10.14 and later, narwhal-jsc must use modern c++ standard library.
libedit must do the same, so the dependency reference to it is updated to version which is current at this time.
2018-08-31 14:51:20 +02:00
Didier KorthoudtandMartin Carlberg 8a3eb0d83b FIXED: This PR fixes a side effect of #2744 on table data views (#2767)
When a textField is used in a tableView, setValue forThemeAttribute
should refer to state CPThemeStateTableDataView instead of
CPThemeStateNormal as #2744 is now more accurate on states.
2018-08-31 09:14:54 +02:00
Didier KorthoudtandMartin Carlberg dd68b37a2a Fixed: CPButtonBar with actionPopupButton was outside the visible rect. Fixes #2770 (#2771) 2018-08-31 09:10:44 +02:00
cacaodev 96793b39ba Revert "Fixed: Define header-view-height attribute in ThemeDescriptors. (#2764)"
This reverts commit 9ad44e6dc2.
2018-08-30 16:02:48 +02:00
Didier Korthoudt aa4f91b3b0 Merge remote-tracking branch 'cappuccino/master' 2018-08-30 09:13:16 +02:00
cacaodevandMartin Carlberg 9ad44e6dc2 Fixed: Define header-view-height attribute in ThemeDescriptors. (#2764)
This is a better solution for problem introduced by  #2758
2018-08-29 14:28:02 +02:00
Didier KorthoudtandMartin Carlberg 988b3d27b8 Fixed: allowFadingOut side effect introduced by #2718 (#2768) 2018-08-29 11:27:50 +02:00
Didier Korthoudt 043cc13bb3 Merge remote-tracking branch 'cappuccino/master' 2018-08-27 23:24:52 +02:00
Didier KorthoudtandMartin Carlberg c0ee5f6563 Fixed: Problem with nib2cib introduced by #2758 (#2763) 2018-08-22 09:41:37 +02:00
cacaodevandMartin Carlberg f02d0ab1dd STYLE: refactoring using CPCharacterSet (#2762) 2018-08-22 09:18:21 +02:00
Didier Korthoudtandcacaodev ff6d17990f FIXED: Before this PR, starting a column drag within a scrolled down table would scroll the table content to the bottom (#2753)
Before this commit, in the method [view convertPoint:aPoint toView:aView] : aPoint was not in the coordinate system of view.
2018-08-21 19:26:42 +02:00
Didier Korthoudt 9be52f639d Merge remote-tracking branch 'cappuccino/master' 2018-08-21 11:22:22 +02:00
Didier KorthoudtandMartin Carlberg 586c419c46 Fixed: Before this PR, Nib2Cib wasn't using theme attribute "header-view-height" (#2758) 2018-08-21 09:26:40 +02:00
Didier KorthoudtandMartin Carlberg 73f02cc1bf NEW: Add first responder management based on content view to CPScrollView (#2757) 2018-08-21 09:14:21 +02:00
Didier KorthoudtandMartin Carlberg eb27d24923 Fixed: CPTableView-visual-problem(#2755) 2018-08-21 09:08:16 +02:00
Didier KorthoudtandMartin Carlberg eeb056d509 Fixed: CPCursor-fix-and-update, some cursors were badly or not displayed (#2752) 2018-08-20 13:35:18 +02:00
Didier KorthoudtandMartin Carlberg 3615912ce0 Fixed: Before this PR, loading of a CPTabView from a CIB file could crash the app (#2686) 2018-08-20 13:30:02 +02:00
Didier KorthoudtandMartin Carlberg 4518dfc0ca FIXED: CPSearchField was positionning its menu with hard coded offset (#2691) 2018-08-20 13:12:22 +02:00
Didier KorthoudtandMartin Carlberg d5a7e8938d CPTrackingArea-manual-test-fix (#2759) 2018-08-20 13:01:52 +02:00
Didier KorthoudtandMartin Carlberg 3d15844f7c CPTableView-bad-selection-color (#2756) 2018-08-20 12:55:35 +02:00
Didier KorthoudtandMartin Carlberg 62ed21b316 Fixed: Before this PR, when dragging a partially masked column, the dragged view was truncated (#2754) 2018-08-20 12:54:17 +02:00
daboe01andMartin Carlberg a178d43b01 Fixed: return key was not swallowed by textview but triggered the default button. (#2598) 2018-08-20 12:52:53 +02:00
daboe01andMartin Carlberg ef2152beea FIXED: click to key window in the background did not bring it forward (take 2) (#2705) 2018-08-20 12:48:58 +02:00
daboe01andMartin Carlberg e6475c1a9e Fixed: setMinSize: had no effect in platform windows (#2609) 2018-08-20 12:47:29 +02:00
daboe01andMartin Carlberg 091debd2a7 Fixed: deviation from cocoa when calling setTitle:'' (#2607) 2018-08-20 11:40:00 +02:00
daboe01andMartin Carlberg e7eff3e585 Fixed: attached sheets were not put to front after calls to makeKeyAndOrderFront: in code (#2606) 2018-08-20 11:34:21 +02:00
daboe01andMartin Carlberg 86a90e39d2 Fixed: key equivalent takes precedence over event tracking which is not the case in cocoa (#2600) 2018-08-20 11:31:36 +02:00
Didier KorthoudtandMartin Carlberg 2887fbb251 Edge-browser-detection (#2751) 2018-08-20 11:29:28 +02:00
daboe01andMartin Carlberg fa6177b56d Fixed: touch scrolling events to scrollviews also scrolled the document body (#2750) 2018-08-20 11:05:13 +02:00
Alexandre WilhelmandMartin Carlberg 78524cdde7 Fixed: deprecate FlashView, this old technology is now almost banned for all browser (#2748) 2018-08-20 10:59:27 +02:00
Didier KorthoudtandMartin Carlberg 6077d3f4c3 FIXED: _CPThemeAttribute valueForState was, under some circumstances, ignoring theme settings (#2744) 2018-08-20 10:50:08 +02:00
cacaodevandMartin Carlberg fd3acc456d FIXED: Before this commit, CPScrollerView was not correctly differentiating the scroller-width attribute (#2740)
between the vertical scroller and the horizontal scroller.
The scroller width was picked from a common CPScroller instance cached in CPScroller.
The bug was not visible because the method -_adjustScrollerSize was correcting the size repeatidly in the layout pass.
This was causing unnecessary calls to setFrame: in a performance sensitive method (triggered by scroll events).
The ScrollView is now picking the right width from the theme and a setFrame: call is skipped.

Tests: Manual/CPScrollView2/

TODO : The _adjustScrollerSize method should be enabled with a flag only when a width change is explicitely requested.
2018-08-20 10:36:59 +02:00
Didier Korthoudt 257fe6dd3f Merge remote-tracking branch 'cappuccino/master' 2018-08-16 14:54:27 +02:00
Alexandre WilhelmandGitHub cbb7cc93e9 Merge pull request #2739 from Rosch/origin/bugfix-cpdateformatter-german
FIXED: CPDateFormatter not ready for german dates. Fixes #2736
2018-08-10 18:15:16 -07:00
Alexandre WilhelmandGitHub 8b608c5cce Merge pull request #2745 from daboe01/removed--dead-code-from-nativehost
Removed:  dead code from nativehost
2018-08-10 17:58:03 -07:00
Alexandre WilhelmandGitHub 163fb14d0e Merge pull request #2746 from didierkorthoudt/CPTableView-viewWillMoveToWindow-fix
FIXED: In CPTableView, viewWillMoveToWindow was incorrectly removing an observer on the wrong window
2018-08-10 17:57:43 -07:00
Didier Korthoudt e4f87bd805 CPTableView-viewWillMoveToWindow-fix 2018-08-08 22:06:42 +02:00
Didier Korthoudt 864c7d96f1 Revert "NEW: Added automatic Xcode syntax coloring"
This reverts commit 0a497d8c3f.
2018-08-08 22:05:57 +02:00
daboe01 e35e87fb05 fixed: obsolete code remnants from nativehost 2018-08-08 19:19:58 +02:00
Didier Korthoudt 3246bdbc8b Merge remote-tracking branch 'cappuccino/master' 2018-08-08 11:03:03 +02:00
daboe01 710bb5fe1c Merge remote-tracking branch 'cappuccino/master' 2018-08-08 09:32:07 +02:00
bedaltonandcacaodev 53a52da709 Fixed: _CPObjectAnimator initialize method did not match superclass return (#2743)
* Fixed: _CPObjectAnimator initialize method did not match superclass return

_CPObject animator labeled return type as BOOL, not only is this value not returned, but the super class CPProxy's initialize method returns void. Changed return type to void in _CPObject animator, to match superclass
2018-08-03 08:28:05 +02:00
Martin Carlberg 4afbfc6e57 Revert "Removed: dead code and dom clutter from nativehost (#2608)"
This reverts commit 5bfafaf241.
2018-07-11 09:20:49 +02:00
daboe01 f7aa9efe13 Merge remote-tracking branch 'cappuccino/master' 2018-07-09 22:24:30 +02:00
Rosch 1fce0ee551 fix CPDateformatter for german dates 2018-07-08 09:15:58 +02:00
daboe01andMartin Carlberg ee695f9000 Fixed: CPSearchPanel did not support PredicateBindings (#2699) 2018-07-05 15:44:36 +02:00
daboe01andMartin Carlberg 8f99ebb577 Fixed: optical clutter, link duplications in readme (#2729) 2018-07-05 13:01:41 +02:00
daboe01andMartin Carlberg 5bfafaf241 Removed: dead code and dom clutter from nativehost (#2608) 2018-07-05 11:40:10 +02:00
Kjell NilssonandMartin Carlberg 64e1a2ce4b Fixed: Protocol selector signature spell error (#2734) 2018-07-05 11:25:38 +02:00
Didier Korthoudtandcacaodev 27883c5beb FIXED: This fixes problems with default theme attributes values in CPTableView (#2732) 2018-06-21 16:47:21 +02:00
Didier Korthoudt 2772725fe3 Merge remote-tracking branch 'cappuccino/master' 2018-06-17 08:55:10 +02:00
Didier Korthoudtandcacaodev 3f58a64827 FIXED: Before this PR, images specified in IB could crash the app at Cib loading (#2731) 2018-06-16 19:14:58 +02:00
Didier Korthoudt cb37c7d218 Merge remote-tracking branch 'cappuccino/master' 2018-06-16 12:25:12 +02:00
daboe01 88c1846e1a fixed: interpunctation 2018-06-12 13:23:56 +02:00
daboe01 be3d559d8a fixed: optical clutter, link duplications 2018-06-12 13:20:00 +02:00
daboe01 45d951071f Merge remote-tracking branch 'cappuccino/master' 2018-06-12 13:13:57 +02:00
daboe01andMartin Carlberg 2ac60684fa Fixed: CPDateFormatter was agnostic for the German language (#2724) 2018-06-08 14:00:25 +02:00
daboe01andMartin Carlberg af71742110 Fixed: Update README.markdown 2018-06-08 13:57:13 +02:00
daboe01andMartin Carlberg 1adf27427a Fixed: The word Site changed to Application in index.html (#2723) 2018-06-04 13:51:15 +02:00
daboe01 082f78eefa Merge remote-tracking branch 'cappuccino/master' 2018-06-03 19:44:23 +02:00
cacaodevandGitHub a8a9ec0023 Fix typo in CPPredicateEditorTest
Fixes #2714
2018-06-02 08:18:01 +02:00
Didier Korthoudt df4f945bc6 Merge remote-tracking branch 'cappuccino/master' 2018-05-29 12:04:40 +02:00
daboe01andcacaodev ad756a5b86 fixed: some mouse events were not propagated when sheets are open (#2603)
* fixed: mouse dragged was not propagated to parent window when sheet was opened

* better detection for attached sheet
2018-05-29 07:17:41 +02:00
Didier Korthoudtandcacaodev 2ca99ba669 FIXED: Before this PR, CPScroller theme attribute knob-inset was partially used (#2720) 2018-05-29 07:15:47 +02:00
daboe01andcacaodev 6443575114 fixed: nib2cib was susceptible to IB button issue with alternateImage (#2721) 2018-05-28 21:10:40 +02:00
Didier Korthoudtandcacaodev 52a51d81f1 FIXED: Before this PR, legacy scroller won't ever loose its selected theme state (#2718) 2018-05-28 20:56:00 +02:00
Didier Korthoudtandcacaodev 7d20b06e52 FIXED: Before this PR, CPSearchField was handling cursor updates by direct DOM manipulation. This PR uses tracking areas. (#2684) 2018-05-28 20:46:28 +02:00
Didier Korthoudtandcacaodev ad9d8a7200 FIXED: When attaching an externally owned tracking area to a view, view tracking areas were not correctly handled (#2681)
This PR also removes the -removeAllTrackingAreas method.
2018-05-28 20:44:31 +02:00
Didier Korthoudt 44da699343 Merge remote-tracking branch 'cappuccino/master' 2018-05-23 23:49:51 +02:00
cacaodev 0a542f9be9 Revert "FIXED: _CPTableColumnHeaderView was using hard coded values instead of theme values (#2703)"
This reverts commit e27bb4de50.
2018-05-17 05:48:59 +02:00
Didier Korthoudt 8f1601ca55 Merge remote-tracking branch 'cappuccino/master' 2018-05-15 22:03:16 +02:00
Didier Korthoudtandcacaodev ff0ce164da FIXED: CPTabView tabs were not reacting as in modern Cocoa (#2694)
Before this commit, the segments of a tab view were selected at mouse down. Now, they are selected at mouse up and there is a visible feedback when tracking tabs.
2018-05-14 07:10:43 +02:00
Didier Korthoudtandcacaodev 3650c38aa9 FIXED: In CPSearchField, the space between the magnifier and the textfield was hard coded (#2690) 2018-05-13 19:49:18 +02:00
Didier Korthoudtandcacaodev 61d97338e2 FIXED: _CPMenuItemStandardView was bypassing theme for disabled text color (#2695) 2018-05-13 19:48:33 +02:00
Didier Korthoudtandcacaodev e27bb4de50 FIXED: _CPTableColumnHeaderView was using hard coded values instead of theme values (#2703) 2018-05-13 19:47:53 +02:00
Didier Korthoudtandcacaodev 4462d8bf22 CHANGED: CPSegmentedControl code refactoring (#2704) 2018-05-13 19:40:33 +02:00
Didier Korthoudt f23a8ac928 Merge remote-tracking branch 'cappuccino/master' 2018-05-12 01:05:02 +02:00
daboe01 9e1750ee13 Merge remote-tracking branch 'cappuccino/master' 2018-05-08 22:46:19 +02:00
Didier Korthoudtandcacaodev 5cd284cb36 FIXED: CPSearchField menu diverged from modern Cocoa (#2697) 2018-05-07 21:52:06 +02:00
daboe01andcacaodev 5ea58430fe fixed: typo in theme attribute name (#2683) 2018-05-07 20:05:49 +02:00
Didier Korthoudtandcacaodev acfbda0d91 FIXED: CPSearchMenu _updateSearchMenu wasn't disabling autoenabling of menu items (#2693) 2018-05-07 19:57:35 +02:00
Didier Korthoudtandcacaodev 437e5a62f1 FIXED: CPSearchField -(void)resetSearchButton was using valueForThemeAttribute instead of currentValueForThemeAttribute (#2692) 2018-05-07 19:57:01 +02:00
Didier Korthoudtandcacaodev bbb5aea77d FIXED: CPTableView was using hard coded values instead of theme attributes (#2698) 2018-05-07 19:54:46 +02:00
Didier Korthoudt b5d04f84e7 Merge remote-tracking branch 'cappuccino/master' 2018-05-06 20:44:07 +02:00
cacaodevandGitHub 93b12eb14f Revert "FIXED: click to key window in the background did not bring it forward" (#2702)
Fixes #2700
2018-05-06 14:07:25 +02:00
Didier Korthoudt 0aa67c4cf2 Merge remote-tracking branch 'cappuccino/master' 2018-05-05 23:26:35 +02:00
Didier Korthoudtandcacaodev 1c1affac5c FIXED: CPTableHeaderView was faulty using inset.right instead of inset.left 2018-05-05 10:47:24 +02:00
Didier Korthoudt 0919797edc Merge remote-tracking branch 'cappuccino/master' 2018-05-04 23:22:49 +02:00
Didier Korthoudtandcacaodev 94ac8fd837 FIXED: Before this PR, CPButton setImage was also changing the alternate image 2018-05-04 16:16:01 +02:00
cacaodevandGitHub ecc82b5b51 Fixed: Typo CGSizZero() -> CGSizeMakeZero()
In BadgedOutlineView.j from SmartFoldersDemo test application.
2018-05-02 19:38:37 +02:00
daboe01 add021fd67 Merge remote-tracking branch 'cappuccino/master' 2018-04-28 16:35:05 +02:00
Didier Korthoudt 2daf309d7d Merge remote-tracking branch 'cappuccino/master' 2018-04-27 14:15:30 +02:00
Didier KorthoudtandMartin Carlberg 20be4a40ea NEW: Add CSS theming support (#2613) 2018-04-27 09:27:01 +02:00
bedaltonandcacaodev 7de91da87a Fix: rename _placeHolderView and add accessors in CPTabView.j (#2678)
In CPTabView.j on line 706 method is called `[_source
_setPlaceholderView:aValue]; ` this method does not exist, and this
call could possibly be expecting the property `_placeHolderView` to
have an accessor method. To add the correct accessor method the case of
either the method call selector or the variable must change —
`_placeHolderView` vs `_placeholderView`. For this fix I selected the
`_placeholderView` spelling, as opposed to the alternative with the
capitalized “holder” in “placeholder.”

Fixes #2674
2018-04-13 12:09:46 +02:00
cacaodevandMartin Carlberg b8b48b533b Previously, animating frame origin was not working on recent versions of Gecko (Firefox). The format for the matrix value of the transform property was matrix(a,b,c,d,tx,ty) and tx and ty had the "px" suffix. According to this page, tx and ty were accept (#2677)
ed (in my understanding not required) to have this suffix before FireFox
16.

All parameters are now numbers without suffix and this code will work
for all FireFox versions supporting css animations.
2018-04-12 11:25:43 +02:00
daboe01andMartin Carlberg 535f981739 Fixed: TextView bindings wrote data on blur even when not editable (#2582) 2018-04-12 09:25:12 +02:00
cacaodevandGitHub 0404ac0395 Merge pull request #2602 from daboe01/issue2545
FIXED: click to key window in the background did not bring it forward
2018-04-12 09:11:57 +02:00
daboe01 f961625f03 fixed: some events were not propagated to the bridge window 2018-04-11 18:30:47 +02:00
daboe01andMartin Carlberg 7909bae065 Fixed: touch-scrolling did not work (#2612) 2018-04-09 13:19:18 +02:00
bedaltonandMartin Carlberg 7e6e117b17 Formatting: add semi-colon to CPTextContainer.j (#2675) 2018-04-09 12:40:41 +02:00
bedaltonandMartin Carlberg 314e64d7b1 Formatting: add semi-colon to CPTextView.j (#2676) 2018-04-09 12:40:11 +02:00
daniel 8c72e77e0d Merge remote-tracking branch 'cappuccino/master' 2018-04-08 12:07:30 -07:00
Alexandre WilhelmandGitHub 77eb6b358f Merge pull request #2626 from daboe01/fix-cptextview-backspace-issue
fixed: backspace did not trigger scrolling properly 927d6d2
2018-04-08 11:37:19 -07:00
Alexandre WilhelmandGitHub 25f86e2542 Merge pull request #2616 from bedalton/add-idea-to-gitignore
New: Add *.idea to .gitignore
2018-04-08 11:36:25 -07:00
Alexandre WilhelmandGitHub 8bc0461463 Merge pull request #2627 from bedalton/formatting-add-semi-colon-cpcolorpicker
Formatting: add missing semi-colon to CPColorPicker.j
2018-04-08 11:35:34 -07:00
Alexandre WilhelmandGitHub c9dd1ea71d Merge pull request #2628 from bedalton/formatting-add-semi-colon-cpdatepicker
Formatting: add missing semi-colons to CPDatePicker.j
2018-04-08 11:35:26 -07:00
Alexandre WilhelmandGitHub 85d3529bc3 Merge pull request #2629 from bedalton/formatting-add-semi-colon-cpdatepickertextfield
Formatting: add semi-colon CPDatePickerTextField.j
2018-04-08 11:35:10 -07:00
Alexandre WilhelmandGitHub 3aac8bb072 Merge pull request #2630 from bedalton/formatting-add-semi-colon-cpevent
Formatting: add missing semi-colons to CPEvent.j
2018-04-08 11:34:59 -07:00
Alexandre WilhelmandGitHub e9a7e21aca Merge pull request #2638 from bedalton/formatting-add-semi-colon-cptableview
Formatting: add semi colon to CPTableView.j
2018-04-08 11:34:47 -07:00
Alexandre WilhelmandGitHub fc0e4da33c Merge pull request #2641 from bedalton/formatting-add-semi-colon-cpwindow
Formatting: add missing semi-colons to CPWindow.j
2018-04-08 11:33:54 -07:00
Alexandre WilhelmandGitHub ef3c1315dd Merge pull request #2642 from bedalton/formatting-add-semi-colon-cpwindowcontroller
Formatting: add semi-colon to CPWindowController.j
2018-04-08 11:33:45 -07:00
Alexandre WilhelmandGitHub 5c12890faf Merge pull request #2643 from bedalton/formatting-add-semi-colon-to-cpfontpanel
Formatting: add semi-colon to CPFrontPanel.j
2018-04-08 11:33:33 -07:00
Alexandre WilhelmandGitHub c4fd5e0999 Merge pull request #2645 from bedalton/formatting-add-semi-colons-cpdatepickercalendar
Formatting: add missing semi-colons to _CPDatePickerCalendar.j
2018-04-08 11:33:24 -07:00
Alexandre WilhelmandGitHub d3c56d635d Merge pull request #2669 from bedalton/formatting-add-semi-colons-cpplatformwindow
Formatting: minor formatting updates in CPPlatformWindow+DOM.j
2018-04-08 11:32:58 -07:00
Alexandre WilhelmandGitHub c53cb49034 Merge pull request #2640 from bedalton/formatting-add-semi-colon-cpview
Formatting: add semi-colon to CPView.j
2018-04-08 11:32:44 -07:00
Alexandre WilhelmandGitHub d38463c5d9 Merge pull request #2631 from bedalton/formatting-add-semi-colon-cpfont
Formatting: add semi-colon to CPFont.j
2018-04-08 11:32:33 -07:00
Alexandre WilhelmandGitHub bc4b424f0e Merge pull request #2632 from bedalton/formatting-add-semi-colon-cpimage
Formatting: add semi-colon to CPImage.j
2018-04-08 11:32:23 -07:00
Alexandre WilhelmandGitHub 6bd4f3b7b4 Merge pull request #2633 from bedalton/formatting-add-semi-colon-cpkeyvaluebinding
Formatting: add missing semi-colon to CPKeyValueBinding
2018-04-08 11:32:13 -07:00
Alexandre WilhelmandGitHub d91d83f115 Merge pull request #2634 from bedalton/formatting-add-semi-colon-cplayoutmanager
Formatting: add missing semi-colon to CPLayoutManager.j
2018-04-08 11:32:02 -07:00
Alexandre WilhelmandGitHub 584b6ab219 Merge pull request #2639 from bedalton/formatting-add-semi-colon-cptokenfield
Formatting: add semi colon to  CPTokenField.j
2018-04-08 11:31:47 -07:00
Alexandre WilhelmandGitHub 04b155dbfb Merge pull request #2635 from bedalton/formatting-add-semi-colon-cpoutlineview
Formatting: add semi-colons to CPOutlineView.j
2018-04-08 11:31:35 -07:00
Alexandre WilhelmandGitHub ddc5488db7 Merge pull request #2636 from bedalton/formatting-add-semi-colon-cpprogressindicator
Formatting: add semi-colons to CPProgressIndicator.j
2018-04-08 11:31:23 -07:00
Alexandre WilhelmandGitHub 5c6906d5fd Merge pull request #2637 from bedalton/formatting-add-semi-colon-cpscrollview
Formatting: add missing semi-colon to CPScrollView.j
2018-04-08 11:31:08 -07:00
Alexandre WilhelmandGitHub 56fee5d286 Merge pull request #2657 from bedalton/formatting-remove-redundant-variable-declaration
Formatting: remove redundant variable in _CPRuleEditorViewSlice.j
2018-04-08 11:29:35 -07:00
Alexandre WilhelmandGitHub c4dcab04b6 Merge pull request #2656 from bedalton/formatting-remove-semi-colon-cplevelindicator
Formatting: Remove semi-colon after method header in CPLevelIndicator
2018-04-08 11:29:17 -07:00
Alexandre WilhelmandGitHub 8dd9a96769 Merge pull request #2655 from bedalton/formatting-update-semi-colons-in-cpimageandtextview
Formatting: fix possible misuse of commas in _CPImageAndTextView.j
2018-04-08 11:28:59 -07:00
Alexandre WilhelmandGitHub 6864015af6 Merge pull request #2654 from bedalton/formatting-declare-variable-in-CPColor
Formatting: formally declared variable before use in CPColor.j
2018-04-08 11:28:46 -07:00
Alexandre WilhelmandGitHub ebaa60e929 Merge pull request #2653 from bedalton/formatting-cptextview
Formatting: add semi-colons and comma to CPTextView.j
2018-04-08 11:28:24 -07:00
Alexandre WilhelmandGitHub ac9962c624 Merge pull request #2644 from bedalton/formatting-add-semi-colon-to-cpmenuitem
Formatting: add missing semi-colons to CPMenuItem.j
2018-04-08 11:27:57 -07:00
Alexandre WilhelmandGitHub ccd1773643 Merge pull request #2646 from bedalton/formatting-add-semi-colons-cphudwindowview
Formatting: add missing semi-colon to _CPHudWindowView.j
2018-04-08 11:27:46 -07:00
Alexandre WilhelmandGitHub ac970ff768 Merge pull request #2647 from bedalton/formatting-add-semi-colons-cpplatformpasteboard
Formatting: add semi-colons to CPPlatformPasteboard.j
2018-04-08 11:27:38 -07:00
Alexandre WilhelmandGitHub 53ff45425d Merge pull request #2648 from bedalton/formatting-add-semi-colons-cptablecolumn
Formatting: add semi-colon to CPTableColumn.j
2018-04-08 11:27:25 -07:00
Alexandre WilhelmandGitHub de435c741f Merge pull request #2649 from bedalton/formatting-add-semi-colons-cptextfield
Formatting: add semi-colons to CPTextField.j
2018-04-08 11:27:12 -07:00
Alexandre WilhelmandGitHub 1dc9a44aec Merge pull request #2650 from bedalton/formatting-add-semi-colons-cptoolbar
Formatting: add semi-colon to CPToolBar.j
2018-04-08 11:26:37 -07:00
Alexandre WilhelmandGitHub 0206379315 Merge pull request #2651 from bedalton/formatting-add-semi-colons-cptypesetter
Formatting: add missing semi-colons to CPTypesetter.j
2018-04-08 11:26:29 -07:00
Alexandre WilhelmandGitHub c39adbbbc5 Merge pull request #2652 from bedalton/formatting-add-semi-colons-to-cprtfparser
Formatting: add semi-colons to _CPRTFParser.j
2018-04-08 11:26:16 -07:00
Alexandre WilhelmandGitHub 061a15d4f7 Merge pull request #2658 from bedalton/formatting-remove-redundant-variable-cpsearchfield
Formatting: removed redundant variable in CPSearchField.j
2018-04-08 11:25:25 -07:00
Alexandre WilhelmandGitHub 7c2518f027 Merge pull request #2659 from bedalton/formatting-add-semi-colons-cpusernotification
Formatting: add semi colons CPUserNotification.j
2018-04-08 11:25:04 -07:00
Alexandre WilhelmandGitHub a07459454b Merge pull request #2660 from bedalton/formatting-add-semi-colons-nib2cib-converter
Formatting: add semi-colons to nib2cib/Converter.j
2018-04-08 11:24:56 -07:00
Alexandre WilhelmandGitHub 752f9d2694 Merge pull request #2661 from bedalton/fix-cpproxy-respondstoselector
Fix: CPProxy respondsToSelector incorrect parameter name
2018-04-08 11:24:42 -07:00
Alexandre WilhelmandGitHub 0a180f9e20 Merge pull request #2662 from bedalton/format-add-semi-colon-cpdateformatter
Format: add semi-colons to CPDateFormatter.j
2018-04-08 11:24:21 -07:00
Alexandre WilhelmandGitHub 6fff2e1c2f Merge pull request #2663 from bedalton/formatting-add-semi-colon-cparray-kvo
Formatting: add semi-colon CPArray+KVO.j
2018-04-08 11:24:07 -07:00
Alexandre WilhelmandGitHub 4266900d2e Merge pull request #2664 from bedalton/formatting-add-semi-colon-cpdictionary
Formatting: add semi-colon to CPDictionary.j
2018-04-08 11:23:47 -07:00
Alexandre WilhelmandGitHub a2a1d84bc0 Merge pull request #2665 from bedalton/formatting-add-semi-colon-cpindexset
Formatting: add semi-colons to CPIndexSet
2018-04-08 11:23:39 -07:00
Alexandre WilhelmandGitHub 7648c562f1 Merge pull request #2666 from bedalton/formatting-add-semi-colon-cpnotificationqueue
Formatting: add semi-colons to CPNotificationQueue.j
2018-04-08 11:23:21 -07:00
Alexandre WilhelmandGitHub 403fd30926 Merge pull request #2667 from bedalton/formatting-add-semi-colon-cppredicate
Formatting: add semi-colon to CPPredicate.j
2018-04-08 11:23:07 -07:00
Alexandre WilhelmandGitHub 17ccddf541 Merge pull request #2668 from bedalton/formatting-add-semi-colon-cptheme
Formatting: add semi-colon to CPTheme.j
2018-04-08 11:22:59 -07:00
Alexandre WilhelmandGitHub c24366fcec Merge pull request #2670 from bedalton/formatting-add-semi-colons-nib2cib-nib2cib
Formatting: add semi-colon to nib2cib/Nib2Cib.j
2018-04-08 11:22:31 -07:00
Alexandre WilhelmandGitHub 9a258a6265 Merge pull request #2671 from bedalton/formatting-add-semi-colon-nscustomerresource
Formatting: add semi colon NSCustomResource.j
2018-04-08 11:22:24 -07:00
Alexandre WilhelmandGitHub 4542ccbdc2 Merge pull request #2672 from bedalton/formatting-add-semi-colon-capp-main
Formatting: add semi-colon capp/main.j
2018-04-08 11:22:01 -07:00
Daniel 41d8ad2b86 Formatting: add semi-colons to capp/main.j 2018-04-07 17:20:00 -07:00
Daniel a3a2ee118e Formatting: add semi-colon to NSCustomResource.j 2018-04-07 17:17:19 -07:00
Daniel f285efc277 Formatting: add semi-colon to Nib2Cib.j 2018-04-07 17:15:36 -07:00
Daniel fbaf87ec6c Formatting: add missing semi-colon in CPTheme.j 2018-04-07 17:03:33 -07:00
Daniel a005a468a7 Formatting: add semi-colons to nib2cib/Converter.j 2018-04-07 16:40:52 -07:00
Daniel c8d01549ba Formatting: add semi-colon to CPUserNotification.j 2018-04-07 16:13:20 -07:00
daniel 23af4505dd Fix: CPProxy uses incorrect variable name in method call
On line 51 of CPProxy, `class_getInstanceMethod` is called with the
variable  `aSelector`, but this variable does not exist in the
context, but a variable named `selector` does. This fix changes the
method parameter from `selector` to `aSelector` to match the parameter
used within the method.
2018-04-07 15:37:19 -07:00
daniel 53cb6a5b3e Formatting: add missing semi-colons to CPNotificationQueue.j 2018-04-07 15:29:27 -07:00
daniel 765384ace1 Formatting: add semi-colons to CPIndexSet.j
Variable assignments terminated in commas instead of semi-colons
2018-04-07 15:26:52 -07:00
daniel d39e3e2f69 Formatting: add semi-colon to CPDictionary.j 2018-04-07 15:22:36 -07:00
daniel d75124066e Formatting: add semi-colons to CPDateFormatter.j 2018-04-07 15:20:08 -07:00
daniel f4400876da Formatting: add semi-colon to CPArray+KVO.j 2018-04-07 14:10:03 -07:00
daniel c222c5127f Formatting: add semi-colon to _CPPredicate.j 2018-04-07 14:07:43 -07:00
daniel 90d04956e6 Merge remote-tracking branch 'cappuccino/master' 2018-04-07 11:34:21 -07:00
cacaodevandGitHub 5d3b918339 Merge pull request #2624 from bedalton/formatting-add-semi-colon-cparraycontroller
Formatting: add semi-colon CPArrayController
2018-04-07 06:53:38 +02:00
cacaodevandGitHub eba0091764 Merge pull request #2623 from bedalton/formatting-add-semi-colon-cpapplication
Formatting: add semi-colon CPApplication
2018-04-07 06:53:13 +02:00
cacaodevandGitHub 7999cbbea3 Merge pull request #2622 from bedalton/formatting-add-semi-colon-cppopoverwindow
Formatting: add semi-colon CPPopoverWindow.j
2018-04-07 06:52:57 +02:00
cacaodevandGitHub cee7987b35 Merge pull request #2621 from bedalton/formatting-add-semi-colon-cptooltip
Formatting: add semi-colon to CPToolTip.j
2018-04-07 06:52:40 +02:00
cacaodevandGitHub d475c08024 Merge pull request #2620 from bedalton/formatting-add-semi-colon-cpdatepickerclock
Formatting: add semi colon CPDatePickerClock.j
2018-04-07 06:52:26 +02:00
cacaodevandGitHub d78f83ae49 Merge pull request #2619 from bedalton/formatting-move-redeclared-variable-in-cgpath
Formatting: remove redeclared variables in CGPath.j
2018-04-07 06:52:12 +02:00
cacaodevandGitHub df694f01e7 Merge pull request #2618 from bedalton/add-semi-colons-to-cgcontextvml
Formatting: add semi-colon to CGContextVML.j
2018-04-07 06:51:55 +02:00
cacaodevandGitHub f5d9cbb11b Merge pull request #2615 from bedalton/add-missing-semi-colon-to-cpkeyvaluebinding
Formatting : add missing semi-colon to CPKeyValueBinding.j
2018-04-07 06:51:35 +02:00
cacaodevandGitHub d802588abe Merge pull request #2617 from bedalton/add-missing-semi-colons-cgcontextcanvas
Formatting: Add missing semi-colons to CGContextCanvas.j
2018-04-07 06:51:01 +02:00
daboe01 927d6d2fc6 fixed: backspace did not trigger scrolling properly 2018-04-05 21:36:32 +02:00
daboe01 bab7595078 Merge remote-tracking branch 'cappuccino/master' 2018-04-05 21:26:43 +02:00
daniel 1ea9a6c57f Formatting: add missing semi-colon to CPWindowController.j 2018-04-04 15:14:06 -07:00
daniel f479d415d8 Formatting: add missing semi-colon in CPView.j 2018-04-04 15:13:03 -07:00
daniel f41e7be726 Formatting: add semi-colons to CPToolbar.j 2018-04-04 15:11:52 -07:00
daniel c1fe28124f Formatting: add semi-colons to CPTokenField 2018-04-04 15:10:17 -07:00
daniel 01a1e506e4 Formatting: add missing semi-colons to CPTextField.j 2018-04-04 15:08:21 -07:00
daniel e61aebb55d Formatting: minor formatting changes for CPTableView.j
Added missing semi-colon on line 3763.
Removed the redundant `var` keyword on line 4910
Removed the second redundant variable `CPTableViewHeaderViewKey` which
was declared with the same name and value twice in the same variable
block.
2018-04-04 15:06:00 -07:00
daniel bae5bafc62 Formatting: add semi-colon to CPTableColumn.j 2018-04-04 14:55:01 -07:00
daniel be6cb266de Merge remote-tracking branch 'cappuccino/master' 2018-04-04 14:50:00 -07:00
daniel 6f72edc2d9 Formatting: removed redundant variable in CPSearchField.j
Variable `searches` is defined in the method selector, and then
redeclared with the `var` keyword two lines later. Removed the
redundant declaration, moving the variable assignment to its own line.
2018-04-04 14:45:13 -07:00
daniel 29f66bdfed Formatting: add missing semi-colon to CPScrollView.j 2018-04-04 14:41:53 -07:00
daniel c1c6f5682c Formatting: add semi-colons to CPProgressIndicator.j 2018-04-04 14:40:46 -07:00
daniel a8a74a0246 Formatting: add semi-colons to CPOutlineView.j 2018-04-04 14:37:34 -07:00
daniel f9b867c3eb Formatting: Remove semi-colon after method header in CPLevelIndicator
On lines 295 and 304, the selector for two method declarations are
followed by a semi-colon. Not sure if this causes problems or not, but
removed anyways.
2018-04-04 14:36:07 -07:00
daniel 6c07f9a048 Formatting: add missing semi-colon to CPKeyValueBinding 2018-04-04 14:33:24 -07:00
daniel 573e0acafa Formatting: add semi-colon to CPImage.j
Variable value assignment terminated in comma, despite not being a
variable list. Replaced comma with semi-colon.
2018-04-04 14:25:59 -07:00
daniel 530c664967 Formatting: add semi-colon to CPFont.j 2018-04-04 14:19:26 -07:00
daniel 12c29c8387 Formatting: add missing semi-colons to CPEvent.j 2018-04-04 14:17:42 -07:00
daniel 6c7f61d90f Formatting: add missing semi-colon to CPColorPicker.j 2018-04-04 14:11:59 -07:00
daniel eb6d8d0bb3 Formatting: formally declared variable before use in CPColor.j
Variable `isVertical` is used throughout the function
`patternColorsFromPattern` in CPColor.j despite never being declared.
In this pull request, the variable is declared formally in the variable
list before its use.
2018-04-04 14:09:28 -07:00
daniel ef462973c7 Formatting: add semi-colon to CPArrayController.j 2018-04-04 13:33:53 -07:00
daniel aa7e49cfde Formatting: add semi-colon to CPApplication.j 2018-04-04 13:32:37 -07:00
daniel 186048676a Formatting: add semi-colon to _CPTooltip.j 2018-04-04 13:31:19 -07:00
daniel 0f40721cff Formatting: add semi-colon to _CPPopoverWindow.j 2018-04-04 13:29:40 -07:00
daniel 3799dd92fd Formatting: fix possible misuse of commas in _CPImageAndTextView.j 2018-04-04 10:50:04 -07:00
daniel 9ea1a2b1de Formatting: minor formatting updates in CPPlatformWindow+DOM.j
Missing semi-colon on line 1425.
Removed redundant `var` modifier on already declared variable on line
947.
Added variable declaration to variable used without declaration on line
970.
Removed redundant `var` modifier on already declared variable
`CPDOMEventGetClickCount` removed var on latter declaration as original
declaration has note stating it is to be declared at top so “so
compressor knows about them.”
2018-04-04 10:46:02 -07:00
daniel d8b27d122e Formatting: add semi-colons to CPPlatformPasteboard.j 2018-04-04 09:41:10 -07:00
daniel 15c3a6eae4 Formatting: add missing semi-colons to CPWindow.j 2018-04-04 09:38:43 -07:00
daniel 4fcc90d53e Formatting: add missing semi-colon to _CPHudWindowView.j 2018-04-04 09:28:31 -07:00
daniel 2619a50976 Formatting: add missing semi-colons to CPTypesetter.j 2018-04-04 09:27:12 -07:00
daniel de116c95d4 Formatting: add missing semi-colon to CPLayoutManager.j 2018-04-04 09:23:38 -07:00
daniel 47594c9b07 Formatting: add missing coma and semi-colons to CPTextField 2018-04-04 09:10:52 -07:00
daniel 1b9098b564 Merge remote-tracking branch 'cappuccino/master' into formatting-cptextview 2018-04-04 09:10:14 -07:00
daniel b0306eac87 Formatting: add semi-colon to CPFrontPanel.j 2018-04-04 09:07:06 -07:00
daniel 125e813d25 Formatting: add semi-colons to _CPRTFParser.j 2018-04-04 09:00:36 -07:00
daniel 8e0d4a70f2 Formatting: remove redundant variable in _CPRuleEditorViewSlice.j 2018-04-03 15:04:55 -07:00
daniel 0556fc68b0 Formatting: add missing semi-colons to CPMenuItem.j 2018-04-03 14:59:14 -07:00
daniel e9e2313159 Formatting: add missing semi-colons to CPDatePicker.j 2018-04-03 14:27:07 -07:00
daniel dbb2f03a43 Formatting: add semi-colon CPDatePickerTextField.j 2018-04-03 14:18:36 -07:00
daniel ed0dc2d887 Formatting: add semi-colon to _CPDatePickerClock.j 2018-04-03 12:59:51 -07:00
daniel 0a93f2d0b0 Formatting: add missing semi-colons to _CPDatePickerCalendar.j 2018-04-03 12:54:39 -07:00
daniel fd581cd65c Formatting: removed variable redeclaration in CGPath.j
Variables `arcStartX` and `arcStartY` are declared on 125 and 126, but
are later redeclared in an `else` statement on lines 135 and 136 with
the same equation and presumably the same value used in the initial
declaration.
Also added missing semi-colon
2018-04-03 12:46:56 -07:00
daniel 36702bc971 Formatting: add missing semi-colon to CGContextVML 2018-04-03 12:15:04 -07:00
daniel 0fc3eb38f1 Formatting: Add semi-colons to CGContextCanvas.j 2018-04-03 12:11:57 -07:00
daniel 73c8e945e9 New: add .idea to .gitignore
Prevent Intellij Idea metadata from being committed to git.
2018-04-03 12:07:01 -07:00
daniel b8420303ba Formatting: add missing semi-colon to CPKeyValueBinding.j 2018-04-03 10:55:17 -07:00
daniel 532bfcf937 Merge remote-tracking branch 'cappuccino/master' 2018-04-03 10:20:07 -07:00
cacaodevandGitHub f6d892023e Merge pull request #2604 from bedalton/overshadowing-variable-in-_CPCibObjectData
Fixed: Variable in if statement overshadows variable in enclosing block
2018-04-03 07:54:11 +02:00
daboe01 7f2977f4af fixed: wording in manual test 2018-04-02 08:33:50 +02:00
cacaodevandGitHub e246a2753b Merge pull request #2610 from daboe01/issue2605
fixed: wrong method invocation to delegate of CALayer (displayLayer:)
2018-03-31 12:35:33 +02:00
daboe01 6d06a26c31 fixed: typo in method invocation 2018-03-26 17:53:38 +02:00
Daniel 7cda21b60f Merge remote-tracking branch 'cappuccino/master' 2018-03-20 13:46:13 -07:00
Daniel 1db740039b Fixed: Variable in if statement overshadows variable in enclosing block
If statement variable ‘instantiatedObject’ on line 229 of
_CPCibObjectData.j overshadows a variable with the same name declared
on line 233 in the enclosing block. Variable is added to array outside
the enclosing if statement.

Refs: #2601
2018-03-20 11:36:19 -07:00
daboe01 df462774aa refactured front detection logic to own method _isFrontmostWindow
fixes travis error
2018-03-20 14:25:42 +01:00
daboe01 f55fa789c4 new: manual test for issue 2545 2018-03-18 22:33:49 +01:00
daboe01 488541f26e fixed: click to key window in the background did not bring it forward 2018-03-18 21:45:25 +01:00
daboe01 b404b1cf50 Merge remote-tracking branch 'cappuccino/master' 2018-03-08 15:43:23 +01:00
Martin CarlbergandGitHub 69370aa806 Merge pull request #2596 from mrcarlberg/triple_click_noneditable_selectable_empty_textfield
Fixed: Crash when triple click an empty CPTextField that is not editable but selectable
2018-03-08 09:17:57 +01:00
daboe01andMartin Carlberg b3ddd85b09 Fixed: Editing text in tableviews throws DOM exceptions under certain conditions in Chrome (#2563)
Previosly, we unflagged ```CPTextFieldInputDidBlur``` and ```CPTextFieldInputResigning``` before
the call to removeChild. This resulted in DOM exceptions in Chrome under certain conditions.
See https://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node for
why we need to unflag ```CPTextFieldInputDidBlur``` and ```CPTextFieldInputResigning``` only after removing the element.

This apply to token fields also but with the corresponding flags. Also token fields now have the same behavior as the text field when bluring etc.
2018-03-08 09:17:15 +01:00
Martin Carlberg 033221fd01 Fixed: When triple click in an empty CPTextField that is not editable but selectable this call will crash as _DOMTextElement is nil.
This check will make sure it is not called when no element is present. This is the same way as it is done in other parts of this class.
2018-03-07 11:24:21 +01:00
Didier Korthoudt 1201fa38a8 Merge remote-tracking branch 'cappuccino/master' 2018-03-07 00:05:40 +01:00
Martin CarlbergandGitHub 32059c376e Fixed: Nib2Cib will replace a CPTextFieldCell with a CPTextField as Cappuccino does not use cells. When a binding or connection is made to a CPTextFieldCell the binding / connection will get lost when read as the cell is never stored in the cib file. (#2572)
This fix make sure that the binding / connection is made to the CPTextField instead of the cell by replacing the cell with the text field when reading the xib file. The same mechanism is used in NSButton and NSMatrix.

This fix allows view based TableView bindings from a text field cell to the table cell view in the Interface Builder in Xcode.
2018-03-06 09:09:57 +01:00
Martin CarlbergandGitHub 511d393c78 Merge pull request #2576 from mrcarlberg/cptextview_delegate
New: CPTextView delegate methods added
2018-03-06 09:07:55 +01:00
Martin CarlbergandGitHub 237601cf29 Merge pull request #2579 from mrcarlberg/xml-parser-node
Fixed: The property list test cases can now run on the Node engine.
2018-03-06 09:00:41 +01:00
daboe01andMartin Carlberg 928f14872e Fixed: call to nonexistent method (#2588) 2018-03-06 08:59:36 +01:00
Martin CarlbergandGitHub c6a9adf96c Merge pull request #2556 from daboe01/plainsliderfix
Fixed: legacy scrollers used Aristo1 artwork in Aristo2
2018-03-05 11:06:59 +01:00
Martin CarlbergandGitHub cdd00e0bd4 Merge pull request #2595 from daboe01/cpnumber-unarchiving-fix
Fixed: initWithCoder: called unimplemented method on the coder
2018-03-05 11:04:41 +01:00
Martin CarlbergandGitHub 2503d13402 Merge pull request #2594 from daboe01/CPSavePanel-missing-class-method
Fixed: +proposedFileURLWithDocumentName: was missing
2018-03-05 11:01:18 +01:00
daboe01andMartin Carlberg 2d97970d24 Fixed: numberWithInteger: was erroneously used instead of numberWithInt: (#2587) 2018-03-05 10:50:30 +01:00
Martin CarlbergandGitHub d8945e53ba Merge pull request #2593 from daboe01/CPCoder--remove-bitrot
Fixed: unused and broken code removed from CPCoder
2018-03-05 10:43:41 +01:00
daboe01andMartin Carlberg f18012d07b Fixed: removed erroneous call to sizeValue (#2589) 2018-03-05 10:36:22 +01:00
daboe01 104f8603b9 fixed: initWithCoder: called unimplemented method on the coder 2018-03-04 21:27:08 +01:00
daboe01 19bb083030 fixed: +proposedFileURLWithDocumentName: was missing 2018-03-04 18:40:33 +01:00
daboe01 ae575a19a0 fixed: unused broken code removed from CPCoder 2018-03-04 11:55:34 +01:00
cacaodevandGitHub 5f94d0a5c2 Typo: remove extra semi-colon 2018-02-28 15:33:16 +01:00
Korthoudt Didier f2fe1a7c17 Merge remote-tracking branch 'cappuccino/master' 2018-02-27 23:41:34 +01:00
cacaodevandGitHub 618e6a1604 Merge pull request #2591 from daboe01/_CPFunctionExpression.j-missing-if
fixed: fromObject:(id)object index:(id)anIndex failed to raise if arg…
2018-02-27 15:59:19 +01:00
cacaodevandGitHub cef294513a Merge pull request #2590 from daboe01/CPKeyValueObserving-fix-CPKeyValueChangeReplacement
FIXED: Typo in call to replaceObjectsAtIndexes in CPKeyValueObserving
2018-02-27 15:58:13 +01:00
daboe01 8550abadef fixed: fromObject:(id)object index:(id)anIndex failed to raise if argument was not CPDictionary or CPArray 2018-02-24 15:41:17 +01:00
daboe01 25c8b2d631 fixed: typo in call to replaceObjectsAtIndexes 2018-02-24 14:59:27 +01:00
Martin Carlberg e7b690205e Fixed: The property list test cases can now run on the Node engine.
The xml parser in the Node.js engine is more modern and can handle more cases. Our property list parser does not handle this. This fix makes the test cases for the property lists run on the Node.js engine.
2018-01-15 09:28:39 +01:00
Martin Carlberg 2403f8833a Fixed: Added an application description at the top of the window 2018-01-09 13:43:09 +01:00
Martin Carlberg 9f25743b4d Fixed: Never send CPTextViewDidChangeTypingAttributes notification when selection the same selection again. This is how Cocoa works. 2018-01-09 13:41:34 +01:00
Martin Carlberg f1c76f65cf Fixed: Added some comments on some methods 2018-01-09 13:37:35 +01:00
Martin Carlberg 7b39533410 New: Added manual test application to monitor order and number of calls for CPTextView delegate methods and notifications. 2018-01-08 14:11:55 +01:00
Martin Carlberg 1fe9c39dd5 Fixed: Test cases for the new or changed delegate methods and notifications. 2018-01-08 13:57:53 +01:00
Martin Carlberg 2b7be2e3f1 Fixed: Make sure the delegate methods and notifications are handled in the same way as in the Cooca frameworks.
CPTextViewDidChangeSelectionNotification should not be send if the text view is empty.
CPTextDidBeginEditingNotification is send when an edit is started.
CPTextDidEndEditingNotification is send when an edit is ending.

textShouldBeginEditing: delegate method is called to allow control if edit should start.
textShouldEndEditing: delegate method is called to allow control if edit should end.
2018-01-08 13:56:24 +01:00
Martin Carlberg 1ee9a3f279 Fixed: Check if the correct delegate method is implemented before calling it 2018-01-08 13:47:45 +01:00
Martin Carlberg 679acfb239 Fixed: Register the delegate as an observer on any notification that the delegate implement.
Also make sure we remove the observers if for example the view does not have a superview. This will make sure that the Notification Center is not hanging on to the view and delegate if the view is not used any more. Can cause a memory leak.
2018-01-08 13:44:47 +01:00
Martin Carlberg 3c7ab4b49b Fixed: Make the view aware of some more delegate methods 2018-01-08 13:20:03 +01:00
Martin Carlberg 3f062e1c0a Fixed: Make all method in CPTextDelegate and CPTextViewDelegate protocol optional. 2018-01-08 13:15:31 +01:00
Didier Korthoudt 0a497d8c3f NEW: Added automatic Xcode syntax coloring
In Xcode, Objective-C source files are automatically recognised and the usual syntax coloring is applied. With this PR, Xcode will do so for Objective-J source files.
2017-12-17 20:49:56 +01:00
Korthoudt Didier 462351f485 Merge remote-tracking branch 'cappuccino/master' 2017-12-17 20:06:35 +01:00
Martin CarlbergandGitHub c4cbb631a9 Merge pull request #2561 from mrcarlberg/compile_with_sourcemap
Full support for source map when debugging
2017-12-11 10:52:05 +01:00
Martin Carlberg 76f2d15b1b Fixed: Added the option to only inline message send function without also compress the output javascript file.
This allows us to inline the message send function in DEBUG mode. This is great when stepping into a method in the debugger as the message send function is not a separate function. Also the stack traces in the debugger is much more compact as the message send function is not there.
2017-12-08 14:02:11 +01:00
cacaodevandGitHub 5d63865ae5 Merge pull request #2546 from cacaodev/CPSplitView
CPSplitView -trackDivider: performance
2017-11-08 18:53:22 +01:00
daboe01 ad7c88fb45 Merge remote-tracking branch 'cappuccino/master' 2017-09-27 11:46:45 +02:00
daboe01 1af3877758 FIXED: scroll-knob was slightly too bold 2017-09-27 11:05:59 +02:00
Martin CarlbergandGitHub 146ce3b12b Merge pull request #2544 from FrankReh/pullreq/cleanup/old/unused/ensurePackageUpToDate
Remove: ensurePackageUpToDate() from common.jake
2017-09-26 08:43:41 +02:00
Martin CarlbergandGitHub bf67721ac4 Merge pull request #2548 from cacaodev/CPWindow-live-resize-notifications
CPWindow live resize notifications
2017-09-25 14:04:01 +02:00
Martin CarlbergandGitHub 436f736a8e Merge pull request #2543 from FrankReh/pullreq/cleanup/remove/property/sub_classes
Remove objj_class.sub_classes property
2017-09-22 13:03:54 +02:00
Martin Carlberg 1446e90dd9 Fixed: Use correct naming on function parameter. Also changed som comments to reflect the correct meaning of the parameter 2017-09-18 13:39:28 +02:00
Martin Carlberg 54716c03d1 Fixed: Missed new line when increment tokPos. Now we use the correct skip space function that will take care of things in correct manners. 2017-09-18 13:36:51 +02:00
daboe01 fad5e97e0a Merge remote-tracking branch 'cappuccino/master' into plainsliderfix 2017-09-14 09:10:56 +02:00
Martin Carlberg 8cf79590c5 Fixed: When target was ’self’ in a message send expression the source map was showing the wrong variable. 2017-09-12 13:17:49 +02:00
Martin Carlberg c6aafac6c8 Fixed: Compile Foundation and AppKit frameworks with source maps for the debug version. 2017-09-11 12:50:49 +02:00
Martin Carlberg 94a5995197 New: We now support source maps when compiling both from command line and in browsers.
To add source maps add the ”-S” flag to the Jake file when compiling from command line. In the browser you need to add the compiler option into your index.html file. It should look something like this:
OBJJ_COMPILER_FLAGS = [... , "SourceMap"];

All browsers has support for source maps but currently Chrome works best.
2017-09-11 12:49:59 +02:00
Martin CarlbergandGitHub 74081dc981 Merge pull request #2551 from mrcarlberg/compile_without_gcc
Compile without gcc [+2]
2017-09-11 11:14:44 +02:00
daboe01 7a593b13a7 Merge remote-tracking branch 'cappuccino/master' into plainsliderfix 2017-09-09 10:56:37 +02:00
Martin CarlbergandGitHub 25dc38b2b1 Merge pull request #2558 from daboe01/smartcopypastefixes
Fixed: smart copy/paste did not work properly [+1]
2017-09-08 09:09:26 +02:00
Alexandre WilhelmandGitHub 403b12f7b7 Merge pull request #2550 from cacaodev/CPAnimationContext
FIXED: CPAnimationContext completionHandler was not called under certain circumstances.
2017-09-07 18:21:56 -07:00
Alexandre WilhelmandGitHub cd223be88a Merge pull request #2557 from daboe01/placeholderselectablefix
fixed: cursor blinks for placeholder
2017-09-07 18:21:12 -07:00
Alexandre WilhelmandGitHub a6a3fca1e7 Merge pull request #2559 from daboe01/onpaste-ff-fix
Fixed: CPTextView's onpaste handler used non-standard window.event
2017-09-07 18:20:32 -07:00
daboe01 63815eb5c2 fixed: onpaste handler used window.event (non-standard)
broke paste on FF/win
2017-09-07 19:19:19 +02:00
daboe01 4444264830 formatting 2017-09-04 23:02:50 +02:00
daboe01 255f91f7ca fixed: cornerview looked odd without scroller margins 2017-09-04 22:59:34 +02:00
cacaodevandGitHub 700207d29b Merge pull request #2554 from cacaodev/cptableview-issue#2549
CPTableView: fix invisible dragged rows
2017-08-30 08:35:40 +02:00
daboe01 72d96e9a3b fixed: smart pasting did not work properly 2017-08-29 15:11:29 +02:00
daboe01 a55b6de1b4 removed: unused artwork from aristo2 2017-08-28 23:32:47 +02:00
daboe01 9faac05666 fixed: cursor blinks for placeholder 2017-08-23 23:07:42 +02:00
daboe01 7bf408aba0 fixed: legacy scrollers used aristo1 artwork in aristo2 2017-08-22 22:02:42 +02:00
daboe01 70ee391b33 Merge remote-tracking branch 'origin/master' 2017-08-22 14:40:47 +02:00
cacaodev f5eb1158db Fix issue 2549: table rows where invisible when dragging. 2017-07-28 21:35:24 +02:00
cacaodev 81916d0f45 FIXED: A grouping context does not inherit the completion handler ...
… from the current context.
Also removed CPAnimationContext -copy method. CPAnimationContext is not
CPCopying compliant.

TEST: CPAnimationContextTest ->
testCompletionHandlerWithoutAnimatorWithGrouping2
2017-07-25 12:04:58 +02:00
Alexander Ljungberg 76d77cc244 This will be Cappuccino 0.9.10. 2017-07-11 14:26:57 +01:00
cacaodev 03dccbe613 Add a manual test for the previous fixes.
The tests must be run independently because animations are launched at
the end of the current run loop.
2017-06-30 12:26:15 +02:00
cacaodev 1ccec65619 Add debug information 2017-06-30 12:03:29 +02:00
cacaodev b78e9062a1 CompletionHandlerAgent fire() is a one shot. 2017-06-30 12:03:09 +02:00
cacaodev dc87354702 CPAnimationContext setCompletionHandler: add an equality check 2017-06-30 12:00:08 +02:00
Martin Carlberg 033e8583db Fixed: A more robust prettify message function 2017-06-26 17:59:08 +02:00
Martin Carlberg d46b51fa9a Fixed: In Rhino a function must be declared before it is used even if it is in the same scope. 2017-06-26 17:58:13 +02:00
Martin Carlberg dd1dfdcfeb Fixed: Added a comment on tricky code line 2017-06-26 16:25:44 +02:00
Martin Carlberg c0233c803d Fixed: Removed old print statements for debugging 2017-06-26 16:24:51 +02:00
Martin Carlberg 1fa8c88f6b Fixed: Speed up of objj_msgSend function. Don’t need to use complex check if class is initialized. 2017-06-26 16:23:59 +02:00
Martin Carlberg 195b109a36 New: Added ’#include’ to the preprocessor, compiler and Cappuccino load system. Also a lot of bug fixes in the preprocessor 2017-06-26 16:19:56 +02:00
Martin Carlberg 994ac3fc8e Fixed: Support for more options to the objj tool 2017-06-26 16:04:01 +02:00
Martin Carlberg a8ec5cf5e9 New: Support to create source maps for the frameworks and applications 2017-06-26 16:03:20 +02:00
Martin Carlberg b4263aa3ec New: Make sure we can use String function ’startsWith’ and ’endsWith’ even if an older browser or Javascript engine is used. 2017-06-26 15:50:08 +02:00
Martin Carlberg 6456809a72 Fixed: Use semicolon on last line in #included files. The new preprocessor might think that the statement will continue on the next line.
Due to Javascript don’t need to have semicolon at end of statement there are times when the parser thinks the line continues on the next line.

For example we have a file.js:
var f = function(x)
{
	return x + x;
}

Now in another file we do:
#include file.js
(function(exports) {
a = 42;
})(exports.b)

There will be two different behaviors if the ’file.js’ has a semicolon at the end of the last line. This commit make sure we have the right behavior.
2017-06-26 15:45:02 +02:00
Martin Carlberg 024c0a3186 Fixed: Handle source maps if source-map.js is included from the index.html file.
To create source maps the compiler flag ”SourceMap” has to be added in the OBJJ_COMPILER_FLAGS array in the index.html file.
2017-06-22 11:05:16 +02:00
Martin Carlberg 560b1d48bc Merge branch 'master' of https://github.com/cappuccino/cappuccino 2017-06-22 10:46:53 +02:00
cacaodev bb2d860807 CPViewAnimator: guard in the frame updater function
… in case the transform property value is ‘none’.
2017-06-12 12:46:05 +02:00
cacaodev 6f1c0fa4a8 FIXED: CPAnimationContext completionHandler was not called
Previously, if a context group was not generating any animation (no
calls to animator or non implemented method in the animator proxy), the
completionHandler was not called.
If the end value (frame, frameorigin, frameSize) was equal to the start
value, the completionHandler was not called neither.

This commit makes sure that the completionHandlerAgent counter is
incremented by the count of animations that actually start.
2017-06-12 12:46:05 +02:00
cacaodev 35808e7bde FIXED: uninitialized ivar 2017-06-02 11:15:23 +02:00
cacaodev 1b4eeb35b3 Add a manual test CPWindowLiveResizeTest 2017-06-02 09:44:42 +02:00
cacaodev cee032bbc9 FIXED: send live resize notifications when the window animates
Like in cocoa, these notifications are not sent when a sheet is
displayed.
2017-06-02 09:42:59 +02:00
cacaodevandGitHub 9547b70743 Merge pull request #2542 from FrankReh/pullreq/cleanup/fix/unused/cachedSearchResults
Removed an unused variable: cachedSearchResults.
2017-05-30 19:29:14 +02:00
cacaodevandGitHub 5df8939d84 Merge pull request #2541 from FrankReh/pullreq/cleanup/fix/syntax/protocol_list
Fix aClass.protocol_list syntax
2017-05-30 19:27:53 +02:00
cacaodevandGitHub 0b9c689273 Merge pull request #2540 from FrankReh/pullreq/cleanup/fix/syntax/global-var-key
Make variable local: key
2017-05-30 19:25:25 +02:00
cacaodev ac193cd97a Test: Add liveResize testing in CPWindowResizeStyle manual test
The window title changes during live resize.
2017-05-30 19:06:50 +02:00
cacaodev 7355a60f16 Call _setupDOMDivider when necessary only
Previoulsy, the backgroundColor and url css properties where set at
each drawing pass.
2017-05-30 18:33:08 +02:00
cacaodev fb6f6325b5 CPSplitView: refactor trackDivider: using _dividerAtPoint:
instead of a for loop.
2017-05-23 08:17:16 +02:00
cacaodev 0f767d9dfc CPWindow: add CPWindowWillStartLiveResizeNotification and CPWindowDidEndLiveResizeNotification
Also fixed CPView -(BOOL)inLiveResize
If a view is added during a live resize, it will not receive a
willStart notif.
If a view is removed during a live resize it will not receive a Didend
notif.
2017-05-20 13:52:50 +02:00
cacaodev d1addcab91 CPSplitView: do not track events outside of a divider.
Added private method -_dividerAtIndex:
2017-05-13 19:46:39 +02:00
cacaodev 7a02cd794b Properly catch single-click, double-click, dragging on a divider
Previously, a double-click was also catched as a single-click on mouse
up,
dragging was handled is mouse down before tracking actually begins.

Manual test: added collapse capabilities to all split views.
2017-05-12 11:09:08 +02:00
cacaodev 258cd9209f CPSplitView: exit a loop when the current divider is found 2017-05-12 10:28:47 +02:00
Frank Rehwinkel 700f6022d9 Remove: ensurePackageUpToDate() from common.jake
Some unused code in common.jake that had referred to tusk.  Has not
been used in years.
2017-05-06 13:10:49 -04:00
Frank Rehwinkel a0f6b815e8 Removed an unused variable: cachedSearchResults.
It is polluting the global namespace.

Seven years ago this line was added and even then the variable wasn't
being used but it was a local variable so would have been hard to spot
unless a linter should catch things like that.

Then some four years ago it was accidently converted to a global
variable because the line above it ended in a semicolon rather than
a comma.  'use strict' didn't exist at the time this file was created
and hasn't been added in general yet.
2017-05-04 16:03:11 -04:00
Frank Rehwinkel 54477b8b8f Make variable local: key
This line of code was introduced years ago with a variable that
javascript is happy to add to the global name space.  Clearly it
was meant as a local variable.  Probably a cut and paste error
because other places in the code use the same syntax but there the
value is a local because the var statement for it appeared elsewhere
in the function.

Once code like this is cleaned up to use the new 'let' construct,
as well as 'use strict', mistakes like this will be much less likely
to go uncaught.
2017-05-04 16:01:53 -04:00
Frank Rehwinkel 6183f8d4e9 Fix aClass.protocol_list syntax
A long standing bug that never mattered because each class is
initialized with a protocol_list set to an empty array.  This
is not the case for the protocol objects but there the syntax
was correct already.
2017-05-04 15:58:47 -04:00
Frank Rehwinkel d98e247665 Remove objj_class.sub_classes property
From the first day this class was added to the repo, the sub_classes
property, which is initialized with an empty array, has gone unused.

Removing it.  A little less code.  A little subsequent processing
and memory used.
2017-05-04 15:57:14 -04:00
Martin Carlberg d0cd976246 Fixed: Get class method instead of instance method
When asking a protocol for a specific class method we asked each super protocol for an instance method.

Thanks Frank for finding this!
2017-04-11 12:30:16 +02:00
daboe01 15f6f6a90a Merge remote-tracking branch 'cappuccino/master' 2017-03-18 11:02:38 +01:00
cacaodevandGitHub f18074bf49 Merge pull request #2532 from cacaodev/CPWindowAnimator
New: Optional declarative layout for use in CoreAnimation (css animations).
Code refactor.
2017-03-13 08:34:01 +01:00
cacaodevandGitHub 3f64b74453 Merge pull request #2533 from cacaodev/CPCompatibility-SetFeature
NEW: CPCompatibility: add a function to set a feature externally.
```CPSetPlatformFeature(aFeature, aBool)```
2017-03-13 08:28:51 +01:00
cacaodev 1c86256f83 Add CPCSSAnimationFeature to CPCompatibility constants 2017-03-02 11:21:57 +01:00
cacaodev 98105ad4dc Merge branch 'CPCompatibility-SetFeature' into CPWindowAnimator-copy 2017-03-02 10:57:23 +01:00
cacaodev 6bf2d6c1ab Move css animation creation & frame updaters to CPViewAnimator
Reason: • consistency : animations handled by animator.
• Views can now animate several DOM elements. For example
a view with a nine part image need to animate its sub divs.

• Do not create any css animation when the start and end values are
equal and there is no intermediate values.

+ This commit adds an optional method to CPView
- (CGRect)frameRectOfView:(CPView)aView inSuperviewSize:(CGSize)aSize
Returns the frame of a view given its superview frame. This will allow
views implementing -layoutSubviews to gain an optional declarative
layout. The animation system will use this method to compute the end
value of an animation.
2017-03-02 10:30:09 +01:00
cacaodev cebd51aac1 reduce the number of args in a private method 2017-03-02 10:30:08 +01:00
cacaodev 42b8b3aa75 Ask the animator if periodic frame updates are needed 2017-03-02 10:30:08 +01:00
cacaodev c0eaf79d25 Remove jshashtable.js in favor of Map() 2017-03-02 10:30:07 +01:00
cacaodev fb2fe3eb63 Enable forced frame updates in CPViewAnimator 2017-03-02 10:30:07 +01:00
cacaodev 184d63f7eb Move the FrameUpdater function to CPViewAnimator 2017-03-02 10:30:07 +01:00
cacaodev 14ebd58c96 Move CFRunLoopObserver to CPRunLoop.j as a global function 2017-03-02 10:30:05 +01:00
cacaodev 3edddb8e91 Add CSSAnimation.description() function 2017-03-02 10:30:05 +01:00
cacaodev d2340dff1a Style, whitespace 2017-03-02 10:30:04 +01:00
cacaodev d30c6aec7d Fix label in demo (AutoSize checkbox) 2017-03-02 09:56:47 +01:00
cacaodev 4854b3e08f Fix typo 2017-03-02 09:48:12 +01:00
cacaodev facc748090 NEW: CPCompatibility add a function to set a feature externally.
CPSetPlatformFeature(aFeature, aBool)
2017-03-01 19:10:00 +01:00
cacaodevandGitHub 5c71612e34 Typo 2017-02-10 20:01:57 +01:00
cacaodevandGitHub f00a5fbaf4 Merge pull request #2531 from cacaodev/cptableview-diddragcolumn
FIXED: tableView:didDragTableColumn: was never sent
With tests.
Fixes #2530
2017-02-03 08:13:46 +01:00
cacaodev 34769da9cc FIXED: tableview:didDragTableColumn: was never sent
With manual test.
2017-02-02 09:36:48 +01:00
cacaodev 458e6ed531 FiXED: CPTableColumn description 2017-02-02 09:34:17 +01:00
daboe01 05814e9351 Merge remote-tracking branch 'cappuccino/master' 2017-01-26 19:55:12 +01:00
daboe01 b6c0b1b396 Merge remote-tracking branch 'cappuccino/master' 2017-01-19 20:03:10 +01:00
Martin CarlbergandGitHub 1a299d85b6 Merge pull request #2503 from enquora/master
Fix CPBox drawing artifacts when used in CPSeparator mode
2017-01-19 09:46:33 +01:00
David Richardson 1816274ebe Revert "Fix CPBox drawing artifacts when used in CPSeparator mode"
This reverts commit 7596c66703.
2017-01-17 11:59:26 -07:00
David Richardson b02e4c5308 Handle case when CPBox is used as a separator line having no title
If _titlePosition is correctly set to CPNoTitle value, class already handles removing title view from display.
Previously, _titlePosition was not correctly set, title view was left in place.
With no supplied value for title, outline of title view was rendered with visible artifacts.
2017-01-16 11:23:32 -07:00
Martin CarlbergandGitHub 12ed42e163 Merge pull request #2505 from HTPC-Schrauber/fix-RegEx
Fixed: RegEx for gcc > 4.9
2017-01-16 12:36:57 +01:00
Martin CarlbergandGitHub 6c9c135062 Merge pull request #2525 from daboe01/new-textview-bindings
New: bindings support for CPTextView + dragging improvements
2017-01-16 12:24:56 +01:00
Martin CarlbergandGitHub 6aea0160df Merge pull request #2508 from daboe01/fix-sizing-issue
Fixed: text-sizing by canvas and via DOM sizing differed by 1px
2017-01-16 12:20:06 +01:00
daboe01 30edd9e8dd Merge remote-tracking branch 'cappuccino/master' 2017-01-06 13:05:18 +01:00
David Richardson 7734402e8e Merge branch 'master' of https://github.com/cappuccino/cappuccino into enquora-master 2017-01-05 16:00:51 -07:00
daboe01 ace1332ca5 fixed: semipredicate issue caused typingAttributes being overwritten by placeholder attibutes 2016-12-28 23:16:03 +01:00
daboe01 baa313e8f8 fixed: insertion point was wrong sometimes when dragging beyond last character 2016-12-26 23:30:23 +01:00
daboe01 3d9aa24f3c formatting 2016-12-26 23:18:24 +01:00
daboe01 c287d457d6 formatting 2016-12-26 22:14:12 +01:00
daboe01 7537b27a58 fixed: pboad types were wrong when unarchiving from cib 2016-12-26 22:11:11 +01:00
daboe01 1f560c92fc fixed: dragging pasteboard was not respecting rich vs. plain 2016-12-26 21:49:36 +01:00
daboe01 74cd11c763 minor cleanup 2016-12-26 14:50:33 +01:00
daboe01 ee7d89331b fixed: selection dragging was broken by changes to insertText 2016-12-26 12:50:16 +01:00
daboe01 5b51077823 fixed: changing the selection was possible despite isSelectable returning NO 2016-12-26 12:35:23 +01:00
daboe01 d2f94d1945 formatting 2016-12-26 11:53:49 +01:00
daboe01 0170a099bb formatting 2016-12-26 11:50:25 +01:00
daboe01 2b893d0ea1 fixed: placeholder string was not supported in textview bindings 2016-12-26 11:47:33 +01:00
daboe01 a08da57cbb fixed: reverse setting did not work 2016-12-26 10:56:43 +01:00
daboe01 a313fe5a86 add basic binder + manual test 2016-12-26 09:40:58 +01:00
daboe01 544de699f6 Merge remote-tracking branch 'cappuccino/master' into new-textview-bindings 2016-12-24 20:46:26 +01:00
daboe01 d9d35b7f50 add binder class router 2016-12-24 20:46:12 +01:00
daboe01 7dbec00f0e Merge remote-tracking branch 'cappuccino/master' into new-textview-bindings 2016-12-18 21:07:21 +01:00
Martin CarlbergandGitHub 51b9b40c7e Merge pull request #2515 from daboe01/chrome-rich-paste-fix
fixed: native paste on chrome was not working as robustly as it should [+1]
2016-12-13 09:40:18 +01:00
daboe01 b2383a084a fixed: wrong name in CPTextView manual test 2016-12-12 22:32:00 +01:00
daboe01 61848c5e24 fixed: selection was not properly restored upon undo/redo 2016-12-12 20:29:19 +01:00
Martin CarlbergandGitHub 60b2f7d243 Merge pull request #2513 from daboe01/fix-combobox-scroll-issue
Fixed: legacy scrollers were not clickable under some circumstances
2016-12-12 13:16:01 +01:00
daboe01 d2a5e126ae fixed: native rich text was parsed when pasted into plain textview
+ newline fix in rtf parser
2016-12-11 22:42:43 +01:00
daboe01 9d7d8ddde9 fixed: native rich pasting pasted rich text into plain textviews 2016-12-11 14:05:51 +01:00
Martin CarlbergandGitHub 249ebeb4aa Merge pull request #2517 from daboe01/fix-initial-blinking-on-sheet
Fixed: caret did not blink in empty textview, e.g. when on freshly opened popover
2016-12-10 11:11:13 +01:00
daboe01 378c14d790 formatting 2016-12-09 23:53:08 +01:00
daboe01 30c1093c2f fixed: rich native pasting caused FF to flicker 2016-12-09 23:45:16 +01:00
daboe01 b966c8cb0b formatting 2016-12-09 21:35:42 +01:00
daboe01 38cddf029b fixed: rich native pasting was not using the rtf pasteboard type on chrome 2016-12-09 21:32:21 +01:00
Martin CarlbergandGitHub 6652a27781 Merge pull request #2518 from cacaodev/CPControl-usesSingleLine
New: Allow a text based control to stay on a single line when text wraps.
2016-12-09 15:13:28 +01:00
daboe01andcacaodev 08b067cfc5 fixed: cared was blinking during typing (#2519) 2016-12-04 13:33:24 +01:00
daboe01 4959b84b4c fixed: implicit casting of NULL aWith argument to string in _sizeWithFont:inWidth: 2016-12-02 22:08:48 +01:00
cacaodev d42562fdcb New: Allow a text based control to stay on a single line when wrapping
A private flag have been added in _CPImageAndTextView. It must be set
to YES when it does not make sense for a control to hold multiline text.
This is now the case for all CPSegmentedControl.

Test: In CPSegmentedControlTest, hit « Add Segment with flexible
width » button. The new label should be vertically centered.
2016-12-02 09:10:39 +01:00
Alexandre WilhelmandGitHub 1e0aacd811 Merge pull request #2511 from cacaodev/CPObjet-instancesImplementSelector
New: CPObject -instancesImplementSelector:(SEL)aSelector
2016-11-22 22:44:30 -08:00
daboe01 329469fe84 fixed: _textContainerOrigin.y was not respected when predicting the cared position in empty textview 2016-11-20 22:36:24 +01:00
daboe01 93b0c5a7e5 fixed: font height was nil under certain circumstances 2016-11-20 21:44:08 +01:00
daboe01 9e456fd315 Merge remote-tracking branch 'cappuccino/master' 2016-11-20 15:31:25 +01:00
daboe01andcacaodev 8203c71b03 fixed: CPTextView's needsPanelToBecomeKey did not return YES (#2512)
This is confirmed to conform to cocoa.
2016-11-17 16:43:54 +01:00
daboe01 80d1e9a9a7 fixed: ceiling the sizing width caused subtle issues with CPTextView 2016-11-15 22:43:24 +01:00
daboe01 3c27e57b19 fixed: native rich pasting curly braces crashed chrome 2016-11-15 21:02:04 +01:00
daboe01 b46b2f048d fixed: native paste on chrome was not working as robustly as it should 2016-11-14 23:09:04 +01:00
cacaodev 8d403f579c FIxed: IB connections in CPTabViewNibTest 2016-11-14 22:03:51 +01:00
daboe01 12b274ab43 formatting 2016-11-13 22:55:29 +01:00
daboe01 986cccb800 fixed: legacy scroller was not clickable under some circumstances 2016-11-12 23:03:45 +01:00
cacaodev 5da8728d6c New: CPObject instancesImplementSelector:(SEL)aSelector
Tests if instances of given class implement a selector.

With test.

Note: The naming is from cocoa where this method is semi-public.
2016-11-12 18:30:02 +01:00
daboe01 66c9a1e90a ceil is a better solution than adding one 2016-11-11 23:02:57 +01:00
daboe01 a1925e1d0b fixed: the canvas sizing and DOM sizing differed by 1px 2016-11-08 19:52:45 +01:00
daboe01 da0910b1e5 Merge remote-tracking branch 'cappuccino/master' 2016-11-08 19:34:28 +01:00
Didier Korthoudt 8dbbe34dfc Merge remote-tracking branch 'cappuccino/master' 2016-11-08 15:21:17 +01:00
cacaodev 947d552509 Fix merging 2016-11-07 22:57:33 +01:00
cacaodev 037bc4ea2b Test: demonstrate the new wantsPeriodicFrameUpdates animator property
In Tests/Manual/CPAnimatablePropertyContainerTest/
2016-11-07 22:26:30 +01:00
cacaodev cfbef141d8 CPAnimationContext: Inhibit DOM updates during size animation
Previously, during frame updates, we were writing the css values we
just read before.
Canvas sizing is preserved as it is needed when the view has custom
drawing.
2016-11-07 22:26:30 +01:00
cacaodev 410ad0dc09 Style: print debug logs in debug mode only 2016-11-07 22:26:29 +01:00
cacaodev 3d9b42f0c2 Style: CSSAnimation.j linting
Using default eslint config for js
2016-11-07 22:26:29 +01:00
Didier Korthoudtandcacaodev eeea21c882 Fixed: CPViewAnimator was not giving correct frame information (#2502)
Fixed: When only animating frameSize, there’s no matrix defined. This is now
correctly handled.
New: The animator can explicitly request frames updates when the frameOrigin 
keyPath changes with the new method [animator setWantsPeriodicFramesUpdates:YES]
2016-11-07 22:20:55 +01:00
Maik Opitz d94e5480c9 Merge branch 'fix-RegEx' of https://github.com/HTPC-Schrauber/cappuccino into fix-RegEx 2016-11-03 12:42:05 +00:00
Maik Opitz a5c8e87e6d Removed mistaken triplequotes 2016-11-03 12:33:39 +00:00
Maik Opitz 08f74201b6 RegEx angepasst 2016-11-02 16:21:03 +00:00
Maik Opitz 3a25c683b6 Regex nochmals angepasst 2016-11-02 16:48:10 +01:00
Maik Opitz fb43f8bf1a Regex nochmals angepasst 2016-11-02 16:45:01 +01:00
Maik Opitz bf082600c3 fixed RegEx with newer gcc versions 2016-11-01 15:46:40 +01:00
David Richardson 7596c66703 Fix CPBox drawing artifacts when used in CPSeparator mode
Suppress title positioning when CPBox used as a line or separator - by definition the title is null.
2016-10-31 16:21:46 -06:00
Didier Korthoudt e1bcf6c47e Merge remote-tracking branch 'cappuccino/master' 2016-10-31 16:11:38 +01:00
Alexander Ljungberg ef1d6c78ac Merge branch 'master' of github.com:cappuccino/cappuccino 2016-10-30 21:06:25 +00:00
Alexander Ljungberg f36406af08 Text tweak. 2016-10-30 21:05:54 +00:00
Alexandre WilhelmandGitHub 4b7d215d9d Merge pull request #2498 from daboe01/fixed--font-can-be-nil
Fixed:  font can be nil sometimes
2016-10-30 13:52:03 -07:00
daboe01 e09f12dc0b fixed: accessors for _font were not used sometimes 2016-10-26 19:58:21 +02:00
daboe01 efa7406741 fixed: typing font was nil in sometimes cib-generated textviews 2016-10-26 19:53:59 +02:00
daboe01 5a2181c064 Merge remote-tracking branch 'cappuccino/master' 2016-10-26 19:51:22 +02:00
cacaodev dda81b12da Revert "nib2cib: discard warnings in the nib file."
This reverts commit 4e11152e76.
2016-10-22 08:40:38 +02:00
Martin CarlbergandGitHub c4bf67d133 Merge pull request #2496 from cacaodev/cptabview-initial-selection
Fixed: CPTabView should not allow empty selection
2016-10-21 18:16:31 +02:00
daboe01 f355c8679d Merge remote-tracking branch 'cappuccino/master' 2016-10-21 17:38:56 +02:00
cacaodev 095864de39 Fixed: CPTabView should not allow empty selection
Before this commit, a tab view could have an empty selection with the
following circumstances:
- The tab view was not created in IB
- the Tab View selection had not binding management

With test.
2016-10-21 10:13:47 +02:00
cacaodevandGitHub 0eb196e852 CPTabView: load tab view items asynchronously when possible (#2479)
When an item created with +tabViewItemWithViewController:, the view is loaded asynchronously

Manual test CPTabViewNib have been modified to show a tab view item
managed by a view controller. Any setup happens in
-viewDidAppear: because we cannot use any more the
tabView:didSelectTabViewItem: delegate method.
2016-10-20 17:24:18 +02:00
cacaodevandGitHub 62e5d8d841 Fixed: type declaration 2016-10-20 08:48:01 +02:00
cacaodevandGitHub 2f82fb842d Merge pull request #2495 from daboe01/fixed--typo-in-CPControl-comment
Fixed:  typo in CPControl comment
2016-10-19 08:37:27 +02:00
daboe01 f9fa6abbad fixed: small typo in comment 2016-10-18 21:46:21 +02:00
cacaodevandGitHub 4b55a69150 Merge pull request #2447 from cacaodev/CPAnimationContext-fixes
Fixed: The animation was reverting back to its initial position.
Fixed: custom layout and custom drawing was not animated in Chrome and Firefox.
2016-10-17 22:01:15 +02:00
Martin CarlbergandGitHub 42e12fae24 Merge pull request #2485 from cacaodev/ibtool-warnings
nib2cib: discard warnings in the nib file.
2016-10-17 18:07:59 +02:00
daboe01andcacaodev 2f55d5130a fixed: typo in accessor setHorizontallyResizable: (#2494) 2016-10-16 21:24:30 +02:00
daboe01 cd4b47b397 Merge remote-tracking branch 'cappuccino/master' 2016-10-16 13:20:13 +02:00
daboe01andcacaodev 126cba1e94 Fixed: textsizing was wrong in safari (#2486)
* fixed: color panel creation in font panel init did break text sizing in safari

* fixed: font sizing did not robustly work on safari

* removal of dead code

* fixed: browser sniffing was not using the mechanisms from CPCompatibility.j

* fixed: blink and webkit engines must not be mutually exclusive

* improved documentation
2016-10-16 10:11:10 +02:00
daboe01 e4e894fa54 Merge remote-tracking branch 'cappuccino/master' 2016-10-15 17:10:59 +02:00
Martin CarlbergandGitHub 65f701d459 Merge pull request #2487 from daboe01/fixed--CPTextView-did-not-support-dragging-the-selection
Fixed: CPTextView did not support dragging the selection
2016-10-13 17:22:51 +02:00
Martin CarlbergandGitHub c2b7cc7964 Merge pull request #2302 from ahankinson/fix-cpview-formatting
Formatting: CPView
2016-10-13 16:55:46 +02:00
Martin CarlbergandGitHub 969d1dff2a Merge pull request #2492 from didierkorthoudt/cpview-updateTA-recursive-bug
Fixed: CPView setFrameSize was calling _updateTrackingAreas too often + empty overlappingTA test
2016-10-13 16:07:10 +02:00
Didier Korthoudt e0fab808b9 fix-overlappingTA-could-be-empty 2016-10-08 17:33:21 +02:00
Didier Korthoudt 77031d7082 Fixed: setFrameSize was calling _updateTrackingAreas too often 2016-10-06 22:54:32 +02:00
Didier Korthoudt 5a0f7ea706 Merge remote-tracking branch 'cappuccino/master' 2016-10-06 20:25:25 +02:00
daboe01 9d041c4b62 fixed: unnecessary safety margin for dragview width 2016-10-02 21:28:38 +02:00
daboe01 ec4d29c327 fixed: transaction issue from the undo manager 2016-10-02 10:34:30 +02:00
daboe01 5f8d8988e3 revert: move drag indicator uncoditionally to the right side 2016-10-01 20:21:52 +02:00
daboe01 a31912f710 fixed: insertion marker was off by one 2016-10-01 20:11:25 +02:00
daboe01 e0d42962e7 fixed: wrapping issue when dragging last word 2016-10-01 19:56:35 +02:00
daboe01 47688bf016 formatting 2016-10-01 18:32:19 +02:00
daboe01 39a695959d formatting 2016-10-01 18:29:47 +02:00
daboe01 71d45a67b7 fixed: dragging placeholder did not always look like dragged text 2016-10-01 18:28:44 +02:00
daboe01 8dc1369ba3 fixed: direct DOM manipulation on drag placeholder instead of using capp methods + formatting 2016-09-30 16:03:36 +02:00
daboe01 1206654d98 fixed: drag placeholder was CPTextField instead of CPTextView 2016-09-29 15:41:46 +02:00
daboe01 1492763d0c fixed: par issue in rtf parser 2016-09-28 20:55:30 +02:00
daboe01 285fafc464 rtf parser fix + formatting 2016-09-28 20:34:46 +02:00
daboe01 b15cc59b95 prettify drag placeholder 2016-09-28 19:42:59 +02:00
daboe01 55cfe31dfe fixed: selection did not work directly after aborted drag 2016-09-28 17:36:28 +02:00
daboe01 dafa6e0310 fixed: space escape was missed for rich text pasting 2016-09-28 17:31:39 +02:00
daboe01 4befe17cab fixed: target index in selection dragging was sometimes wrong 2016-09-28 17:24:39 +02:00
daboe01 2217b560cb formatting 2016-09-28 16:21:55 +02:00
daboe01 39d01a1da8 sizing correction for dragging 2016-09-28 16:15:31 +02:00
daboe01 4e14ad7821 basic support of text selection dragging 2016-09-28 15:12:26 +02:00
daboe01 aee5a2cbe4 refactor point conversion to private method _characterIndexFromEvent 2016-09-28 11:55:56 +02:00
daboe01 de2d5aec1a Merge remote-tracking branch 'cappuccino/master' 2016-09-27 19:34:06 +02:00
Martin CarlbergandGitHub bea03de562 Merge pull request #2068 from daboe01/NEW--CPTextView
New: CPTextView, CPTextStorage, CPTextContainer, CPLayoutManager and CPSimpleTypesetter + CPFontPanel + RTF parser/producer [+2]
2016-09-26 09:53:29 +02:00
Martin CarlbergandGitHub 0aeccbb02c Merge pull request #2462 from daboe01/pasteboard-type-fix
fixed: cappuccino pasteboad is always clobbered by system pasteboard
2016-09-26 09:49:41 +02:00
cacaodev 4e11152e76 nib2cib: discard warnings in the nib file. 2016-09-26 09:44:23 +02:00
Alexandre WilhelmandGitHub 5059028e8a Merge pull request #2484 from daboe01/fixed--documentation-of-autoscroll--was-missing
Fixed:  documentation of autoscroll:  was missing
2016-09-25 13:00:21 -07:00
daboe01 070e6cf848 fixed: documentation of autoscroll: was missing 2016-09-24 17:04:12 +02:00
daboe01 4187fe7261 Merge remote-tracking branch 'cappuccino/master' 2016-09-17 17:07:44 +02:00
daboe01 bdec147874 formatting 2016-09-17 16:34:39 +02:00
daboe01 189fc76dad fixed: highspeed typing issue 2016-09-16 23:59:09 +02:00
daboe01 c0a68005d4 fixed: native rich pasting was not working robustly enough in chrome 2016-09-16 23:09:54 +02:00
daboe01 8187e4cdc2 formatting 2016-09-16 19:22:13 +02:00
daboe01 cc98fd4167 fixed: keystroke duplication when shift is pressed immediately after 2016-09-16 19:21:12 +02:00
daboe01 422e014eff fixed ^a and ^e semantics 2016-09-16 18:32:14 +02:00
daboe01 dd4e044608 fixed: various tab related line height issues 2016-09-15 22:26:20 +02:00
daboe01 13fad1ca47 fixed regression: line spacing was odd when small line followed large line 2016-09-15 20:25:28 +02:00
daboe01 70aaa29016 removal of dead code 2016-09-14 22:38:50 +02:00
daboe01 7dcec9afcf fixed: infinite loop due to undo edge case 2016-09-14 22:20:33 +02:00
daboe01 608642fc25 formatting 2016-09-14 22:18:52 +02:00
cacaodevandGitHub dd62c08b09 FIXED: Fix a build error in XcodeCapp (#2480) 2016-09-09 21:03:17 +02:00
daboe01 315fc21eca fixed: setFrameSize: was iterating every character 2016-09-09 21:00:22 +02:00
daboe01 8cfd3aeda9 fixed: sizing cache was not set up correctly 2016-09-09 20:31:53 +02:00
cacaodevandGitHub 6061323c36 Test CPSegmentedControl enabled state (#2481) 2016-09-07 20:56:49 +02:00
cacaodev 17da8b04bf Remove references to CPAnimationTriggerOrderIn 2016-09-05 21:43:54 +02:00
daboe01 3a948406d5 optimisation: _indexOfObject used a method call instead of js property 2016-09-03 10:00:24 +02:00
daboe01 91c52f8ec8 Merge remote-tracking branch 'cappuccino/master' into NEW--CPTextView 2016-09-03 09:40:49 +02:00
daboe01 6076bbb554 Fixed: CPRange was not copied before altering its content 2016-09-02 19:50:57 +02:00
daboe01 f70d3359c4 removed: temporary debugging code 2016-09-01 21:46:41 +02:00
daboe01 2dbfbe36c3 fixed: lineheight and base wrap issue 2016-09-01 21:45:12 +02:00
daboe01 a25bd03566 fixed: container switch semantics 2016-09-01 20:37:35 +02:00
daboe01 109da1c2cc fixed: typsetting the paragraph geometry was not as fast as it could be 2016-09-01 20:08:34 +02:00
daboe01 9a48e25bb1 fixed: container geometry was not cached appropriately 2016-09-01 14:48:51 +02:00
daboe01 a3ade200a9 fixed: textContainers were not cached appropriately 2016-09-01 14:43:10 +02:00
daboe01 eb1901f03d fixed: comparison was not using the characterCode 2016-09-01 13:50:46 +02:00
daboe01 f8c7cdfc28 typesetter optimization: use integer constants instead of strings 2016-09-01 13:31:40 +02:00
daboe01 ea58d84b55 fixed: text sizing was slower than it should be
thanks mrcarlberg for spotting this
2016-09-01 13:25:12 +02:00
Martin CarlbergandGitHub 1360caaa81 Merge pull request #2467 from daboe01/tableview-cell-editing-fix-2-take
Fixed: tableview corruption when clicking on a cell-based control during an editing session
2016-09-01 06:08:14 +02:00
Alexandre WilhelmandGitHub 315a7b9e6a Merge pull request #2476 from daboe01/New-CGContextText
fixed: CoreGraphics does not have basic text drawing support
2016-08-30 17:43:55 -06:00
daboe01 79311d107b fixed: line fragements were searched linearly instead of using binary search 2016-08-27 22:54:09 +02:00
daboe01 77435fcbd7 fixed: void scanning for fragments during typesetting 2016-08-27 14:25:44 +02:00
Alexander Ljungberg 4395c07648 Merge branch 'fix-mobile-text-entry' 2016-08-25 13:28:29 +01:00
Martin CarlbergandGitHub f9cc77382a Merge pull request #2475 from daboe01/daboe01-readme-fix
Fixed: missing requirements for developers...
2016-08-24 16:45:19 +02:00
Martin CarlbergandGitHub e4fce08eff Merge pull request #2477 from daboe01/setTimeout-scrolling-fix
fixed: scrolling events are missed on slower maschines in chrome 52
2016-08-24 16:23:56 +02:00
Alexander Ljungberg 47196cbc55 Typos. 2016-08-19 14:57:57 +01:00
Alexander Ljungberg c0bf301434 Test: full manual test for the iOS hit test bug. 2016-08-19 14:35:44 +01:00
Alexander Ljungberg f6671f8561 Fixed: iOS virtual keyboard broke hit tests.
Without this fix, tapping on the screen while the virtual keyboard of iOS was open, and it had forced scrolling (due to the input being low on the screen), would cause incorrect mouse event locations. So you might tap on a text field below your current field to edit it and something entirely different would get selected.
2016-08-19 14:35:19 +01:00
Alexander Ljungberg 26aab2961b New: iOS Safari virtual keyboard "Done" button support.
Without this change, clicking the done button in iOS Safari would dismiss the virtual keyboard but then it would immediately come back (as we forced a refocus).

The fix is to no longer force a refocus since it turns out we don't need to anymore given another previous fix.
2016-08-19 13:07:43 +01:00
Alexander Ljungberg c233abe3a4 New: -CPEvent subtype. 2016-08-19 12:45:41 +01:00
Alexander Ljungberg beb3646bef New: test which demonstrates a hit test bug in iOS Safari. 2016-08-19 01:15:12 +01:00
Alexander Ljungberg eb8330a420 New: test which demonstrates an iOS virtual keyboard bug. 2016-08-18 23:53:25 +01:00
Alexander Ljungberg 7f3c2176f8 Fix typos. 2016-08-18 23:52:10 +01:00
Alexander Ljungberg 1d0fdd68f4 Formatting. 2016-08-18 23:51:35 +01:00
daboe01 b26c78828e make manual test more explicit 2016-08-17 21:16:16 +02:00
daboe01 85648c7f11 fixed: manual test was missing 2016-08-17 20:10:02 +02:00
daboe01 8588f446aa formatting 2016-08-17 19:17:47 +02:00
daboe01 d723c5c1c7 formatting 2016-08-17 19:16:57 +02:00
daboe01 7897f2489c fixed: setString: altered selection 2016-08-17 19:15:41 +02:00
daboe01 86e2e0a2c3 minor rewording 2016-08-17 19:06:20 +02:00
daboe01 e2349019a3 minor rewording 2016-08-17 19:05:04 +02:00
daboe01 e485044cfb remove mentioning of gcc and clang dependencies 2016-08-17 19:02:34 +02:00
daboe01 72d80e2857 fixed: scrolling events were missed on slower maschines in chrome 52 2016-08-16 20:53:49 +02:00
daboe01 79a4733698 fixed: CoreGraphics does not have basic text drawing support 2016-08-16 20:30:42 +02:00
daboe01 c64e1daa99 minor rewording 2016-08-15 21:21:53 +02:00
daboe01 d6b4933b05 Merge remote-tracking branch 'origin/daboe01-readme-fix' into daboe01-readme-fix
# Conflicts:
#	README.markdown
2016-08-15 21:20:48 +02:00
daboe01 bcf9cee0f8 more details and compiler dependencies 2016-08-15 21:20:19 +02:00
Alexander Ljungberg a2960a7997 Merge pull request #2463 from daboe01/cppasteboad-cleanup
CPPasteboad.j removal of dead code
2016-08-15 14:50:04 +01:00
daboe01andGitHub dac9874895 minor rewording 2016-08-12 17:16:52 +02:00
daboe01andGitHub a4d374ecb6 Fixed: missing requirements for developers...
... in README.markdown
2016-08-12 16:43:42 +02:00
Alexander Ljungberg 3e56c8ed8b Fixed: XcodeCapp nib2cib incorrect vs jake release.
This bug is similar to the previous one in that the effect was the same. If XcodeCapp was used to `nib2cib`, the `NSClasses` setting would be ignored.

The reason for this, apart from the previous bug, was that `nib2cib` needs to be run from the project folder in order to find the main `Info.plist` file.

The fix is to change the CWD to the project folder before building.
2016-08-10 13:23:01 +01:00
Alexander Ljungberg f4c11d14e7 Fixed: nib2cib'ed files different with jake release and jake deploy.
Without this fix, if you ran `nib2cib` on its own on your UI files you could get a different result than what `jake release` or `jake deploy` would produce. In particular the `NSClasses` setting would be ignored unless `jake release` was used.

The reason for this bug was that the main bundle was never loaded and so the `Info.plist` was not read. But when using `jake release` (and deploy) the main bundle and the `Info.plist` gets baked in so it's always automatically loaded.

The fix is to load the main bundle before checking its settings.
2016-08-10 13:22:04 +01:00
Alexander Ljungberg dde805931d New: show successfully loaded NSClasses in nib2cib verbose.
It's helpful to see which ones are loaded when troubleshooting nib2cib.
2016-08-10 13:22:04 +01:00
Alexandre WilhelmandGitHub fedc721432 Merge pull request #2439 from cacaodev/CPView-viewDidAppear
NEW: CPViewController -viewWill/DidAppear & viewWill/didDisappear
2016-08-09 23:06:10 -07:00
Alexandre WilhelmandGitHub b09cb24804 Merge pull request #2473 from enquora/capp
Move XcodeCapp 'Preferences' menuItem to standard position
2016-08-09 22:54:39 -07:00
Alexandre WilhelmandGitHub 80bfeb1a29 Merge pull request #2470 from daboe01/remove-rakefile
Fixed: obsolete file in root directory
2016-08-09 22:54:00 -07:00
David Richardson 63ad51a637 Move XcodeCapp 'Preferences' menuItem to standard position 2016-08-09 11:33:45 -06:00
daboe01 5ee6232bfc fixed: unused file in the root directory 2016-08-05 12:47:49 +02:00
daboe01 b7d399faf7 fixed: bold and italic extraction from dom for native paste 2016-08-01 09:17:05 +02:00
daboe01 28691bb8a0 improved formatting extraction from the dom in native paste operations 2016-08-01 08:50:24 +02:00
daboe01 fd61f35f78 fixed: rtf-color table was not written 2016-07-29 18:49:38 +02:00
daboe01 0505b59781 formatting 2016-07-29 17:32:48 +02:00
daboe01 a3601e5167 preparations for native rich paste 2016-07-29 17:31:40 +02:00
daboe01 99f63ab64d fixed: are double typing issue in webkit browsers 2016-07-29 16:28:17 +02:00
daboe01 bb2a9fe180 formatting 2016-07-24 17:45:26 +02:00
daboe01 e9c7c7ee4c fixed: editing state was deleted after committing a non-text-control 2016-07-24 17:43:45 +02:00
daboe01 ff7d4f237a fixed: hitTest: had side effects 2016-07-24 17:03:00 +02:00
daboe01 8bf9f8057e Merge remote-tracking branch 'cappuccino/master' 2016-07-24 16:44:00 +02:00
daboe01 27b0f531d7 fixed: more support for RTF for Pages.app 2016-07-17 15:19:40 +02:00
daboe01 01e96d3086 fixed: Pages.app rtf did not parse correctly 2016-07-17 15:00:35 +02:00
daboe01 95e7a7c8eb fixed: wrong comparison (for proper null handling) 2016-07-17 07:53:53 +02:00
daboe01 bbc2f4766e fixed: one other reference to _stateUID 2016-07-17 07:33:21 +02:00
Alexandre WilhelmandGitHub 12dde5154f Merge pull request #2461 from cacaodev/CPView-viewDidHide
FIXED: CPView received multiple -viewDidHide/Unhide messages
2016-07-11 07:46:16 -07:00
Alexandre WilhelmandGitHub 5bc3609ad7 Merge pull request #2464 from daboe01/remove-nativehost
Remove nativehost
2016-07-09 19:14:29 -07:00
daboe01 5c1cdea3c6 Removed: Obsoleted Nativehost stuff 2016-07-08 21:34:14 +02:00
daboe01 3da5b3a278 fixed: entering return blocked the nativeinputmanager 2016-07-01 18:26:01 +02:00
daboe01 9f6460ddae prevent certain race conditions in deadkey handling in FF and safari 2016-06-30 19:45:09 +02:00
daboe01 f1dcf85d7b turned off logging in RTF parser 2016-06-26 21:33:17 +02:00
daboe01 7b78edf38f fixed: removed dead code from obsoleted native pasteboard approach 2016-06-26 18:35:25 +02:00
daboe01 4a726a0c75 Merge remote-tracking branch 'cappuccino/master' 2016-06-26 18:27:33 +02:00
daboe01 5afa7e9fc3 fixed: rtf parser did not propagate paragraph style sometimes 2016-06-26 17:05:19 +02:00
daboe01 75c102f304 fixed: rtf-producer colortable dead index issue 2016-06-25 21:23:35 +02:00
daboe01 68f3c10055 fixed: only one color was supported on the pasteboard 2016-06-25 20:49:56 +02:00
daboe01 e9aeda2a34 fixed: _RTFProducer was referencing wrong colortbl entries 2016-06-25 20:09:56 +02:00
daboe01 45c7a110b6 prevent blinking during cursor navigation more robustly 2016-06-22 22:04:10 +02:00
daboe01 e438e6aedb fixed: same pasteboardtype was used for plain and rich text 2016-06-22 20:39:41 +02:00
daboe01 8dd888bcbd fixed: native pasteboard unconditionally overwrites capp-pasteboard 2016-06-21 20:56:56 +02:00
cacaodev 5f12291ac4 Fixed: add missing import 2016-06-20 19:34:00 +02:00
daboe01 e206d78a52 removed unused range parameter from _characterRangeForIndex 2016-06-19 13:51:53 +02:00
daboe01 d94a82b0a7 paragraph selection dragging fix 2016-06-19 13:45:41 +02:00
daboe01 3bee3d1701 improved: paragraph selection via triple click 2016-06-18 22:51:41 +02:00
daboe01 631352e1ef improved FF deadkey detection 2016-06-18 20:36:22 +02:00
daboe01 f2d5dee49e backspace fix 2016-06-18 19:49:11 +02:00
daboe01 2d578c7cd9 Merge remote-tracking branch 'cappuccino/master' 2016-06-18 19:18:41 +02:00
daboe01 b07ff100d1 fixed: function is missing 2016-06-18 19:18:23 +02:00
daboe01 b0c10b2248 Merge remote-tracking branch 'cappuccino/master' into NEW--CPTextView 2016-06-18 19:17:53 +02:00
daboe01 7b8249442d selection fixes 2016-06-18 17:59:26 +02:00
cacaodev 8221339318 Style 2016-06-18 13:40:27 +02:00
cacaodev b773af96b8 FIXED : css display attribute was incorrectly set 2016-06-18 13:40:14 +02:00
daboe01 dbc7808c70 code simplification 2016-06-18 11:58:42 +02:00
daboe01 77c5000f16 selection fix 2016-06-18 11:47:47 +02:00
daboe01 56928b6ca0 formatting 2016-06-18 11:46:10 +02:00
daboe01 8178dfe7bd revert: fixed: word granularity selection was not always correct 2016-06-18 11:43:50 +02:00
daboe01 0db6c667c6 fixed: word granularity selection was not always correct 2016-06-18 11:40:12 +02:00
cacaodev 2b0a642827 FIXED: CPView received multiple -viewDidHide/Unhide messages
FIXED: when a view is hidden by a hidden superview and added to a non
hidden superview, the -viewDidUnhide message is sent.

With test : CPViewTest.j
2016-06-18 11:29:47 +02:00
daboe01 f71c0f8cb8 selection fixes 2016-06-18 11:25:02 +02:00
daboe01 3782372f65 optimisations for native input manager 2016-06-17 18:31:44 +02:00
daboe01 9e35e42030 nativeinputmanager optmisations for safari 2016-06-16 20:36:12 +02:00
daboe01 7a8195e54c fixed: cursor extended selections did not copy in safari 2016-06-15 22:55:48 +02:00
daboe01 b50ee28693 fixed: the native input manager did not get the focus after recent safari update 2016-06-15 20:52:55 +02:00
Martin CarlbergandGitHub e134f910aa Merge pull request #2436 from mrcarlberg/updated_compiler
Fixed: Merged in the parser and compiler that is used in the node objj-runtime
2016-06-14 21:32:28 +02:00
daboe01 17eb6e47eb improve wordBoundaryRegex to recognize numbers 2016-06-12 13:08:10 +02:00
cacaodev 82ce626d8a Underscore prefix on private methods 2016-06-12 10:52:19 +02:00
cacaodev e063ff5e54 Remove CPAnimationTriggerOrderIn partial support. Adds a warning if someone tries to use it. 2016-06-12 10:20:26 +02:00
cacaodev 5aa98966bc Cucapp test for CPView animation
Tests for commits #1, #2, #3 of this PR
2016-06-12 10:12:08 +02:00
cacaodev fca2746a82 Fixed: custom layout and custom drawing was not animated
The bug was appearing with the following conditions:
keypath : frame , frameSize
browsers : Google Chrome, Firefox
2016-06-12 10:12:08 +02:00
cacaodev 7faffc2de7 Use cancelAnimationFrame when available 2016-06-12 10:12:07 +02:00
cacaodev 86a8a9dc85 Fixed: The animation was reverting back to its initial position.
Although the fill-mode property was correctly set to forwards allowing
the final state to persist, the animation-name property was deleted too
early . It was causing the animation to be cancelled and the initial
state restored.
2016-06-12 10:12:07 +02:00
daboe01 c13a0c1c26 fixed: selection semantics differed from macos 2016-06-11 20:53:05 +02:00
daboe01 6ce3c070b8 fixed: selection semantics differed from macos 2016-06-11 20:42:39 +02:00
daboe01 e1cb11da22 fixed: double clicking right to a word selected too much in some cases 2016-06-11 17:16:06 +02:00
daboe01 c628ea34b7 formatting 2016-06-11 17:11:55 +02:00
daboe01 33b7c726f6 fixed: whitespace double-clicking resulted in cross-paragraph selection 2016-06-11 16:40:28 +02:00
daboe01 6f3fd424e1 fixed: umlaute were treated as word bounderies 2016-06-11 16:18:18 +02:00
daboe01 b048ce9c8e formatting 2016-06-09 22:46:59 +02:00
daboe01 6a1040d659 fixed: setTextColor: issue second take 2016-06-09 22:35:50 +02:00
daboe01 fc05c28231 fixed: change in model did not propagate resize 2016-06-09 20:54:09 +02:00
daboe01 5d165bc6df Merge remote-tracking branch 'cappuccino/master' 2016-06-09 19:22:25 +02:00
daboe01 c139b5eabb Merge remote-tracking branch 'cappuccino/master' into NEW--CPTextView 2016-06-09 19:20:38 +02:00
cacaodev d6dd0e551d Revert "FIXED: CPView received multiple -viewDidHide/Unhide messages"
This reverts commit e58420b031.
2016-06-09 10:13:21 +02:00
Alexandre Wilhelm a84a7bb7c3 Merge pull request #2460 from daboe01/fixed-colorpicker-brightness-bar
Fixed: brightness_bar.png was not displayed in CPColorPicker
2016-06-09 00:59:17 -07:00
daboe01 7195ae6e46 Merge remote-tracking branch 'cappuccino/master' into NEW--CPTextView 2016-06-08 20:05:31 +02:00
daboe01 3c70b2f033 fixed: brightness_bar.png was not displayed in CPColorPicker because the size was not initialized 2016-06-08 19:54:44 +02:00
daboe01 c46340a830 Merge remote-tracking branch 'cappuccino/master' into fixed-colorpicker-brightness-bar 2016-06-08 19:45:16 +02:00
daboe01 8d703b1459 fixed: updates to storage were not propageted to the view 2016-06-08 19:23:37 +02:00
Alexandre Wilhelm f1145ef169 Merge pull request #2457 from cacaodev/CPView-viewDidHide
FIXED: CPView received multiple -viewDidHide/Unhide messages
2016-06-07 20:21:52 -07:00
Alexandre Wilhelm a28f9ceeba Merge pull request #2456 from didierkorthoudt/Fixed--right-mouse-clic-during-a-drag-operation-problem
Fixed: right mouse clic during a drag operation cancels it
2016-06-07 20:17:31 -07:00
Alexandre Wilhelm 1927bb4798 Merge pull request #2452 from didierkorthoudt/CPTextField-tracking-area-optimization
Fixed: CPTextField doesn't optimize tracking areas management
2016-06-07 12:07:11 -07:00
daboe01 d4c77f9786 fixed: Setting textColor only changes first lines color 2016-06-07 20:54:11 +02:00
daboe01 021f8d0331 revert fixed: premature optimization 2016-06-05 22:23:26 +02:00
daboe01 e7d25550ee fixed: premature optimization 2016-06-05 22:21:01 +02:00
daboe01 8227276437 fixed: tabstops were accidentally cornercutting relayout 2016-06-05 21:20:50 +02:00
daboe01 38ef76bcdc fixed: fist tabstop was not working correctly 2016-06-02 20:19:00 +02:00
daboe01 7faa0f60b4 fixed: accidental duplication of variable definition 2016-05-28 13:40:30 +02:00
daboe01 faebbb2b8a fixed: font css properties were not considered when assessing visual identity 2016-05-27 23:13:54 +02:00
daboe01 bc9beb1e87 new: render to dom only the visible stuff 2016-05-27 21:24:30 +02:00
daboe01 f161f723d7 fixed: mimetype of rtf 2016-05-27 17:39:51 +02:00
daboe01 6c94550de3 fixed: accidental creation of a global variable 2016-05-27 17:25:19 +02:00
daboe01 b39a45781a fixed: accidental creation of a global variable 2016-05-27 17:23:30 +02:00
Alexandre Wilhelm 9273c9ee10 Merge pull request #2455 from didierkorthoudt/CPColorPanel-platform-window
Fixed: CPColorPanel always opens in main platform window.
2016-05-25 11:36:28 -07:00
daboe01 e6285914b0 Merge remote-tracking branch 'cappuccino/master' into NEW--CPTextView 2016-05-24 11:29:11 +02:00
daboe01 7d2eb83a1b fixed: undo support for fontpanel 2016-05-24 10:56:19 +02:00
daboe01 7e622f1153 undo color: fallback for textcolor 2016-05-24 10:44:09 +02:00
daboe01 9b30a634b1 formatting 2016-05-24 10:42:33 +02:00
daboe01 e897d2ef81 fixed: missing undo support for textcolor 2016-05-24 10:39:23 +02:00
daboe01 f5b177dda8 fixed: passing range directly instead of copy 2016-05-24 10:24:57 +02:00
daboe01 5f51b5d565 revert: no not make copy of range 2016-05-24 10:13:08 +02:00
daboe01 8d7753fb40 formatting 2016-05-24 09:07:41 +02:00
daboe01 4c6f5744d8 fixed: setFont semantics are wrong 2016-05-24 09:07:03 +02:00
daboe01 43eb2932ba fixed: textColor only changes first lines color 2016-05-23 21:42:25 +02:00
daboe01 59de0e7a4a fixed: text sizing did not work for javascript function names 2016-05-23 17:14:13 +02:00
cacaodev 7b204cf910 Documentation for CPViewController -view*Appear methods 2016-05-23 16:06:05 +02:00
cacaodev b587a19a0f NEW: CPViewController -viewWill/DidAppear & -viewWill/DidDisappear
When a view belonging to a CPViewController is hidden, unhidden, added
or removed, its viewController receives the appropriate message.

Added AppKit/CPViewControllerTest.
2016-05-23 16:06:04 +02:00
cacaodev e58420b031 FIXED: CPView received multiple -viewDidHide/Unhide messages
FIXED: when a view is hidden by a hidden superview and added to a non
hidden superview, the -viewDidUnhide message is sent.

With test : CPViewTest.j
2016-05-23 15:39:47 +02:00
Didier Korthoudt 24313d6709 Fixed: right mouse clic during a drag operation cancels it 2016-05-22 22:14:05 +02:00
daboe01 7acb30b981 fixed: text wrapping in textviews from nibs was wrong 2016-05-22 20:33:41 +02:00
Didier Korthoudt 7454620aa2 Fixed: color panel always open in main platform window 2016-05-18 19:43:19 +02:00
Alexandre Wilhelm abb0799817 Merge pull request #2453 from daboe01/tableview-autoscroll
enable autoscrolling for tableview columns
2016-05-18 10:13:36 -07:00
Didier Korthoudt aebd28dbef CPTextField and CPTrackingArea: optimization and cursor management. 2016-05-18 11:09:06 +02:00
daboe01 152db9fb8e fixed: autoscrolling message was commented out 2016-05-17 20:37:33 +02:00
daboe01 980cd520cd fix travis failure 2016-05-15 11:58:22 +02:00
daboe01 373ec8b5a1 Merge remote-tracking branch 'cappuccino/master' into NEW--CPTextView 2016-05-15 11:57:52 +02:00
daboe01 bacb78e9e8 move background color fallbacks to +themeAttributes 2016-05-15 11:51:44 +02:00
cacaodev 333e112e37 Fixed: CPPredicateEditor predicate when the template has a CPFloatAttributeType 2016-05-15 10:06:52 +02:00
Philippe Fuentesandcacaodev 818d5d782f Fixed: date comparisons in CPPredicateEditor (#2449)
CPPredicateEditorRowTemplate has been modified to allow a correct comparision of date objects when using the CPDateAttributeType
Before this correction, the predicate format was using a string representation of a date instead of a date object.
2016-05-14 23:16:10 +02:00
daboe01 8774090086 formatting 2016-05-14 16:27:01 +02:00
daboe01 0ca65c6d61 fix travis 2016-05-14 13:10:37 +02:00
cacaodev c38daac646 Test: test for pr #2449
In CPPredicateEditor, tests that the input predicate is equal to the
output when the right control is editable.
2016-05-14 10:00:44 +02:00
cacaodev c31f5b1d59 Fixed: CPPredicateEditor in OJTest
OJTest is reading theme attributes from the class default. In
CPPredicateEditor implementation, nil check an attribute and change the
default of another attribute to be non null.
2016-05-14 09:57:29 +02:00
daboe01 2320a56a63 revert travis test 2016-05-13 20:51:25 +02:00
daboe01 07bd91b456 travis test 2016-05-13 20:34:24 +02:00
daboe01 803f7c3a4b formatting 2016-05-13 19:39:30 +02:00
cacaodev a1926982c6 Fixed: make CPObject implementsSelector: work with proxies and KVO swizzled classes 2016-05-12 21:43:44 +02:00
daboe01 b3306d50eb move selection color to the themes 2016-05-12 20:17:14 +02:00
Alexandre Wilhelm 3e6e5f95ec Merge pull request #2448 from cacaodev/CPViewController-async
Fixed: CPViewController isViewLoaded property in async mode
2016-05-11 12:20:48 -07:00
daboe01 df71fb42ac remove duplicate code 2016-05-10 22:41:08 +02:00
daboe01 fe81a354a3 formatting 2016-05-10 22:28:35 +02:00
daboe01 67ba2e49c3 workaround for FF crash in very large documents 2016-05-10 22:27:21 +02:00
daboe01 8dec42f295 fix missing font panel in manual test of CPTextView 2016-05-10 20:35:16 +02:00
daboe01 5942553370 make selection handling more mac like 2016-05-10 19:40:27 +02:00
Martin Carlbergandcacaodev ae5d31746e Dependent key paths use a relationship (#2431)
Fixed: When dependent key paths use a relationship, only the first added observer will observe the attribute for the relationship object.

Also, an optimisation on when the willChange… chain is running, there is no need to replace the observers as no new values are set yet. That will be done when the didChange… chain is running.
2016-05-09 22:14:20 +02:00
cacaodev 1c1976abbf Simple cucapp test for the CPViewController isViewLoaded property in async mode 2016-05-09 09:45:18 +02:00
cacaodev b3465ddf89 Fixed: CPViewController -isViewLoaded property was always NO in async mode (loadViewWithCompletionHandler:).
The async mode calls the completionHandler whereas the sync mode calls the -viewDidLoad method.
Both are now run within the will/did change block of the isViewLoaded property.
2016-05-08 23:09:50 +02:00
Alexandre Wilhelm f67fafdb8e Merge pull request #2446 from cacaodev/CPCollectionView-fixes
CPCollectionView content binding & minor code refactor
2016-05-08 11:16:34 -07:00
cacaodev ffcac677e0 Fixed: Manual test SmartFoldersDemo
The sheet was not closing when asked.
2016-05-07 21:14:35 +02:00
cacaodev a9d643aa55 OJTest for CPCollectionView selectionIndexes, CPCollectionViewItem isSelected, CPSelectionIndexesBinding and CPContentBinding. 2016-05-03 21:51:10 +02:00
cacaodev fa3647e8e5 Refactor selection with modern code.
Handle out of range errors when reflecting selection.
2016-05-03 21:50:41 +02:00
cacaodev db839f6d6d Rename _lockResizing to _inLiveResize
_inLiveResize is a CPView flag.
2016-05-03 21:50:20 +02:00
cacaodev 3a85d5b89f CPCollectionView content binding support 2016-05-03 21:50:20 +02:00
cacaodev 1575c2bc10 CPTableView ViewBasedCib test app: a bug in CPBox decoding was crashing the app. Remove the box until this bug is fixed. 2016-05-03 19:09:32 +02:00
cacaodev 57189183d6 Merge pull request #2444 from cacaodev/fixed-deprecated-getPropertyCSSValue
FIXED: in CPAnimationContext, 'Unknown function getPropertyCSSValue' error in chrome
2016-04-24 13:24:12 +02:00
cacaodev 01c5cf98df FIXED: Unknown function getPropertyCSSValue in Chrome
getPropertyCSSValue have been deprecated in chrome. Replacing with
getPropertyValue supported by all browsers.
2016-04-23 10:54:22 +02:00
Martin Carlberg 9ad2995168 Fixed: Some Javascript minifiers will convert ”\u2028” to a string with the actual line separator inside. It is not valid Javascript to have the actual character inside a string literal.
Instead of fixing bugs in different minifiers it is easier to rewrite this small function. The same applies to \u2029
2016-04-07 10:32:47 +02:00
Martin Carlberg 51fc6518e1 Fixed: Removed some trailing white spaces 2016-04-07 10:32:46 +02:00
Martin Carlberg 530db4e904 Fixed: Merged in the same parser and compiler that is used in the objj-runtime that is running on node.
The life will be easier to have the same in both environments.
Warnings and errors now don’t use read only properties for line and column information. So the test cases in Toolstest.j now work in modern JS engines.
It is also easier to set options for the compiler.
2016-04-07 10:32:46 +02:00
Alexandre Wilhelm cd92ebd69c Merge pull request #2437 from mrcarlberg/cpobjectcontroller_set_entity_name_from_code
Fixed: Setting entity name from code does not work
2016-04-06 18:15:23 -07:00
Alexandre Wilhelm 5c58552d91 Merge pull request #2438 from mrcarlberg/cppopupbutton_binding_select_is_done_before_content
Fixed: First popup item is always selected if selected binding value is set before content binding value
2016-04-06 18:14:53 -07:00
Antoine Mercadal 584aebf76b FIXED: used the master of cappuccino-base 2016-04-06 09:55:38 -07:00
Antoine Mercadal 43a2480699 Merge pull request #2430 from herbatnik/master
FIXED: Cursor key binding methods do not fire in IE11 #2365
2016-04-06 08:35:14 -07:00
Martin Carlberg 699503042f Fixed: First item is always selected if selected binding value is set before content binding value.
Test case is provided.
2016-04-05 23:22:29 +02:00
Martin Carlberg 02e71c23de Merge branch 'master' of https://github.com/cappuccino/cappuccino 2016-04-05 23:12:07 +02:00
Martin Carlberg c24c3868bb Merge pull request #2434 from Dogild/TrackingAreaTableViewHeader
Fixed: crash when having a CPTableViewHeader without a CPTableView
2016-04-05 19:49:27 +02:00
Martin Carlberg 5c60819668 Merge pull request #2432 from Dogild/CacheURLRequest
Fixed: cache control was not set properly in CPURLRequest
2016-04-05 19:05:59 +02:00
Martin Carlberg 480a125a20 Fixed: When setting entity name from code the _managedProxy will be null and the name will not be stored 2016-04-05 18:43:32 +02:00
Antoine Mercadal 598a6ebaed Merge pull request #2278 from cacaodev/CPAnimationContext
Animations: CPAnimationContext & animator
2016-04-04 09:57:55 -07:00
Alexandre Wilhelm 185ad5e747 Fixed: crash when having a CPTableViewHeader without a CPTableView
Previously, when having a CPTableViewHeader without a CPTableView, cappuccino just crashed.
We now check if _tableView is set before iterating on the tableview's columns.
2016-04-04 06:48:26 -07:00
Alexander Ljungberg 42d7e4312b Formatting of CPCache. 2016-04-02 01:20:23 +01:00
Alexander Ljungberg 41a741724f New: Sweden / Swedish as a possible locale. 2016-04-01 23:47:13 +01:00
Alexander Ljungberg 087e811586 Bump XcodeCapp copyright year. 2016-04-01 22:44:23 +01:00
Alexander Ljungberg fa07bc143e Fixed: missed some version number bumps in the 0.9.9 release. 2016-04-01 22:25:21 +01:00
Alexander Ljungberg e13f5ed730 Fixed: CPNumber intValue did not return an integer.
We expect this method to return a whole number like in Objective-C. This also goes for the related methods, `shortValue`, `longValue` and `longLongValue`.

Also adds more tests (in addition to the existing, already failing test).
2016-04-01 22:22:03 +01:00
Alexander Ljungberg 48108cebc9 Merge branch '0.9.9' 2016-04-01 01:05:40 +02:00
Alexander Ljungberg ecac17a00a This will be Cappuccino 0.9.9. 2016-03-29 23:32:12 +01:00
Alexandre Wilhelm 3ba215887c Merge pull request #2426 from mrcarlberg/source_url_fix
Fixed: //@ sourceURL=... is deprecated since many years ago.
2016-03-28 23:43:45 -07:00
Alexandre Wilhelm edbc1baf32 Merge pull request #2427 from cacaodev/cpbutton-image-scaling
NEW: nib2cib support for CPButton -imageScaling.
2016-03-28 23:43:22 -07:00
Alexandre Wilhelm 500fa7f314 Fixed: cache control was not set properly in CPURLRequest 2016-03-28 23:32:48 -07:00
Alexandre Wilhelm a10dfa4ab4 Merge pull request #2429 from mrcarlberg/integer_value
Fixed: Added ’integerValue’ method for CPNumber and CPString.
2016-03-27 09:20:12 -07:00
Martin Carlberg b208571ed7 Fixed: Formatting 2016-03-23 10:17:15 +01:00
Martin Carlberg c1ec27045c Fixed: Rhino does not support numbers starting with ’0’ 2016-03-23 09:35:19 +01:00
Martin Carlberg 111b12161c Added: test cases for ’intValue’ and ’integerValue’ methods on CPString and CPNumber 2016-03-23 09:02:09 +01:00
daboe01 d23d18933c altgr fix for recent FF 2016-03-22 20:39:28 +01:00
Marek Kresnicki f0e736962d FIXED: Cursor key binding methods do not fire in IE11 #2365 2016-03-21 11:23:51 +00:00
Alexandre Wilhelm b12d1491d4 Merge pull request #2415 from cacaodev/CPViewController-loadviewAsynchronously
NEW: CPViewController -loadViewAsynchronously:(Function/*view, error*/)
2016-03-20 19:40:20 -07:00
Alexandre Wilhelm 6d1a094b15 Merge pull request #2428 from cacaodev/CPSearchField-indentationLevel
CPSearchField: use CPMenuItem -setIndentationLevel:
2016-03-20 19:35:48 -07:00
daboe01 88d14792a4 do not select all on setString 2016-03-20 20:29:16 +01:00
Martin Carlberg 7780a98abe Fixed: Added ’integerValue’ method for CPNumber and CPString.
This makes the classes more compliant to Cocoa
2016-03-15 22:25:10 +01:00
cacaodev a1248db3e7 CPSearchField: use CPMenuItem -setIndentationLevel: now that it's available. 2016-03-13 16:09:27 +01:00
cacaodev 6fd2d12d3f Merge remote-tracking branch 'cappuccino/master' into CPAnimationContext 2016-03-12 23:29:46 +01:00
cacaodev 9fab52c044 NEW: nib2cib support for CPButton -imageScaling. With manual test CPButtonTest. Fixes #2425. 2016-03-12 22:18:46 +01:00
daboe01 a32f55d64e add fontpanel to manual test 2016-03-09 19:55:50 +01:00
Aparajita Fishman 96d2b001ea Merge branch 'tableview-edit-fix' 2016-03-08 15:04:55 -08:00
Martin Carlberg e38d6c9f9e Fixed: //@ sourceURL=... is deprecated since many years ago.
We have not changed it for support of older browsers.
Chrome version 51 and above writes a warning about it
in the console so it is time now.
2016-03-07 13:44:29 +01:00
Alexandre Wilhelm be129d243b Fixed: refactoring of CPTextView 2016-03-06 21:48:42 -08:00
Alexandre Wilhelm c85355986b Fixed: manual merged with master 2016-03-06 18:47:58 -08:00
Alexandre Wilhelm 310c0e8379 Merge pull request #2423 from mrcarlberg/cpobject_controller_extension
Allow CPObjectController to get more attributes from the xib/cib file.
2016-03-06 18:20:51 -08:00
Martin Carlberg f0dbffe4f8 Fixed: Formatting 2016-03-06 11:14:22 +01:00
Alexandre Wilhelm 598a482538 New: added gitter badge in the README 2016-03-05 13:38:23 -08:00
Alexandre Wilhelm a995f3ab52 Merge pull request #2424 from mrcarlberg/invalid_plist_fix
Does not throw exception if plist is not valid. It will return nil instead
2016-03-05 13:32:15 -08:00
Martin Carlberg ec5454de44 Merge branch 'cpobject_controller_extension' 2016-03-03 15:09:10 +01:00
Martin Carlberg ec55bddbd4 Fixed: Does not throw exception if plist is not valid. It will return nil instead. 2016-03-01 21:45:08 +01:00
Martin Carlberg 808591df56 Fixed: Allow CPObjectController to get more attributes from the xib/cib file. 2016-03-01 21:40:00 +01:00
Alexandre Wilhelm 96ae3da1e0 Merge pull request #2412 from cacaodev/CPArray-arrayByApplyingBlock-replace
Replace loops with the new -arrayByApplyingBlock: method
2016-02-18 20:25:24 -08:00
Alexandre Wilhelm 4f13df69c7 Merge pull request #2419 from Dogild/cappuccino-test-launched
Test: some manual tests were broken
2016-02-18 20:24:36 -08:00
Alexandre Wilhelm 6d305e4a84 Test: some manual tests were broken
This PR fixes the manual tests. We can no launch every manual tests of Cappuccino...
2016-02-13 11:36:42 -08:00
cacaodev d3dab982a1 Remove ignored argument _ in CPColorPanel 2016-02-07 11:54:16 +01:00
cacaodev 2f6adbae3c Documentation for -loadViewWithCompletionHandler:
Also clarified a note in -loadView doc.
2016-02-07 11:19:26 +01:00
cacaodev ccdc1eed5a Rename to loadViewWithCompletionHandler: 2016-02-07 11:17:53 +01:00
cacaodev 65dee154f7 Remove ignored argument _ 2016-02-06 20:39:49 +01:00
Alexandre Wilhelm 662db009de Merge pull request #2418 from mrcarlberg/build_with_space_in_path
Fixed: Allow build in directory with space in path
2016-02-05 12:05:35 -06:00
cacaodev d07d8f6857 _CPBlockExpression: replace loops with arrayByApplyingBlock: method 2016-02-04 21:51:37 +01:00
cacaodev 0327f6b1ea Merge remote-tracking branch 'cappuccino/master' into CPArray-arrayByApplyingBlock-replace 2016-02-04 21:46:10 +01:00
Martin Carlberg 16dea2ee09 Fixed: Allow build in directory with space in path 2016-02-03 17:26:24 +01:00
Alexandre Wilhelm 5e3fe139fc Fixed: @cappuccino is now a link in the README... 2016-02-02 13:05:46 -08:00
Alexandre Wilhelm 55d41fca05 New: added twitter account in the READMA 2016-02-02 13:05:01 -08:00
daboe01 0b7bb29557 setString selection fix 2016-01-26 19:16:12 +01:00
cacaodev 6b683bd02d Merge pull request #2402 from cacaodev/CPExpression-constructors
NEW : CPExpression +expressionForBlock:arguments:
                                   +expressionForConditional:trueExpression:falseExpression:
2016-01-25 10:22:18 +01:00
daboe01 8f1a498953 added credits to andrew 2016-01-24 15:33:21 +01:00
daboe01 293ed9065d fix chrome duplicate key inserts cursor 2016-01-24 15:24:12 +01:00
Aparajita Fishman 8908fb7ee0 Merge pull request #2414 from didierkorthoudt/CPTrackingArea-fixes2
FIXED: CPTrackingArea incorrect handling of view hierarchy updates while processing events
2016-01-23 07:02:03 -08:00
Didier Korthoudt 2877178b34 Formatting: following Aparajita's recommandations 2016-01-21 20:46:37 +01:00
Antoine Mercadal d527139f48 Merge pull request #2416 from didierkorthoudt/CPBox-contentView-incorrect-initWithCoder
FIXED: CPBox contentView incorrect initWithCoder
2016-01-20 15:11:37 -08:00
Didier Korthoudt b3d8180541 FORMATTING: added comment to workaround 2016-01-21 00:10:38 +01:00
Didier Korthoudt 3411c340ab FIXED: incorrect view hierarchy update 2016-01-20 23:50:22 +01:00
Didier Korthoudt 4482089712 FIXED: corrected replaceSubview parameters order 2016-01-20 22:08:10 +01:00
Didier Korthoudt c8ce2343d7 Merge remote-tracking branch 'cappuccino/master' 2016-01-20 21:43:35 +01:00
cacaodev d547d04277 NEW: CPViewController -loadViewAsynchronously:(Function/*view, error*/)
with manual test CPViewControllerTest
2016-01-20 11:54:02 +01:00
Aparajita Fishman 8f1191733b Fixed race condition when editing table cells
Previously, when double-clicking on a table cell to edit it, there was a possibility of a race condition. A refresh of the display was requested, then the run loop was passed through once. But it was possible for the display refresh to not be queued when the run loop was passed through, in which case the refresh would cancel the editing. Or at least I think that's what was happening.  ;-)

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

Made some miscellaneous formatting fixes as well.
2016-01-19 23:12:11 -08:00
Antoine Mercadal 00bdbcf857 FIXED: only set a content view of CPBox if it is not a separator 2016-01-19 16:10:31 -08:00
Antoine Mercadal bf8070c42d Revert "FIXED: Bug introduced by https://github.com/cappuccino/cappuccino/commit/9fba72cbab42145dc585c01efd82ce645373c04e#diff-227ef108401c2160357ae3fc65018c65R620"
This reverts commit ad48381744.
2016-01-19 16:08:59 -08:00
Antoine Mercadal 346a0b84ae Merge branch 'master' of https://github.com/cappuccino/cappuccino 2016-01-19 13:28:17 -08:00
Antoine Mercadal ad48381744 FIXED: Bug introduced by https://github.com/cappuccino/cappuccino/commit/9fba72cbab42145dc585c01efd82ce645373c04e#diff-227ef108401c2160357ae3fc65018c65R620
The changes in CPBox were messing with the subviews encoding. This patch restores the previous behavior of CPBox.
The original bug might be reintroduced, but this was definitly not the good solution
2016-01-19 13:26:10 -08:00
Didier Korthoudt 0f30d7744d FIXED: CPTrackingArea incorrect handling of view hierarchy updates while processing events 2016-01-19 07:07:45 +01:00
cacaodev 095a978120 Replace loops with arrayByApplyingBlock: 2016-01-17 20:54:43 +01:00
cacaodev 1b72646772 Style: remove tabs 2016-01-17 20:54:20 +01:00
Didier Korthoudt a7b129147f Merge remote-tracking branch 'cappuccino/master' 2016-01-17 12:32:31 +01:00
cacaodev 6496a6643f Merge remote-tracking branch 'upstream/master' 2016-01-16 22:06:42 +01:00
cacaodev e172217427 Merge pull request #2404 from cacaodev/CPArray-mapUsingBlock
New: CPArray -arrayByApplyingBlock:
2016-01-16 19:25:07 +01:00
cacaodev 8c67eee4c8 (CPArray)arrayByApplyingBlock: documentation 2016-01-16 18:28:04 +01:00
Didier Korthoudt ca9039b8d5 Merge remote-tracking branch 'cappuccino/master' 2016-01-16 18:22:20 +01:00
Antoine Mercadal e260632a45 Merge pull request #2408 from slevenbits/xcodecapp-settings-tweaks
Fix some typos/grammar in XcodeCapp settings, plus improve layout.
2016-01-14 16:03:52 -08:00
Antoine Mercadal 678f4fca8a Merge pull request #2409 from primalmotion/predicate-fix
FIXED: CPPredicate's predicateFormat with a CPNull value as right exp…
2016-01-14 15:54:56 -08:00
Antoine Mercadal 5d88d07edc FIXED: CPPredicate's predicateFormat with a CPNull value as right expression wasn't working
Previously a CPPredicate created with format `value == nil` was converted back to `value == <CPNull @ xxxx>`

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

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

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

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

This commit adds support for custom formatters in nib2cib (and thus in xibs), and updates the CPFormatter test app to demonstrate that this works.
2016-01-03 15:26:06 -08:00
daboe01 0e17658269 use fraction again in cursorUp 2015-12-31 10:42:33 +01:00
daboe01 87edda5972 more cursor up fixes 2015-12-30 21:16:02 +01:00
daboe01 ab8c3c2b75 granular selection refac 2015-12-30 17:35:14 +01:00
daboe01 44cef002ed make selection upon doubleclick more mac like 2015-12-30 15:41:36 +01:00
daboe01 bc80fe693a formatting 2015-12-30 14:49:06 +01:00
daboe01 1898b51dc2 fix for cursor up issue in multiple newlines 2015-12-30 14:39:41 +01:00
daboe01 a5dcf5958e updated credits section 2015-12-30 11:28:07 +01:00
Alexandre Wilhelm 9fba72cbab Fixed: nib2cib on a NSBox fails when having a CPTableView in it
Previously, when having a CPTableView in a CPBox, nib2cib failed due to a superview not defined yet. This was raised because in nib2cib we used the method setFrame on the CPBox to modify the frame of the CPBox (we need to modify this frame because the sizing difference between cappuccino and cocoa). We now directly modify the attribute _frame from the object and update the bounds in the same time.

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

Fixed #2400
2015-12-28 17:58:10 -08:00
cacaodev 912fdeaf09 Merge pull request #2403 from cacaodev/cptabviewitem-test
TEST: CPTabViewBindings manual test
Adds a test for tab view item label change.
2015-12-28 16:37:52 +01:00
cacaodev 36c417458d STYLE: replaced expression methods with accessors
Also added methods and documentation for the new methods:
-trueExpression, -falseExpression and -expressionBlock.
Completed documentation for -predicate and -arguments.
2015-12-27 22:08:30 +01:00
cacaodev f1591e593a Coding style 2015-12-27 22:03:35 +01:00
cacaodev 117ac0f5d6 FIXED: replace js Array.map function with capp API
Also fixed BlockExpression description to match a little more with
cocoa impl.
2015-12-27 19:31:05 +01:00
cacaodev e72a42fd2d FIXED: block & conditional expressions: support for CPPredicate -predicateWithSubstitutionVariables: 2015-12-27 19:28:03 +01:00
cacaodev e6e2f4c22c TEST: CPTabViewBindings manual test
Add test for pr #1921 *Label size not calculated when they change*

closes #1921
2015-12-27 18:19:48 +01:00
cacaodev 07b6e2b9f1 CPExpression +expressionForConditional:trueExpression:falseExpression:
Conditional Expression : an expression using different expressions for
evaluation, depending of a predicate result.

Support for predicate parsing: ```TERNARY(predicate, trueExpression,
falseExpression)```

With init, equal, evaluation, parsing tests.
2015-12-27 15:45:03 +01:00
cacaodev 5ab10f4024 NEW: CPExpression +expressionForBlock:arguments:
An expression that returns the result of evaluating a block.

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

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

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

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

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

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

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

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

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

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

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

    CPURLConnection -operation gives access to the operation generated by the new CPURLConnection creator.
    This allows to create dependencies between operations and setup priorities early.
    When the connection fails with a status code 404 or is cancelled, the
    operation is also cancelled and
    the next operation in the queue is started.
2015-10-01 12:36:57 +02:00
Alexandre Wilhelm 9129301c89 Merge pull request #2381 from mrcarlberg/cp_dictionary_duplicate_keys_in_init
CPDictionary handles duplicate keys in correct way on init
2015-09-30 14:54:24 -07:00
Alexandre Wilhelm 9545e0145f Fixed: conflict with auto-merge in CPTextView 2015-09-30 11:31:32 -07:00
Alexandre Wilhelm 15d3e4023b Merge remote-tracking branch 'origin' into NEW--CPTextView 2015-09-30 11:30:18 -07:00
Alexandre Wilhelm 205e4dedd1 Test: updated test CPTextViewCibTest 2015-09-30 11:30:12 -07:00
Alexandre Wilhelm 324d796aca Fixed: issue with typingAttributes and selectingAttributes when the CPTextView was created from a xib 2015-09-30 11:29:55 -07:00
daboe01 5b31e8e9d8 tighten atomicity in kvb 2015-09-30 19:49:04 +02:00
daboe01 0afb62ee88 formatting 2015-09-30 19:46:18 +02:00
Martin Carlberg 1b0d053ab9 Fixed: Check the abbreviation from the correct place in the test 2015-09-30 18:15:32 +02:00
Martin Carlberg 5871c4f092 Fixed: Fixed test cases where CPTimeZone does not handle daylight saving time.
For example the method ’initWithName:’ will return different abbreviation depending on the order CPDictionary returns keys from the method ’keyEnumerator’. As this is undefined the answer can vary. Test cases now handle all cases.
2015-09-30 17:03:36 +02:00
Martin Carlberg 5d415e6679 Fixed: Speed improvement 2015-09-30 16:59:16 +02:00
Martin Carlberg 3a7384ec26 Fixed: CPDictionary initWithObjectsAndKeys was not compliant with Cocoa or the init method ’initWithObjects:forKeys:’ when duplicated keys was passed to the method.
Test cases are also added
2015-09-30 16:59:04 +02:00
daboe01 1d3a8bcacd fix for moveRight: with non-zero length selection 2015-09-30 12:19:04 +02:00
daboe01 dd187aef08 refactor rich copy: to CPTextView 2015-09-30 12:11:39 +02:00
daboe01 78b2c4d094 kvb fix for editable and selectable 2015-09-30 12:00:12 +02:00
Alexandre Wilhelm 32b7a314b1 Fixed: warning when compiling Foundation adn CPException 2015-09-29 15:36:22 -07:00
Alexandre Wilhelm c8d768c59a New: added protocol CPTheme 2015-09-29 12:54:32 -07:00
cacaodev 3ef1c9167f Merge remote-tracking branch 'upstream/master' into CPTabView
Conflicts:
	AppKit/CPTabView.j
2015-09-29 11:04:51 +02:00
Alexandre Wilhelm 36cdcbc3c1 Fixed: setupViewFlags was not called when creating a view from a xib
Previously, the method setupViewFlags was not called when creating from a xib. This issue was introduced by the commit #448c2a2

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

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

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

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

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

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

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

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

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

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

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

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

nib2cib support has also been added.

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

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

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

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

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

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

Credit to @primalmotion
2015-07-08 13:34:57 -07:00
Alexandre Wilhelm 923d32ee6d Test: finally put the init of the CPApplication in the instance method setUp. See previous commit #6fe9dae3d62af995f2481bfebdd706aeb0b21365 2015-07-07 15:04:29 -07:00
Alexandre Wilhelm 4b0fe879cb Test: [[CPApplication alloc] init]; in instance setUp method 2015-07-07 14:46:38 -07:00
Alexandre Wilhelm b87aeffcd8 Test: updated unittest 2015-07-07 14:29:50 -07:00
Alexandre Wilhelm 88aafec6e3 Merge branch 'master' into NEW--CPTextView 2015-07-07 14:29:15 -07:00
Alexandre Wilhelm 6fe9dae3d6 Fixed: AppKit unit-tests are not standalone
Previously, every AppKit tests used the same sharedApplication. Due to this implementation, a could not pass because a previous test made failed the current test. For instance, a test could fail because the window of the previous test resigned (just imagine a new window is the key window in the current test), and this resign could raise an error. The error was displayed for the current test thought this test was perfect !

We now instead of using sharedApplication create a new CPApplication per unit-test file in the class method setUp.
2015-07-07 14:12:19 -07:00
Alexandre Wilhelm 2cb6ddf3ca Fixed: document was not wrap in a #if PLATFORM 2015-07-07 11:14:56 -07:00
daboe01 799622a932 Merge remote-tracking branch 'upstream/master' into NEW--CPTextView 2015-07-07 18:24:54 +02:00
Alexandre Wilhelm 4c1e22b0f6 Fixed: travis failed because the unittests tried to display a layer
Previously, the unittests for the CPDatePicker broke the tests suite of cappuccino. A CPDatePicker uses a CALayer, and the CALayer display methods wasn't wrap in a #if PLATFOrM(DOM). Now it does, so during the tests we do not try to access do the element document anymore.

This PR fixes also some warnings when compiling.
2015-07-06 14:14:39 -07:00
daboe01 2283652704 add missing ivar 2015-07-05 22:50:21 +02:00
daboe01 33fb2daabf fix shift selection 2015-07-05 14:19:11 +02:00
daboe01 c79f59ec05 fix manual test title 2015-07-05 14:02:30 +02:00
daboe01 845aaad891 fix keywindow observer mechanics 2015-07-05 08:57:25 +02:00
daboe01 3c0b061220 cursor fixes 2015-07-04 21:18:40 +02:00
daboe01 af7de73f5c fix dom protection 2015-07-04 19:24:52 +02:00
daboe01 1a8a9f0047 make dom protection more stringent 2015-07-04 19:17:22 +02:00
daboe01 a3b4e8db44 fix non-working unit test 2015-07-04 17:42:07 +02:00
daboe01 f9e8875803 merge current master in 2015-07-04 16:46:24 +02:00
daboe01 e4c11d9a88 various fixes 2015-07-03 23:13:57 +02:00
daboe01 6d1d8da101 recursion fix 2015-07-03 22:56:58 +02:00
daboe01 77303e668f fix missing reference 2015-07-03 21:03:28 +02:00
daboe01 72725b91c7 fix undefined constant 2015-07-03 20:56:14 +02:00
daboe01 46f50f55c2 various baseline fixes 2015-07-03 20:03:53 +02:00
daboe01 5e880f6bfa Revert "_textContainer refac preparation"
This reverts commit 80cf88dcc1.
2015-07-03 17:26:20 +02:00
Alexandre Wilhelm 058a73f8a0 New: added delegate methods applicationShouldTerminate and applicationShouldTerminateMessage
This PR adds the delegate methods applicationShouldTerminate and applicationShouldTerminateMessage in CPApplicationDelegateProtocol.

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

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

Test app in Tests/Manual/CPPlatformWindow/
2015-07-02 16:35:35 -07:00
Alexandre Wilhelm 1ad8d34ecb Fixed: fixed travis with datePicker test 2015-07-02 15:12:18 -07:00
Alexandre Wilhelm 3f9f926c8e New: added method closeAllPlatformWindows in CPPlatform
Added the method closeAllPlatformWindows in CPPlatform. This platform close all platform windows of the application (except the main one). This method is now used when reloading or leaving a cappuccino application, the application will now close every external window openend by the application.
2015-07-02 13:41:37 -07:00
daboe01 80cf88dcc1 _textContainer refac preparation 2015-07-02 19:56:54 +02:00
daboe01 dddc7f348f fix lineheight issue during wrapping 2015-07-02 19:48:12 +02:00
daboe01 ce45f2e114 move to end of line fix 2015-07-02 06:03:05 +02:00
daboe01 d22a7c0a63 FF copy paste fix 2015-07-01 22:52:01 +02:00
daboe01 59162155de caret loc refac 2015-07-01 07:20:17 +02:00
daboe01 0f347cd0b4 revert to capp pasteboard 2015-07-01 05:58:12 +02:00
Alexandre Wilhelm 1f983db47c Fixed: refactoring of the class _CPDatePickerTextField.j. We now use the method called by interpreteKeyEvents instead of handling the key equivalent in performKeyEquivalent 2015-06-29 14:28:28 -07:00
daboe01 dc50793733 cosmetics 2015-06-29 20:58:33 +02:00
daboe01 b98329f91c end of doc fix 2015-06-29 20:54:43 +02:00
daboe01 587d8fbfa9 fix caret position 2015-06-29 20:19:47 +02:00
daboe01 26a2e9ef53 capture mode for copy/paste 2015-06-29 19:43:23 +02:00
daboe01 e5ca98a1d3 fix baseline cross multiple fragments issue 2015-06-28 21:19:33 +02:00
daboe01 5bd5e9000e fix baseline issue with native input 2015-06-28 21:07:51 +02:00
daboe01 d566642c98 improved baseline support 2015-06-28 17:16:24 +02:00
daboe01 f81ddc60ec simplify baseline code 2015-06-28 16:20:06 +02:00
daboe01 6e0b20c921 prepare baseline support 2015-06-28 16:03:23 +02:00
daboe01 0c308b38c5 move to right end simplification 2015-06-28 15:18:59 +02:00
daboe01 8e0fdf68e4 move left/right of line fix 2015-06-28 12:36:40 +02:00
daboe01 9c7a9b5674 cursor up down fix 2015-06-27 16:13:10 +02:00
daboe01 0a49e31b30 add lastmarker to linefrag 2015-06-27 16:01:42 +02:00
daboe01 9478206202 simplify insertText: 2015-06-27 15:43:10 +02:00
daboe01 c86e6f5b88 fix rare invisibility bug 2015-06-27 13:58:58 +02:00
daboe01 87868acecd formatitng 2015-06-27 13:46:31 +02:00
daboe01 6d877754da focusForClipboard fixes 2015-06-27 13:10:53 +02:00
daboe01 dd5521a07c various native deadkey fixes 2015-06-27 13:03:41 +02:00
Alexandre Wilhelm 3a6a84917b New: added CPUserNotification and CPUserNotificationCenter in Foundation
This PR adds the features CPUserNotification and CPUserNotificationCenter in Foundation.
The CPUserNotificationCenter allows you to send user notification to the system.
Right now, we only propose what the W3C proposes. We can set for a notification the title, informativeText and the icon.
We only support local notification yet.

The protocol CPUserNotificationCenterDelegate has been added as well.

Test app in Tests/Manual/CPUserNotificationTest
2015-06-26 16:36:11 -07:00
daboe01 cca142ce86 smart paste at end fix 2015-06-25 20:48:30 +02:00
daboe01 fa3567cb9f fix arrow down last line 2015-06-25 06:03:02 +02:00
daboe01 7d22230b13 keyboard-selection-extension fix 2015-06-25 05:54:34 +02:00
Alexandre Wilhelm 9694c796e3 Revert "fix disappearing buttons"
This reverts commit 7323a5e393.

Fixed #2361
2015-06-24 14:35:23 -07:00
daboe01 1b7f684ab2 make safari fix more robust 2015-06-24 21:14:24 +02:00
daboe01 fef8f4d29c safari backspace fix 2015-06-23 20:36:57 +02:00
daboe01 0d7e945623 fix native cut 2015-06-22 20:30:44 +02:00
daboe01 13ea8eae77 fix initial platform paste 2015-06-21 14:20:14 +02:00
daboe01 cdb2ef3ac7 prevent dom-flickering 2015-06-20 19:11:05 +02:00
daboe01 f216e0198f harmonize capp and system pasteboards 2015-06-20 13:23:33 +02:00
daboe01 c5c3698f43 make undo work again 2015-06-19 23:26:14 +02:00
daboe01 299d3b9f60 formatting 2015-06-19 22:41:12 +02:00
daboe01 eaaebeaebb refactoring paste 2015-06-19 22:39:41 +02:00
daboe01 2ef2d2b9c5 fix native pasteboard support 2015-06-19 22:31:57 +02:00
Antoine Mercadal da6698b216 Merge branch 'master' into capp_env 2015-06-19 10:12:02 -07:00
Antoine Mercadal c9f391dfc1 Update the installation process 2015-06-19 10:11:40 -07:00
Antoine Mercadal 0f62926623 rename cappenv_deactivate to capp_env_deactivate 2015-06-19 09:59:14 -07:00
daboe01 a856b4027b formatting 2015-06-18 20:30:05 +02:00
daboe01 e2f72ecffc prepare baseline support 2015-06-18 20:23:00 +02:00
daboe01 d888bcd473 deadkey backspace safari fix 2015-06-17 20:10:19 +02:00
daboe01 0bfd5fa9a3 fix missing parameters to _flushRange 2015-06-16 19:04:50 +02:00
daboe01 9c05ccef69 preparations for baseline alignment 2015-06-16 18:59:38 +02:00
cacaodev c7eb2ffe5c Merge remote-tracking branch 'upstream/master' into cpview-skip_settransform 2015-06-15 21:10:05 +02:00
daboe01 5281b6df77 increase timeout on FF 2015-06-15 20:49:10 +02:00
daboe01 cd5bb6a309 fix deadkey after deadkey on FF 2015-06-15 20:45:58 +02:00
daboe01 94a44d7af4 remove dead code 2015-06-15 19:50:09 +02:00
daboe01 662050b89f formatting 2015-06-14 21:34:19 +02:00
daboe01 3f7f3140eb rich copy fixes 2015-06-14 21:24:40 +02:00
Alexandre Wilhelm a419694f70 Test: updated unit test ToolsTest.j. Fixed travis... 2015-06-13 21:58:09 -06:00
cacaodev 0a20f77b08 FIXED: In CPView, the transformation matrix was set at each drawing pass, even if the matrix was the identity matrix.
Now we check if the highDPI ratio is == 1 and skip the setTransform call in this case.
2015-06-13 21:06:17 +02:00
daboe01 dc11d18161 formatting 2015-06-12 17:32:22 +02:00
daboe01 cde83e9d7d smart paste fix 2015-06-12 17:28:48 +02:00
daboe01 1476fda014 backspace fix 2015-06-12 17:22:55 +02:00
daboe01 7a875f2db4 filter out apple command keys 2015-06-11 23:03:44 +02:00
daboe01 b868f37da5 remove call to super cancelOperation:sender 2015-06-10 06:41:15 +02:00
daboe01 9bef5a62dd formatting 2015-06-09 22:46:47 +02:00
daboe01 d1aff30c9e fix invisible deadkey at beginning of newline 2015-06-09 22:43:31 +02:00
daboe01 a356dafde0 enter deadkey on exit 2015-06-09 21:14:38 +02:00
daboe01 09477384fe illegal deadkey sequence fix 2015-06-09 19:26:41 +02:00
daboe01 6b923542e8 invisibility fix 2015-06-08 21:36:30 +02:00
daboe01 9722cf718c mouse down fix 2015-06-08 20:30:24 +02:00
daboe01 702b973c87 fix Dead key input element leaves margin on right 2015-06-08 20:25:56 +02:00
Alexandre Wilhelm 472372c9ed Test: updated unittest for the tools 2015-06-08 10:35:13 -07:00
Alexandre Wilhelm d358f0d27b Fixed: xml format now has the key sourcePath instead of path for the command objj and objj2objcskeleton 2015-06-08 10:30:54 -07:00
daboe01 d66bbd95f0 add native support for deadkeys 2015-06-07 21:18:27 +02:00
daboe01 aae498509b typing attributes refactoring 2015-06-07 21:00:25 +02:00
daboe01 4987fb5f15 fontpanel color well fix 2015-06-06 19:03:37 +02:00
daboe01 b1ed299286 fix for paragraph style not being carried forward 2015-06-03 22:13:17 +02:00
daboe01 679ac3e5b1 fix caret height on last line 2015-06-02 23:25:55 +02:00
daboe01 181ef788a3 caret height fix 2015-06-02 22:47:53 +02:00
daboe01 d2caa0953f various caret fixes 2015-06-02 22:23:32 +02:00
Alexandre Wilhelm 3f88e02587 New: added the option -n to specify the name of the cocoa files generated by objj2objcskeleton 2015-06-01 15:22:56 -07:00
Antoine Mercadal 245da9b422 Add capp_env 2015-06-01 15:20:20 -07:00
Alexandre Wilhelm 8cc075aff3 Fixed: objj2objcskeleton does not work with categories on cappuccino class
Previously, when making objj2objcskeleton on a category of a cappuccino class, the cocoa category was not a cocoa class but still a cappuccino class. For instance, a CPView category was still a CPView category instead of being a NSView category.

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

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

Unit test in Tests/AppKit/CPScrollViewTest.j

Fixed #2356
2015-05-19 14:30:09 -07:00
daboe01 4896043599 fix caching issue with numeric strings 2015-05-19 20:22:55 +02:00
daboe01 5f0ddeedd0 draw caret permantently during editing...
...or cursor navigation
2015-05-17 20:43:18 +02:00
daboe01 e679e760ee backspace fix 2015-05-17 20:23:41 +02:00
Alexandre Wilhelm a439688b3c Doc: move private variables to the top of files to not show them in the documentation 2015-05-15 09:55:17 -07:00
Antoine Mercadal 0d17757729 FIXED: CPPogressIndicator needs display when updateing it's state 2015-05-14 15:20:48 -07:00
Alexandre Wilhelm 899bbc2dbc Fixed: issue with determinate spinning progressBar annd value not between 0 and 100 2015-05-13 10:06:54 -07:00
Alexandre Wilhelm 57b68880db New: added indeterminate spinning progressBar
Previously, it wasn't possible to display an indeterminate spinning progressBar. Now we can as in cocoa.

Test app in Tests/Manual/CPProgressIndicator
2015-05-12 17:18:31 -07:00
Martin Carlberg 06c59763bc Merge pull request #2353 from Dogild/optionObjc
New: added option --objc to objj to generate objc files from a .j file
2015-05-12 09:19:08 +02:00
Alexandre Wilhelm 71c1d16c06 Test: fixed test ToolsTest.j 2015-05-11 21:52:04 -07:00
Alexandre Wilhelm 64c1b72481 Test: added unit test for objj2objcskeleton 2015-05-11 17:03:45 -07:00
Alexandre Wilhelm e5742e057d Fixed: issue with objj2objcskeleton and destination folder 2015-05-11 16:27:06 -07:00
Alexandre Wilhelm a9d6176fd2 New: added command objj2objskeleton. This feature is now not available from the command objj 2015-05-11 11:33:52 -07:00
cacaodev 1b0aa2a9b1 FIXED: test app merge issue with cib. Regenerated cib. 2015-05-11 10:37:00 +02:00
cacaodev a49c230bb2 FiXED: animatorClass when the receiver is a CPView custom subclass. 2015-05-11 10:34:12 +02:00
cacaodev b65749cf70 FIXED : setObject:nil in test app 2015-05-11 10:32:33 +02:00
cacaodev 0cf54a6a03 Fix a 'duplicate class' build error 2015-05-11 10:31:27 +02:00
cacaodev 4d77277f2d Merge branch 'master' of git://github.com/cappuccino/cappuccino into CPAnimationContext 2015-05-11 08:44:20 +02:00
daboe01 8b81506ed3 fix aWidth usecase 2015-05-11 05:56:52 +02:00
cacaodev 24c6509a61 FIXED: build error 'CPDictionary nil object'
CPViewAnimator : default mapping between css and capp props is now
lazily computed instead of a class var.
This was causing a build error because some values were not ready to be
read.
2015-05-10 19:23:41 +02:00
daboe01 3c32c4160d formatting 2015-05-10 18:03:42 +02:00
daboe01 1f912d86e1 small speed improvement
avoid the covering method send
2015-05-10 17:59:26 +02:00
daboe01 9c93ab0b6d fixed confusion between font an cssString 2015-05-10 15:30:40 +02:00
daboe01 6646a20a5d DOM protection 2015-05-10 15:22:56 +02:00
daboe01 92f471c476 move fontHeight to where it is needed 2015-05-10 15:19:26 +02:00
daboe01 4c7ce97650 measureText return type 2015-05-10 15:16:12 +02:00
daboe01 4d1fc994c0 refactor private sizing method to sizeWithFont: 2015-05-10 15:12:31 +02:00
daboe01 c4310c554e make DOM sizing faster 2015-05-10 13:37:36 +02:00
Antoine Mercadal c603326e58 Use try/finally to ensure popen streams are always closed 2015-05-09 10:22:13 -07:00
Alexandre Wilhelm a93e04a44e New: added option --objc to objj to generate objc files from a .j file
The command objj has now the option -c or --objc to generate objective-c files. The first arg is the file and the second the destination folder.

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

Tests: All delegate methods are tested in CPTabViewNib manual test.
2015-05-07 22:35:02 +02:00
Alexandre Wilhelm c26aca4686 Fixed: memory leaks in CPSplitView with delegate 2015-05-04 16:43:37 -07:00
Alexandre Wilhelm f822268ae1 Test: updated manual test CPSplitViewTest 2015-05-04 16:43:13 -07:00
Alexandre Wilhelm 4ebcf40619 Fixed: make travis happy with DOM elements in CPTextView 2015-05-04 14:03:33 -07:00
Alexandre Wilhelm a884b94ac0 Test: fixe global var in CPTextViewTest.j 2015-05-04 14:02:46 -07:00
Alexandre Wilhelm 194e705982 Test: comment future test for CPTextView 2015-05-01 16:09:08 -07:00
Alexandre Wilhelm c77ec3bf20 Fixed: added coder for delegate of a CPTextView 2015-05-01 16:08:49 -07:00
Alexandre Wilhelm 0a1857249b Test: updated manual test CPTextViewCibTest 2015-05-01 16:08:30 -07:00
Alexandre Wilhelm 3be3a1f2e6 Merge pull request #2329 from daboe01/fix-issue-10
Added documentation for CPArray's hash method
2015-05-01 14:05:19 -07:00
Alexandre Wilhelm 62f9488829 New: added property baseWritingDirection in CPControl
This PR adds the support for baseWritingDirection in CPControl.
Support of nib2cib has been added as well.

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

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

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

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

Now it does !

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

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

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

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

Tests : CPSegmentedControlTest & Nib2CibAlignment manual tests.

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

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

Test app in Tests/Manual/CPPlatformWindow/
2015-04-06 17:37:08 -07:00
Alexandre Wilhelm 1c61afd3ee Test: updated CPPlatformWindow manual test 2015-04-06 17:36:53 -07:00
Alexandre Wilhelm 737ef583ee New: added delegate method windowDidMiniaturize: windowWillMiniaturize: and windowDidDeminiaturize: in CPWindow 2015-04-06 13:50:30 -07:00
Alexandre Wilhelm 7b4365bd8a New: added delegate method - (CPSize)windowWillResize:(CPWindow)sender toSize:(CPSize)aSize; in CPWindow 2015-04-06 13:34:36 -07:00
Alexandre Wilhelm 041fce743e New: added new options in nib2cib for CPTextView. It now supports the paragraphStyle, font and textColor 2015-03-31 18:17:57 -07:00
Alexandre Wilhelm ad4904a0ec Added nib2cib options _allowsUndo and _usesFontPanel for CPTextView 2015-03-31 16:38:47 -07:00
Alexandre Wilhelm 42a1bca64d Fixed: style in CPTextView 2015-03-31 16:08:16 -07:00
Alexandre Wilhelm 492a5bbb38 Fixed: memory leaks with delegate of CPTextStorage. Added protocol CPTextStorageDelegate 2015-03-31 15:48:15 -07:00
Alexandre Wilhelm c40ef22c20 New: create class _CPSelectionBox 2015-03-31 15:34:23 -07:00
Alexandre Wilhelm 783aac561a Fixed: capp_lint on AppKit/CPTextView 2015-03-31 15:14:15 -07:00
Alexandre Wilhelm 02245c150c Fixed: small issue due to refactoring 2015-03-31 15:10:59 -07:00
Alexandre Wilhelm 54a93775e4 Merge branch 'NEW--CPTextView' of https://github.com/daboe01/cappuccino into NEW--CPTextView 2015-03-31 15:05:53 -07:00
Alexandre Wilhelm ca778aac70 Merge remote-tracking branch 'origin' into NEW--CPTextView 2015-03-31 15:04:35 -07:00
Antoine Mercadal 007ca4c6b0 Merge pull request #2336 from cpasslack/cpasslack/corruptDecimalNumberAfterDecodingFix
Fixed: Encoding a [CPDecimalNumber zero] causes a corrupt CPDecimalNumbe...
2015-03-31 11:25:20 -07:00
Martin Carlberg b3dfce7b5a Fixed: Compared the rects minimum x value with it self instead of the visible minimum x value. Also fixed the test case to catch this. 2015-03-31 17:30:57 +02:00
cacaodev c75f1685c5 _CPObjectAnimator & CPViewAnimator
Make subclassing easier.
For a specific animator, can choose the implementation to invoke by
subclassing - (id) forwardingTargetForSelector:

The pattern for the animator class, if no -(id)animator is implemented,
is now <TargetClass>Animator by default and it searches for a
<TargetSuperClass>Animator if necessary.

NEW: Added support for CPAnimationTriggerOrderIn|Out animated key.
2015-03-31 00:33:28 +02:00
cacaodev db5b103ab3 Merge remote-tracking branch 'upstream/master' into CPAnimationContext 2015-03-30 22:20:46 +02:00
Alexander Ljungberg 1925b7b324 Merge branch 'refs/heads/0.9.8' 2015-03-29 13:39:28 +01:00
daboe01 b2f1c9e832 moved _CPCaret to the end 2015-03-28 21:06:28 +01:00
daboe01 cff4eebb74 caret refactoring + delegate notification fix 2015-03-28 20:59:17 +01:00
daboe01 a3aab5aeca prevent delegate related memory leaks 2015-03-28 19:59:33 +01:00
daboe01 63e5d8f7ea acceptsFirstResponder cleanup 2015-03-28 19:39:03 +01:00
daboe01 e4df7b10f6 _backgroundColor' is already declared for class CPText in superclass CPView 2015-03-28 15:27:37 +01:00
daboe01 f39a1e7d03 move inherited properties to CPText 2015-03-28 15:22:52 +01:00
daboe01 a473f6aa16 richTextPropery 2015-03-28 15:05:58 +01:00
daboe01 6907c24690 fix access to editable propery 2015-03-28 15:01:33 +01:00
daboe01 ef3d8a9277 fix access to selectable property 2015-03-28 14:59:13 +01:00
cacaodev 1a183b7f17 Merge remote-tracking branch 'upstream/master' into CPAnimationContext 2015-03-27 21:56:08 +01:00
cacaodev 88f7b30128 NEW: +tabViewItemWithViewController: 2015-03-27 14:38:59 +01:00
cacaodev 4b38389dc8 Update Manual test 2015-03-27 14:38:58 +01:00
cacaodev 865a45044f NEW: bindings support 2015-03-27 14:38:58 +01:00
cacaodev 7378d06bfd FIXED: CPTabView -insertTabViewItem:atIndex:
Before this commit, the tabViewItems and the segments were 2 different
collections. Any indexed change in the tabViewItems asked to re-sync
the segments making it difficult to maintain any persistent state. Now
the tabView items are the segments content.

Also fixed the selection update when items are removed.
See CPTabViewTest.j  and manual test Manual/CPTabViewNib/
2015-03-27 14:27:46 +01:00
Alexandre Wilhelm 55e19de8a1 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-03-26 10:41:33 -07:00
Alexandre Wilhelm 4d7cb06481 Fixed: changed theme attributes image-search-left-margin image-cancel-right-margin by image-search-inset and image-cancel-inset in CPSearchField 2015-03-26 10:41:21 -07:00
cacaodev 7179a2995d CPTabViewItem image support 2015-03-24 23:42:44 +01:00
cacaodev 2b853df75b Merge pull request #2337 from cacaodev/CPSegmentedControl-setSelectedSegment
FIXED: CPSegmentedControl -setSelectedSegment:-1
2015-03-24 23:21:54 +01:00
daboe01 ae6bcc2987 improve critically low performance in safari
by means of caching cheap sizing data.
2015-03-24 09:59:58 +01:00
Alexandre Wilhelm ed61e5c7f7 Added test file CPTextViewTest.j 2015-03-23 20:56:40 -07:00
Alexandre Wilhelm 3581ac5ebd Fixed: nib2cib with CPTextView works. Editable, Selectable, RichText, StringValue, BackgroundColor, InsertionPointColor works 2015-03-23 14:50:43 -07:00
Alexandre Wilhelm 1f369b47d7 Added hierarchy for nib2cib of a CPTextView. NSTextViewSharedData is a object to retrieve attributes of the CPTextView 2015-03-23 13:06:22 -07:00
Christian Passlack bb9049bef4 Code-styling to match official style-guide. 2015-03-23 20:45:31 +01:00
Christian Passlack 94f15aba40 Updated Test-Cases of CPNumberFormater and CPTextfield because of the
changes for fix #2332. The test-cased failed, because now a
a CPDecimalNumber has a different UID compared to a CPNumber with the same
value. That means isEqual: will return false. In the test-cases the formatter now
generates CPNumbers to make the two Numbers compareable via isEqual.
2015-03-23 20:30:01 +01:00
Alexandre Wilhelm b16b8388ef Merge pull request #2335 from t00f/cpsearchfield_update_frame
Fixed: CPSearchField alignment from nib2cib
2015-03-23 11:39:04 -07:00
Alexandre Wilhelm b1e89e7904 New: added themeAttributes image-search-left-margin and image-cancel-right-margin in CPSearchField 2015-03-23 10:45:43 -07:00
Christophe Serafin f48e2f3b18 Fixed alignement content-inset and images for control size small and mini. 2015-03-20 15:44:37 -07:00
cacaodev bd673a3f26 FIXED: CPSegmentedControl -setSelectedSegment:-1
previously, -setSelectedSegment:-1 was not visualy deselecting the
segments and selectedSegment was not set to -1.
2015-03-20 21:19:36 +01:00
Alexandre Wilhelm 51ec699649 Removed tmp files... 2015-03-20 12:23:38 -07:00
Alexandre Wilhelm ed6be510fb Fixed: first work on nib2cib for the CPTextView. The example does not crash anymore 2015-03-20 12:22:23 -07:00
Alexandre Wilhelm 55f05ba839 Fixed: warnings when compiling 2015-03-20 10:03:42 -07:00
Alexandre Wilhelm 17c9382a21 Merge remote-tracking branch 'origin' into NEW--CPTextView 2015-03-20 09:52:51 -07:00
Christian Passlack 43c0eda2be Fixed: Encoding a [CPDecimalNumber zero] causes a corrupt CPDecimalNumber after decoding
Previously, CPNumber and CPDecimalNumber shared the same method UID and CPNumberUIDs-Dictionary.
This leads to some unexpected errors for example a [CPDecimalNumber zero] which refers to itself after
decoding. CPDecimalNumber now overwrites the method UID and has its own UID-Dictionary.

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

This patch ensure `-(void)close` is called correctly by using the DOM
`unload` event. Also, delegate method `- (void)windowWillClose:` is
correctly called.
2015-03-10 16:35:03 -07:00
Antoine Mercadal 14349cb583 Merge pull request #2240 from Dogild/PlatformOrderOUt
New: added method initWithWindow in CPPlatformWindow
2015-03-10 13:24:00 -07:00
Aparajita Fishman eca959034c Merge pull request #2330 from daboe01/fix-issue-1686
Clarification for CPTableColumn's binding documentation
2015-03-10 11:25:58 +13:00
cacaodev 9844550d6c Use the translate(x,y) property for frameOrigin animated keyPath
Also adds a startValue arg to the function translating Capp values to
css values (needed for translate because you translate the offset).
CPViewAnimator: when ensuing animations, separate the fallback (the
function executed when animation are not supported) and the completion
function executed when the animation ends.
Cleanup xib in manual test: removed controls.
2015-03-09 21:15:17 +01:00
cacaodev 82fcf08d84 Rename 2015-03-08 21:52:42 +01:00
daboe01 845a35a9b8 rewording 2015-03-08 20:49:01 +01:00
daboe01 0e4751ebee wording 2015-03-08 20:46:56 +01:00
daboe01 56badd44f3 updated binding documentation 2015-03-08 19:20:57 +01:00
daboe01 399b3fd198 documentation for the hash method 2015-03-08 19:03:35 +01:00
cacaodev 43a7ca7fe8 Style & doc
Added @ignore directive for private methods.
Changed a parameter naming
Moved some layout code from indexed accessors to public method.
Accessors should only deal with the model, not the layout.
2015-03-07 23:04:32 +01:00
cacaodev b951624ed3 Merge remote-tracking branch 'origin/CPAnimationContext' into CPAnimationContext
Conflicts:
	AppKit/CoreAnimation/CPAnimationContext.j
	AppKit/CoreAnimation/CPViewAnimator.j
2015-03-07 20:03:05 +01:00
cacaodev 1b21f2d1c7 CPView (Animation) : simplified CSS_DESCRIPTORS.
Now easiest to subclass and not much more verbose.
CPVIEW_PROPERTIES_DESCRIPTOR is, for a given CPView subclass, the
mapping between animated keypaths and css properties and between
animated values and css values.
2015-03-07 19:51:13 +01:00
cacaodev 43ea0effa2 Replace objj FrameUpdater with a js function.
Also uses the native function window.requestAnimationFrame();
Expected improvement: the effective redraws should now happen only when
needed.
browser support = same as css animations.
2015-03-07 19:45:06 +01:00
cacaodev 6df9b95dea Code style. NEW CPView -DOMElementForKeyPath:keyPath:
-DOMElementForKeyPath:keyPath returns the DOMElement animated by the
css Animation. This allow for an element different from the view
_DOMElement to be animated.
2015-03-07 19:38:48 +01:00
Antoine Mercadal 630aa53d98 Merge pull request #2321 from william57m/master
New: added the CPCache class
2015-03-05 18:08:18 -08:00
Antoine Mercadal 40c81b88d4 Merge branch 'master' of https://github.com/cappuccino/cappuccino 2015-03-05 17:00:53 -08:00
Antoine Mercadal 027a317291 FIXED: Memory Leak with tooltips
Only populate the tooltips handler functions when necessary (when there is a tooltip and when the view is in a window) or clear them otherwise.

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

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

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

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

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

```objj
capp gen [-f] [-l]  -T CustomTheme1 -T  CustomTheme2 MyProject
```
This will copy/symlink custom theme(s) from your CAPP_BUILD into the
current project’s `Resources` folder
2015-02-06 11:31:25 -08:00
cacaodev 80c8dcacb3 Code Style 2015-02-02 20:15:44 +01:00
cacaodev 0ad2dea722 Merge remote-tracking branch 'upstream/master' into CPAnimationContext 2015-01-30 22:35:01 +01:00
cacaodev dec3c45f6c Merge remote-tracking branch 'upstream/master' into CPAnimationContext 2015-01-29 15:09:08 +01:00
cacaodev 32a73ad4b9 FIXED: The text color of a checkbox inside a selected table row was black.\n Now it is white. the text-color values match the CPTextField values for the same theme states 2015-01-28 14:45:35 +01:00
cacaodev 452e03b894 FIXED: remove observation in -init. It was already handled in -viewWillMoveToWindow: 2015-01-27 22:01:32 +01:00
cacaodev 01bdddaee4 Merge 34c7ded (CPSelectionHighlightStyleNone and mouse click) 2015-01-27 13:09:33 +01:00
cacaodev f7c82b0920 FIXED : -startObservingFirstResponder: remaining renaming. 2015-01-27 13:04:45 +01:00
cacaodev 820eeb7752 _stopObservingFirstResponder -> _stopObservingFirstResponderForWindow: 2015-01-25 17:30:14 +01:00
cacaodev 2e25ef6f81 Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows
Conflicts:
	AppKit/CPTableHeaderView.j
	AppKit/CPTableView.j
	Tests/AppKit/CPTableViewTest.j
2015-01-25 17:25:32 +01:00
Andrew Hankinson 5736d2c9cc Formatting: CPView
Previously CPView used "==" as an equality checker. This commit changes these to use "===" for consistency.
2015-01-22 13:31:04 -05:00
Udo Schneider 056a1f6a33 Fixed: Wrong value parameter in CPObjectController>>setValue:forKey: 2015-01-21 23:53:49 +01:00
Andrew Hankinson f96e211cd6 Tests: New tests for CPDOMDisplayServer 2015-01-20 16:19:24 -05:00
Andrew Hankinson 0d473cbe18 Formatting
Adjusting line lengths
2015-01-20 16:17:03 -05:00
Andrew Hankinson 95514b05ca Fixed: Remove unreachable code in CPDOMDisplayServer.h
The DOM_OPTIMIZATION flag was not used by any of the current compilers. This resulted in unreachable code in CPDOMDisplayServer.h. This commit removes this code.
2015-01-20 14:16:23 -05:00
Andrew Hankinson dc622a9bd5 Merge branch 'refs/heads/master' into NEW--CPTextView 2015-01-14 17:23:50 -05:00
cacaodev 3ddd1d5f21 Added CPAnimatablePropertyContainer protocol 2014-12-19 16:01:49 +01:00
Mathieu Monney c528421a8e Added unit test for pull request #2018 2014-12-19 13:42:40 +01:00
cacaodev ea15f0690c FIXED: compilers warnings in _CPObjectAnimator 2014-12-18 23:40:56 +01:00
Andrew Hankinson 8751ef7439 Fixed: Wrap drawRect method in #if/#endif 2014-12-18 11:04:08 -05:00
Andrew Hankinson 8c393e3144 Fixed: removed self on _DOMElement
Also a bit of formatting.
2014-12-18 09:47:00 -05:00
Andrew Hankinson a3ef2a43ac Fix file modes on CPTextView files
Was 755; now 644
2014-12-17 20:48:29 -05:00
Andrew Hankinson d2b3029f67 Fixed: Removed local declaration of _DOMElement 2014-12-17 20:47:12 -05:00
Andrew Hankinson 3e800ec7c8 Fixing type for DOM Elements
This commit fixes the type for DOM Elements in CPTextView.j
2014-12-17 12:40:24 -05:00
Andrew Hankinson 47d963b585 Fixed: Forward declaration of classes and types
Typedef in the ObjJ compiler introduced a few new warnings in this branch. This commit fixes the warnings by either declaring a type or forward-declaring a class.
2014-12-17 12:39:39 -05:00
Andrew Hankinson 397ae3407e Merge branch 'refs/heads/master' into NEW--CPTextView
Conflicts:
	AppKit/CPControl.j
2014-12-17 12:11:27 -05:00
cacaodev 3f9e2d5d39 CPAnimationContext : add typedef for HashTable 2014-12-09 22:36:37 +01:00
cacaodev 31449552d1 Merge remote-tracking branch 'upstream/master' into CPAnimationContext
Conflicts:
	AppKit/CPView.j
2014-12-09 22:35:29 +01:00
daboe01 5f2fee5a99 redo support added 2014-11-29 18:57:05 +01:00
cacaodev db63c91f99 Manual/TableTest/ViewBasedCib: show how to bind in code a CPTableCellView subview 2014-11-03 16:59:42 +01:00
Alexandre Wilhelm 9c87c36644 Fixed: refactoring of CPPlatformWindow 2014-10-30 18:08:03 -07:00
Alexandre Wilhelm be9f5ad951 Fixed: issue when initialize with contentRect in CPPlatformWindow 2014-10-30 18:04:20 -07:00
Alexandre Wilhelm d065a0cbc4 New: added method initWithWindow in CPPlatformWindow
The constructor initWithWindow: initializes a new CPPlatformWindow and set the given CPWindow as a fullPlatformWindow and bridgless window.
When using this constructor, you don't need to work with the platformWindow anymore, you can use the method orderFront: and orderOut: of the CPWindow to open or close the CPPlatformWindow.
You can also use the method setFrame: of the CPWindow to automatically change the contentRect of the CPPlatformWindow. In one word we assume that the given CPWindow and the CPPlatformWindow will have the same behavior. When not using the constructor initWithWindow:, the method setFrame: of the given CPWindow won't do anything to the CPPlateformWindow.

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

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

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

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

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

Fixed #2175
2014-09-17 10:27:26 -07:00
daboe01 49c7e7ce2d style 2014-09-15 20:10:24 +02:00
daboe01 1af7d01d96 fix of resizing regression 2014-09-15 18:20:53 +02:00
daboe01 66c6938df0 changed selection drawing from canvas to DOM
fixes height limitation, is faster and looks better
2014-09-14 21:24:23 +02:00
daboe01 bb9fb04788 make triple clicks behave more apple like 2014-09-13 20:24:27 +02:00
daboe01 f5cf2b2b55 fix selecting last character 2014-09-13 20:13:08 +02:00
cacaodev 3e8afaeae4 Fixed: -removeTableColumn: now works without error. OJTest in AppKit/CPTableColumnTest.j, manual test in TableTest/TestTanleColumn/
Fixed: Added an out of bounds check to _unloadDataViews:...
Revert: revert -reloadData to the previous behavior where views & data were reloaded, not only data. That's what cocoa does for view based tables.
2014-09-11 23:14:21 +02:00
daboe01 43169ced69 fix of the fix 2014-09-11 10:45:27 +02:00
daboe01 708844857d make canvas sizing detection more robust 2014-09-11 10:22:50 +02:00
cacaodev abe2e544bb Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows 2014-09-06 21:33:18 +02:00
daboe01 ce777348b3 cursor up/down+ frames protection 2014-08-20 23:09:53 +02:00
daboe01 c052325487 various fixes and cleanup 2014-08-20 22:39:15 +02:00
daboe01 ce69410f49 revert of breaking change wrt backspace 2014-08-14 18:21:53 +02:00
daboe01 5f0be0cc7f make storage length zero more tolerant 2014-08-14 14:14:49 +02:00
Andrew Hankinson dca6347ea9 Fixed: Remove lingering references to i
Refs 75e347e8e3
2014-08-14 07:19:53 -04:00
daboe01 94c4db9072 fixed equality operator 2014-08-14 07:17:00 +02:00
Andrew Hankinson 7f63be2cd2 Formatting: Make if statement more readable
The if statement had a lot of sub-clauses. These were factored out into variable assignments.
2014-08-13 19:56:50 -04:00
Andrew Hankinson 1236dea4c5 Fixed: equality operators 2014-08-13 19:47:06 -04:00
Andrew Hankinson c5c46df613 Fixed: typo 2014-08-13 19:44:23 -04:00
Andrew Hankinson a96269150a Fixed: AppKit.j is alphabetized
CPTextView.j was out of alphabetical order. This commit fixes that.
2014-08-13 19:34:56 -04:00
Andrew Hankinson 75e347e8e3 Fixed: obscure use of variables
Previously, the counting variable "i" was used as a variable throughout this method, leading to the value of 'i' becoming unclear as the method progressed.

Additionally, a later loop in this method redeclared 'i', providing more confusion.

This commit renames 'i' to the more descriptive 'targetLine' and uses that in its place. The redeclared value of 'i' was also renamed to "newTargetLine".
2014-08-13 18:15:58 -04:00
daboe01 d641cc5057 fix fussy optimization 2014-08-14 00:12:02 +02:00
daboe01 cf64145ff3 formatting 2014-08-12 16:06:49 +02:00
daboe01 195eca6b09 typesetter early exit in flushing fix 2014-08-12 15:48:31 +02:00
daboe01 b0ef077e48 fix typesetter nil bug 2014-08-12 15:12:19 +02:00
daboe01 e40a698d1a fixed accessor 2014-08-12 10:37:05 +02:00
daboe01 69c34b1b27 formatting 2014-08-12 08:33:28 +02:00
daboe01 4700c2583d fix acessor names 2014-08-12 08:29:28 +02:00
daboe01 6cf79a3c73 size should not be smaller than clipview 2014-08-06 13:31:31 +02:00
daboe01 2c110f7796 fixed: display error at bottom in case of resizing 2014-08-06 13:17:35 +02:00
daboe01 aebea0b57f Update CPTypesetter.j 2014-08-04 13:27:42 +02:00
daboe01 8684455ea5 make canvas sizing validity detection more robust 2014-08-04 13:22:38 +02:00
daboe01 7cbb103376 fix selecting last newline error 2014-07-31 20:35:29 +02:00
daboe01 9cf8ec5dd7 formatting 2014-07-20 19:55:49 +02:00
daboe01 f5187b1533 _characterRangeForUn...: ternary operator 2014-07-20 11:44:03 +02:00
daboe01 6230a65a79 removed useless unit test 2014-07-20 11:24:22 +02:00
Alexandre Wilhelm f13c96839e Fixed synthax in CPTextView 2014-07-19 19:07:08 -07:00
Alexandre Wilhelm 776f68c9dd Fixed style in CPTextView 2014-07-19 18:46:25 -07:00
Alexandre Wilhelm 714ed89a4c Merged 2014-07-19 16:58:59 -07:00
daboe01 0b2b29b7cf removed unit test 2014-07-12 14:09:59 +02:00
Alexandre Wilhelmanddaboe01 a41c0a0519 Fixed: Update test CPTextViewCibTest 2014-07-12 14:03:32 +02:00
Alexandre Wilhelmanddaboe01 026e28114e Added : added default NSTextView for nib2cib 2014-07-12 14:03:32 +02:00
Alexandre Wilhelmanddaboe01 0840719289 Added: added CPTextViewCibTest 2014-07-12 14:03:32 +02:00
daboe01 aad875e113 make the plain look as white as in cocoa 2014-07-12 14:03:32 +02:00
Alexandre Wilhelmanddaboe01 6b059d94ba Fixed: reduce size of the test of CPTextView 2014-07-12 14:03:32 +02:00
Alexandre Wilhelmanddaboe01 39eeaea7e7 Fixed: circular import with CPText
Previously CPText was in CPTextView.j, now it is in CPText.j.
It fixes some other circular import as well, fixes some global var to import.
Fixed some code style.
2014-07-12 14:03:32 +02:00
daboe01 81a8e91180 fix cut with empty selection 2014-07-12 13:58:39 +02:00
Alexandre Wilhelmanddaboe01 4fc6f6f59b Fixed: refactoring delegate methods 2014-07-12 13:58:39 +02:00
Alexandre Wilhelmanddaboe01 dd2f573f27 Fixed: changed a bit the readability of the file 2014-07-12 13:58:39 +02:00
Alexandre Wilhelmanddaboe01 ff8ddc431b New: Added protocol CPTextViewDelegate 2014-07-12 13:58:38 +02:00
daboe01 d703945aa3 corrected inheritance 2014-07-12 13:58:38 +02:00
Alexandre Wilhelmanddaboe01 f4aba5f9ea New: Added protocol CPTextDelegate 2014-07-12 13:58:38 +02:00
Alexandre Wilhelmanddaboe01 393113c3e3 Fixed: capp_lint 2014-07-12 13:58:38 +02:00
Alexandre Wilhelmanddaboe01 f2db18820a Fixed: CPLog.error is used instead of abstract exception 2014-07-12 13:58:38 +02:00
daboe01 e04d300e18 next try 2014-07-12 13:58:38 +02:00
daboe01 685f836f41 another try 2014-07-12 13:58:38 +02:00
daboe01 d4f5ad641a more dom protection 2014-07-12 13:58:38 +02:00
daboe01 007d437ee8 dom projection 2014-07-12 13:58:38 +02:00
daboe01 00e380e8d8 more dom protection 2014-07-12 13:58:38 +02:00
daboe01 56749d9494 protecting document access with PLATFORM(DOM) 2014-07-12 13:58:38 +02:00
daboe01 cf10d37c90 automated tests and cleanup 2014-07-12 13:58:38 +02:00
daboe01 f9de34e36d font panel fix 2014-07-12 13:58:38 +02:00
daboe01 aa889be140 formatting 2014-07-12 13:58:38 +02:00
daboe01 6da6838bde beginning/end of line navigation + cleanup 2014-07-12 13:58:37 +02:00
daboe01 07a0be303f smart cutting+cleanup 2014-07-12 13:58:37 +02:00
daboe01 ddbe0af290 fix <cr> issue 2014-07-12 13:58:37 +02:00
daboe01 e71f0f44a4 fix relayouting optimization bug 2014-07-12 13:58:37 +02:00
daboe01 39f3bc7100 better workaorund for chrome bug 2014-07-12 13:58:37 +02:00
daboe01 211a145e58 stability improvement 2014-07-12 13:58:37 +02:00
daboe01 d2339b915f spooky chrome fix 2014-07-12 13:58:37 +02:00
daboe01 24c2651745 reverting erroneous patch 2014-07-12 13:58:37 +02:00
daboe01 36e95b93b4 typesetter fix 2014-07-12 13:58:37 +02:00
daboe01 e7a44e0b15 removed debugging code 2014-07-12 13:58:37 +02:00
daboe01 fbdb21ac72 style 2014-07-12 13:58:37 +02:00
daboe01 c9522cf6ae more style 2014-07-12 13:58:37 +02:00
daboe01 7bf9d334ad style 2014-07-12 13:58:37 +02:00
daboe01 422ff93dcd added support for setWidthTracksTextView: 2014-07-12 13:58:37 +02:00
daboe01 eee1ea7a09 fontmanager fix 2014-07-12 13:58:36 +02:00
daboe01 b90e482add typo 2014-07-12 13:58:36 +02:00
daboe01 875b51720b more missing prototypes 2014-07-12 13:58:36 +02:00
daboe01 5272d4f9e3 style 2014-07-12 13:58:36 +02:00
daboe01 c980f4f6e3 prototypes+style 2014-07-12 13:58:36 +02:00
daboe01 89525330c4 formatting+prototypes 2014-07-12 13:58:36 +02:00
daboe01 806db24c0e formatting+ fix prototypes 2014-07-12 13:58:36 +02:00
daboe01 ba7d6df5a4 formatting 2014-07-12 13:58:36 +02:00
daboe01 112301e0e0 formatting 2014-07-12 13:58:36 +02:00
daboe01 c07065e457 more formatting 2014-07-12 13:58:36 +02:00
daboe01 b9e1afae17 formatting 2014-07-12 13:58:36 +02:00
daboe01 549b4a9ebf formatting 2014-07-12 13:58:36 +02:00
daboe01 fa9a3fb7c4 formatting 2014-07-12 13:58:36 +02:00
daboe01 ccb15f278a formatting 2014-07-12 13:58:36 +02:00
daboe01 f0fb4b8842 formatting 2014-07-12 13:58:35 +02:00
daboe01 dacaf293b2 fix of circular imports 2014-07-12 13:58:35 +02:00
daboe01 627d0e4e6f fix circular imports 2014-07-12 13:58:35 +02:00
daboe01 f32b9e26e0 eliminate warnings + style 2014-07-12 13:58:35 +02:00
daboe01 db2b1a0a35 typos 2014-07-12 13:58:35 +02:00
daboe01 f734a60c62 manual test 2014-07-12 13:58:35 +02:00
daboe01 9aa815c1ed CPTextView commit 2014-07-12 13:58:35 +02:00
cacaodev e0772500c9 Merge remote-tracking branch 'upstream/master' into CPAnimationContext
Conflicts:
	AppKit/CPView.j
2014-06-11 20:24:46 +02:00
cacaodev 78c5522966 Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows
Conflicts:
	AppKit/CPTableView.j
2014-06-11 19:42:33 +02:00
Alexandre Wilhelm 718f79775e Fixed: Update test CPTextViewCibTest 2014-06-04 16:30:05 -07:00
Alexandre Wilhelm 4261c1768a Added : added default NSTextView for nib2cib 2014-06-04 16:29:45 -07:00
Alexandre Wilhelm 01603185c3 Added: added CPTextViewCibTest 2014-06-04 15:00:12 -07:00
daboe01 f1adf24f3b make the plain look as white as in cocoa 2014-06-04 18:34:46 +02:00
Alexandre Wilhelm 856765a775 Fixed: reduce size of the test of CPTextView 2014-06-03 21:15:21 -07:00
Alexandre Wilhelm 075da34cdb Fixed: circular import with CPText
Previously CPText was in CPTextView.j, now it is in CPText.j.
It fixes some other circular import as well, fixes some global var to import.
Fixed some code style.
2014-06-03 21:05:04 -07:00
daboe01 5ce543002d fix cut with empty selection 2014-06-02 20:39:53 +02:00
Alexandre Wilhelm 700b1c50af Fixed: refactoring delegate methods 2014-05-30 12:27:43 -07:00
Alexandre Wilhelm 9e1c363a0a Fixed: changed a bit the readability of the file 2014-05-30 12:04:51 -07:00
Alexandre Wilhelm bbe3065f19 Merge branch 'NEW--CPTextView' of https://github.com/daboe01/cappuccino into NEW--CPTextView 2014-05-30 11:37:50 -07:00
Alexandre Wilhelm 1716a0ebca New: Added protocol CPTextViewDelegate 2014-05-30 11:37:37 -07:00
daboe01 ed66339e94 corrected inheritance 2014-05-30 20:36:14 +02:00
Alexandre Wilhelm 441774e61c New: Added protocol CPTextDelegate 2014-05-30 11:27:31 -07:00
Alexandre Wilhelm c6fd14afd2 Fixed: capp_lint 2014-05-30 11:12:29 -07:00
Alexandre Wilhelm 27ed9ca7fd Merge branch 'NEW--CPTextView' of https://github.com/daboe01/cappuccino into NEW--CPTextView 2014-05-30 10:58:29 -07:00
Alexandre Wilhelm 50fda906bd Fixed: CPLog.error is used instead of abstract exception 2014-05-30 10:49:10 -07:00
daboe01 6c7c60037b next try 2014-05-30 19:44:37 +02:00
daboe01 91a429ea77 another try 2014-05-30 19:19:15 +02:00
daboe01 2dbe01e90a more dom protection 2014-05-30 18:52:00 +02:00
daboe01 3d7dd41223 dom projection 2014-05-30 17:17:28 +02:00
daboe01 91495c8595 more dom protection 2014-05-29 20:37:27 +02:00
daboe01 c3e997168a protecting document access with PLATFORM(DOM) 2014-05-29 20:31:55 +02:00
daboe01 c3e6f4f7b6 automated tests and cleanup 2014-05-29 16:27:07 +02:00
daboe01 e600ec95ed font panel fix 2014-05-25 09:27:54 +02:00
daboe01 cbd5853389 formatting 2014-05-24 19:32:25 +02:00
daboe01 4e3a086993 beginning/end of line navigation + cleanup 2014-05-24 08:53:53 +02:00
daboe01 5f23dfb034 smart cutting+cleanup 2014-05-23 21:46:59 +02:00
daboe01 99f59490a3 fix <cr> issue 2014-05-23 13:14:29 +02:00
cacaodev da82887cc0 Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows 2014-05-12 18:41:41 +02:00
cacaodev 45a59f9aec Code style
Rename _numberOfRowsDidChange -> _dataViewsNeedReloadAfterContentChange.
Subclasses use this method to tell if a full view reloading is needed
when calling -reloadData.
Currently CPOutlineView returns YES - this is the previous behavior.

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

Conflicts:
	AppKit/CPOutlineView.j
	AppKit/CPTableHeaderView.j
	AppKit/CPTableView.j
2014-04-18 18:12:55 +02:00
daboe01 4a44b35ea5 fix relayouting optimization bug 2014-04-18 17:17:01 +02:00
daboe01 47aafe9042 better workaorund for chrome bug 2014-04-18 13:28:51 +02:00
daboe01 53d94a1d3d stability improvement 2014-04-18 00:41:33 +02:00
daboe01 5dd3c7c8e8 spooky chrome fix 2014-04-18 00:36:18 +02:00
daboe01 0c2a13f27f reverting erroneous patch 2014-04-17 14:26:01 +02:00
daboe01 da0d3133df typesetter fix 2014-04-17 11:11:53 +02:00
daboe01 7abd9a3db9 removed debugging code 2014-04-17 10:03:24 +02:00
daboe01 98b9f57e0e style 2014-04-17 10:00:50 +02:00
daboe01 e064ccd585 more style 2014-04-17 09:59:36 +02:00
daboe01 b5a63ed3fc style 2014-04-17 09:58:04 +02:00
daboe01 147edddb90 added support for setWidthTracksTextView: 2014-04-17 09:55:25 +02:00
cacaodev 4b8a91fb8c Merge remote-tracking branch 'upstream/master' into CPAnimationContext
Conflicts:
	AppKit/CPCompatibility.j
2014-02-21 20:39:35 +01:00
daboe01 4fb677ed23 fontmanager fix 2014-02-19 20:55:05 +01:00
daboe01 4f0bd7f461 typo 2014-02-18 20:08:39 +01:00
daboe01 01eecdb698 more missing prototypes 2014-02-18 20:07:29 +01:00
daboe01 5ca41a6fff style 2014-02-15 19:47:50 +01:00
daboe01 e4f71420fc prototypes+style 2014-02-15 12:43:09 +01:00
daboe01 d5456ed1db formatting+prototypes 2014-02-14 18:09:04 +01:00
daboe01 e3335c5843 formatting+ fix prototypes 2014-02-14 18:05:45 +01:00
daboe01 067e6bf93d formatting 2014-02-12 18:00:11 +01:00
daboe01 13dc31dc1f formatting 2014-02-12 07:14:49 +01:00
daboe01 7f6bf21270 more formatting 2014-02-12 07:13:21 +01:00
daboe01 d647258856 formatting 2014-02-12 05:59:34 +01:00
daboe01 1eb7ed5e9c formatting 2014-02-11 22:04:39 +01:00
daboe01 e78f1b05a7 formatting 2014-02-11 20:33:47 +01:00
daboe01 e45140b7ea formatting 2014-02-10 21:28:25 +01:00
daboe01 41747a7a04 formatting 2014-02-10 21:25:56 +01:00
daboe01 e448430d8a fix of circular imports 2014-02-10 21:07:57 +01:00
daboe01 394b1a1602 fix circular imports 2014-02-10 06:02:28 +01:00
daboe01 85abd5a8c3 eliminate warnings + style 2014-02-09 11:00:25 +01:00
daboe01 b9e0eb5b76 typos 2014-02-08 21:14:56 +01:00
daboe01 a8eae73b66 manual test 2014-02-08 20:46:04 +01:00
daboe01 9edd8d6f66 CPTextView commit 2014-02-08 20:43:15 +01:00
Mathieu Monney 0cfb53cea4 Changing a CALayer in a menu callback doesn't trigger re-draw / re-layout 2013-11-18 15:29:41 +01:00
cacaodev 4d8f7c67d4 Fixed removeTableColumn: was not reloading views immediatly. Added test for issue 1913 in Manual/TableTest/TestTableColumn. Throws an exception when you click the button twice 2013-05-02 19:39:27 +02:00
cacaodev e53337bcdc Remove check in -hitTest: that was preventing reverse set binding 2013-04-13 21:39:20 +02:00
cacaodev e5d883f97e CAKeyframeAnimation
CPAnimatablePropertyContainer protocol (CPView)
CPAnimationContext
_CPObjectAnimator CPViewAnimator
CSS Animations wrapper

CPAnimatablePropertyContainerTest : animations of views with custom Layout / Regular Layout / Custom DrawRect: / No DrawRect: / Subviews / No Subviews and combinations of these situations.
controls animation (text field, button, popup, slider ...). They animate their size via their action.
2013-04-13 16:29:43 +02:00
cacaodev 548a55792c Merge remote-tracking branch 'upstream/master' into CPTableView-enumerateRows 2013-04-13 11:52:51 +02:00
cacaodev 43f0f2a0fd Simplified -numberOfRows
Added -_numberOfRows that computes the new number of rows. Removed the
nil check.

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

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

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

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

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

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

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

This commit reverses the way views are stored and accessed in the table view: rows>columns instead of columns>rows.
Applied the second method where it is relevant, making the code more compact and readable.
2013-04-01 19:49:41 +02:00
cacaodev 377ec37257 Fixed: In CPTrace, fixed error when an argument was not a cappuccino object.
Arguments such as CGPoint, CGRect and functions are now correctly
printed to the console.
2013-04-01 19:34:04 +02:00
Kevin Jamieson b349f0a2fb If target view of a drag is a CPTableView, set inset bounds based on the row height of the table 2011-10-03 22:13:41 -07:00
2160 changed files with 180560 additions and 215173 deletions
+12
View File
@@ -0,0 +1,12 @@
# EditorConfig reference: https://editorconfig.org
# https://editorconfig.org/#file-format-details
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
+34
View File
@@ -0,0 +1,34 @@
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node build
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [24.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: echo "${PWD}/dist/cappuccino/bin" >> $GITHUB_PATH
- run: echo "${PWD}/dist/objective-j/bin" >> $GITHUB_PATH
- run: npm install
- run: npm update
- run: jake dist
- run: jake test-only
@@ -0,0 +1,180 @@
name: Main branch - Build Testbook with fresh frameworks and manual tests & deploy to GitHub Pages
on:
push:
branches: [ main ]
workflow_dispatch:
permissions:
contents: write
pages: write # required by deploy-pages
id-token: write # required by deploy-pages
pull-requests: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout cappuccino (this repo)
uses: actions/checkout@v5
- name: Checkout Cappuccino-Testbook into ./testbook
uses: actions/checkout@v5
with:
repository: ArgosOz/Cappuccino-Testbook
path: testbook
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 24.x
- run: echo "${PWD}/dist/cappuccino/bin" >> $GITHUB_PATH
- run: echo "${PWD}/dist/objective-j/bin" >> $GITHUB_PATH
- run: npm install
- run: npm update
- run: jake dist
- run: jake test-only
# Refresh Frameworks from dist → testbook/Frameworks
- name: Refresh Frameworks
run: |
set -euo pipefail
if [ ! -d dist ]; then
echo "❌ dist/ not found at repo root" >&2
exit 1
fi
mkdir -p testbook/Frameworks
rm -rf testbook/Frameworks/*
cp -a dist/cappuccino/Frameworks/{Foundation,AppKit} testbook/Frameworks/
cp -a dist/objective-j/Frameworks/Objective-J testbook/Frameworks/
echo "Frameworks populated:" && ls -la testbook/Frameworks || true
- name: Generate manifest of manual test directories
if: ${{ hashFiles('Tests/Manual/**') != '' }}
run: |
set -euo pipefail
mkdir -p artifacts
OUT="testbook/Resources/recipes.txt"
: > "$OUT"
if [ ! -d "Tests/Manual" ]; then
echo "❌ Tests/Manual not found at repo root" >&2
exit 1
fi
# Iterate over immediate subdirectories of Tests/Manual (null-safe for spaces)
while IFS= read -r -d '' d; do
name=$(basename "$d")
ts=$(date +%s%3N) # milliseconds since epoch
printf '%s|%s|\n' "$ts" "$name" >> "$OUT"
# Optional tiny sleep to help keep timestamps distinct:
sleep 0.01
done < <(find "Tests/Manual" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z)
echo "Wrote $OUT:"
cat "$OUT"
# Replace subdirectories inside testbook/Resources with Tests/Manual subdirectories
- name: Sync Resources from Tests/Manual
run: |
set -euo pipefail
if [ ! -d "Tests/Manual" ]; then
echo "❌ Tests/Manual not found at repo root" >&2
exit 1
fi
mkdir -p testbook/Resources
# Remove only immediate subdirectories (keep stray files if any)
find testbook/Resources -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -r rm -rf
# Copy each immediate subdirectory from Tests/Manual → testbook/Resources
while IFS= read -r -d '' d; do
cp -a "$d" testbook/Resources/
done < <(find Tests/Manual -mindepth 1 -maxdepth 1 -type d -print0)
echo "Resources now contains:" && ls -la testbook/Resources || true
# Ensure each test Index.html includes the include path line before OBJJ_MAIN_FILE
- name: Inject OBJJ_INCLUDE_PATHS into Index.html files
run: |
set -euo pipefail
found=0
while IFS= read -r -d '' f; do
found=1
if grep -q 'OBJJ_INCLUDE_PATHS' "$f"; then
echo "Already updated: $f"
continue
fi
perl -0777 -i -pe 's/OBJJ_MAIN_FILE\s*=\s*"main\.j";/OBJJ_INCLUDE_PATHS = ["..\/..\/Frameworks"];\nOBJJ_MAIN_FILE = "main.j";/i' "$f"
perl -0777 -i -pe 's/Frameworks\/Objective-J\/Objective-J.js/\.\.\/\.\.\/Frameworks\/Objective-J\/Objective-J.js/' "$f"
echo "Updated: $f"
done < <(find testbook/Resources -mindepth 2 -maxdepth 2 -type f -iname "index.html" -print0)
if [ "$found" -eq 0 ]; then
echo "⚠️ No index.html files found under testbook/Resources/*/" >&2
fi
# Optional: prune VCS/CI metadata from the served content
- name: Clean testbook for Pages (optional)
run: |
set -euo pipefail
test -f testbook/index.html
test -d testbook/Resources
test -d testbook/Frameworks
rm -rf testbook/.git testbook/.github || true
find testbook -maxdepth 1 -type f -name ".git*" -delete || true
- name: Publish to gh-pages (preserve PR previews)
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: testbook
clean-exclude: pr-preview/ # keep PR preview folders
force: false # avoid force-push so previews survive
- name: Checkout gh-pages ./gh-pages
uses: actions/checkout@v5
with:
path: gh-pages
ref: gh-pages
- name: Clean gh-pages for Pages
run: |
set -euo pipefail
test -d gh-pages
rm -rf gh-pages/.git gh-pages/.github || true
find gh-pages -maxdepth 1 -type f -name ".git*" -delete || true
find gh-pages -type f -name "*.xib" -delete || true
find gh-pages -type f -name "*xcode*" -delete || true
find gh-pages -name "Jakefile" -delete || true
- name: Upload Pages artifact (testbook/)
uses: actions/upload-pages-artifact@v4
with:
path: gh-pages
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
concurrency:
group: pages
cancel-in-progress: true
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
- name: Comment Pages URL on PR
if: ${{ github.event_name == 'pull_request' }}
uses: actions/github-script@v7
with:
script: |
const url = `${{ toJSON(steps.deployment.outputs.page_url) }}`;
const body = `📄 GitHub Pages preview for this PR:\n\n${url}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body
});
+11
View File
@@ -17,3 +17,14 @@ Tests/Manual/**/*.xcodeproj
*.sublime-project
*.sublime-workspace
*.tm_properties
*.idea
node_modules
*.vscode
/dist/objective-j/objj-executable
/dist/objective-j/package.json
/dist/objective-j/lib
/dist/cappuccino/package.json
/dist/cappuccino/lib
/dist/cappuccino/bin
Tests/Manual/.Frameworks
/Tests/Manual/index.html
+28
View File
@@ -0,0 +1,28 @@
.DS_Store
Frameworks
!/dist/objective-j/Frameworks
!/dist/cappuccino-j/Frameworks
Build
Demos
./Aristo
WebSite
.push-package
*.xcodeproj*
*.xcodeproj/*.pbxuser
*.xcodeproj/*.perspectivev3
xcuserdata/
!*.xcodeproj/project.pbxproj
*.xCodeSupport/
*.XcodeSupport/
*XcodeSupport/
Tests/Manual/**/*.xcodeproj
*.sublime-project
*.sublime-workspace
*.tm_properties
*.idea
node_modules
*.vscode
/dist/objective-j/objj-executable
/dist/objective-j/package.json
/dist/cappuccino/package.json
+10 -2
View File
@@ -20,12 +20,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "_CPObject+Theme.j"
@import "_CPToolTip.j"
@import "CALayer.j"
@import "CGGeometry.j"
@import "CPAccordionView.j"
@import "CPAlert.j"
@import "CPAnimation.j"
@import "CPAnimationContext.j"
@import "CPAppearance.j"
@import "CPApplication.j"
@import "CPArrayController.j"
@import "CPBezierPath.j"
@@ -59,8 +62,6 @@
@import "CPDocument.j"
@import "CPDocumentController.j"
@import "CPEvent.j"
@import "CPFlashMovie.j"
@import "CPFlashView.j"
@import "CPFont.j"
@import "CPFontManager.j"
@import "CPGradient.j"
@@ -92,21 +93,28 @@
@import "CPSlider.j"
@import "CPSound.j"
@import "CPSplitView.j"
@import "CPStackView.j"
@import "CPStepper.j"
@import "CPTableColumn.j"
@import "CPTableView.j"
@import "CPTabView.j"
@import "CPText.j"
@import "CPTextField.j"
@import "CPTextView.j"
@import "CPTokenField.j"
@import "CPToolbar.j"
@import "CPToolbarItem.j"
@import "CPTrackingArea.j"
@import "CPTreeNode.j"
@import "CPUserDefaultsController.j"
@import "CPView.j"
@import "CPViewAnimator.j"
@import "CPViewAnimation.j"
@import "CPViewController.j"
@import "CPVisualEffectView.j"
@import "CPWebView.j"
@import "CPWindow.j"
@import "CPWindowController.j"
@import "CPWorkspace.j"
@import "CPFontPanel.j"
@import "CPTreeController.j"
+10 -10
View File
@@ -172,7 +172,7 @@ var bottomHeight = 71;
BOOL _needsLayout;
}
#pragma mark Creating Alerts
// MARK: Creating Alerts
/*!
Returns a CPAlert object with the provided info
@@ -249,8 +249,8 @@ var bottomHeight = 71;
}
#pragma mark -
#pragma mark Delegate
// MARK: -
// MARK: Delegate
/*!
Set the delegate of the receiver
@@ -272,7 +272,7 @@ var bottomHeight = 71;
}
#pragma mark Accessors
// MARK: Accessors
- (CPTheme)theme
{
@@ -402,7 +402,7 @@ var bottomHeight = 71;
_needsLayout = YES;
}
#pragma mark Accessing Buttons
// MARK: Accessing Buttons
/*!
Adds a button with a given title to the receiver.
@@ -438,7 +438,7 @@ var bottomHeight = 71;
[_buttons insertObject:button atIndex:0];
}
#pragma mark Layout
// MARK: Layout
/*!
@ignore
@@ -664,7 +664,7 @@ var bottomHeight = 71;
_needsLayout = NO;
}
#pragma mark Displaying Alerts
// MARK: Displaying Alerts
/*!
Displays the \c CPAlert panel as a modal dialog. The user will not be
@@ -742,7 +742,7 @@ var bottomHeight = 71;
[self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
}
#pragma mark Private
// MARK: Private
/*!
@ignore
@@ -821,12 +821,12 @@ var bottomHeight = 71;
else if (_modalDelegate)
{
if (_didEndSelector)
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
_modalDelegate.isa.objj_msgSend3(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
}
else if (_delegate)
{
if (_didEndSelector)
objj_msgSend(_delegate, _didEndSelector, self, returnCode);
_delegate.isa.objj_msgSend2(_delegate, _didEndSelector, self, returnCode);
else
[self _sendDelegateAlertDidEndReturnCode:returnCode];
}
+162
View File
@@ -0,0 +1,162 @@
/*
* CPAppearance.j
* AppKit
*
* Created by Antoine Mercadal.
* Copyright 2015, Cappuccino Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPTheme.j"
CPAppearanceNameAqua = @"CPAppearanceNameAqua";
CPAppearanceNameLightContent = @"CPAppearanceNameLightContent";
CPAppearanceNameVibrantDark = @"CPAppearanceNameVibrantDark";
CPAppearanceNameVibrantLight = @"CPAppearanceNameVibrantLight";
var _CPAppearanceCurrent = nil,
_CPAppearancesRegistry = @{};
@protocol CPAppearanceCustomization <CPObject>
@required
- (CPAppearance)appearance;
- (void)setAppearance:(CPAppearance)appearance;
- (CPAppearance)effectiveAppearance;
- (void)setEffectiveAppearance:(CPAppearance)appearance;
@end
CPThemeStateAppearanceAqua = CPThemeState("appearance-aqua");
CPThemeStateAppearanceLightContent = CPThemeState("appearance-light-content");
CPThemeStateAppearanceVibrantLight = CPThemeState("appearance-vibrant-light");
CPThemeStateAppearanceVibrantDark = CPThemeState("appearance-vibrant-dark");
/*!
@ingroup appkit
A CPAppareance represents the appearance of an to a subset of UI elements.
This is a very lightweight implementation of the NSAppearance system, but
We are using it for compliance, and especially for the CPVisualEffectView
*/
@implementation CPAppearance : CPObject
{
BOOL _allowsVibrancy @accessors(property=allowsVibrancy);
CPString _name;
}
// MARK: -
// MARK: Class Methods
/*! Returns the current default CPAppearance
*/
+ (CPAppearance)currentAppearance
{
if (!_CPAppearanceCurrent)
_CPAppearanceCurrent = [CPAppearance appearanceNamed:CPAppearanceNameAqua];
return _CPAppearanceCurrent;
}
/*! Sets the current default CPAppearance
@param appearance the new current appearance
*/
+ (void)setCurrentAppearance:(CPAppearance)anAppearance
{
_CPAppearanceCurrent = anAppearance;
}
/*! Returns the CPAppearance object with the given name
@param name the name of the appearance
*/
+ (CPAppearance)appearanceNamed:(CPString)aName
{
if (![_CPAppearancesRegistry containsKey:aName])
{
[_CPAppearancesRegistry setObject:[[CPAppearance alloc] initWithAppearanceNamed:aName bundle:nil]
forKey:aName];
}
return [_CPAppearancesRegistry objectForKey:aName];
}
// MARK: -
// MARK: Initialization
/*! Creates a CPAppearance object initialized to the specified appearance file in the specified bundle
This method does actually nothing special. It just creates a default appearance object
*/
- (id)initWithAppearanceNamed:(CPString)aName bundle:(CPBundle)bundle
{
if (self = [super init])
{
_name = aName;
_allowsVibrancy = YES;
if ([_CPAppearancesRegistry containsKey:aName])
[CPException raise:CPInternalInconsistencyException reason:"Appearance with name '" + aName + "' is already declared."];
[_CPAppearancesRegistry setObject:self forKey:aName];
}
return self;
}
// MARK: -
// MARK: Implementation
- (BOOL)isEqual:(id)anObject
{
if (![anObject isKindOfClass:CPAppearance])
return NO;
return self._name == anObject._name;
}
- (CPString)description
{
return @"<CPAppearance @" + [self UID] + @" name: " + _name + ">";
}
// MARK: -
// MARK: CPCoding
- (id)initWithCoder:(CPCoder)aCoder
{
if (self = [super init])
{
_name = [aCoder decodeObjectForKey:@"_name"];
_allowsVibrancy = [aCoder decodeBoolForKey:@"_allowsVibrancy"];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_name forKey:@"_name"];
[aCoder encodeBool:_allowsVibrancy forKey:@"_allowsVibrancy"];
}
@end
+56 -15
View File
@@ -47,6 +47,8 @@ var CPMainCibFile = @"CPMainCibFile",
@protocol CPApplicationDelegate <CPObject>
@optional
- (CPApplicationTerminateReply)applicationShouldTerminate:(CPApplication)sender;
- (CPString)applicationShouldTerminateMessage:(CPApplication)sender;
- (void)applicationDidBecomeActive:(CPNotification)aNotification;
- (void)applicationDidChangeScreenParameters:(CPNotification)aNotification;
- (void)applicationDidFinishLaunching:(CPNotification)aNotification;
@@ -58,6 +60,9 @@ var CPMainCibFile = @"CPMainCibFile",
@end
var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
CPApplicationDelegate_applicationShouldTerminateMessage_ = 1 << 1;
/*!
@ingroup appkit
@class CPApplication
@@ -103,6 +108,8 @@ var CPMainCibFile = @"CPMainCibFile",
//
id <CPApplicationDelegate> _delegate;
CPInteger _implementedDelegateMethods;
BOOL _finishedLaunching;
BOOL _isActive;
@@ -125,7 +132,7 @@ var CPMainCibFile = @"CPMainCibFile",
+ (CPApplication)sharedApplication
{
if (!CPApp)
CPApp = [[CPApplication alloc] init];
CPApp = [[self alloc] init];
return CPApp;
}
@@ -165,6 +172,8 @@ var CPMainCibFile = @"CPMainCibFile",
if (_delegate == aDelegate)
return;
_implementedDelegateMethods = 0;
var defaultCenter = [CPNotificationCenter defaultCenter],
delegateNotifications =
[
@@ -205,6 +214,12 @@ var CPMainCibFile = @"CPMainCibFile",
if ([_delegate respondsToSelector:selector])
[defaultCenter addObserver:_delegate selector:selector name:notificationName object:self];
}
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminate_;
if ([_delegate respondsToSelector:@selector(applicationShouldTerminateMessage:)])
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminateMessage_
}
/*!
@@ -426,12 +441,7 @@ var CPMainCibFile = @"CPMainCibFile",
{
// callback method for terminate:
if (didCloseAll)
{
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
[self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]];
else
[self replyToApplicationShouldTerminate:YES];
}
[self replyToApplicationShouldTerminate:[self _sendDelegateApplicationShouldTerminate]];
}
- (void)replyToApplicationShouldTerminate:(BOOL)terminate
@@ -445,6 +455,9 @@ var CPMainCibFile = @"CPMainCibFile",
- (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps
{
if (_isActive)
return;
[self _willBecomeActive];
[CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps];
@@ -455,6 +468,9 @@ var CPMainCibFile = @"CPMainCibFile",
- (void)deactivate
{
if (!_isActive)
return;
[self _willResignActive];
[CPPlatform deactivate];
@@ -463,7 +479,7 @@ var CPMainCibFile = @"CPMainCibFile",
[self _didResignActive];
}
- (void)isActive
- (BOOL)isActive
{
return _isActive;
}
@@ -480,6 +496,15 @@ var CPMainCibFile = @"CPMainCibFile",
- (void)run
{
[self finishLaunching];
[self sendEvent:[CPEvent otherEventWithType:CPAppKitDefined
location:CGPointMakeZero()
modifierFlags:0
timestamp:[CPEvent currentTimestamp]
windowNumber:[_keyWindow windowNumber]
context:nil
subtype:nil
data1:nil
data2:nil]];
}
// Managing the Event Loop
@@ -603,11 +628,6 @@ var CPMainCibFile = @"CPMainCibFile",
var theWindow = [anEvent window];
// Check if this is a candidate for key equivalent...
if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
// The key equivalent was handled.
return;
if ([anEvent type] == CPMouseMoved)
{
if (theWindow !== _lastMouseMoveWindow)
@@ -644,6 +664,11 @@ var CPMainCibFile = @"CPMainCibFile",
_eventListenerInsertionIndex = _eventListeners.length;
// Check if this is a candidate for key equivalent...
if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
// The key equivalent was handled.
return;
if (theWindow)
[theWindow sendEvent:anEvent];
}
@@ -959,7 +984,7 @@ var CPMainCibFile = @"CPMainCibFile",
*/
- (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
{
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }, shouldDequeue));
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { if (aTarget != null) aTarget.isa.objj_msgSend1(aTarget, aSelector, anEvent); }, shouldDequeue));
}
/*!
@@ -1195,6 +1220,22 @@ var CPMainCibFile = @"CPMainCibFile",
userInfo:nil];
}
- (BOOL)_sendDelegateApplicationShouldTerminate
{
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminate_))
return YES;
return [_delegate applicationShouldTerminate:self];
}
- (CPString)_sendDelegateApplicationShouldTerminateMessage
{
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminateMessage_))
return @"You have attempted to leave this page. Are you sure you want to exit this page?";
return [_delegate applicationShouldTerminateMessage:self];
}
- (void)_didResignActive
{
if (self._activeMenu)
@@ -1317,7 +1358,7 @@ var _CPAppBootstrapperActions = nil;
{
var action = _CPAppBootstrapperActions.shift();
if (objj_msgSend(self, action))
if (self.isa.objj_msgSend0(self, action))
return;
}
+1
View File
@@ -31,6 +31,7 @@ CPApplicationWillResignActiveNotification = @"CPApplicationWillResignA
CPApplicationDidResignActiveNotification = @"CPApplicationDidResignActiveNotification";
CPApplicationDidChangeScreenParametersNotification = @"CPApplicationDidChangeScreenParametersNotification";
@typedef CPApplicationTerminateReply
CPTerminateNow = YES;
CPTerminateCancel = NO;
CPTerminateLater = -1; // not currently supported
+25 -26
View File
@@ -219,17 +219,6 @@
_clearsFilterPredicateOnInsertion = aFlag;
}
/*!
Whether the receiver will always return the multiple values marker when multiple
items are selected, even if the items have the same value.
@return BOOL YES if the receiver always uses the multiple values marker
*/
- (BOOL)alwaysUsesMultipleValuesMarker
{
return _alwaysUsesMultipleValuesMarker;
}
/*!
Sets whether the receiver should always return the multiple values marker when multiple
items are selected, even if the items have the same value.
@@ -282,7 +271,7 @@
if (_disableSetContent)
return;
if (value === nil)
if (value == nil)
value = [];
if (![value isKindOfClass:[CPArray class]])
@@ -308,27 +297,18 @@
class.
*/
if (_clearsFilterPredicateOnInsertion)
[self willChangeValueForKey:@"filterPredicate"];
// Don't use [super setContent:] as that would fire the contentObject change.
// We need to be in control of when notifications fire.
// Note that if we have a contentArray binding, setting the content does /not/
// cause a reverse binding set.
_contentObject = value;
if (_clearsFilterPredicateOnInsertion && _filterPredicate != nil)
[self __setFilterPredicate:nil]; // Causes a _rearrangeObjects.
else
[self _rearrangeObjects];
[self _rearrangeObjects];
if ([self preservesSelection])
[self __setSelectedObjects:oldSelectedObjects];
else
[self __setSelectionIndexes:oldSelectionIndexes];
if (_clearsFilterPredicateOnInsertion)
[self didChangeValueForKey:@"filterPredicate"];
}
/*!
@@ -563,7 +543,7 @@
*/
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
{
[self _selectionWillChange]
[self _selectionWillChange];
var r = [self __setSelectionIndexes:indexes avoidEmpty:NO];
[self _selectionDidChange];
return r;
@@ -629,6 +609,25 @@
return YES;
}
- (void)_selectionWillChange
{
// Push back all data from the dirty editors before it is too late.
var editorsCount = [_editors count];
while (editorsCount--)
{
var allBindings = [CPBinder allBindingsForObject:_editors[editorsCount]],
allKeys = [allBindings allKeys],
keysCount = allKeys.length;
while (keysCount--)
[[allBindings objectForKey:allKeys[keysCount]] reverseSetValueFor:allKeys[keysCount]];
}
[super _selectionWillChange];
}
/*!
Returns an array of the selected objects.
@@ -774,7 +773,7 @@
_filterPredicate = nil;
[self _rearrangeObjects];
}
else if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
else if (_filterPredicate == nil || [_filterPredicate evaluateWithObject:object])
{
// Insert directly into the array.
var pos = [_arrangedObjects insertObject:object inArraySortedByDescriptors:_sortDescriptors];
@@ -787,7 +786,7 @@
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:1];
}
/*
else if (_filterPredicate !== nil)
else if (_filterPredicate != nil)
...
// Implies _filterPredicate && ![_filterPredicate evaluateWithObject:object], so the new object does
// not appear in arrangedObjects and we do not have to update at all.
@@ -887,7 +886,7 @@
_disableSetContent = NO;
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
if (_filterPredicate == nil || [_filterPredicate evaluateWithObject:object])
{
// selectionIndexes change notification will be fired as a result of the
// content change. Don't fire manually.
+46 -2
View File
@@ -231,7 +231,7 @@ var DefaultLineWidth = 1.0;
/*!
Cocoa compatibility.
*/
- (void)getLineDash:(CPArrayRef)patternRef count:(NSInteger)count phase:(CGFloatRef)phaseRef
- (void)getLineDash:(CPArrayRef)patternRef count:(CPInteger)count phase:(CGFloatRef)phaseRef
{
return [self getLineDash:patternRef phase:phaseRef];
}
@@ -250,7 +250,7 @@ var DefaultLineWidth = 1.0;
/*!
Cocoa compatibility.
*/
- (void)setLineDash:(CPArray)aPattern count:(NSInteger)count phase:(CGFloat)aPhase
- (void)setLineDash:(CPArray)aPattern count:(CPInteger)count phase:(CGFloat)aPhase
{
[self setLineDash:aPattern phase:aPhase];
}
@@ -378,3 +378,47 @@ var DefaultLineWidth = 1.0;
}
@end
@implementation CPBezierPath (AnimationAdditions)
- (CPString)SVGString
{
var pathString = "";
var elements = _path.elements;
var count = _path.count;
for (var i = 0; i < count; i++)
{
var element = elements[i];
// Use the kCGPathElement* constants defined in CGPath.j
switch (element.type)
{
case kCGPathElementMoveToPoint:
pathString += "M " + element.x + " " + element.y + " ";
break;
case kCGPathElementAddLineToPoint:
pathString += "L " + element.x + " " + element.y + " ";
break;
case kCGPathElementAddQuadCurveToPoint:
pathString += "Q " + element.cpx + " " + element.cpy + " " + element.x + " " + element.y + " ";
break;
case kCGPathElementAddCurveToPoint:
pathString += "C " + element.cp1x + " " + element.cp1y + " " + element.cp2x + " " + element.cp2y + " " + element.x + " " + element.y + " ";
break;
case kCGPathElementCloseSubpath:
pathString += "Z ";
break;
}
}
return pathString.trim();
}
@end
+371 -58
View File
@@ -22,13 +22,14 @@
@import "CPTextField.j"
@import "CPView.j"
@import <Foundation/CPGeometry.j>
// CPBoxType
@typedef CPBoxType
CPBoxPrimary = 0;
CPBoxSecondary = 1;
CPBoxSecondary = 1; // Deprecated
CPBoxSeparator = 2;
CPBoxOldStyle = 3;
CPBoxOldStyle = 3; // Deprecated
CPBoxCustom = 4;
// CPBorderType
@@ -58,12 +59,16 @@ CPBelowBottom = 6;
@implementation CPBox : CPView
{
CPBoxType _boxType;
CPBorderType _borderType;
CPBorderType _borderType; // deprecated
CPView _contentView;
CPView _boxView; // needed for CSS theming, will be transparent for non CSS themes
BOOL _transparent @accessors(getter=isTransparent);
CPString _title @accessors(getter=title);
int _titlePosition @accessors(getter=titlePosition);
CPString _title @accessors(getter=title);
int _titlePosition @accessors(getter=titlePosition);
CPTextField _titleView;
BOOL _cachedAutoresizesSubviews;
}
+ (Class)_binderClassForBinding:(CPString)aBinding
@@ -90,6 +95,14 @@ CPBelowBottom = 6;
@"inner-shadow-size": 6.0,
@"inner-shadow-color": [CPNull null],
@"content-margin": CGSizeMakeZero(),
@"title-font": [CPNull null],
@"title-left-offset": 5.0,
@"title-top-offset": 0.0,
@"title-color": [CPNull null],
@"nib2cib-adjustment-primary-frame": CGRectMake(4, -4, -8, -6),
@"content-adjustment": CGRectMakeZero(),
@"min-y-correction-no-title": 0,
@"min-y-correction-title": 0
};
}
@@ -114,16 +127,26 @@ CPBelowBottom = 6;
if (self)
{
_borderType = CPBezelBorder;
_borderType = CPGrooveBorder; // Was CPBezelBorder but Cocoa default is CPGrooveBorder
_boxType = CPBoxPrimary;
_titlePosition = CPNoTitle;
_titleView = [CPTextField labelWithTitle:@""];
[_titleView setFont:[self titleFont]];
[_titleView setTextColor:[self titleColor]];
_boxView = [[CPView alloc] initWithFrame:[self bounds]];
[_boxView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
_contentView = [[CPView alloc] initWithFrame:[self bounds]];
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
[self setAutoresizesSubviews:YES];
[self addSubview:_contentView];
[self addSubview:_boxView];
[_boxView setAutoresizesSubviews:YES];
[_boxView addSubview:_contentView];
[self sizeToFit];
}
return self;
@@ -155,6 +178,8 @@ CPBelowBottom = 6;
*/
- (CPBorderType)borderType
{
CPLog.warn("CPBox borderType is deprecated.");
return _borderType;
}
@@ -177,7 +202,8 @@ CPBelowBottom = 6;
return;
_borderType = aBorderType;
[self setNeedsDisplay:YES];
[self refreshDisplay];
}
/*!
@@ -217,13 +243,46 @@ CPBelowBottom = 6;
*/
- (void)setBoxType:(CPBoxType)aBoxType
{
if ((aBoxType == CPBoxSecondary) || (aBoxType == CPBoxOldStyle))
CPLog.warn("CPBox setBoxType: CPBoxSecondary and CPBoxOldStyle are deprecated.");
if (_boxType === aBoxType)
return;
_boxType = aBoxType;
[self setNeedsDisplay:YES];
[self refreshDisplay];
}
- (void)setTransparent:(BOOL)shouldBeTransparent
{
if (_transparent == shouldBeTransparent)
return;
_transparent = shouldBeTransparent;
[self _manageTitlePositioning];
}
// MARK: -
// MARK: Style properties which override theme values
/*!
The borderColor, borderWidth, cornerRadius and fillColor properties for the receiver
are only supported for boxes with boxType === CPBoxCustom and borderType === CPLineBorder.
Boxes with the Primary boxType have fixed values which are defined by the system theme.
Apple does support lineTypes of Groove and Bezel for boxes of type CPBoxCustom, CPBoxSecondary and CPBoxOldStyle,
but they are deprecated as of macOS 10.15.
Cappuccino has supported these in the past but no longer does so - both to simplify CSS-based theming and
to avoid the effort needed for supporting something which will be very short-lived.
These styles can be recreated as custom theme elements by developers, as needed.
Additionally, boxes with boxType === CPBoxSeparator (horizontal and vertical lines) have never allowed changing these values.
No warnings are generated for separator boxes.
*/
// See discussion above.
// MARK: borderColor
- (CPColor)borderColor
{
return [self valueForThemeAttribute:@"border-color"];
@@ -231,12 +290,25 @@ CPBelowBottom = 6;
- (void)setBorderColor:(CPColor)color
{
if (_boxType === CPBoxSeparator)
{
return;
}
if ((_boxType !== CPBoxCustom) || (_borderType !== CPLineBorder))
{
CPLog.warn("CPBox setBorderColor: the box must be of type CPBoxCustom AND border of type CPLineBorder in order to use setBorderColor. Property is ignored.");
return;
}
if ([color isEqual:[self borderColor]])
return;
[self setValue:color forThemeAttribute:@"border-color"];
}
// See discussion above.
// MARK: borderWidth
- (float)borderWidth
{
return [self valueForThemeAttribute:@"border-width"];
@@ -244,12 +316,25 @@ CPBelowBottom = 6;
- (void)setBorderWidth:(float)width
{
if (_boxType === CPBoxSeparator)
{
return;
}
if ((_boxType !== CPBoxCustom) || (_borderType !== CPLineBorder))
{
CPLog.warn("CPBox setBorderWidth: the box must be of type CPBoxCustom AND border of type CPLineBorder in order to use setBorderWidth. Property is ignored.");
return;
}
if (width === [self borderWidth])
return;
[self setValue:width forThemeAttribute:@"border-width"];
}
// See discussion above.
// MARK: cornerRadius
- (float)cornerRadius
{
return [self valueForThemeAttribute:@"corner-radius"];
@@ -257,12 +342,25 @@ CPBelowBottom = 6;
- (void)setCornerRadius:(float)radius
{
if (_boxType === CPBoxSeparator)
{
return;
}
if ((_boxType !== CPBoxCustom) || (_borderType !== CPLineBorder))
{
CPLog.warn("CPBox setCornerRadius: the box must be of type CPBoxCustom AND border of type CPLineBorder in order to use setCornerRadius. Property is ignored.");
return;
}
if (radius === [self cornerRadius])
return;
[self setValue:radius forThemeAttribute:@"corner-radius"];
}
// See discussion above.
// MARK: fillColor
- (CPColor)fillColor
{
return [self valueForThemeAttribute:@"background-color"];
@@ -270,6 +368,17 @@ CPBelowBottom = 6;
- (void)setFillColor:(CPColor)color
{
if (_boxType === CPBoxSeparator)
{
return;
}
if ((_boxType !== CPBoxCustom) || (_borderType !== CPLineBorder))
{
CPLog.warn("CPBox setFillColor: the box must be of type CPBoxCustom AND border of type CPLineBorder in order to use setFillColor. Property is ignored.");
return;
}
if ([color isEqual:[self fillColor]])
return;
@@ -286,21 +395,20 @@ CPBelowBottom = 6;
if (aView === _contentView)
return;
var borderWidth = [self borderWidth],
contentMargin = [self valueForThemeAttribute:@"content-margin"];
[aView setFrame:CGRectInset([self bounds], contentMargin.width + borderWidth, contentMargin.height + borderWidth)];
[aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
// A nil contentView is allowed (tested in Cocoa 2013-02-22).
if (!aView)
[_contentView removeFromSuperview];
else if (_contentView)
[self replaceSubview:_contentView with:aView];
[_boxView replaceSubview:_contentView with:aView];
else
[self addSubview:aView];
[_boxView addSubview:aView];
_contentView = aView;
[self sizeToFit];
[self refreshDisplay];
}
- (CGSize)contentViewMargins
@@ -320,9 +428,14 @@ CPBelowBottom = 6;
{
var offset = [self _titleHeightOffset],
borderWidth = [self borderWidth],
contentMargin = [self valueForThemeAttribute:@"content-margin"];
contentMargin = [self valueForThemeAttribute:@"content-margin"],
contentAdjustment = [self valueForThemeAttribute:@"content-adjustment"],
minYCorrection = [self valueForThemeAttribute:(_titlePosition === CPNoTitle ? @"min-y-correction-no-title" : @"min-y-correction-title")];
[self setFrame:CGRectInset(aRect, -(contentMargin.width + borderWidth), -(contentMargin.height + offset[0] + borderWidth))];
[self setFrame:CGRectMake(aRect.origin.x - contentAdjustment.origin.x - contentMargin.width + borderWidth,
aRect.origin.y - contentAdjustment.origin.y - contentMargin.height + borderWidth - minYCorrection,
aRect.size.width + 2 * contentMargin.width - contentAdjustment.size.width,
aRect.size.height + 2 * contentMargin.height - contentAdjustment.size.height)];
}
- (void)setTitle:(CPString)aTitle
@@ -347,14 +460,42 @@ CPBelowBottom = 6;
- (CPFont)titleFont
{
return [_titleView font];
if ([self hasThemeAttribute:@"title-font"])
return [self valueForThemeAttribute:@"title-font"];
else
return [_titleView font];
}
- (void)setTitleFont:(CPFont)aFont
{
if ([aFont isEqual:[self titleFont]])
return;
if ([self hasThemeAttribute:@"title-font"])
[self setValue:aFont forThemeAttribute:@"title-font"];
[_titleView setFont:aFont];
}
- (CPColor)titleColor
{
if ([self hasThemeAttribute:@"title-color"])
return [self valueForThemeAttribute:@"title-color"];
else
return [_titleView textColor];
}
- (void)setTitleColor:(CPColor)aColor
{
if ([aColor isEqual:[self titleColor]])
return;
if ([self hasThemeAttribute:@"title-color"])
[self setValue:aColor forThemeAttribute:@"title-color"];
[_titleView setTextColor:aColor];
}
/*!
Return the text field used to display the receiver's title.
@@ -365,25 +506,39 @@ CPBelowBottom = 6;
return _titleView;
}
/*!
Return the rectangle in which the receivers title is drawn.
*/
- (CGRect)titleRect
{
return [_titleView frame];
}
- (void)_manageTitlePositioning
{
if (_titlePosition == CPNoTitle)
if ((_titlePosition == CPNoTitle) || _transparent)
{
[_titleView removeFromSuperview];
[self setNeedsDisplay:YES];
if (_boxType !== CPBoxSeparator)
[self sizeToFit];
[self refreshDisplay];
return;
}
[_titleView setStringValue:_title];
[_titleView sizeToFit];
[self addSubview:_titleView];
var titleLeftOffset = [self valueForThemeAttribute:@"title-left-offset"],
titleTopOffset = [self valueForThemeAttribute:@"title-top-offset"];
switch (_titlePosition)
{
case CPAtTop:
case CPAboveTop:
case CPBelowTop:
[_titleView setFrameOrigin:CGPointMake(5.0, 0.0)];
[_titleView setFrameOrigin:CGPointMake(titleLeftOffset, titleTopOffset)]; // FIXME: was 0.0
[_titleView setAutoresizingMask:CPViewNotSizable];
break;
@@ -391,39 +546,51 @@ CPBelowBottom = 6;
case CPAtBottom:
case CPBelowBottom:
var h = [_titleView frameSize].height;
[_titleView setFrameOrigin:CGPointMake(5.0, [self frameSize].height - h)];
[_titleView setFrameOrigin:CGPointMake(titleLeftOffset, [self frameSize].height - h - titleTopOffset)];
[_titleView setAutoresizingMask:CPViewMinYMargin];
break;
}
if (!_transparent)
[self addSubview:_titleView];
[self sizeToFit];
[self setNeedsDisplay:YES];
[self refreshDisplay];
}
- (void)sizeToFit
{
var contentFrame = [_contentView frame],
offset = [self _titleHeightOffset],
contentMargin = [self valueForThemeAttribute:@"content-margin"];
var offset = [self _titleHeightOffset],
size = [self frameSize];
if (!contentFrame)
[_boxView setFrame:CGRectMake(0, offset[1], size.width, size.height - offset[0])];
if (!_contentView)
return;
[_contentView setFrameOrigin:CGPointMake(contentMargin.width, contentMargin.height + offset[1])];
var boxSize = [_boxView frameSize],
contentMargin = [self valueForThemeAttribute:@"content-margin"],
contentAdjustment = [self valueForThemeAttribute:@"content-adjustment"],
borderWidth = [self valueForThemeAttribute:@"border-width"],
minYCorrection = [self valueForThemeAttribute:(_titlePosition === CPNoTitle ? @"min-y-correction-no-title" : @"min-y-correction-title")];
[_contentView setFrame:CGRectMake(contentAdjustment.origin.x + contentMargin.width - borderWidth,
contentAdjustment.origin.y + contentMargin.height - borderWidth + minYCorrection,
boxSize.width - 2 * contentMargin.width + contentAdjustment.size.width,
boxSize.height - 2 * contentMargin.height + contentAdjustment.size.height)];
}
- (float)_titleHeightOffset
- (CPArray)_titleHeightOffset
{
if (_titlePosition == CPNoTitle)
return [0.0, 0.0];
var titleTopOffset = [self valueForThemeAttribute:@"title-top-offset"];
switch (_titlePosition)
{
case CPAtTop:
return [[_titleView frameSize].height, [_titleView frameSize].height];
return [[_titleView frameSize].height + titleTopOffset, [_titleView frameSize].height + titleTopOffset];
case CPAtBottom:
return [[_titleView frameSize].height, 0.0];
return [[_titleView frameSize].height + titleTopOffset, 0.0];
default:
return [0.0, 0.0];
@@ -440,21 +607,24 @@ CPBelowBottom = 6;
- (void)drawRect:(CGRect)rect
{
if ([self isCSSBased] && (_boxType !== CPBoxCustom))
return;
var bounds = [self bounds];
switch (_boxType)
if (_boxType === CPBoxSeparator)
{
case CPBoxSeparator:
// NSBox does not include a horizontal flag for the separator type. We have to determine
// the type of separator to draw by the width and height of the frame.
if (CGRectGetWidth(bounds) === 5.0)
return [self _drawVerticalSeparatorInRect:bounds];
else if (CGRectGetHeight(bounds) === 5.0)
return [self _drawHorizontalSeparatorInRect:bounds];
break;
// NSBox does not include a horizontal flag for the separator type. We have to determine
// the type of separator to draw by the width and height of the frame.
if (CGRectGetWidth(bounds) === 5.0)
return [self _drawVerticalSeparatorInRect:bounds];
else if (CGRectGetHeight(bounds) === 5.0)
return [self _drawHorizontalSeparatorInRect:bounds];
}
if (_transparent)
return;
if (_titlePosition == CPAtTop)
{
bounds.origin.y += [_titleView frameSize].height;
@@ -475,9 +645,6 @@ CPBelowBottom = 6;
switch (_borderType)
{
case CPBezelBorder:
[self _drawBezelBorderInRect:bounds];
break;
case CPGrooveBorder:
case CPLineBorder:
[self _drawLineBorderInRect:bounds];
@@ -594,11 +761,83 @@ CPBelowBottom = 6;
@end
// MARK: -
@implementation CPBox (CSSTheming)
- (void)layoutSubviews
{
if (![self isCSSBased] || (_boxType === CPBoxCustom))
return;
var bounds = [self bounds];
if (_boxType === CPBoxSeparator)
{
if (bounds.size.width === 5.0)
{
// Vertical separator
[_boxView setFrame:CGRectMake(2,0,1,bounds.size.height)];
}
else
{
// Horizontal separator
[_boxView setFrame:CGRectMake(0,2,bounds.size.width,1)];
}
[_boxView setBackgroundColor:[self valueForThemeAttribute:@"border-color"]];
return;
}
// All types of boxes (beside custom which is not covered here) always draw the same way, unless they are CPNoBorder.
if ((_borderType !== CPNoBorder) && !_transparent)
{
[_boxView setBackgroundColor:[self valueForThemeAttribute:@"background-color"]];
return;
}
// No border or transparent
[_boxView setBackgroundColor:nil];
}
- (BOOL)isCSSBased
{
return [[self theme] isCSSBased];
}
- (void)refreshDisplay
{
if ([self isCSSBased] && (_boxType !== CPBoxCustom))
[self setNeedsLayout:YES];
else
[self setNeedsDisplay:YES];
}
- (void)setAutoresizesSubviews:(BOOL)flag
{
// CPBox should always resize its subviews, like in Cocoa, whatever is the corresponding flag set.
// We have to keep the flag value as we could have to return it in -autoresizesSubview method.
_cachedAutoresizesSubviews = !!flag;
[super setAutoresizesSubviews:YES];
}
- (BOOL)autoresizesSubview
{
return _cachedAutoresizesSubviews;
}
@end
// MARK: -
var CPBoxTypeKey = @"CPBoxTypeKey",
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
CPBoxTitle = @"CPBoxTitle",
CPBoxTitlePosition = @"CPBoxTitlePosition",
CPBoxTitleView = @"CPBoxTitleView";
CPBoxTitleKey = @"CPBoxTitleKey",
CPBoxTitlePositionKey = @"CPBoxTitlePositionKey",
CPBoxTitleViewKey = @"CPBoxTitleViewKey",
CPBoxContentViewKey = @"CPBoxContentViewKey",
CPBoxBoxViewKey = @"CPBoxBoxViewKey";
@implementation CPBox (CPCoding)
@@ -611,16 +850,56 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
_boxType = [aCoder decodeIntForKey:CPBoxTypeKey];
_borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey];
_title = [aCoder decodeObjectForKey:CPBoxTitle];
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePosition];
_titleView = [aCoder decodeObjectForKey:CPBoxTitleView] || [CPTextField labelWithTitle:_title];
_title = [aCoder decodeObjectForKey:CPBoxTitleKey];
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePositionKey];
_contentView = [self subviews][0];
// Important : see comment on encodeWithCoder below
_boxView = [aCoder decodeObjectForKey:CPBoxBoxViewKey];
if (!_boxView)
{
// We're coming from nib2cib.
_boxView = [[CPView alloc] initWithFrame:[self bounds]];
_titleView = [CPTextField labelWithTitle:_title];
}
else
{
// We're coming from elsewhere
_titleView = [aCoder decodeObjectForKey:CPBoxTitleViewKey];
}
_contentView = [aCoder decodeObjectForKey:CPBoxContentViewKey];
// FIXME: super-mega-hyper-trick : _contentView has a superview which is not normal !
// FIXME: (see encodeWithCoder to understand why this is not possible)
// FIXME: we fix this by hand. This is horrible so please find a structural solution !
if (_contentView)
_contentView._superview = nil;
[self setAutoresizesSubviews:YES];
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
[_boxView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
[_boxView setAutoresizesSubviews:YES];
[self setAutoresizesSubviews:YES];
if (_contentView)
[_boxView setSubviews:@[_contentView]];
[self addSubview:_boxView];
[self addSubview:_titleView];
if (_boxType === CPBoxSeparator)
_titlePosition = CPNoTitle;
[_titleView setFont:[self titleFont]];
[_titleView setTextColor:[self titleColor]];
[self _manageTitlePositioning];
[self refreshDisplay];
}
return self;
@@ -628,13 +907,47 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
- (void)encodeWithCoder:(CPCoder)aCoder
{
// We have to distinguish between 2 cases :
// - we come from nib2cib
// - we come from elsewhere
//
// When coming from nib2cib, we have no _boxView, _contentView, _titleView.
// We fix _contentView to be the first (and only) subview.
// They will have to be added on decoding.
//
// When coming from elsewhere, we remove _boxView (and thus _contentView) and _titleView
// from the view hierarchy as we'll already encode them via variables.
// They will be putted back during decoding. This way, we reduce the space and speed needed for coding.
var subviews = [self subviews];
if (!_boxView)
{
// We're coming from nib2cib.
_contentView = subviews[0];
[_contentView removeFromSuperview];
}
else
{
// We're coming from elsewhere.
[_boxView removeFromSuperview];
[_titleView removeFromSuperview];
}
[super encodeWithCoder:aCoder];
[self setSubviews:subviews];
[aCoder encodeInt:_boxType forKey:CPBoxTypeKey];
[aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
[aCoder encodeObject:_title forKey:CPBoxTitle];
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePosition];
[aCoder encodeObject:_titleView forKey:CPBoxTitleView];
[aCoder encodeObject:_title forKey:CPBoxTitleKey];
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePositionKey];
[aCoder encodeConditionalObject:_contentView forKey:CPBoxContentViewKey];
[aCoder encodeConditionalObject:_titleView forKey:CPBoxTitleViewKey];
[aCoder encodeConditionalObject:_boxView forKey:CPBoxBoxViewKey];
}
@end
+14 -19
View File
@@ -42,7 +42,6 @@
- (CPDragOperation)browser:(CPBrowser)browser validateDrop:(id)info proposedRow:(CPInteger)row column:(CPInteger)column dropOperation:(CPTableViewDropOperation)dropOperation;
- (CPImage)browser:(CPBrowser)browser imageValueForItem:(id)anItem;
- (CPImage)browser:(CPBrowser)browser draggingImageForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)column withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
- (CPImage)browser:(CPBrowser)browser imageValueForItem:(id)item;
- (CPIndexSet)browser:(CPBrowser)browser selectionIndexesForProposedSelection:(CPIndexSet)proposedSelectionIndexes inColumn:(CPInteger)column;
- (CPInteger)browser:(CPBrowser)browser numberOfChildrenOfItem:(id)item;
- (CPView)browser:(CPBrowser)browser draggingViewForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(CPInteger)column withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
@@ -62,19 +61,18 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
CPBrowserDelegate_browser_shouldSelectRowIndexes_inColumn_ = 1 << 4,
CPBrowserDelegate_browser_writeRowsWithIndexes_inColumn_toPasteboard_ = 1 << 5,
CPBrowserDelegate_browser_validateDrop_proposedRow_column_dropOperation_ = 1 << 6,
CPBrowserDelegate_browser_imageValueForItem_ = 1 << 7,
CPBrowserDelegate_browser_draggingImageForRowsWithIndexes_inColumn_withEvent_offset_ = 1 << 8,
CPBrowserDelegate_browser_imageValueForItem_ = 1 << 9,
CPBrowserDelegate_browser_selectionIndexesForProposedSelection_inColumn_ = 1 << 10,
CPBrowserDelegate_browser_numberOfChildrenOfItem_ = 1 << 11,
CPBrowserDelegate_browser_draggingViewForRowsWithIndexes_inColumn_withEvent_offset_ = 1 << 12,
CPBrowserDelegate_browser_child_ofItem_ = 1 << 13,
CPBrowserDelegate_browser_objectValueForItem_ = 1 << 14,
CPBrowserDelegate_rootItemForBrowser_ = 1 << 15,
CPBrowserDelegate_browser_didChangeLastColumn_toColumn_ = 1 << 16,
CPBrowserDelegate_browser_didResizeColumn_ = 1 << 17,
CPBrowserDelegate_browserSelectionIsChanging_ = 1 << 18,
CPBrowserDelegate_browserSelectionDidChange_ = 1 << 19;
CPBrowserDelegate_browser_draggingImageForRowsWithIndexes_inColumn_withEvent_offset_ = 1 << 7,
CPBrowserDelegate_browser_imageValueForItem_ = 1 << 8,
CPBrowserDelegate_browser_selectionIndexesForProposedSelection_inColumn_ = 1 << 9,
CPBrowserDelegate_browser_numberOfChildrenOfItem_ = 1 << 10,
CPBrowserDelegate_browser_draggingViewForRowsWithIndexes_inColumn_withEvent_offset_ = 1 << 11,
CPBrowserDelegate_browser_child_ofItem_ = 1 << 12,
CPBrowserDelegate_browser_objectValueForItem_ = 1 << 13,
CPBrowserDelegate_rootItemForBrowser_ = 1 << 14,
CPBrowserDelegate_browser_didChangeLastColumn_toColumn_ = 1 << 15,
CPBrowserDelegate_browser_didResizeColumn_ = 1 << 16,
CPBrowserDelegate_browserSelectionIsChanging_ = 1 << 17,
CPBrowserDelegate_browserSelectionDidChange_ = 1 << 18;
/*!
@ingroup appkit
@@ -211,9 +209,6 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
if ([_delegate respondsToSelector:@selector(browser:validateDrop:proposedRow:column:dropOperation:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_validateDrop_proposedRow_column_dropOperation_;
if ([_delegate respondsToSelector:@selector(browser:imageValueForItem:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_imageValueForItem_;
if ([_delegate respondsToSelector:@selector(browser:draggingImageForRowsWithIndexes:inColumn:withEvent:offset:)])
_implementedDelegateMethods |= CPBrowserDelegate_browser_draggingImageForRowsWithIndexes_inColumn_withEvent_offset_;
@@ -468,7 +463,7 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
- (id)itemAtRow:(CPInteger)row inColumn:(CPInteger)column
{
return [_tableDelegates[column] childAtIndex:row];
return [_tableDelegates[column] childAtIndex:row] || nil;
}
- (BOOL)isLeafItem:(id)item
@@ -478,7 +473,7 @@ var CPBrowserDelegate_browser_acceptDrop_atRow_column_dropOperation_
- (id)parentForItemsInColumn:(CPInteger)column
{
return [_tableDelegates[column] _item];
return [_tableDelegates[column] _item] || nil;
}
- (CPSet)selectedItems
+252 -172
View File
@@ -44,6 +44,7 @@ CPTexturedRoundedBezelStyle = 11; // Round Textured
CPRoundRectBezelStyle = 12; // Round Rect
CPRecessedBezelStyle = 13; // Recessed
CPRoundedDisclosureBezelStyle = 14; // Disclosure
CPInlineBezelStyle = 15; // Inline
CPHUDBezelStyle = -1;
@@ -72,14 +73,32 @@ CPPushInCellMask = CPPushInButtonMask;
CPChangeGrayCellMask = CPGrayButtonMask;
CPChangeBackgroundCellMask = CPBackgroundButtonMask;
CPButtonStateMixed = CPThemeState("mixed");
CPButtonStateBezelStyleRounded = CPThemeState("rounded");
CPButtonStateBezelStyleRoundRect = CPThemeState("roundRect");
CPButtonStateMixed = CPThemeState("mixed");
CPButtonStateBezelStyleRounded = CPThemeState("rounded"); // IB style : Push
CPButtonStateBezelStyleShadowlessSquare = CPThemeState("square"); // IB style : Square
CPButtonStateBezelStyleSmallSquare = CPThemeState("gradient"); // IB style : Gradient
CPButtonStateBezelStyleTexturedRounded = CPThemeState("textured-rounded"); // IB style : Textured rounded
CPButtonStateBezelStyleRoundRect = CPThemeState("roundRect"); // IB style : Round rect
CPButtonStateBezelStyleRecessed = CPThemeState("recessed"); // IB style : Recessed
CPButtonStateBezelStyleInline = CPThemeState("inline"); // IB style : Inline
CPButtonStateBezelStyleRegularSquare = CPThemeState("bevel"); // IB style : Bevel
CPButtonStateBezelStyleTextured = CPThemeState("textured"); // IB style : Textured
CPButtonStateBezelStyleDisclosure = CPThemeState("disclosure"); // IB style : Disclosure triangle
CPButtonStateBezelStyleRoundedDisclosure = CPThemeState("rounded-disclosure"); // IB style : Rounded disclosure
// add all future correspondance between bezel styles and theme state here.
var CPButtonBezelStyleStateMap = @{
CPRoundedBezelStyle: CPButtonStateBezelStyleRounded,
CPRoundRectBezelStyle: CPButtonStateBezelStyleRoundRect,
CPRoundedBezelStyle: CPButtonStateBezelStyleRounded,
CPShadowlessSquareBezelStyle: CPButtonStateBezelStyleShadowlessSquare,
CPSmallSquareBezelStyle: CPButtonStateBezelStyleSmallSquare,
CPTexturedRoundedBezelStyle: CPButtonStateBezelStyleTexturedRounded,
CPRoundRectBezelStyle: CPButtonStateBezelStyleRoundRect,
CPRecessedBezelStyle: CPButtonStateBezelStyleRecessed,
CPInlineBezelStyle: CPButtonStateBezelStyleInline,
CPRegularSquareBezelStyle: CPButtonStateBezelStyleRegularSquare,
CPTexturedSquareBezelStyle: CPButtonStateBezelStyleTextured,
CPDisclosureBezelStyle: CPButtonStateBezelStyleDisclosure,
CPRoundedDisclosureBezelStyle: CPButtonStateBezelStyleRoundedDisclosure
};
/// @cond IGNORE
@@ -108,6 +127,7 @@ CPButtonImageOffset = 3.0;
// NS-style Display Properties
CPBezelStyle _bezelStyle;
ThemeState _bezelState;
CPString _keyEquivalent;
unsigned _keyEquivalentModifierMask;
@@ -117,7 +137,7 @@ CPButtonImageOffset = 3.0;
float _periodicDelay;
float _periodicInterval;
BOOL _isTracking;
BOOL _isHighlighted;
}
+ (Class)_binderClassForBinding:(CPString)aBinding
@@ -154,9 +174,17 @@ CPButtonImageOffset = 3.0;
return @{
@"image": [CPNull null],
@"image-offset": 0.0,
@"image-vertical-offset": 0.0,
@"bezel-inset": CGInsetMakeZero(),
@"content-inset": CGInsetMakeZero(),
@"bezel-color": [CPNull null],
@"image-position": CPImageLeft,
@"vertical-alignment": CPCenterVerticalTextAlignment,
@"alignment": CPCenterTextAlignment,
@"image-scaling": CPImageScaleNone,
@"invert-image": NO,
@"invert-image-on-push": NO,
@"image-color": [CPNull null] // If null, image color follows text color
};
}
@@ -171,12 +199,6 @@ CPButtonImageOffset = 3.0;
if (self)
{
// Should we instead override the defaults?
[self setValue:CPCenterTextAlignment forThemeAttribute:@"alignment"];
[self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
[self setValue:CPImageScaleNone forThemeAttribute:@"image-scaling"];
[self setBezelStyle:CPRoundRectBezelStyle];
[self setBordered:YES];
@@ -198,8 +220,8 @@ CPButtonImageOffset = 3.0;
[self setButtonType:CPMomentaryPushInButton];
}
#pragma mark -
#pragma mark Control Size
// MARK: -
// MARK: Control Size
- (void)setControlSize:(CPControlSize)aControlSize
{
@@ -210,7 +232,7 @@ CPButtonImageOffset = 3.0;
}
#pragma mark -
// MARK: -
// Setting the state
/*!
@@ -258,30 +280,6 @@ CPButtonImageOffset = 3.0;
anObjectValue = CPOnState;
[super setObjectValue:anObjectValue];
switch ([self objectValue])
{
case CPMixedState:
[self unsetThemeState:CPThemeStateSelected];
[self setThemeState:CPButtonStateMixed];
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self setThemeState:CPThemeStateHighlighted];
else
[self unsetThemeState:CPThemeStateHighlighted];
break;
case CPOnState:
[self unsetThemeState:CPButtonStateMixed];
[self setThemeState:CPThemeStateSelected];
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self setThemeState:CPThemeStateHighlighted];
else
[self unsetThemeState:CPThemeStateHighlighted];
break;
case CPOffState:
[self unsetThemeState:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
}
}
/*!
@@ -378,12 +376,21 @@ CPButtonImageOffset = 3.0;
- (void)setImage:(CPImage)anImage
{
[self setValue:anImage forThemeAttribute:@"image"];
// This is needed when compiling themes
if (!_bezelState)
_bezelState = CPThemeStateNormal;
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState];
// if we omit this, images will disappear as soon as the button becomes disabled
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateDisabled)];
}
- (CPImage)image
{
return [self valueForThemeAttribute:@"image" inState:CPThemeStateNormal];
if (!_bezelState)
_bezelState = CPThemeStateNormal;
return [self valueForThemeAttribute:@"image" inState:_bezelState];
}
/*!
@@ -392,7 +399,8 @@ CPButtonImageOffset = 3.0;
*/
- (void)setAlternateImage:(CPImage)anImage
{
[self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateHighlighted)];
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateSelected)];
}
/*!
@@ -400,7 +408,17 @@ CPButtonImageOffset = 3.0;
*/
- (CPImage)alternateImage
{
return [self valueForThemeAttribute:@"image" inState:CPThemeStateHighlighted];
return [self valueForThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateSelected)];
}
- (void)setHoveredImage:(CPImage)anImage
{
[self setValue:anImage forThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateHovered)];
}
- (CPImage)hoveredImage
{
return [self valueForThemeAttribute:@"image" inState:_bezelState.and(CPThemeStateHovered)];
}
- (void)setImageOffset:(float)theImageOffset
@@ -423,11 +441,6 @@ CPButtonImageOffset = 3.0;
_showsStateBy = aMask;
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask) && [self state] != CPOffState)
[self setThemeState:CPThemeStateHighlighted];
else
[self unsetThemeState:CPThemeStateHighlighted];
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
@@ -444,11 +457,8 @@ CPButtonImageOffset = 3.0;
_highlightsBy = aMask;
if ([self hasThemeState:CPThemeStateHighlighted])
{
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
- (CPInteger)highlightsBy
@@ -533,9 +543,20 @@ CPButtonImageOffset = 3.0;
_periodicInterval = anInterval;
}
- (void)highlight:(BOOL)shouldHighlight
{
if (_isHighlighted == shouldHighlight)
return;
_isHighlighted = shouldHighlight;
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
- (void)mouseDown:(CPEvent)anEvent
{
if ([self isContinuous])
if ([self isEnabled] && [self isContinuous])
{
_continuousDelayTimer = [CPTimer scheduledTimerWithTimeInterval:_periodicDelay callback: function()
{
@@ -555,46 +576,12 @@ CPButtonImageOffset = 3.0;
[_target performSelector:_action withObject:self];
}
- (BOOL)startTrackingAt:(CGPoint)aPoint
{
_isTracking = YES;
var startedTracking = [super startTrackingAt:aPoint];
if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
{
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self highlight:[self state] == CPOffState];
else
[self highlight:YES];
}
else
{
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self highlight:[self state] != CPOffState];
else
[self highlight:NO];
}
return startedTracking;
}
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
{
_isTracking = NO;
if (mouseIsUp && CGRectContainsPoint([self bounds], aPoint))
[self setNextState];
else
{
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
[self highlight:[self state] != CPOffState];
else
[self highlight:NO];
}
[self setNeedsLayout];
[self setNeedsDisplay:YES];
[self highlight:NO];
[self invalidateTimers];
}
@@ -615,7 +602,7 @@ CPButtonImageOffset = 3.0;
- (CGRect)contentRectForBounds:(CGRect)bounds
{
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
var contentInset = [self valueForThemeAttribute:@"content-inset" inState:[self _contentVisualState]];
return CGRectInsetByInset(bounds, contentInset);
}
@@ -642,7 +629,7 @@ CPButtonImageOffset = 3.0;
size = [contentView frameSize];
}
else
size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]];
size = [([self title] || " ") sizeWithFont:[self font]];
var contentInset = [self currentValueForThemeAttribute:@"content-inset"],
minSize = [self currentValueForThemeAttribute:@"min-size"],
@@ -665,10 +652,10 @@ CPButtonImageOffset = 3.0;
*/
- (void)sizeToFit
{
[self setFrameSize:[self _minimumFrameSize]];
[self layoutSubviews];
[self setFrameSize:[self _minimumFrameSize]];
if ([self ephemeralSubviewNamed:@"content-view"])
[self layoutSubviews];
}
@@ -698,86 +685,164 @@ CPButtonImageOffset = 3.0;
return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
}
- (CPThemeState)_backgroundVisualState
{
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
currentState = [self state],
buttonIsOn = (currentState !== CPOffState);
if (_isHighlighted && (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask)))
visualState = visualState.and(CPThemeStateHighlighted);
else
visualState = visualState.without(CPThemeStateHighlighted);
if (buttonIsOn && (_showsStateBy & (CPPushInCellMask | CPChangeGrayCellMask)))
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
else
visualState = visualState.without(CPThemeStateSelected);
return visualState;
}
// Note : We have to split content and image visual states as, for example, radio buttons don't follow push buttons behavior
- (CPThemeState)_contentVisualState
{
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
currentState = [self state],
buttonIsOn = (currentState !== CPOffState);
// If the button is pushed (_isHighlighted), always add the highlighted state
if (_isHighlighted || (((_showsStateBy & CPChangeGrayCellMask) || (_showsStateBy & CPChangeBackgroundCellMask)) && buttonIsOn))
visualState = visualState.and(CPThemeStateHighlighted);
else
visualState = visualState.without(CPThemeStateHighlighted);
if (buttonIsOn && (_showsStateBy & CPContentsCellMask))
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
else
visualState = visualState.without(CPThemeStateSelected);
return visualState;
}
- (CPThemeState)_imageVisualState
{
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
currentState = [self state],
buttonIsOn = (currentState !== CPOffState);
// Remove highlighted & selected theme states
visualState = visualState.without(CPThemeStateHighlighted);
visualState = visualState.without(CPThemeStateSelected);
// Note : We have to deal with special case where button is ON, highlightsBy and showsStateBy use content, and button is pushed
// BUT this should not be used for disclosure buttons !
if (_isHighlighted && buttonIsOn && (_highlightsBy & CPContentsCellMask) && (_showsStateBy & CPContentsCellMask) && (_bezelStyle !== CPDisclosureBezelStyle))
return visualState;
if (_isHighlighted && ((_highlightsBy & CPContentsCellMask) || (_highlightsBy & CPChangeGrayCellMask)))
visualState = visualState.and(CPThemeStateHighlighted);
if (buttonIsOn && (_showsStateBy & CPContentsCellMask))
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
return visualState;
}
- (CPString)_currentTitle
{
var buttonIsOn = ([self state] !== CPOffState);
// Note : We have to deal with special case where button is ON, highlightsBy and showsStateBy use content, and button is pushed
if (_isHighlighted && buttonIsOn && (_highlightsBy & CPContentsCellMask) && (_showsStateBy & CPContentsCellMask))
return _title;
else if (_alternateTitle && ((_isHighlighted && (_highlightsBy & CPContentsCellMask)) || (buttonIsOn && (_showsStateBy & CPContentsCellMask))))
return _alternateTitle;
else
return _title;
}
- (CPImage)_currentImage
{
var visualState = [self _imageVisualState],
currentImage = [self valueForThemeAttribute:@"image" inState:visualState],
imageColor = [self valueForThemeAttribute:@"image-color" inState:visualState],
buttonIsOn = ([self state] !== CPOffState);
if ([currentImage isMaterialIconImage])
{
if (([self valueForThemeAttribute:@"invert-image" inState:visualState] || ([self valueForThemeAttribute:@"invert-image-on-push" inState:visualState] && (_isHighlighted || (((_showsStateBy & CPChangeGrayCellMask) || (_showsStateBy & CPChangeBackgroundCellMask)) && buttonIsOn)))))
currentImage = [currentImage invertedImage];
else if (imageColor && [imageColor isKindOfClass:CPColor])
// In some buttons, image color doesn't follow text color !
currentImage = [currentImage imageVersionWithColor:imageColor];
else
// By default, image color follows text color
currentImage = [currentImage imageVersionWithColor:[self valueForThemeAttribute:@"text-color" inState:[self _contentVisualState]]];
}
return currentImage;
}
- (void)layoutSubviews
{
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"],
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
relativeToEphemeralSubviewNamed:@"bezel-view"],
if (contentView)
{
var title = nil,
image = nil;
image = [self _currentImage],
contentVisualState = [self _contentVisualState];
if (_isTracking)
{
if (_highlightsBy & CPContentsCellMask)
{
if (_showsStateBy & CPContentsCellMask)
{
title = ([self state] == CPOffState && _alternateTitle) ? _alternateTitle : _title;
image = ([self state] == CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
}
else
{
title = [self alternateTitle];
image = [self alternateImage];
}
}
else if (_showsStateBy & CPContentsCellMask)
{
title = ([self state] != CPOffState && _alternateTitle) ? _alternateTitle : _title;
image = ([self state] != CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
}
else
{
title = _title;
image = [self image];
}
}
else
{
if (_showsStateBy & CPContentsCellMask)
{
title = ([self state] != CPOffState && _alternateTitle) ? _alternateTitle : _title;
image = ([self state] != CPOffState && [self alternateImage]) ? [self alternateImage] : [self image];
}
else
{
title = _title;
image = [self image];
}
}
[bezelView setBackgroundColor:[self valueForThemeAttribute:@"bezel-color" inState:[self _backgroundVisualState]]];
[contentView setText:[self _currentTitle]];
[contentView setImage:image];
[contentView setText:title];
[contentView setImage:image];
[contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]];
[contentView setImageOffset:[self valueForThemeAttribute:@"image-offset" inState:contentVisualState]];
[contentView setImageVerticalOffset:[self valueForThemeAttribute:@"image-vertical-offset" inState:contentVisualState]];
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
[contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]];
[contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]];
[contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
[contentView setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
[contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
[contentView setImagePosition:[self currentValueForThemeAttribute:@"image-position"]];
[contentView setImageScaling:[self currentValueForThemeAttribute:@"image-scaling"]];
[contentView setDimsImage:[self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled];
}
[contentView setFont:[self font]];
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:contentVisualState]];
[contentView setAlignment:[self valueForThemeAttribute:@"alignment" inState:contentVisualState]];
[contentView setVerticalAlignment:[self valueForThemeAttribute:@"vertical-alignment" inState:contentVisualState]];
[contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:contentVisualState]];
[contentView _setUsesSingleLineMode:YES];
[contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:contentVisualState]];
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:contentVisualState]];
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position"]];
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling"]];
// We don't automatically dim material icon images as the color is driven by the theme
[contentView setDimsImage:[self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled && ![image isMaterialIconImage]];
}
- (void)setBordered:(BOOL)shouldBeBordered
{
if (shouldBeBordered)
{
[self setThemeState:CPThemeStateBordered];
if (_bezelState)
_bezelState = _bezelState.and(CPThemeStateBordered);
else
_bezelState = CPThemeStateBordered;
}
else
{
[self unsetThemeState:CPThemeStateBordered];
if (_bezelState)
_bezelState = _bezelState.without(CPThemeStateBordered);
else
_bezelState = CPThemeStateNormal;
}
}
- (BOOL)isBordered
@@ -807,7 +872,7 @@ CPButtonImageOffset = 3.0;
{
var selfWindow = [self window];
if (selfWindow === aWindow || aWindow === nil)
if (selfWindow === aWindow || aWindow == nil)
return;
if ([selfWindow defaultButton] === self)
@@ -873,17 +938,7 @@ CPButtonImageOffset = 3.0;
[self setState:[self nextState]];
var shouldHighlight = NO;
if (_highlightsBy & (CPPushInCellMask | CPChangeGrayCellMask))
{
if (_showsStateBy & (CPChangeGrayCellMask | CPChangeBackgroundCellMask))
shouldHighlight = [self state] == CPOffState;
else
shouldHighlight = YES;
}
[self highlight:shouldHighlight];
[self highlight:YES];
try
{
@@ -895,8 +950,7 @@ CPButtonImageOffset = 3.0;
}
finally
{
if (shouldHighlight)
[CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
[CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
}
}
@@ -919,6 +973,22 @@ CPButtonImageOffset = 3.0;
[self setThemeState:newState];
_bezelStyle = aBezelStyle;
if (_bezelState && newState)
{
if (currentState)
_bezelState =_bezelState.without(currentState);
_bezelState = _bezelState.and(newState);
}
else
_bezelState = newState || CPThemeStateNormal;
// For disclosure triangle and rounded, we have to move away from
// what Xcode tells us as we implement visual behavior with images (so content)
// and not background
if ((_bezelStyle === CPDisclosureBezelStyle) || (_bezelStyle === CPRoundedDisclosureBezelStyle))
[self setShowsStateBy:CPContentsCellMask];
}
- (unsigned)bezelStyle
@@ -942,7 +1012,8 @@ var CPButtonImageKey = @"CPButtonImageKey",
CPButtonPeriodicDelayKey = @"CPButtonPeriodicDelayKey",
CPButtonPeriodicIntervalKey = @"CPButtonPeriodicIntervalKey",
CPButtonHighlightsByKey = @"CPButtonHighlightsByKey",
CPButtonShowsStateByKey = @"CPButtonShowsStateByKey";
CPButtonShowsStateByKey = @"CPButtonShowsStateByKey",
CPButtonBezelStyleKey = @"CPButtonBezelStyleKey";
@implementation CPButton (CPCoding)
@@ -991,6 +1062,12 @@ var CPButtonImageKey = @"CPButtonImageKey",
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey];
if ([aCoder containsValueForKey:CPButtonIsBorderedKey])
[self setBordered:[aCoder decodeBoolForKey:CPButtonIsBorderedKey]];
if ([aCoder containsValueForKey:CPButtonBezelStyleKey])
[self setBezelStyle:[aCoder decodeIntForKey:CPButtonBezelStyleKey]];
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
@@ -1025,6 +1102,9 @@ var CPButtonImageKey = @"CPButtonImageKey",
[aCoder encodeObject:_periodicDelay forKey:CPButtonPeriodicDelayKey];
[aCoder encodeObject:_periodicInterval forKey:CPButtonPeriodicIntervalKey];
[aCoder encodeBool:[self isBordered] forKey:CPButtonIsBorderedKey];
[aCoder encodeInt: [self bezelStyle] forKey:CPButtonBezelStyleKey];
}
@end
+79 -21
View File
@@ -36,6 +36,7 @@
BOOL _hasResizeControl;
BOOL _resizeControlIsLeftAligned;
CPArray _buttons;
CPArray _rightButtons;
}
+ (id)plusButton
@@ -70,7 +71,7 @@
[button addItemWithTitle:nil];
[[button lastItem] setImage:image];
[button setImagePosition:CPImageOnly];
[button setValue:CGInsetMake(0, 0, 0, 0) forThemeAttribute:"content-inset"];
[button setValue:CGInsetMake(0, 0, 0, 0) forThemeAttribute:"content-inset" inState:CPPopUpButtonStatePullsDown];
[button setPullsDown:YES];
@@ -104,6 +105,7 @@
if (self)
{
_buttons = [];
_rightButtons = [];
[self setNeedsLayout];
}
@@ -112,6 +114,8 @@
- (void)awakeFromCib
{
[super awakeFromCib];
var view = [self superview],
subview = self;
@@ -155,6 +159,30 @@
return [CPArray arrayWithArray:_buttons];
}
- (void)setRightButtons:(CPArray)buttons
{
for (var i = [_rightButtons count] - 1; i >= 0; i--)
{
[_rightButtons[i] removeFromSuperview];
[_rightButtons[i] removeObserver:self forKeyPath:@"hidden"];
}
_rightButtons = [CPArray arrayWithArray:buttons];
for (var i = [_rightButtons count] - 1; i >= 0; i--)
{
[_rightButtons[i] addObserver:self forKeyPath:@"hidden" options:CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld context:nil];
[_rightButtons[i] setBordered:YES];
}
[self setNeedsLayout];
}
- (CPArray)rightButtons
{
return [CPArray arrayWithArray:_rightButtons];
}
- (void)setHasResizeControl:(BOOL)shouldHaveResizeControl
{
if (_hasResizeControl === shouldHaveResizeControl)
@@ -241,46 +269,73 @@
}
}
var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1,
bounds = [self bounds],
var rightButtonsNotHidden = [CPArray arrayWithArray:_rightButtons],
rightCount = [rightButtonsNotHidden count];
while (rightCount--)
{
var button = rightButtonsNotHidden[rightCount];
if ([button isHidden])
{
[button removeFromSuperview];
[rightButtonsNotHidden removeObject:button];
}
}
var bounds = [self bounds],
height = CGRectGetHeight(bounds) - 1,
frameWidth = CGRectGetWidth(bounds),
resizeRect = _hasResizeControl ? [self rectForEphemeralSubviewNamed:"resize-control-view"] : CGRectMakeZero(),
resizeWidth = CGRectGetWidth(resizeRect),
availableWidth = frameWidth - resizeWidth - 1;
for (var i = 0, count = [buttonsNotHidden count]; i < count; i++)
var currentLeftOffset = _resizeControlIsLeftAligned ? resizeWidth - 1 : -1,
currentRightOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX(bounds) + 1 : CGRectGetMaxX(bounds) - resizeWidth + 1;
var setupButton = function(button, isRightAligned)
{
var button = buttonsNotHidden[i],
width = CGRectGetWidth([button frame]);
var width = CGRectGetWidth([button frame]);
if (availableWidth > width)
availableWidth -= width;
else
break;
return NO;
if (_resizeControlIsLeftAligned)
if (isRightAligned)
{
[button setFrame:CGRectMake(currentButtonOffset - width, 1, width, height)];
currentButtonOffset -= width - 1;
[button setFrame:CGRectMake(currentRightOffset - width, 1, width, height)];
currentRightOffset -= width - 1;
}
else
{
[button setFrame:CGRectMake(currentButtonOffset, 1, width, height)];
currentButtonOffset += width - 1;
{[button setFrame:CGRectMake(currentLeftOffset, 1, width, height)];
currentLeftOffset += width - 1;
}
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered]];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, ]];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered]];
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered]];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered]];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered]];
[button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
// FIXME shouldn't need this
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
// FIXME shouldn't need this[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
[self addSubview:button];
return YES;
};
for (var i = 0, count = [buttonsNotHidden count]; i < count; i++)
{
if (!setupButton(buttonsNotHidden[i], _resizeControlIsLeftAligned))
break;
}
for (var i = 0, count = [rightButtonsNotHidden count]; i < count; i++)
{
if (!setupButton(rightButtonsNotHidden[i], YES))
break;
}
if (_hasResizeControl)
@@ -312,7 +367,8 @@
var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
CPButtonBarResizeControlIsLeftAlignedKey = @"CPButtonBarResizeControlIsLeftAlignedKey",
CPButtonBarButtonsKey = @"CPButtonBarButtonsKey";
CPButtonBarButtonsKey = @"CPButtonBarButtonsKey",
CPButtonBarRightButtonsKey = @"CPButtonBarRightButtonsKey";
@implementation CPButtonBar (CPCoding)
@@ -323,6 +379,7 @@ var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
[aCoder encodeBool:_hasResizeControl forKey:CPButtonBarHasResizeControlKey];
[aCoder encodeBool:_resizeControlIsLeftAligned forKey:CPButtonBarResizeControlIsLeftAlignedKey];
[aCoder encodeObject:_buttons forKey:CPButtonBarButtonsKey];
[aCoder encodeObject:_rightButtons forKey:CPButtonBarRightButtonsKey];
}
- (id)initWithCoder:(CPCoder)aCoder
@@ -330,6 +387,7 @@ var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
if (self = [super initWithCoder:aCoder])
{
_buttons = [aCoder decodeObjectForKey:CPButtonBarButtonsKey] || [];
_rightButtons = [aCoder decodeObjectForKey:CPButtonBarRightButtonsKey] || [];
_hasResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasResizeControlKey];
_resizeControlIsLeftAligned = [aCoder decodeBoolForKey:CPButtonBarResizeControlIsLeftAlignedKey];
}
+49 -19
View File
@@ -94,26 +94,9 @@ CPCheckBoxImageOffset = 4.0;
[self takeStateFromKeyPath:aKeyPath ofObjects:objects];
}
- (CPImage)image
{
return [self currentValueForThemeAttribute:@"image"];
}
- (CPImage)alternateImage
{
return [self currentValueForThemeAttribute:@"image"];
}
- (BOOL)startTrackingAt:(CGPoint)aPoint
{
var startedTracking = [super startTrackingAt:aPoint];
[self highlight:YES];
return startedTracking;
}
#pragma mark -
#pragma mark Override methods from CPButton
// MARK: -
// MARK: Override methods from CPButton
- (CGSize)_minimumFrameSize
{
@@ -135,6 +118,36 @@ CPCheckBoxImageOffset = 4.0;
return size;
}
- (CPThemeState)_contentVisualState
{
// Note : Behavior differs from CPButton as title doesn't follow the highlightsBy content flag
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
currentState = [self state];
if ((currentState !== CPOffState) && (_showsStateBy & CPContentsCellMask))
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
return visualState;
}
- (CPThemeState)_imageVisualState
{
// Note : Behavior differs from CPButton as we don't force "not selected" theme state
// when button is highglighted and selected
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
currentState = [self state];
if (_isHighlighted && (_highlightsBy & CPContentsCellMask))
visualState = visualState.and(CPThemeStateHighlighted);
if ((currentState !== CPOffState) && (_showsStateBy & CPContentsCellMask))
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
return visualState;
}
@end
@implementation _CPCheckBoxValueBinder : CPBinder
@@ -163,3 +176,20 @@ CPCheckBoxImageOffset = 4.0;
}
@end
// MARK: -
@implementation CPCheckBox (TableDataView)
// We overide here _CPObject+Theme setValue:forThemeAttribute as CPCheckBox can be used as tableView data view
// So, when outside a table data view, setValue:forThemeAttribute should store the value with the CPThemeStateNormal (default behavior)
// When inside a table data view, it should store the value with the CPThemeStateTableDataView. If not, the value won't be used if the
// theme defined a value for this attribute for state CPThemeStateTableDataView
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
{
[super setValue:aValue forThemeAttribute:aName];
[super setValue:aValue forThemeAttribute:aName inState:CPThemeStateTableDataView];
}
@end
+1 -1
View File
@@ -195,7 +195,7 @@
- (CGRect)documentVisibleRect
{
return [self convertRect:[self bounds] fromView:_documentView];
return [_documentView visibleRect];
}
@end
+59 -34
View File
@@ -31,6 +31,7 @@
@import "CPDragServer_Constants.j"
@import "CPPasteboard.j"
@import "CPView.j"
@import "CPKeyValueBinding.j"
@class _CPCollectionViewDropIndicator
@@ -55,11 +56,13 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
- (CPData)collectionView:(CPCollectionView)collectionView dataForItemsAtIndexes:(CPIndexSet)indices forType:(CPString)aType;
- (CPDragOperation)collectionView:(CPCollectionView)collectionView validateDrop:(id)draggingInfo proposedIndex:(CPInteger)proposedDropIndex dropOperation:(CPCollectionViewDropOperation)proposedDropOperation;
- (CPMenu)collectionView:(CPCollectionView)collectionView menuForItemAtIndex:(CPInteger)anIndex;
- (CPView)collectionView:(CPCollectionView)collectionView dragginViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
- (CPView)collectionView:(CPCollectionView)collectionView draggingViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
- (void)collectionView:(CPCollectionView)collectionView didDoubleClickOnItemAtIndex:(int)index;
@end
var HORIZONTAL_MARGIN = 2;
/*!
@ingroup appkit
@class CPCollectionView
@@ -88,9 +91,6 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
@param indices the indices to obtain drag types
@return an array of drag types (CPString)
*/
var HORIZONTAL_MARGIN = 2;
@implementation CPCollectionView : CPView
{
CPArray _content;
@@ -133,7 +133,6 @@ var HORIZONTAL_MARGIN = 2;
CGSize _storedFrameSize;
BOOL _uniformSubviewsResizing @accessors(property=uniformSubviewsResizing);
BOOL _lockResizing;
CPInteger _currentDropIndex;
CPDragOperation _currentDragOperation;
@@ -141,6 +140,14 @@ var HORIZONTAL_MARGIN = 2;
_CPCollectionViewDropIndicator _dropView;
}
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding == CPContentBinding)
return [_CPCollectionViewContentBinder class];
return [super _binderClassForBinding:aBinding];
}
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
@@ -183,7 +190,7 @@ var HORIZONTAL_MARGIN = 2;
_needsMinMaxItemSizeUpdate = YES;
_uniformSubviewsResizing = NO;
_lockResizing = NO;
_inLiveResize = NO;
_currentDropIndex = -1;
_currentDragOperation = CPDragOperationNone;
@@ -195,8 +202,8 @@ var HORIZONTAL_MARGIN = 2;
#pragma mark -
#pragma mark Delegate
// MARK: -
// MARK: Delegate
/*!
Set the delegate of the receiver
@@ -385,14 +392,7 @@ var HORIZONTAL_MARGIN = 2;
_isSelectable = isSelectable;
if (!_isSelectable)
{
var index = CPNotFound,
itemCount = [_items count];
// Be wary of invalid selection ranges since setContent: does not clear selection indexes.
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound && index < itemCount)
[_items[index] setSelected:NO];
}
[self _applySelectionToItems:NO];
}
/*!
@@ -446,22 +446,15 @@ var HORIZONTAL_MARGIN = 2;
{
if (!anIndexSet)
anIndexSet = [CPIndexSet indexSet];
if (!_isSelectable || [_selectionIndexes isEqual:anIndexSet])
return;
var index = CPNotFound,
itemCount = [_items count];
// Be wary of invalid selection ranges since setContent: does not clear selection indexes.
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) !== CPNotFound && index < itemCount)
[_items[index] setSelected:NO];
[self _applySelectionToItems:NO];
_selectionIndexes = anIndexSet;
var index = CPNotFound;
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) !== CPNotFound)
[_items[index] setSelected:YES];
[self _applySelectionToItems:YES];
var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
[[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
@@ -526,14 +519,14 @@ var HORIZONTAL_MARGIN = 2;
- (void)resizeWithOldSuperviewSize:(CGSize)oldBoundsSize
{
if (_lockResizing)
if (_inLiveResize)
return;
_lockResizing = YES;
_inLiveResize = YES;
[self tile];
_lockResizing = NO;
_inLiveResize = NO;
}
- (void)tile
@@ -607,9 +600,12 @@ var HORIZONTAL_MARGIN = 2;
if (_maxNumberOfRows > 0)
numberOfRows = MIN(numberOfRows, _maxNumberOfRows);
height = MAX(height, numberOfRows * (_minItemSize.height + _verticalMargin));
// calculate the required height: (sum of item heights) + (sum of margins between items).
var requiredHeight = (numberOfRows * _minItemSize.height) + (MAX(0, numberOfRows - 1) * _verticalMargin);
height = MAX(height, requiredHeight);
var itemSizeHeight = FLOOR(height / numberOfRows) - _verticalMargin;
// calculate individual item height based on the total available height.
var itemSizeHeight = (numberOfRows > 0) ? FLOOR((height - (MAX(0, numberOfRows - 1) * _verticalMargin)) / numberOfRows) : 0;
if (maxItemSizeHeight > 0)
itemSizeHeight = MIN(itemSizeHeight, maxItemSizeHeight);
@@ -628,7 +624,7 @@ var HORIZONTAL_MARGIN = 2;
_horizontalMargin = _uniformSubviewsResizing ? FLOOR((aFrameSize.width - numberOfColumns * anItemSize.width) / (numberOfColumns + 1)) : HORIZONTAL_MARGIN;
var x = _horizontalMargin,
y = -anItemSize.height;
y = -anItemSize.height;
[displayItems enumerateObjectsUsingBlock:function(item, idx, stop)
{
@@ -643,7 +639,11 @@ var HORIZONTAL_MARGIN = 2;
if (idx % numberOfColumns == 0)
{
x = _horizontalMargin;
y += _verticalMargin + anItemSize.height;
// For the first row, don't add a margin. For all subsequent rows, add the margin.
if (idx === 0)
y += anItemSize.height;
else
y += _verticalMargin + anItemSize.height;
}
[view setFrameOrigin:CGPointMake(x, y)];
@@ -746,7 +746,7 @@ var HORIZONTAL_MARGIN = 2;
*/
- (void)setMinItemSize:(CGSize)aSize
{
if (aSize === nil || aSize === undefined)
if (aSize == nil)
[CPException raise:CPInvalidArgumentException reason:"Invalid value provided for minimum size"];
if (CGSizeEqualToSize(_minItemSize, aSize))
@@ -973,6 +973,20 @@ var HORIZONTAL_MARGIN = 2;
return frame;
}
- (void)_applySelectionToItems:(BOOL)select
{
var numberOfItems = [_items count];
[_selectionIndexes enumerateIndexesUsingBlock:function(idx, stop)
{
if (idx < numberOfItems)
[[_items objectAtIndex:idx] setSelected:select];
else {
stop(YES);
}
}];
}
@end
@implementation CPCollectionView (DragAndDrop)
@@ -1587,3 +1601,14 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
}
@end
@implementation _CPCollectionViewContentBinder : CPBinder
{
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source setContent:aValue];
}
@end
+303 -11
View File
@@ -25,6 +25,7 @@
@import "CGColor.j"
@import "_CPObject+Theme.j"
@import "CPCompatibility.j"
@import "CPImage.j"
@@ -65,7 +66,8 @@ var cachedBlackColor,
cachedOrangeColor,
cachedPurpleColor,
cachedShadowColor,
cachedClearColor;
cachedClearColor,
cachedThemeColor;
/// @endcond
@@ -78,13 +80,37 @@ var cachedBlackColor,
<p>It also provides some class helper methods that
returns instances of commonly used colors.</p>
*/
@implementation CPColor : CPObject
@implementation CPColor : CPObject <CPTheme>
{
CPArray _components;
CPImage _patternImage;
CPString _cssString;
}
@global document
// MARK: -
// MARK: Theming
+ (CPString)defaultThemeClass
{
return "color";
}
+ (CPDictionary)themeAttributes
{
return @{
@"alternate-selected-control-color": [CPNull null],
@"secondary-selected-control-color": [CPNull null],
@"selected-text-background-color": [CPNull null],
@"selected-text-inactive-background-color": [CPNull null],
@"css-based": NO
};
}
// MARK: -
// MARK: Static methods
/*!
Creates a color in the RGB colorspace, with an alpha value.
@@ -241,7 +267,7 @@ var cachedBlackColor,
@return an initialized RGB color
*/
+ (CPColor)colorWithHexString:(string)hex
+ (CPColor)colorWithHexString:(CPString)hex
{
var rgba = hexToRGB(hex);
return rgba ? [[CPColor alloc] _initWithRGBA: rgba] : null;
@@ -436,14 +462,22 @@ var cachedBlackColor,
return cachedClearColor;
}
+ (CPColor)_cachedThemeColor
{
if (!cachedThemeColor)
cachedThemeColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
return cachedThemeColor;
}
+ (CPColor)alternateSelectedControlColor
{
return [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]];
return [[self _cachedThemeColor] valueForThemeAttribute:@"alternate-selected-control-color"];
}
+ (CPColor)secondarySelectedControlColor
{
return [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]];
return [[self _cachedThemeColor] valueForThemeAttribute:@"secondary-selected-control-color"];
}
/*!
@@ -467,6 +501,16 @@ var cachedBlackColor,
return [[CPColor alloc] _initWithCSSString: aString];
}
+ (CPColor)selectedTextBackgroundColor
{
return [[self _cachedThemeColor] valueForThemeAttribute:@"selected-text-background-color"] || [CPColor colorWithHexString:"99CCFF"];
}
+ (CPColor)_selectedTextBackgroundColorUnfocussed
{
return [[self _cachedThemeColor] valueForThemeAttribute:@"selected-text-inactive-background-color"] || [CPColor colorWithHexString:"CCCCCC"];
}
/* @ignore */
- (id)_initWithCSSString:(CPString)aString
{
@@ -489,6 +533,10 @@ var cachedBlackColor,
// use it (issue #1413.)
[self _initCSSStringFromComponents];
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
[self _loadThemeAttributes];
return self;
}
@@ -502,6 +550,10 @@ var cachedBlackColor,
_components = components;
[self _initCSSStringFromComponents];
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
[self _loadThemeAttributes];
}
return self;
@@ -509,7 +561,11 @@ var cachedBlackColor,
- (void)_initCSSStringFromComponents
{
var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
// Fix to avoid a problem when compiling a theme (missing the alpha component as theme compiler doesn't have CSS rgba capability)
var hasAlpha = YES;
#if PLATFORM(DOM)
hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
#endif
_cssString = (hasAlpha ? "rgba(" : "rgb(") +
parseInt(_components[0] * 255.0) + ", " +
@@ -528,6 +584,10 @@ var cachedBlackColor,
_patternImage = anImage;
_cssString = "url(\"" + [_patternImage filename] + "\")";
_components = [0.0, 0.0, 0.0, 1.0];
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
[self _loadThemeAttributes];
}
return self;
@@ -744,6 +804,9 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
var description = [super description],
patternImage = [self patternImage];
if ([self isCSSBased])
return description + "\n" + [self cssDictionary]+ "\nBefore:\n" + [self cssBeforeDictionary] + "\nAfter:\n" + [self cssAfterDictionary];
if (!patternImage)
return description + " " + [self cssString];
@@ -821,9 +884,221 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
@end
// MARK: -
// MARK: CSS Theming
// The code below adds support for CSS theming with 100% compatibility with current theming system.
// The idea is to extend CPColor (and CPImage) with CSS components and adapt low level UI components to
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
//
// To create a CPColor that uses CSS, simply use the new class method :
// + (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
// where beforeDictionary & afterDictionary are related to ::before & ::after pseudo-elements.
// If you don't need them, just use the simplified class method :
// + (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary
//
// Example :
// buttonCssColor = [CPColor colorWithCSSDictionary:@{
// @"background-color": A3ColorBackgroundWhite,
// @"border-color": A3ColorActiveBorder,
// @"border-style": @"solid",
// @"border-width": @"1px",
// @"border-radius": @"3px",
// @"box-sizing": @"border-box"
// }
// beforeDictionary:@{
// @"background-color": @"rgb(225,225,225)",
// @"bottom": @"3px",
// @"content": @"''",
// @"position": @"absolute",
// @"right": @"21px",
// @"top": @"3px",
// @"width": @"1px"
// }
// afterDictionary:@{
// @"content": @"''",
// @"bottom": @"3px",
// @"right": @"6px",
// @"top": @"1px",
// @"position": @"absolute",
// @"height": @"14px",
// @"width": @"9px",
// @"margin": @"2px 0px 2px 0px",
// @"background-image": @"url(%%packed.png)",
// @"background-position": @"0px -64px",
// @"background-repeat": @"no-repeat",
// @"background-size": @"100px 400px"
// }];
//
// Remark : Please note the special URL of the background image used in this example : url(%%packed.png)
// During theme loading, "%%" will be replaced by the path to the theme blend resources folder.
// Typically, a CSS theme will use some (rare) images all packed together in a single image resource (see packed.png in Aristo3 theme)
//
// Also, please note that if you don't use one of the CSS components, you can either set it to nil (best solution) or to an empty dictionary, like :
// aCssColor = [CPColor colorWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:@{ ... }];
//
// You can use -(BOOL)isCSSBased to determine how to cope with it in your code.
// -(BOOL)hasCSSDictionary, -(BOOL)hasCSSBeforeDictionary and -(BOOL)hasCSSAfterDictionary are convience methods you can use.
//
// Remark : +(void)restorePreviousCSSState and -(DOMElement)applyCSSColorForView are meant to be used by low level UI widgets (like CPView) to implement
// CSS theme support.
@implementation CPColor (CSSTheming)
{
CPDictionary _cssDictionary @accessors(property=cssDictionary);
CPDictionary _cssBeforeDictionary @accessors(property=cssBeforeDictionary);
CPDictionary _cssAfterDictionary @accessors(property=cssAfterDictionary);
}
+ (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary
{
return [[CPColor alloc] _initWithCSSDictionary:aDictionary beforeDictionary:nil afterDictionary:nil];
}
+ (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
{
return [[CPColor alloc] _initWithCSSDictionary:aDictionary beforeDictionary:beforeDictionary afterDictionary:afterDictionary];
}
- (id)_initWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
{
self = [super init];
if (self)
{
_cssDictionary = aDictionary;
_cssBeforeDictionary = beforeDictionary;
_cssAfterDictionary = afterDictionary;
_components = [0.0, 0.0, 0.0, 1.0];
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
[self _loadThemeAttributes];
}
return self;
}
- (BOOL)isCSSBased
{
return !!(_cssDictionary || _cssBeforeDictionary || _cssAfterDictionary);
}
- (BOOL)hasCSSDictionary
{
return ([_cssDictionary count] > 0);
}
- (BOOL)hasCSSBeforeDictionary
{
return ([_cssBeforeDictionary count] > 0);
}
- (BOOL)hasCSSAfterDictionary
{
return ([_cssAfterDictionary count] > 0);
}
+ (void)restorePreviousCSSState:(CPArrayRef)aPreviousStateRef forDOMElement:(DOMElement)aDOMElement
{
#if PLATFORM(DOM)
var aPreviousState = @deref(aPreviousStateRef);
for (var i = 0, count = aPreviousState.length; i < count; i++)
aDOMElement.style[aPreviousState[i][0]] = aPreviousState[i][1];
@deref(aPreviousStateRef) = @[];
#endif
}
- (DOMElement)applyCSSColorForView:(CPView)aView onDOMElement:(DOMElement)aDOMElement styleNode:(DOMElement)aStyleNode previousState:(CPArrayRef)aPreviousStateRef
{
#if PLATFORM(DOM)
var aPreviousState = @deref(aPreviousStateRef);
[_cssDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
[aPreviousState addObject:@[aKey, aDOMElement.style[aKey]]];
aDOMElement.style[aKey] = anObject;
}];
if ([self hasCSSBeforeDictionary] || [self hasCSSAfterDictionary])
{
// We need to create a unique class name
var styleClassName = @".CP" + [aView UID],
styleContent = @"";
if ([self hasCSSBeforeDictionary])
{
styleContent += styleClassName + @"::before { ";
[_cssBeforeDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
styleContent += aKey + ": " + anObject + "; ";
}];
styleContent += "} ";
}
if ([self hasCSSAfterDictionary])
{
styleContent += styleClassName + @"::after { ";
[_cssAfterDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
styleContent += aKey + ": " + anObject + "; ";
}];
styleContent += "} ";
}
var styleDescription = document.createTextNode(styleContent);
if (!aStyleNode)
{
aStyleNode = document.createElement("style");
aView._DOMElement.insertBefore(aStyleNode, aView._DOMElement.firstChild);
aStyleNode.appendChild(styleDescription);
}
else
{
aStyleNode.replaceChild(styleDescription, aStyleNode.firstChild);
}
[aView setDOMClassName:@"CP"+[aView UID]];
}
else
{
// no before/after so remove aStyleNode if existing
if (aStyleNode)
{
aView._DOMElement.removeChild(aStyleNode);
aStyleNode = nil;
}
}
// Return actualised values
@deref(aPreviousStateRef) = aPreviousState;
return aStyleNode;
#endif
}
@end
// MARK: -
/// @cond IGNORE
var CPColorComponentsKey = @"CPColorComponentsKey",
CPColorPatternImageKey = @"CPColorPatternImageKey";
var CPColorComponentsKey = @"CPColorComponentsKey",
CPColorPatternImageKey = @"CPColorPatternImageKey",
CPColorCssDictionaryKey = @"CPColorCssDictionaryKey",
CPColorCssBeforeDictionaryKey = @"CPColorCssBeforeDictionaryKey",
CPColorCssAfterDictionaryKey = @"CPColorCssAfterDictionaryKey";
/// @endcond
@implementation CPColor (CPCoding)
@@ -835,9 +1110,17 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
- (id)initWithCoder:(CPCoder)aCoder
{
if ([aCoder containsValueForKey:CPColorPatternImageKey])
return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
self = [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
else if ([aCoder containsValueForKey:CPColorCssDictionaryKey])
self = [self _initWithCSSDictionary:[aCoder decodeObjectForKey:CPColorCssDictionaryKey]
beforeDictionary:[aCoder decodeObjectForKey:CPColorCssBeforeDictionaryKey]
afterDictionary:[aCoder decodeObjectForKey:CPColorCssAfterDictionaryKey]];
else
self = [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
[self _decodeThemeObjectsWithCoder:aCoder];
return self;
}
/*!
@@ -848,8 +1131,16 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
{
if (_patternImage)
[aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey];
else if (_cssDictionary)
{
[aCoder encodeObject:_cssDictionary forKey:CPColorCssDictionaryKey];
[aCoder encodeObject:_cssBeforeDictionary forKey:CPColorCssBeforeDictionaryKey];
[aCoder encodeObject:_cssAfterDictionary forKey:CPColorCssAfterDictionaryKey];
}
else
[aCoder encodeObject:_components forKey:CPColorComponentsKey];
[self _encodeThemeObjectsWithCoder:aCoder];
}
@end
@@ -1186,7 +1477,8 @@ var patternColorsFromPattern = function(pattern, attributes, imageFactory)
bottomHeight,
centerWidthHeight,
centerIsNil,
numParts;
numParts,
isVertical;
// positions are mandatory
if (pattern.indexOf("{position}") < 0)
+85 -39
View File
@@ -51,6 +51,7 @@ CPColorPickerViewWidth = 265;
CPColorPickerViewHeight = 370;
CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
CPColorDragType = CPColorPboardType;
var PREVIEW_HEIGHT = 20.0,
TOOLBAR_HEIGHT = 32.0,
@@ -71,7 +72,6 @@ var SharedColorPanel = nil,
*/
@implementation CPColorPanel : CPPanel
{
_CPColorPanelToolbar _toolbar;
_CPColorPanelSwatches _swatchView;
_CPColorPanelPreview _previewView;
@@ -130,8 +130,6 @@ var SharedColorPanel = nil,
if (self)
{
//[[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]];
[self setTitle:@"Color Panel"];
[self setLevel:CPFloatingWindowLevel];
@@ -150,20 +148,37 @@ var SharedColorPanel = nil,
*/
- (void)setColor:(CPColor)aColor
{
if ([_color isEqual:aColor])
return;
_color = aColor;
[_previewView setBackgroundColor:_color];
[CPApp sendAction:@selector(changeColor:) to:nil from:self];
// Check if the color change originated from user interaction inside the panel itself.
// We only broadcast `changeColor:` if the user picked a color via the panel's UI.
// If an external CPColorWell called `setColor:` programmatically, broadcasting it
// back down the responder chain would incorrectly change the previous First Responder.
var currentEvent = [CPApp currentEvent],
isFromPanel = currentEvent && ([currentEvent window] === self);
if (_target && _action)
[CPApp sendAction:_action to:_target from:self];
if (isFromPanel)
{
// Push color via Responder Chain (targets First Responder, i.e., the active CPColorWell)
[CPApp sendAction:@selector(changeColor:) to:nil from:self];
if (_target && _action)
[CPApp sendAction:_action to:_target from:self];
}
[[CPNotificationCenter defaultCenter]
postNotificationName:CPColorPanelColorDidChangeNotification
object:self];
[_activePicker setColor:_color];
[_opacitySlider setFloatValue:[_color alphaComponent]];
if (_activePicker)
[_activePicker setColor:_color];
if (_opacitySlider)
[_opacitySlider setFloatValue:[_color alphaComponent]];
}
/*!
@@ -175,7 +190,7 @@ var SharedColorPanel = nil,
{
[self setColor:aColor];
if (bool)
if (bool && _activePicker)
[_activePicker setColor:_color];
}
@@ -333,7 +348,7 @@ var SharedColorPanel = nil,
buttonForLater = button;
}
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
// Preview
var previewBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10, CGRectGetWidth(bounds) - 86, PREVIEW_HEIGHT)];
_previewView = [[_CPColorPanelPreview alloc] initWithFrame:CGRectInset([previewBox bounds], 2.0, 2.0)];
@@ -351,7 +366,7 @@ var SharedColorPanel = nil,
[_previewLabel setTextColor:[CPColor blackColor]];
[_previewLabel setAlignment:CPRightTextAlignment];
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
// Swatches
var swatchBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5, CGRectGetWidth(bounds) - 86, SWATCH_HEIGHT + 2.0)];
[swatchBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]];
@@ -392,8 +407,6 @@ var SharedColorPanel = nil,
[contentView addSubview:opacityLabel];
[contentView addSubview:_opacitySlider];
_target = nil;
_action = nil;
_activePicker = nil;
[_previewView setBackgroundColor:_color];
@@ -413,8 +426,6 @@ var SharedColorPanel = nil,
@end
CPColorDragType = "CPColorDragType";
var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
/* @ignore */
@@ -424,12 +435,14 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
CPColor _dragColor;
CPColorPanel _colorPanel;
CPCookie _swatchCookie;
CGPoint _mouseDownPoint;
}
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
_mouseDownPoint = CGPointMake(0, 0);
[self setBackgroundColor:[CPColor grayColor]];
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
@@ -443,7 +456,6 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
for (var i = 0; i < 50; i++)
{
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
var view = [[CPView alloc] initWithFrame:CGRectMake(13 * i + 1, 1, 12, 12)],
fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)];
@@ -484,19 +496,17 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
];
}
var cookieValue = eval(cookieValue),
result = [];
var cookieValue = JSON.parse(cookieValue);
for (var i = 0; i < cookieValue.length; i++)
result.push([CPColor colorWithHexString:cookieValue[i]]);
return result;
return [cookieValue arrayByApplyingBlock:function(value)
{
return [CPColor colorWithHexString:value];
}];
}
- (CPArray)saveColorList
{
var result = [];
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
for (var i = 0; i < _swatches.length; i++)
result.push([[[_swatches[i] subviews][0] backgroundColor] hexString]);
@@ -523,39 +533,44 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
- (void)setColor:(CPColor)aColor atIndex:(int)index
{
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
[[_swatches[index] subviews][0] setBackgroundColor:aColor];
[self saveColorList];
}
- (void)mouseDown:(CPEvent)anEvent
{
_mouseDownPoint = [anEvent locationInWindow];
}
- (void)mouseUp:(CPEvent)anEvent
{
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
bounds = [self bounds];
if (!CGRectContainsPoint(bounds, point) || point.x > [self bounds].size.width - 1 || point.x < 1)
return NO;
return;
[_colorPanel setColor:[self colorAtIndex:FLOOR(point.x / 13)] updatePicker:YES];
}
- (void)mouseDragged:(CPEvent)anEvent
{
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
var windowPoint = [anEvent locationInWindow];
// Prevent accidental drags from rapid clicking causing small micro-movements
if (ABS(windowPoint.x - _mouseDownPoint.x) < 3 && ABS(windowPoint.y - _mouseDownPoint.y) < 3)
return;
var point = [self convertPoint:windowPoint fromView:nil];
if (point.x > [self bounds].size.width - 1 || point.x < 1)
return NO;
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
return;
var swatch = _swatches[FLOOR(point.x / 13)];
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
_dragColor = [[swatch subviews][0] backgroundColor];
var bounds = CGRectMakeCopy([swatch bounds]);
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
var dragView = [[CPView alloc] initWithFrame:bounds],
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
@@ -564,11 +579,15 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
[dragView addSubview:dragFillView];
var pasteboard = [CPPasteboard pasteboardWithName:CPDragPboard];
[pasteboard declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
[pasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:CPColorDragType];
[self dragView:dragView
at:CGPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
offset:CGPointMake(0.0, 0.0)
event:anEvent
pasteboard:nil
pasteboard:pasteboard
source:self
slideBack:YES];
}
@@ -579,7 +598,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType];
}
- (void)performDragOperation:(id /*<CPDraggingInfo>*/)aSender
- (BOOL)performDragOperation:(id /*<CPDraggingInfo>*/)aSender
{
var location = [self convertPoint:[aSender draggingLocation] fromView:nil],
pasteboard = [aSender draggingPasteboard],
@@ -589,6 +608,13 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
return NO;
[self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex:FLOOR(location.x / 13)];
return YES;
}
- (unsigned)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
return CPDragOperationCopy;
}
@end
@@ -597,11 +623,13 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
@implementation _CPColorPanelPreview : CPView
{
CPColorPanel _colorPanel;
CGPoint _mouseDownPoint;
}
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
_mouseDownPoint = CGPointMake(0, 0);
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
@@ -618,7 +646,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
return _colorPanel;
}
- (void)performDragOperation:(id /*<CPDraggingInfo>*/)aSender
- (BOOL)performDragOperation:(id /*<CPDraggingInfo>*/)aSender
{
var pasteboard = [aSender draggingPasteboard];
@@ -627,6 +655,8 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]];
[_colorPanel setColor:color updatePicker:YES];
return YES;
}
- (BOOL)isOpaque
@@ -634,15 +664,22 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
return YES;
}
- (void)mouseDown:(CPEvent)anEvent
{
_mouseDownPoint = [anEvent locationInWindow];
}
- (void)mouseDragged:(CPEvent)anEvent
{
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
var windowPoint = [anEvent locationInWindow];
if (ABS(windowPoint.x - _mouseDownPoint.x) < 3 && ABS(windowPoint.y - _mouseDownPoint.y) < 3)
return;
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
var point = [self convertPoint:windowPoint fromView:nil];
var bounds = CGRectMake(0, 0, 15, 15);
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
var dragView = [[CPView alloc] initWithFrame:bounds],
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
@@ -651,11 +688,15 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
[dragView addSubview:dragFillView];
var pasteboard = [CPPasteboard pasteboardWithName:CPDragPboard];
[pasteboard declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
[pasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:CPColorDragType];
[self dragView:dragView
at:CGPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
offset:CGPointMake(0.0, 0.0)
event:anEvent
pasteboard:nil
pasteboard:pasteboard
source:self
slideBack:YES];
}
@@ -666,6 +707,11 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:aType];
}
- (unsigned)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
return CPDragOperationCopy;
}
@end
@import "CPColorPicker.j"
+3 -2
View File
@@ -125,7 +125,8 @@
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
[_brightnessSlider setValue:15.0 forThemeAttribute:@"track-width"];
[_brightnessSlider setValue:[CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPColorPicker class]] pathForResource:@"brightness_bar.png"]]] forThemeAttribute:@"track-color"];
var brightnessImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPColorPicker class]] pathForResource:@"brightness_bar.png"] size:CGSizeMake(272, 20)];
[_brightnessSlider setValue:[CPColor colorWithPatternImage:brightnessImage] forThemeAttribute:@"track-color"];
[_brightnessSlider setMinValue:0.0];
[_brightnessSlider setMaxValue:100.0];
@@ -246,7 +247,7 @@
_blackWheelImage = new Image();
_blackWheelImage.src = path;
_blackWheelImage.style.opacity = "0";
_blackWheelImage.style.filter = "alpha(opacity=0)"
_blackWheelImage.style.filter = "alpha(opacity=0)";
_blackWheelImage.style.position = "absolute";
_DOMElement.appendChild(_wheelImage);
+200 -134
View File
@@ -25,7 +25,7 @@
@import "CPView.j"
@import "CPColor.j"
@import "CPColorPanel.j"
@import "CPPasteboard.j"
var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiveNotification";
@@ -39,10 +39,10 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
*/
@implementation CPColorWell : CPControl
{
BOOL _active;
BOOL _bordered;
CPColor _color;
BOOL _isChangingColorFromPanel; // Guard flag to prevent recursion
CGPoint _mouseDownPoint;
}
+ (Class)_binderClassForBinding:(CPString)aBinding
@@ -77,52 +77,54 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
[theBinding reverseSetValueFor:@"color"];
}
- (BOOL)isFirstResponder
{
return [[self window] firstResponder] === self;
}
- (BOOL)acceptsFirstResponder
{
return [self isEnabled];
}
- (void)activate:(BOOL)shouldBeExclusive
{
[[self window] makeFirstResponder:self];
[[CPColorPanel sharedColorPanel] orderFront:self];
}
- (BOOL)isActive
{
return [self isFirstResponder] && [self isEnabled];
}
/*!
Deactivates the color well.
*/
- (void)deactivate
{
if ([self isFirstResponder])
[[self window] makeFirstResponder:nil];
}
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
if (self)
{
_active = NO;
_color = [CPColor whiteColor];
_mouseDownPoint = CGPointMakeZero();
[self setBordered:YES];
[self registerForDraggedTypes:[CPArray arrayWithObject:CPColorPboardType]];
}
return self;
}
- (void)_registerNotifications
{
var defaultCenter = [CPNotificationCenter defaultCenter];
[defaultCenter
addObserver:self
selector:@selector(colorWellDidBecomeExclusive:)
name:_CPColorWellDidBecomeExclusiveNotification
object:nil];
[defaultCenter
addObserver:self
selector:@selector(colorPanelWillClose:)
name:CPWindowWillCloseNotification
object:[CPColorPanel sharedColorPanel]];
}
- (void)_removeNotifications
{
var defaultCenter = [CPNotificationCenter defaultCenter];
[defaultCenter
removeObserver:self
name:_CPColorWellDidBecomeExclusiveNotification
object:nil];
[defaultCenter
removeObserver:self
name:CPWindowWillCloseNotification
object:[CPColorPanel sharedColorPanel]];
}
// MARK: -
// MARK: Draw
/*!
Sets whether the color well is bordered.
@@ -143,7 +145,8 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
return [self hasThemeState:CPThemeStateBordered];
}
// Managing Color From Color Wells
// MARK: -
// MARK: Managing Color
/*!
Returns the color well's current color.
@@ -158,12 +161,17 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
*/
- (void)setColor:(CPColor)aColor
{
if (_color == aColor)
if ([_color isEqual:aColor])
return;
_color = aColor;
[self setNeedsLayout];
// Only push back to the panel if we initiated the change (not if the panel pushed it to us)
// AND if we are the current focus.
if (!_isChangingColorFromPanel && [self isFirstResponder])
[[CPColorPanel sharedColorPanel] setColor:_color];
}
/*!
@@ -175,94 +183,175 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
[self setColor:[aSender color]];
}
// Activating and Deactivating Color Wells
/*!
Standard action method sent by CPColorPanel via the Responder Chain.
*/
- (void)changeColor:(id)aSender
{
if ([aSender isKindOfClass:[CPColorPanel class]])
{
_isChangingColorFromPanel = YES;
[self setColor:[aSender color]];
_isChangingColorFromPanel = NO;
// Forward the action to our target (e.g. controller)
[self sendAction:[self action] to:[self target]];
}
}
// MARK: -
// MARK: Activating and Deactivating
/*!
Activates the color well, displays the color panel, and makes the panel's current color the same as its own.
If exclusive is \c YES, deactivates any other CPColorWells. \c NO, keeps them active.
@param shouldBeExclusive whether other color wells should be deactivated.
*/
- (void)activate:(BOOL)shouldBeExclusive
- (BOOL)becomeFirstResponder
{
if (shouldBeExclusive)
// FIXME: make this queue!
[[CPNotificationCenter defaultCenter]
postNotificationName:_CPColorWellDidBecomeExclusiveNotification
object:self];
[self setThemeState:CPThemeStateFirstResponder];
var panel = [CPColorPanel sharedColorPanel];
// EXPLICITLY set ourselves as the target.
// This ensures that when the user clicks the panel (making Panel key),
// the panel still knows to send messages back to us.
[panel setTarget:self];
[panel setAction:@selector(changeColor:)];
// Sync panel to our current color
[panel setColor:_color];
if ([self isActive])
return;
_active = YES;
[[CPNotificationCenter defaultCenter]
addObserver:self
selector:@selector(colorPanelDidChangeColor:)
name:CPColorPanelColorDidChangeNotification
object:[CPColorPanel sharedColorPanel]];
}
/*!
Deactivates the color well.
*/
- (void)deactivate
{
if (![self isActive])
return;
_active = NO;
[[CPNotificationCenter defaultCenter]
removeObserver:self
name:CPColorPanelColorDidChangeNotification
object:[CPColorPanel sharedColorPanel]];
}
/*!
Returns \c YES if the color well is active.
*/
- (BOOL)isActive
{
return _active;
}
- (void)colorPanelDidChangeColor:(CPNotification)aNotification
{
[self takeColorFrom:[aNotification object]];
[self sendAction:[self action] to:[self target]];
}
- (void)colorWellDidBecomeExclusive:(CPNotification)aNotification
{
if (self != [aNotification object])
[self deactivate];
}
- (void)colorPanelWillClose:(CPNotification)aNotification
{
[self deactivate];
}
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
{
[self highlight:NO];
if (!mouseIsUp || !CGRectContainsPoint([self bounds], aPoint) || ![self isEnabled])
[[CPNotificationCenter defaultCenter] postNotificationName:_CPColorWellDidBecomeExclusiveNotification object:self];
return YES;
}
- (BOOL)resignFirstResponder
{
[self unsetThemeState:CPThemeStateFirstResponder];
var panel = [CPColorPanel sharedColorPanel];
// Clean up if we were the target
if ([panel target] == self)
[panel setTarget:nil];
return YES;
}
// MARK: -
// MARK: Event Handling
- (void)mouseDown:(CPEvent)anEvent
{
if (![self isEnabled])
return;
_mouseDownPoint = [anEvent locationInWindow];
[self activate:YES];
var colorPanel = [CPColorPanel sharedColorPanel];
[colorPanel setColor:_color];
[colorPanel orderFront:self];
}
- (void)mouseDragged:(CPEvent)anEvent
{
if (![self isEnabled])
return;
var windowPoint = [anEvent locationInWindow];
// Prevent accidental drags from rapid clicking causing small micro-movements
if (ABS(windowPoint.x - _mouseDownPoint.x) < 3 && ABS(windowPoint.y - _mouseDownPoint.y) < 3)
return;
var bounds = CGRectMake(0, 0, 15, 15);
var dragView = [[CPView alloc] initWithFrame:bounds],
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
[dragView setBackgroundColor:[CPColor blackColor]];
[dragFillView setBackgroundColor:_color];
[dragView addSubview:dragFillView];
var pasteboard = [CPPasteboard pasteboardWithName:CPDragPboard];
[pasteboard declareTypes:[CPArray arrayWithObject:CPColorPboardType] owner:self];
[pasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_color] forType:CPColorPboardType];
var point = [self convertPoint:windowPoint fromView:nil];
[self dragView:dragView
at:CGPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
offset:CGPointMake(0.0, 0.0)
event:anEvent
pasteboard:pasteboard
source:self
slideBack:YES];
}
// MARK: -
// MARK: Drag and Drop
- (void)draggingEntered:(id)sender
{
var pasteboard = [sender draggingPasteboard];
if ([[pasteboard types] containsObject:CPColorPboardType])
{
[self setThemeState:CPThemeStateHighlighted];
return CPDragOperationCopy;
}
return CPDragOperationNone;
}
- (void)draggingExited:(id)sender
{
[self unsetThemeState:CPThemeStateHighlighted];
}
- (BOOL)performDragOperation:(id)sender
{
var pasteboard = [sender draggingPasteboard];
if ([[pasteboard types] containsObject:CPColorPboardType])
{
var data = [pasteboard dataForType:CPColorPboardType],
newColor = [CPKeyedUnarchiver unarchiveObjectWithData:data];
if (newColor && [newColor isKindOfClass:[CPColor class]])
{
[self setColor:newColor];
[self sendAction:[self action] to:[self target]];
// Activate nicely after drop
[self activate:YES];
[self unsetThemeState:CPThemeStateHighlighted];
return YES;
}
}
return NO;
}
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
{
if (aType == CPColorPboardType)
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_color] forType:aType];
}
- (unsigned)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
return CPDragOperationCopy;
}
// MARK: -
// MARK: Layout
- (CGRect)contentRectForBounds:(CGRect)bounds
{
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
return CGRectInsetByInset(bounds, contentInset);
}
@@ -316,7 +405,6 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
[contentView setBackgroundColor:_color];
var contentBorderView = [self layoutEphemeralSubviewNamed:@"content-border-view"
@@ -326,28 +414,6 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
[contentBorderView setBackgroundColor:[self currentValueForThemeAttribute:@"content-border-color"]];
}
#pragma mark -
#pragma mark Observers method
- (void)_addObservers
{
if (_isObserving)
return;
[super _addObservers];
[self _registerNotifications];
}
- (void)_removeObservers
{
if (!_isObserving)
return;
[super _removeObservers];
[self _removeNotifications];
}
@end
@implementation CPColorWellValueBinder : CPBinder
@@ -396,9 +462,9 @@ var CPColorWellColorKey = "CPColorWellColorKey",
if (self)
{
_active = NO;
_color = [aCoder decodeObjectForKey:CPColorWellColorKey];
[self setBordered:[aCoder decodeBoolForKey:CPColorWellBorderedKey]];
[self registerForDraggedTypes:[CPArray arrayWithObject:CPColorPboardType]];
}
return self;
+27 -19
View File
@@ -74,7 +74,7 @@ var CPComboBoxTextSubview = @"text",
BOOL _usesDataSource;
CGSize _intercellSpacing;
CPArray _items;
id<CPComboBoxDataSource> _dataSource;
id <CPComboBoxDataSource> _dataSource;
CPInteger _implementedDelegateComboBoxMethods;
CPString _selectedStringValue;
float _itemHeight;
@@ -133,7 +133,7 @@ var CPComboBoxTextSubview = @"text",
[self setThemeState:CPComboBoxStateButtonBordered];
}
#pragma mark Setting Display Attributes
// MARK: Setting Display Attributes
- (BOOL)hasVerticalScroller
{
@@ -214,7 +214,7 @@ var CPComboBoxTextSubview = @"text",
_numberOfVisibleItems = MAX(visibleItems, 1);
}
#pragma mark Setting a Delegate
// MARK: Setting a Delegate
- (id <CPComboBoxDelegate>)delegate
{
@@ -254,7 +254,7 @@ var CPComboBoxTextSubview = @"text",
[super setDelegate:aDelegate];
}
#pragma mark Setting a Data Source
// MARK: Setting a Data Source
- (id <CPComboBoxDataSource>)dataSource
{
@@ -305,7 +305,7 @@ var CPComboBoxTextSubview = @"text",
[self reloadData];
}
#pragma mark Working with an Internal List
// MARK: Working with an Internal List
- (void)addItemsWithObjectValues:(CPArray)objects
{
@@ -378,7 +378,7 @@ var CPComboBoxTextSubview = @"text",
return _items.length;
}
#pragma mark Manipulating the Displayed List
// MARK: Manipulating the Displayed List
/*!
Returns the delegate to be used when creating the pop up list.
@@ -564,7 +564,7 @@ var CPComboBoxTextSubview = @"text",
var selectedStringValue = [_listDelegate selectedStringValue];
if (selectedStringValue === nil)
if (selectedStringValue == nil)
return NO;
else
_selectedStringValue = selectedStringValue;
@@ -594,7 +594,7 @@ var CPComboBoxTextSubview = @"text",
[self sendAction:[self action] to:[self target]];
}
#pragma mark Manipulating the Selection
// MARK: Manipulating the Selection
- (void)deselectItemAtIndex:(int)index
{
@@ -646,7 +646,7 @@ var CPComboBoxTextSubview = @"text",
[self selectItemAtIndex:index];
}
#pragma mark Completing the Text Field
// MARK: Completing the Text Field
- (BOOL)completes
{
@@ -693,7 +693,7 @@ var CPComboBoxTextSubview = @"text",
_forceSelection = !!flag;
}
#pragma mark CPTextField Delegate Methods and Overrides
// MARK: CPTextField Delegate Methods and Overrides
/*! @ignore */
- (BOOL)sendAction:(SEL)anAction to:(id)anObject
@@ -843,7 +843,17 @@ var CPComboBoxTextSubview = @"text",
// In FireFox this needs to be done in setTimeout, otherwise there is no caret
// We have to save the input element now, when we lose focus it will change.
var element = [self _inputElement];
window.setTimeout(function() { element.focus(); }, 0);
[[CPRunLoop mainRunLoop] performBlock:function()
{
// This will prevent to jump to the focused element
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
element.focus();
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
#endif
return NO;
@@ -883,7 +893,7 @@ var CPComboBoxTextSubview = @"text",
[_listDelegate setAlignment:alignment];
}
#pragma mark Pop Up Button Layout
// MARK: Pop Up Button Layout
- (CGRect)popupButtonRectForBounds:(CGRect)bounds
{
@@ -928,7 +938,7 @@ var CPComboBoxTextSubview = @"text",
relativeToEphemeralSubviewNamed:@"content-view"];
}
#pragma mark Internal Helpers
// MARK: Internal Helpers
/*! @ignore */
- (void)_dataSourceWarningForMethod:(SEL)cmd condition:(CPString)flag
@@ -1006,8 +1016,8 @@ var CPComboBoxTextSubview = @"text",
}
#pragma mark -
#pragma mark Observers method
// MARK: -
// MARK: Observers method
- (void)_addObservers
{
@@ -1132,11 +1142,9 @@ var CPComboBoxTextSubview = @"text",
// Directly nuke _items, [_items removeAll] will trigger an extra call to setContent
_items = [];
var values = [];
[anArray enumerateObjectsUsingBlock:function(object)
var values = [anArray arrayByApplyingBlock:function(object)
{
values.push([object description]);
return [object description];
}];
[self addItemsWithObjectValues:values];
+86 -16
View File
@@ -26,11 +26,13 @@
// Browser Engines
CPUnknownBrowserEngine = 0;
CPGeckoBrowserEngine = 1;
CPInternetExplorerBrowserEngine = 2;
CPKHTMLBrowserEngine = 3;
CPOperaBrowserEngine = 4;
CPWebKitBrowserEngine = 5;
CPGeckoBrowserEngine = 1 << 0;
CPInternetExplorerBrowserEngine = 1 << 1;
CPKHTMLBrowserEngine = 1 << 2;
CPOperaBrowserEngine = 1 << 3;
CPWebKitBrowserEngine = 1 << 4; // Safari + Chrome
CPBlinkBrowserEngine = 1 << 5; // Recent Chrome
CPEdgeBrowserEngine = 1 << 6;
// Operating Systems
CPMacOperatingSystem = 0;
@@ -80,18 +82,28 @@ CPFileAPIFeature = 31;
CPAltEnterTextAreaFeature = 32;
CPCSSAnimationFeature = 33;
CPBackspaceTriggersPageBack = 34;
/*
When an absolutely positioned div (CPView) with an absolutely positioned canvas in it (CPView with drawRect:) moves things on top of the canvas (subviews) don't redraw correctly. E.g. if you have a bunch of text fields in a CPBox in a sheet which animates in, some of the text fields might not be visible because the CPBox has a canvas at the bottom and the box moved form offscreen to onscreen. This bug is probably very related: https://bugs.webkit.org/show_bug.cgi?id=67203
*/
*/
CPCanvasParentDrawErrorsOnMovementBug = 1 << 0;
// The paste event is only sent if an input or textarea has focus.
CPJavaScriptPasteRequiresEditableTarget = 1 << 1;
CPJavaScriptPasteRequiresEditableTarget = 1 << 1;
// Redirecting the focus of the browser on keydown to an input for Cmd-V or Ctrl-V makes the paste fail.
CPJavaScriptPasteCantRefocus = 1 << 2;
/*
Safari calculates incorrect text size unless you set the canvas font even if it is already set
You can see the bug after disabling the workaround and opening any panel while typing.
You can use the font panel in the manual test for CPTextView.
Look out for a displaced cursor, i.e. after typing letters of small width, such as the 'i'.
https://bugs.webkit.org/show_bug.cgi?id=150224
*/
CPTextSizingAlwaysNeedsSetFontBug = 1 << 3;
var USER_AGENT = "",
PLATFORM_ENGINE = CPUnknownBrowserEngine,
@@ -110,15 +122,15 @@ if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
// Opera
if (typeof window !== "undefined" && window.opera)
{
PLATFORM_ENGINE = CPOperaBrowserEngine;
PLATFORM_ENGINE |= CPOperaBrowserEngine;
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
}
// Internet Explorer
else if (typeof window !== "undefined" && window.attachEvent) // Must follow Opera check.
else if (typeof window !== "undefined" && (window.attachEvent || (!(window.ActiveXObject) && "ActiveXObject" in window))) // Must follow Opera check.
{
PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
PLATFORM_ENGINE |= CPInternetExplorerBrowserEngine;
// Features we can only be sure of with IE (no known independent tests)
PLATFORM_FEATURES[CPVMLFeature] = YES;
@@ -136,10 +148,32 @@ else if (typeof window !== "undefined" && window.attachEvent) // Must follow Ope
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
}
// WebKit
// Edge
else if (USER_AGENT.indexOf("Edge/") != -1)
{
PLATFORM_ENGINE |= CPEdgeBrowserEngine;
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
PLATFORM_FEATURES[CPHTMLContentEditableFeature] = YES;
PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = NO;
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
var versionStart = USER_AGENT.indexOf("Edge/") + "Edge/".length,
versionEnd = USER_AGENT.indexOf(" ", versionStart),
versionString = USER_AGENT.substring(versionStart, versionEnd),
versionDivision = versionString.indexOf('.'),
majorVersion = parseInt(versionString.substring(0, versionDivision)),
minorVersion = parseInt(versionString.substr(versionDivision + 1));
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
}
// Safari + Chrome (WebKit and Blink)
else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
{
PLATFORM_ENGINE = CPWebKitBrowserEngine;
PLATFORM_ENGINE |= CPWebKitBrowserEngine;
// Features we can only be sure of with WebKit (no known independent tests)
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
@@ -179,7 +213,10 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
// https://bugs.webkit.org/show_bug.cgi?id=39689
PLATFORM_BUGS |= CPJavaScriptPasteCantRefocus;
PLATFORM_BUGS |= CPTextSizingAlwaysNeedsSetFontBug;
}
else if ((window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window)
PLATFORM_ENGINE |= CPBlinkBrowserEngine;
// Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
if (majorVersion > 533)
@@ -189,15 +226,16 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
// KHTML
else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
{
PLATFORM_ENGINE = CPKHTMLBrowserEngine;
PLATFORM_ENGINE |= CPKHTMLBrowserEngine;
}
// Gecko
else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
{
PLATFORM_ENGINE = CPGeckoBrowserEngine;
PLATFORM_ENGINE |= CPGeckoBrowserEngine;
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
PLATFORM_FEATURES[CPBackspaceTriggersPageBack] = YES;
var index = USER_AGENT.indexOf("Firefox"),
version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
@@ -208,6 +246,9 @@ else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
if (version < 3.0)
PLATFORM_FEATURES[CPJavaScriptMouseWheelValues_8_15] = YES;
if (version >= 66)
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
// Some day this might be fixed and should be version prefixed. No known fixed version yet.
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
@@ -286,7 +327,7 @@ function CPPlatformHasBug(aBug)
function CPBrowserIsEngine(anEngine)
{
return PLATFORM_ENGINE === anEngine;
return PLATFORM_ENGINE & anEngine;
}
function CPBrowserIsOperatingSystem(anOperatingSystem)
@@ -322,6 +363,14 @@ else
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
}
/*!
Sets a feature with the given value.
*/
function CPSetPlatformFeature(aFeature, aBool)
{
PLATFORM_FEATURES[aFeature] = aBool;
}
/*!
Return the properly prefixed JS property for the given name. E.g. in a webkit browser,
CPBrowserStyleProperty('transition') -> WebkitTransition
@@ -369,6 +418,18 @@ function CPBrowserStyleProperty(aProperty)
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transform']] || nil;
break;
case 'animationend':
var candidates = {
'WebkitAnimation' : 'webkitAnimationEnd',
'MozAnimation' : 'animationend',
'OAnimation' : 'oAnimationEnd',
'msAnimation' : 'MSAnimationEnd',
'animation' : 'animationend'
};
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['animation']] || nil;
break;
default:
var prefixes = ["Webkit", "Moz", "O", "ms"],
strippedProperty = aProperty.split('-').join(' '),
@@ -438,3 +499,12 @@ function CPBrowserCSSProperty(aProperty)
return browserProperty.toLowerCase();
}
function CPBrowserBackingStorePixelRatio(context)
{
return context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
}
+105 -29
View File
@@ -23,10 +23,12 @@
@import <Foundation/CPFormatter.j>
@import <Foundation/CPTimer.j>
@import "CPFont.j"
@import "CPShadow.j"
@import "CPView.j"
@import "CPText.j"
@import "CPKeyValueBinding.j"
@import "CPTrackingArea.j"
@class CPFont
@global CPApp
@@ -42,18 +44,14 @@
@end
@typedef CPTextAlignment
CPLeftTextAlignment = 0;
CPRightTextAlignment = 1;
CPCenterTextAlignment = 2;
CPJustifiedTextAlignment = 3;
CPNaturalTextAlignment = 4;
@typedef CPControlSize
CPRegularControlSize = 0;
CPSmallControlSize = 1;
CPMiniControlSize = 2;
// To get the theme state corresponding to a control size, use CPControlSizeThemeStates[controlSize]
CPControlSizeThemeStates = @[CPThemeStateControlSizeRegular, CPThemeStateControlSizeSmall, CPThemeStateControlSizeMini];
@typedef CPLineBreakMode
CPLineBreakByWordWrapping = 0;
CPLineBreakByCharWrapping = 1;
@@ -126,6 +124,8 @@ var CPControlBlackColor = [CPColor blackColor];
CGPoint _previousTrackingLocation;
CPControlSize _controlSize;
CPWritingDirection _baseWritingDirection @accessors(property=baseWritingDirection);
}
+ (CPDictionary)themeAttributes
@@ -135,14 +135,13 @@ var CPControlBlackColor = [CPColor blackColor];
@"vertical-alignment": CPTopVerticalTextAlignment,
@"line-break-mode": CPLineBreakByClipping,
@"text-color": [CPColor blackColor],
@"font": [CPFont systemFontOfSize:CPFontCurrentSystemSize],
@"font": [CPNull null],
@"text-shadow-color": [CPNull null],
@"text-shadow-offset": CGSizeMakeZero(),
@"image-position": CPImageLeft,
@"image-scaling": CPScaleToFit,
@"min-size": CGSizeMakeZero(),
@"max-size": CGSizeMake(-1.0, -1.0),
@"nib2cib-adjustment-frame": CGRectMakeZero()
@"max-size": CGSizeMake(-1.0, -1.0)
};
}
@@ -173,7 +172,7 @@ var CPControlBlackColor = [CPColor blackColor];
}
/*!
Reverse set the binding iff the CPContinuouslyUpdatesValueBindingOption is set.
Reverse set the binding if the CPContinuouslyUpdatesValueBindingOption is set.
*/
- (void)_continuouslyReverseSetBinding
{
@@ -200,14 +199,16 @@ var CPControlBlackColor = [CPColor blackColor];
{
_sendActionOn = CPLeftMouseUpMask;
_trackingMouseDownFlags = 0;
[self setControlSize:CPThemeStateControlSizeRegular];
[self updateTrackingAreas];
}
return self;
}
#pragma mark -
#pragma mark Control Size
// MARK: -
// MARK: Control Size
/*!
Returns the control's control size
@@ -240,7 +241,7 @@ var CPControlBlackColor = [CPColor blackColor];
*/
- (ThemeState)_controlSizeThemeState
{
switch(_controlSize)
switch (_controlSize)
{
case CPSmallControlSize:
return CPThemeStateControlSizeSmall;
@@ -285,7 +286,7 @@ var CPControlBlackColor = [CPColor blackColor];
}
#pragma mark -
// MARK: -
/*!
Sets the receiver's target action.
@@ -625,15 +626,15 @@ var CPControlBlackColor = [CPColor blackColor];
*/
- (CPString)stringValue
{
if (_formatter && _value !== undefined)
if (_formatter && _value != nil)
{
var formattedValue = [self hasThemeState:CPThemeStateEditing] ? [_formatter editingStringForObjectValue:_value] : [_formatter stringForObjectValue:_value];
if (formattedValue !== nil && formattedValue !== undefined)
if (formattedValue != nil)
return formattedValue;
}
return (_value === undefined || _value === nil) ? @"" : String(_value);
return _value == nil ? @"" : String(_value);
}
/*!
@@ -642,7 +643,7 @@ var CPControlBlackColor = [CPColor blackColor];
- (void)setStringValue:(CPString)aString
{
// Cocoa raises an invalid parameter assertion and returns if you pass nil.
if (aString === nil || aString === undefined)
if (aString == nil)
{
CPLog.warn("nil or undefined sent to CPControl -setStringValue");
return;
@@ -808,7 +809,7 @@ var CPControlBlackColor = [CPColor blackColor];
CPBottomVerticalTextAlignment
</pre>
*/
- (void)setVerticalAlignment:(CPTextVerticalAlignment)alignment
- (void)setVerticalAlignment:(CPVerticalTextAlignment)alignment
{
[self setValue:alignment forThemeAttribute:@"vertical-alignment"];
}
@@ -853,7 +854,12 @@ var CPControlBlackColor = [CPColor blackColor];
*/
- (void)setTextColor:(CPColor)aColor
{
[self setValue:aColor forThemeAttribute:@"text-color"];
[self setValue:aColor forThemeAttribute:@"text-color" inState:[self themeState]];
}
- (void)setTextColor:(CPColor)aColor inThemeStates:(CPArray)themeStates
{
[self setValue:aColor forThemeAttribute:@"text-color" inStates:themeStates];
}
/*!
@@ -911,7 +917,7 @@ var CPControlBlackColor = [CPColor blackColor];
*/
- (CPFont)font
{
return [self valueForThemeAttribute:@"font"];
return [self currentValueForThemeAttribute:@"font"] || [CPFont systemFontForControlSize:_controlSize];
}
/*!
@@ -1016,6 +1022,69 @@ var CPControlBlackColor = [CPColor blackColor];
return [self hasThemeState:CPThemeStateHighlighted];
}
// MARK: -
// MARK: Base writing direction
/*!
Sets the initial writing direction of the receiver
@param writingDirection - It could be CPWritingDirectionNatural, CPWritingDirectionLeftToRight, CPWritingDirectionRightToLeft
*/
- (void)setBaseWritingDirection:(CPWritingDirection)writingDirection
{
if (writingDirection == _baseWritingDirection)
return;
[self willChangeValueForKey:@"baseWritingDirection"];
_baseWritingDirection = writingDirection;
[self didChangeValueForKey:@"baseWritingDirection"];
#if PLATFORM(DOM)
var style;
switch (_baseWritingDirection)
{
case CPWritingDirectionNatural:
style = "initial";
break;
case CPWritingDirectionLeftToRight:
style = "ltr";
break;
case CPWritingDirectionRightToLeft:
style = "rtl";
break;
default:
style = "initial";
}
_DOMElement.style.direction = style;
#endif
}
@end
@implementation CPControl (CPTrackingArea)
{
CPTrackingArea _controlTrackingArea;
}
- (void)updateTrackingAreas
{
if (_controlTrackingArea)
[self removeTrackingArea:_controlTrackingArea];
_controlTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect
owner:self
userInfo:nil];
[self addTrackingArea:_controlTrackingArea];
[super updateTrackingAreas];
}
@end
var CPControlActionKey = @"CPControlActionKey",
@@ -1027,6 +1096,7 @@ var CPControlActionKey = @"CPControlActionKey",
CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
CPControlTargetKey = @"CPControlTargetKey",
CPControlValueKey = @"CPControlValueKey",
CPControlBaseWrittingDirectionKey = @"CPControlBaseWrittingDirectionKey";
__Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
@@ -1055,6 +1125,9 @@ var CPControlActionKey = @"CPControlActionKey",
[self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
[self setControlSize:[aCoder decodeIntForKey:CPControlControlSizeKey]];
[self setBaseWritingDirection:[aCoder decodeIntForKey:CPControlBaseWrittingDirectionKey]];
[self updateTrackingAreas];
}
return self;
@@ -1074,21 +1147,24 @@ var CPControlActionKey = @"CPControlActionKey",
var objectValue = [self objectValue];
if (objectValue !== nil)
if (objectValue != nil)
[aCoder encodeObject:objectValue forKey:CPControlValueKey];
if (_target !== nil)
if (_target != nil)
[aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
if (_action !== nil)
if (_action != nil)
[aCoder encodeObject:_action forKey:CPControlActionKey];
[aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
if (_formatter !== nil)
if (_formatter != nil)
[aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
[aCoder encodeInt:_controlSize forKey:CPControlControlSizeKey];
[aCoder encodeInt:_baseWritingDirection forKey:CPControlBaseWrittingDirectionKey];
}
@end
+2
View File
@@ -37,6 +37,8 @@
CPString _expires;
}
@global document
/*!
Initializes a cookie with a given name \c aName.
@param the name for the cookie
Executable → Regular
+115 -44
View File
@@ -23,6 +23,7 @@ Cursor support by browser:
@import <Foundation/CPObject.j>
@import "CPImage.j"
@import "CPCompatibility.j"
@global CPApp
@@ -31,6 +32,12 @@ var currentCursor = nil,
cursors = {},
ieCursorMap = {};
@typedef CPCursorPlatform
CPCursorPlatformNone = 0;
CPCursorPlatformMac = 1;
CPCursorPlatformWindows = 2;
CPCursorPlatformBoth = 3;
@implementation CPCursor : CPObject
{
CPString _cssString @accessors(readonly);
@@ -123,6 +130,9 @@ var currentCursor = nil,
- (void)set
{
if (currentCursor === self)
return;
currentCursor = self;
#if PLATFORM(DOM)
@@ -154,7 +164,7 @@ var currentCursor = nil,
}
// Internal method that is used to return the system cursors. Caches the system cursors for performance.
+ (CPCursor)_systemCursorWithName:(CPString)cursorName cssString:(CPString)aString hasImage:(BOOL)doesHaveImage
+ (CPCursor)_nativeSystemCursorWithName:(CPString)cursorName cssString:(CPString)aString
{
var cursor = cursors[cursorName];
@@ -162,155 +172,216 @@ var currentCursor = nil,
{
var cssString;
if (doesHaveImage)
{
var themeResourcePath = [[[CPApp themeBlend] bundle] resourcePath],
extension = CPBrowserIsEngine(CPInternetExplorerBrowserEngine) ? @"cur" : @"png";
cssString = [CPString stringWithFormat:@"url(%@cursors/%@.%@), %@", themeResourcePath, cursorName, extension, aString];
}
// IE <= 8 does not support some cursors, map them to supported cursors
var ieLessThan9 = CPBrowserIsEngine(CPInternetExplorerBrowserEngine) && !CPFeatureIsCompatible(CPHTMLCanvasFeature);
if (ieLessThan9)
cssString = ieCursorMap[aString] || aString;
else
{
// IE <= 8 does not support some cursors, map them to supported cursors
var ieLessThan9 = CPBrowserIsEngine(CPInternetExplorerBrowserEngine) && !CPFeatureIsCompatible(CPHTMLCanvasFeature);
cssString = aString;
if (ieLessThan9)
cssString = ieCursorMap[aString] || aString;
else
cssString = aString;
}
cursor = [[CPCursor alloc] initWithCSSString:cssString];
cursors[cursorName] = cursor;
cursors[cursorName] = cursor = [[CPCursor alloc] initWithCSSString:cssString];
}
return cursor;
}
+ (CPCursor)_imageCursorWithName:(CPString)cursorName cssString:(CPString)aString
{
var cursor = cursors[cursorName];
if (typeof cursor === "undefined")
{
var themeResourcePath = [[[CPApp themeBlend] bundle] resourcePath],
extension = CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) ? @"cur" : @"png",
cssString = [CPString stringWithFormat:@"url(%@cursors/%@.%@), %@", themeResourcePath, cursorName, extension, aString];
cursors[cursorName] = cursor = [[CPCursor alloc] initWithCSSString:cssString];
}
return cursor;
}
+ (CPCursor)_tryUsingNativeSystemCursorWithName:(CPString)cursorName cssString:(CPString)cssName onPlatform:(CPCursorPlatform)shouldUseNativeCursorOn fallingBackWithImageAndCSSPointer:(CPString)aString
{
var useNativeSystemCursor = (((shouldUseNativeCursorOn == CPCursorPlatformBoth) ||
((shouldUseNativeCursorOn == CPCursorPlatformMac) && CPBrowserIsOperatingSystem(CPMacOperatingSystem)) ||
((shouldUseNativeCursorOn == CPCursorPlatformWindows) && CPBrowserIsOperatingSystem(CPWindowsOperatingSystem)))
&& [CPCursor _nativeCursorExists:cssName]);
if (useNativeSystemCursor)
return [CPCursor _nativeSystemCursorWithName:cursorName cssString:cssName];
else
return [CPCursor _imageCursorWithName:cursorName cssString:aString];
}
+ (BOOL)_nativeCursorExists:(CPString)cursorCSSName
{
#if PLATFORM(DOM)
// FIXME: Trick until FF/Win & Chrome/Win correctly implement context-menu cursor
// They will answer that they implement it but they actually don't
if ([cursorCSSName isEqualToString:@"context-menu"] && CPBrowserIsOperatingSystem(CPWindowsOperatingSystem) && !CPBrowserIsEngine(CPInternetExplorerBrowserEngine) && !CPBrowserIsEngine(CPEdgeBrowserEngine))
return NO;
// Normal usage : try to set the cursor and check if resulting cursor is the one we tried to set.
// If yes, then the browser implements the cursor. If no (and usually we get "default"), then it doesn't.
var platformWindows = [[CPPlatformWindow visiblePlatformWindows] allObjects],
count = [platformWindows count];
if (count > 0)
{
var currentPlatformCursor = platformWindows[0]._DOMBodyElement.style.cursor;
platformWindows[0]._DOMBodyElement.style.cursor = cursorCSSName;
var doesExist = (platformWindows[0]._DOMBodyElement.style.cursor == cursorCSSName);
platformWindows[0]._DOMBodyElement.style.cursor = currentPlatformCursor;
return doesExist;
}
#endif
return NO;
}
+ (CPCursor)arrowCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"default" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"default"];
}
+ (CPCursor)crosshairCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"crosshair" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"crosshair"];
}
+ (CPCursor)IBeamCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"text" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"text"];
}
+ (CPCursor)pointingHandCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"pointer" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"pointer"];
}
+ (CPCursor)resizeNorthwestCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nw-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nw-resize"];
}
+ (CPCursor)resizeNorthwestSoutheastCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nwse-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nwse-resize"];
}
+ (CPCursor)resizeNortheastCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ne-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ne-resize"];
}
+ (CPCursor)resizeNortheastSouthwestCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nesw-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"nesw-resize"];
}
+ (CPCursor)resizeSouthwestCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"sw-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"sw-resize"];
}
+ (CPCursor)resizeSoutheastCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"se-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"se-resize"];
}
+ (CPCursor)resizeDownCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"s-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"s-resize"];
}
+ (CPCursor)resizeUpCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"n-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"n-resize"];
}
+ (CPCursor)resizeLeftCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"w-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"w-resize"];
}
+ (CPCursor)resizeRightCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"e-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"e-resize"];
}
+ (CPCursor)resizeLeftRightCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"col-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"col-resize"];
}
+ (CPCursor)resizeEastWestCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ew-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ew-resize"];
}
+ (CPCursor)resizeUpDownCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"row-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"row-resize"];
}
+ (CPCursor)resizeNorthSouthCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ns-resize" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"ns-resize"];
}
+ (CPCursor)operationNotAllowedCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"not-allowed" hasImage:NO];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"not-allowed"];
}
+ (CPCursor)dragCopyCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"copy" hasImage:YES];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"copy"];
}
+ (CPCursor)dragLinkCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"alias" hasImage:YES];
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"alias"];
}
+ (CPCursor)contextualMenuCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"context-menu" hasImage:YES];
return [CPCursor _tryUsingNativeSystemCursorWithName:CPStringFromSelector(_cmd)
cssString:@"context-menu"
onPlatform:CPCursorPlatformBoth
fallingBackWithImageAndCSSPointer:@"default"];
}
+ (CPCursor)openHandCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"move" hasImage:YES];
return [CPCursor _tryUsingNativeSystemCursorWithName:CPStringFromSelector(_cmd)
cssString:@"grab"
onPlatform:CPCursorPlatformMac
fallingBackWithImageAndCSSPointer:@"default"];
}
+ (CPCursor)closedHandCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"-moz-grabbing" hasImage:YES];
return [CPCursor _tryUsingNativeSystemCursorWithName:CPStringFromSelector(_cmd)
cssString:@"grabbing"
onPlatform:CPCursorPlatformMac
fallingBackWithImageAndCSSPointer:@"default"];
}
+ (CPCursor)disappearingItemCursor
{
return [CPCursor _systemCursorWithName:CPStringFromSelector(_cmd) cssString:@"auto" hasImage:YES];
return [CPCursor _imageCursorWithName:CPStringFromSelector(_cmd) cssString:@"default"];
}
+ (CPCursor)IBeamCursorForVerticalLayout
{
return [CPCursor _nativeSystemCursorWithName:CPStringFromSelector(_cmd) cssString:@"vertical-text"];
}
@end
+215 -116
View File
@@ -1,7 +1,7 @@
/* CPDatePicker.j
* AppKit
*
* Created by Alexandre Wilhelm
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
@@ -59,31 +59,31 @@ CPEraDatePickerElementFlag = 0x0100;
*/
@implementation CPDatePicker : CPControl
{
BOOL _isBordered @accessors(getter=isBordered, setter=setBordered:);
BOOL _isBezeled @accessors(getter=isBezeled, setter=setBezeled:);
BOOL _drawsBackground @accessors(property=drawsBackground);
CPDate _dateValue @accessors(property=dateValue);
CPDate _minDate @accessors(property=minDate);
CPDate _maxDate @accessors(property=maxDate);
CPFont _textFont @accessors(property=textFont);
CPLocale _locale @accessors(property=locale);
//CPCalendar _calendar @accessors(property=calendar);
CPTimeZone _timeZone @accessors(property=timeZone);
id _delegate @accessors(property=delegate);
CPInteger _datePickerElements @accessors(property=datePickerElements);
CPInteger _datePickerMode @accessors(property=datePickerMode);
CPInteger _datePickerStyle @accessors(property=datePickerStyle);
CPInteger _timeInterval @accessors(property=timeInterval);
BOOL _isBordered @accessors(getter=isBordered, setter=setBordered:);
BOOL _isBezeled @accessors(getter=isBezeled, setter=setBezeled:);
BOOL _drawsBackground @accessors(property=drawsBackground);
CPDate _dateValue @accessors(property=dateValue);
CPDate _minDate @accessors(property=minDate);
CPDate _maxDate @accessors(property=maxDate);
CPFont _textFont @accessors(property=textFont);
CPLocale _locale @accessors(property=locale);
//CPCalendar _calendar @accessors(property=calendar);
CPTimeZone _timeZone @accessors(property=timeZone);
id _delegate @accessors(property=delegate);
CPInteger _datePickerElements @accessors(property=datePickerElements);
CPInteger _datePickerMode @accessors(property=datePickerMode);
CPInteger _datePickerStyle @accessors(property=datePickerStyle);
CPInteger _timeInterval @accessors(property=timeInterval);
BOOL _invokedByUserEvent;
_CPDatePickerTextField _datePickerTextfield;
_CPDatePickerCalendar _datePickerCalendar;
unsigned _implementedCDatePickerDelegateMethods;
BOOL _isTextual;
id _datePickerComponent;
}
#pragma mark -
#pragma mark Theme methods
// MARK: -
// MARK: Theme methods
+ (CPString)defaultThemeClass
{
@@ -144,12 +144,38 @@ CPEraDatePickerElementFlag = 0x0100;
@"hour-hand-size": CGSizeMakeZero(),
@"middle-hand-size": CGSizeMakeZero(),
@"minute-hand-size": CGSizeMakeZero(),
@"previous-button-size": CGSizeMakeZero(),
@"current-button-size": CGSizeMakeZero(),
@"next-button-size": CGSizeMakeZero(),
@"title-inset": [CPNull null],
@"day-label-inset": [CPNull null],
@"tile-content-inset": CGInsetMakeZero(),
@"tile-margin": [CPNull null],
@"tile-inset": CGInsetMakeZero(),
@"separator-color": [CPNull null],
@"separator-margin-width": 0,
@"separator-height": 0,
@"bezel-color-calendar-left": [CPNull null],
@"bezel-color-calendar-middle": [CPNull null],
@"bezel-color-calendar-right": [CPNull null],
@"tile-vertical-alignment": CPCenterVerticalTextAlignment,
@"tile-text-alignment": CPCenterTextAlignment,
@"hour-ampm-margin": 2,
@"time-separator-content-inset": CGInsetMakeZero(),
@"clock-second-hand-over": NO,
@"clock-draws-hours": NO,
@"clock-hours-font": [CPNull null],
@"clock-hours-text-color": [CPColor clearColor],
@"clock-hours-radius": 0,
@"calendar-clock-margin": 10,
@"clock-only-nib2cib-adjustment-frame": CPRectMakeZero(),
@"uses-focus-ring": NO
};
}
#pragma mark -
#pragma mark Binding methods
// MARK: -
// MARK: Binding methods
+ (Class)_binderClassForBinding:(CPString)theBinding
{
@@ -174,8 +200,8 @@ CPEraDatePickerElementFlag = 0x0100;
}
#pragma mark -
#pragma mark Init methods
// MARK: -
// MARK: Init methods
- (id)initWithFrame:(CGRect)aFrame
{
@@ -206,34 +232,61 @@ CPEraDatePickerElementFlag = 0x0100;
if (!_locale)
_locale = [CPLocale currentLocale];
_datePickerTextfield = [[_CPDatePickerTextField alloc] initWithFrame:[self bounds] withDatePicker:self];
[_datePickerTextfield setDateValue:_dateValue];
_datePickerComponent = nil;
_datePickerCalendar = [[_CPDatePickerCalendar alloc] initWithFrame:[self bounds] withDatePicker:self];
[_datePickerCalendar setDateValue:_dateValue];
// We might have been unarchived in a disabled state.
[_datePickerTextfield setEnabled:[self isEnabled]];
[self _createComponents];
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
- (void)_createComponents
{
_isTextual = (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle) || (_datePickerStyle == CPTextFieldDatePickerStyle);
#pragma mark -
#pragma mark Control Size
if (_datePickerComponent)
{
[_datePickerComponent removeFromSuperview];
_datePickerComponent = nil;
}
_datePickerComponent = [[(_isTextual ? _CPDatePickerTextField : _CPDatePickerCalendar) alloc] initWithFrame:[self bounds] withDatePicker:self];
[_datePickerComponent setDateValue:_dateValue];
[_datePickerComponent setControlSize:[self controlSize]];
[_datePickerComponent setDatePickerElements:_datePickerElements];
// FIXME: Don't know why but next line will cause theme compilation to fail...
// Workaround: added "if PLATFORM(DOM)"
#if PLATFORM(DOM)
[_datePickerComponent setEnabled:[self isEnabled]];
#endif
if (_isTextual)
// We need to transmit text color to the text field version (Cocoa doesn't permit adapting the calendar view text color)
[_datePickerComponent setTextColor:[self textColor]];
[self addSubview:_datePickerComponent];
}
// MARK: -
// MARK: Control Size
- (void)setControlSize:(CPControlSize)aControlSize
{
[super setControlSize:aControlSize];
if ([self datePickerStyle] == CPTextFieldAndStepperDatePickerStyle || [self datePickerStyle] == CPTextFieldDatePickerStyle)
[_datePickerComponent setControlSize:aControlSize];
if (_isTextual)
[self _sizeToControlSize];
}
#pragma mark -
#pragma mark Delegate methods
// MARK: -
// MARK: Delegate methods
/*! Set the delegate of the datePicker
@param aDelegate delegate of the datePicker
@@ -249,42 +302,19 @@ CPEraDatePickerElementFlag = 0x0100;
}
#pragma mark -
#pragma mark Layout method
// MARK: -
// MARK: Layout method
/*! Layout the subviews
*/
- (void)layoutSubviews
{
[super layoutSubviews];
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
{
if (![_datePickerTextfield superview])
[self addSubview:_datePickerTextfield];
if ([_datePickerCalendar superview])
[_datePickerCalendar removeFromSuperview];
[_datePickerTextfield setControlSize:[self controlSize]];
[_datePickerTextfield setNeedsLayout];
[_datePickerTextfield setNeedsDisplay:YES];
}
else
{
if (![_datePickerCalendar superview])
[self addSubview:_datePickerCalendar];
if ([_datePickerTextfield superview])
[_datePickerTextfield removeFromSuperview];
[_datePickerCalendar setNeedsLayout];
[_datePickerCalendar setNeedsDisplay:YES];
}
[_datePickerComponent setNeedsLayout];
[_datePickerComponent setNeedsDisplay:YES];
}
#pragma mark -
#pragma mark Setter
// MARK: -
// MARK: Setter
/*! Return the objectValue of the datePicker. The objectValue should take the timeZoneEffect
*/
@@ -323,6 +353,14 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)_setDateValue:(CPDate)aDateValue timeInterval:(CPTimeInterval)aTimeInterval
{
// Make sure to have a valid date and avoid NaN values
if (!isFinite(aDateValue))
{
[CPException raise:CPInvalidArgumentException
reason:@"aDateValue is not valid"];
return;
}
if (_minDate)
aDateValue = new Date (MAX(aDateValue, _minDate));
@@ -333,10 +371,7 @@ CPEraDatePickerElementFlag = 0x0100;
if ([aDateValue isEqualToDate:_dateValue] && aTimeInterval == _timeInterval)
{
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield setDateValue:_dateValue];
else
[_datePickerCalendar setDateValue:_dateValue];
[_datePickerComponent setDateValue:_dateValue];
return;
}
@@ -344,8 +379,8 @@ CPEraDatePickerElementFlag = 0x0100;
if (_implementedCDatePickerDelegateMethods & CPDatePicker_validateProposedDateValue_timeInterval)
{
// constrain timeInterval also
var aStartDateRef = function(x){if (typeof x == 'undefined') return aDateValue; aDateValue = x;}
var aTimeIntervalRef = function(x){if (typeof x == 'undefined') return aTimeInterval; aTimeInterval = x;}
var aStartDateRef = function(x){if (typeof x == 'undefined') return aDateValue; aDateValue = x;};
var aTimeIntervalRef = function(x){if (typeof x == 'undefined') return aTimeInterval; aTimeInterval = x;};
[_delegate datePicker:self validateProposedDateValue:aStartDateRef timeInterval:aTimeIntervalRef];
}
@@ -364,10 +399,7 @@ CPEraDatePickerElementFlag = 0x0100;
if (_invokedByUserEvent)
[self sendAction:[self action] to:[self target]];
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield setDateValue:_dateValue];
else
[_datePickerCalendar setDateValue:_dateValue];
[_datePickerComponent setDateValue:_dateValue];
}
/*! Set the minDate of the datePicker
@@ -375,6 +407,9 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setMinDate:(CPDate)aMinDate
{
if (_minDate === aMinDate)
return;
[self willChangeValueForKey:@"minDate"];
_minDate = aMinDate;
[self didChangeValueForKey:@"minDate"];
@@ -387,6 +422,9 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setMaxDate:(CPDate)aMaxDate
{
if (_maxDate === aMaxDate)
return;
[self willChangeValueForKey:@"maxDate"];
_maxDate = aMaxDate;
[self didChangeValueForKey:@"maxDate"];
@@ -399,10 +437,32 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setDatePickerStyle:(CPInteger)aDatePickerStyle
{
if (_datePickerStyle === aDatePickerStyle)
return;
_datePickerStyle = aDatePickerStyle;
// This is needed in order to specify different theme attributes values for textual / graphical date picker
if (_datePickerStyle === CPClockAndCalendarDatePickerStyle)
[self setThemeState:CPThemeStateAlternateState];
else
[self unsetThemeState:CPThemeStateAlternateState];
// This is needed in order to specify different theme attributes values for with / without stepper textual date picker
if (_datePickerStyle === CPTextFieldAndStepperDatePickerStyle)
[self setThemeState:CPThemeStateComposedControl];
else
[self unsetThemeState:CPThemeStateComposedControl];
[self setControlSize:[self controlSize]];
if (_datePickerComponent)
{
// We already have a component so we need to update it
[_datePickerComponent resignFirstResponder];
[self _createComponents];
}
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
@@ -412,8 +472,14 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setDatePickerElements:(CPInteger)aDatePickerElements
{
if (_datePickerElements === aDatePickerElements)
return;
_datePickerElements = aDatePickerElements;
// Notify the component of the new value
[_datePickerComponent setDatePickerElements:_datePickerElements];
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
@@ -423,6 +489,9 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setDatePickerMode:(CPInteger)aDatePickerMode
{
if (_datePickerMode === aDatePickerMode)
return;
_datePickerMode = aDatePickerMode;
if (_datePickerMode == CPSingleDateMode)
@@ -448,6 +517,9 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setLocale:(CPLocale)aLocale
{
if (_locale === aLocale)
return;
_locale = aLocale;
if (_formatter)
@@ -458,7 +530,9 @@ CPEraDatePickerElementFlag = 0x0100;
}
// This will update the textFields (usefull when changing with a date with pm and am)
[_datePickerTextfield setDateValue:_dateValue];
if (_isTextual)
[_datePickerComponent setDateValue:_dateValue];
[self setNeedsDisplay:YES];
[self setNeedsLayout];
}
@@ -469,6 +543,9 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setBezeled:(BOOL)shouldBeBezeled
{
if (_isBezeled === shouldBeBezeled)
return;
_isBezeled = shouldBeBezeled;
if (shouldBeBezeled)
@@ -483,6 +560,9 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setBordered:(BOOL)shouldBeBordered
{
if (_isBordered === shouldBeBordered)
return;
_isBordered = shouldBeBordered;
if (shouldBeBordered)
@@ -498,6 +578,22 @@ CPEraDatePickerElementFlag = 0x0100;
- (void)setTextFont:(CPFont)aFont
{
[self setFont:aFont];
if (_isTextual)
[_datePickerComponent setTextFont:aFont];
}
/*!
Sets the color of the control.
@param aColor
*/
- (void)setTextColor:(CPColor)aColor
{
[super setTextColor:aColor];
if (_isTextual)
[_datePickerComponent setTextColor:aColor];
// REM: in Cocoa, setTextColor has no effect on calendar view
}
/*! Sets the enabled status of the control. Controls that are not enabled can not be used by the user and obtain the CPThemeStateDisabled theme state.
@@ -507,8 +603,10 @@ CPEraDatePickerElementFlag = 0x0100;
{
[super setEnabled:aBoolean];
[_datePickerTextfield setEnabled:aBoolean];
[_datePickerCalendar setEnabled:aBoolean];
[_datePickerComponent setEnabled:aBoolean];
if (!aBoolean)
[self resignFirstResponder];
}
/*! Set the background color of the datePicker
@@ -525,6 +623,9 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setDrawsBackground:(BOOL)aBoolean
{
if (_drawsBackground === aBoolean)
return;
[self willChangeValueForKey:@"drawsBackground"];
_drawsBackground = aBoolean;
[self didChangeValueForKey:@"drawsBackground"];
@@ -537,32 +638,32 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (void)setTimeZone:(CPTimeZone)aTimeZone
{
if (_timeZone === aTimeZone)
return;
[self willChangeValueForKey:@"timeZone"];
_timeZone = aTimeZone;
[self didChangeValueForKey:@"timeZone"];
[self setNeedsLayout];
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield setDateValue:_dateValue];
else
[_datePickerCalendar setDateValue:_dateValue];
[_datePickerComponent setDateValue:_dateValue];
}
#pragma mark -
#pragma mark First responder methods
// MARK: -
// MARK: First responder methods
/*! Return YES if style is set to CPTextFieldAndStepperDatePickerStyle or CPTextFieldDatePickerStyle
*/
- (BOOL)becomeFirstResponder
{
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
if (_isTextual)
{
if (![super becomeFirstResponder])
return NO;
[_datePickerTextfield _selectTextFieldWithFlags:[[CPApp currentEvent] modifierFlags]];
[_datePickerComponent _selectTextFieldWithFlags:[[CPApp currentEvent] modifierFlags]];
return YES;
}
@@ -581,15 +682,15 @@ CPEraDatePickerElementFlag = 0x0100;
*/
- (BOOL)resignFirstResponder
{
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield resignFirstResponder];
if (_isTextual)
[_datePickerComponent resignFirstResponder];
return YES;
}
#pragma mark -
#pragma mark getter
// MARK: -
// MARK: getter
/*!
Returns \c YES if the textfield is bezeled.
@@ -622,24 +723,16 @@ CPEraDatePickerElementFlag = 0x0100;
return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
}
/*! Check if we are in the english format or not. Depending on the locale
*/
- (BOOL)_isEnglishFormat
{
return [[_locale objectForKey:CPLocaleLanguageCode] isEqualToString:@"en"];
}
#pragma mark -
#pragma mark Key event
// MARK: -
// MARK: Key event
/*! Key down event
@param anEvent
*/
- (void)keyDown:(CPEvent)anEvent
{
if (_datePickerStyle == CPTextFieldAndStepperDatePickerStyle || _datePickerStyle == CPTextFieldDatePickerStyle)
[_datePickerTextfield keyDown:anEvent];
if (_isTextual)
[_datePickerComponent keyDown:anEvent];
}
@end
@@ -665,20 +758,22 @@ var CPDatePickerModeKey = @"CPDatePickerModeKey",
if (self)
{
_textFont = [aCoder decodeObjectForKey:CPTextFontKey];
_minDate = [aCoder decodeObjectForKey:CPMinDateKey] || [CPDate distantPast];
_maxDate = [aCoder decodeObjectForKey:CPMaxDateKey] || [CPDate distantFuture];
_timeInterval = [aCoder decodeDoubleForKey:CPIntervalKey];
_datePickerMode = [aCoder decodeIntForKey:CPDatePickerModeKey];
_datePickerElements = [aCoder decodeIntForKey:CPDatePickerElementsKey];
[self setDatePickerStyle:[aCoder decodeIntForKey:CPDatePickerStyleKey]];
_locale = [aCoder decodeObjectForKey:CPLocaleKey];
_dateValue = [aCoder decodeObjectForKey:CPDateValueKey];
_backgroundColor = [aCoder decodeObjectForKey:CPBackgroundColorKey];
_drawsBackground = [aCoder decodeBoolForKey:CPDrawsBackgroundKey];
_isBordered = [aCoder decodeBoolForKey:CPBorderedKey];
[self setBordered:[aCoder decodeBoolForKey:CPBorderedKey]];
[self setDrawsBackground:[aCoder decodeBoolForKey:CPDrawsBackgroundKey]];
[self setDatePickerElements:[aCoder decodeIntForKey:CPDatePickerElementsKey]];
[self setDatePickerMode:[aCoder decodeIntForKey:CPDatePickerModeKey]];
[self setDatePickerStyle:[aCoder decodeIntForKey:CPDatePickerStyleKey]];
[self setMinDate:[aCoder decodeObjectForKey:CPMinDateKey] || [CPDate distantPast]];
[self setMaxDate:[aCoder decodeObjectForKey:CPMaxDateKey] || [CPDate distantFuture]];
[self setLocale:[aCoder decodeObjectForKey:CPLocaleKey]];
[self _init];
[self setTextFont:[aCoder decodeObjectForKey:CPTextFontKey]];
[self setTimeInterval:[aCoder decodeDoubleForKey:CPIntervalKey]];
[self setDateValue:[aCoder decodeObjectForKey:CPDateValueKey]];
}
return self
@@ -686,7 +781,10 @@ var CPDatePickerModeKey = @"CPDatePickerModeKey",
- (void)encodeWithCoder:(CPCoder)aCoder
{
// FIXME Do we need to encode _datePickerTextfield and _datePickerCalendar? As subviews they'll be encoded, but when we decode we recreate them anyhow.
// Before encoding, we remove all subviews as we'll recreate them at loading
while ([[self subviews] count] > 0)
[[[self subviews] lastObject] removeFromSuperview];
[super encodeWithCoder:aCoder];
[aCoder encodeDouble:_timeInterval forKey:CPIntervalKey];
@@ -694,8 +792,8 @@ var CPDatePickerModeKey = @"CPDatePickerModeKey",
[aCoder encodeInt:_datePickerStyle forKey:CPDatePickerStyleKey];
[aCoder encodeInt:_datePickerElements forKey:CPDatePickerElementsKey];
[aCoder encodeObject:_minDate forKey:CPMinDateKey];
[aCoder encodeObject:_maxDate forKey:CPMaxDateKey]
[aCoder encodeObject:_dateValue forKey:CPDateValueKey];;
[aCoder encodeObject:_maxDate forKey:CPMaxDateKey];
[aCoder encodeObject:_dateValue forKey:CPDateValueKey];
[aCoder encodeObject:_textFont forKey:CPTextFontKey];
[aCoder encodeObject:_locale forKey:CPLocaleKey];
[aCoder encodeObject:_backgroundColor forKey:CPBackgroundColorKey];
@@ -706,6 +804,7 @@ var CPDatePickerModeKey = @"CPDatePickerModeKey",
@end
// FIXME: add support for CPEditorRegistrationProtocol as implemented for CPTextField
@implementation _CPDatePickerValueBinder : CPBinder
{
}
+98
View File
@@ -0,0 +1,98 @@
/* _CPDatePickerBox.j
* AppKit
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPView.j"
@class CPDatePicker
@implementation _CPDatePickerBox : CPView
{
CPDatePicker _datePicker @accessors(property=datePicker);
}
- (void)drawRect:(CGRect)aRect
{
[super drawRect:aRect];
if ([_datePicker isCSSBased])
return;
if ([_datePicker isBordered])
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
borderWidth = [_datePicker valueForThemeAttribute:@"border-width"] / 2;
CGContextBeginPath(context);
CGContextSetStrokeColor(context, [_datePicker valueForThemeAttribute:@"border-color" inState:[_datePicker themeState]]);
CGContextSetLineWidth(context, [_datePicker valueForThemeAttribute:@"border-width"]);
CGContextMoveToPoint(context, borderWidth, borderWidth);
CGContextAddLineToPoint(context, aRect.size.width - borderWidth, borderWidth);
CGContextAddLineToPoint(context, aRect.size.width - borderWidth, aRect.size.height - borderWidth);
CGContextAddLineToPoint(context, borderWidth, aRect.size.height - borderWidth);
CGContextAddLineToPoint(context, borderWidth,borderWidth);
CGContextStrokePath(context);
CGContextClosePath(context);
}
}
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
{
if (aName === "bezel-view")
return [self bounds];
return [super rectForEphemeralSubviewNamed:aName];
}
- (CPView)createEphemeralSubviewNamed:(CPString)aName
{
if (aName === "bezel-view")
{
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
[view setHitTests:NO];
return view;
}
return [super createEphemeralSubviewNamed:aName];
}
- (void)layoutSubviews
{
if ([_datePicker isCSSBased])
{
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:nil];
[bezelView setBackgroundColor:[_datePicker currentValueForThemeAttribute:@"bezel-color"]];
}
if ([_datePicker drawsBackground])
[self setBackgroundColor:[_datePicker backgroundColor]];
else
[self setBackgroundColor:[CPColor clearColor]];
}
@end
File diff suppressed because it is too large Load Diff
+404 -106
View File
@@ -20,13 +20,18 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPKeyedArchiver.j>
@import <Foundation/CPKeyedUnarchiver.j>
@import "CPView.j"
@import "CPTextField.j"
@import "CPImageView.j"
@import "CPImage.j"
@import "CALayer.j"
@class _CPCibCustomResource
@class CPDatePicker
@class HandImageLayer
@class HoursLayer
@class HandLayer
@global CPHourMinuteSecondDatePickerElementFlag
@global CPTextFieldAndStepperDatePickerStyle
@@ -34,28 +39,48 @@
var RADIANS = Math.PI / 180;
@typedef _CPDatePickerClockHand
_CPDatePickerClockHours = 1;
_CPDatePickerClockMinutes = 2;
_CPDatePickerClockSeconds = 3;
@implementation _CPDatePickerClock : CPView
@implementation _CPDatePickerClock : CPControl
{
BOOL _isEnabled;
CALayer _rootLayer;
CALayer _hourHandLayer;
CALayer _minuteHandLayer;
CALayer _secondHandLayer;
CALayer _middleHandLayer;
CPDatePicker _datePicker;
CPTextField _PMAMTextField;
BOOL _isEnabled;
HoursLayer _rootLayer;
HandLayer _hourHandLayer;
HandLayer _minuteHandLayer;
HandLayer _secondHandLayer;
CALayer _middleHandLayer;
CPDatePicker _datePicker;
CPTextField _PMAMTextField;
CALayer _currentHandLayer;
_CPDatePickerClockHand _currentHand;
CPInteger _currentRepresentedValue;
float _currentValueShift;
CPInteger _numberOfUnits;
BOOL _trackingHand;
CPInteger _representedHours;
CPInteger _representedMinutes;
CPInteger _representedSeconds;
BOOL _representedHourIsPM;
CPInteger _datePickerElements @accessors(getter=datePickerElements);
}
#pragma mark -
#pragma mark Init methods
// MARK: -
// MARK: Init methods
- (id)initWithFrame:(CGRect)aFrame datePicker:(CPDatePicker)aDatePicker
{
if (self = [super initWithFrame:aFrame])
{
_datePicker = aDatePicker;
_datePicker = aDatePicker;
_datePickerElements = [_datePicker datePickerElements];
_trackingHand = NO;
_PMAMTextField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
@@ -73,7 +98,7 @@ var RADIANS = Math.PI / 180;
var middleHandSize = [_datePicker valueForThemeAttribute:@"middle-hand-size"],
minuteHandSize = [_datePicker valueForThemeAttribute:@"minute-hand-size"],
hourHandSize = [_datePicker valueForThemeAttribute:@"hour-hand-size"],
hourHandSize = [_datePicker valueForThemeAttribute:@"hour-hand-size"],
secondHandSize = [_datePicker valueForThemeAttribute:@"second-hand-size"];
// We use layer to make the rotation possible
@@ -97,106 +122,132 @@ var RADIANS = Math.PI / 180;
[_middleHandLayer setAnchorPoint:CGPointMakeZero()];
[_middleHandLayer setPosition:CGPointMake(0.0, 0.0)];
_rootLayer = [CALayer layer];
_rootLayer = [[HoursLayer alloc] init];
[self setWantsLayer:YES];
[self setLayer:_rootLayer];
[_hourHandLayer setNeedsDisplay];
[_middleHandLayer setNeedsDisplay];
[_secondHandLayer setNeedsDisplay];
[_minuteHandLayer setNeedsDisplay];
[self _initHands];
[_rootLayer addSublayer:_hourHandLayer];
[_rootLayer addSublayer:_minuteHandLayer];
[_rootLayer addSublayer:_secondHandLayer];
[_rootLayer addSublayer:_middleHandLayer];
if ([_datePicker valueForThemeAttribute:@"clock-second-hand-over"])
{
[_rootLayer addSublayer:_middleHandLayer];
[_rootLayer addSublayer:_secondHandLayer];
}
else
{
[_rootLayer addSublayer:_secondHandLayer];
[_rootLayer addSublayer:_middleHandLayer];
}
[_rootLayer setDrawsHours:[_datePicker valueForThemeAttribute:@"clock-draws-hours"]];
[_rootLayer setNeedsDisplay];
}
return self;
}
#pragma mark -
#pragma mark Layout methods
- (void)layoutSubviews
- (void)_initHands
{
if ([_datePicker datePickerStyle] == CPTextFieldAndStepperDatePickerStyle || [_datePicker datePickerStyle] == CPTextFieldDatePickerStyle)
return;
var middleHandImage = [_datePicker currentValueForThemeAttribute:@"middle-hand-image"],
hourHandImage = [_datePicker currentValueForThemeAttribute:@"hour-hand-image"],
minuteHandImage = [_datePicker currentValueForThemeAttribute:@"minute-hand-image"],
secondHandImage = [_datePicker currentValueForThemeAttribute:@"second-hand-image"];
[super layoutSubviews];
// If hand images are true CPImage, we have to duplicate them to avoid
// the multiple delegates bug when multiple clocks are displayed
var bounds = [self bounds],
dateValue = [[_datePicker dateValue] copy];
if ([middleHandImage isKindOfClass:[CPImage class]])
middleHandImage = [middleHandImage duplicate];
[dateValue _dateWithTimeZone:[_datePicker timeZone]];
if ([hourHandImage isKindOfClass:[CPImage class]])
hourHandImage = [hourHandImage duplicate];
[self setBackgroundColor:[_datePicker currentValueForThemeAttribute:@"bezel-color-clock"]];
[_middleHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"middle-hand-image"]];
[_hourHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"hour-hand-image"]];
[_minuteHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"minute-hand-image"]];
[_secondHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"second-hand-image"]];
if ([minuteHandImage isKindOfClass:[CPImage class]])
minuteHandImage = [minuteHandImage duplicate];
if ([_datePicker _isEnglishFormat])
{
if (dateValue.getHours() > 11)
[_PMAMTextField setStringValue:@"PM"]
else
[_PMAMTextField setStringValue:@"AM"];
if ([secondHandImage isKindOfClass:[CPImage class]])
secondHandImage = [secondHandImage duplicate];
[_PMAMTextField sizeToFit];
[_PMAMTextField setFrameOrigin:CGPointMake(bounds.size.width / 2 - [_PMAMTextField frameSize].width / 2, bounds.size.height / 2 + 15)];
[_PMAMTextField setHidden:NO];
}
else
{
[_PMAMTextField setHidden:YES];
}
[_middleHandLayer setImage:middleHandImage];
[_hourHandLayer setImage:hourHandImage];
[_minuteHandLayer setImage:minuteHandImage];
[_secondHandLayer setImage:secondHandImage];
[_hourHandLayer setRotationRadians:[self _hourPositionRadianForDate:dateValue]];
[_minuteHandLayer setRotationRadians:[self _minutePositionRadianForDate:dateValue]];
[_secondHandLayer setRotationRadians:[self _secondPositionRadianForDate:dateValue]];
[_hourHandLayer setNeedsDisplay];
[_middleHandLayer setNeedsDisplay];
[_secondHandLayer setNeedsDisplay];
[_minuteHandLayer setNeedsDisplay];
[_PMAMTextField setEnabled:_isEnabled];
[_hourHandLayer setEnabled:_isEnabled];
[_middleHandLayer setEnabled:_isEnabled];
[_secondHandLayer setEnabled:_isEnabled];
[_minuteHandLayer setEnabled:_isEnabled];
// Check if we have to display the hand second
if (([_datePicker datePickerElements] & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
[_secondHandLayer setHidden:NO];
else
[_secondHandLayer setHidden:YES];
[_rootLayer setFont:[_datePicker currentValueForThemeAttribute:@"clock-hours-font"]];
[_rootLayer setTextColor:[_datePicker currentValueForThemeAttribute:@"clock-hours-text-color"]];
[_rootLayer setRadius:[_datePicker currentValueForThemeAttribute:@"clock-hours-radius"]];
[_rootLayer setNeedsDisplay];
}
#pragma mark -
#pragma mark Accessors
- (float)_hourPositionRadianForDate:(CPDate)aDate
- (void)setDatePickerElements:(CPInteger)aDatePickerElements
{
var hours = aDate.getHours() + aDate.getMinutes() / 60;
_datePickerElements = aDatePickerElements;
return (360 * hours / 12) * RADIANS;
// Check if we have to display the hand second
// FIXME: Don't know why but next line will cause theme compilation to fail...
// Workaround: added "if PLATFORM(DOM)"
#if PLATFORM(DOM)
[_secondHandLayer setHidden:!((_datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)];
#endif
}
- (float)_secondPositionRadianForDate:(CPDate)aDate
// MARK: Layout methods
- (void)layoutSubviews
{
return (360 * aDate.getSeconds() / 60) * RADIANS;
// While tracking a hand, we don't want the whole thing to be relayouted at each mouse movement
if (_trackingHand)
return;
[self setBackgroundColor:[_datePicker currentValueForThemeAttribute:@"bezel-color-clock"]];
var dateValue = [[_datePicker dateValue] copy];
[dateValue _dateWithTimeZone:[_datePicker timeZone]];
_representedHours = dateValue.getHours();
_representedMinutes = dateValue.getMinutes();
_representedSeconds = dateValue.getSeconds();
_representedHourIsPM = (_representedHours > 11);
// Hours are expressed in 24 hours format, we need 12 hours format
_representedHours -= (_representedHourIsPM ? 12 : 0);
[self _updateHands];
// FIXME: Workaround. Seems that CALayer doesn't redraw without an event
[CALayer runLoopUpdateLayers];
}
- (float)_minutePositionRadianForDate:(CPDate)aDate
- (void)_updateHands
{
var minutes = aDate.getMinutes() + aDate.getSeconds() / 60;
var bounds = [self bounds];
return (360 * minutes / 60) * RADIANS;
[_PMAMTextField setStringValue:_representedHourIsPM ? @"PM" : @"AM"];
[_PMAMTextField sizeToFit];
[_PMAMTextField setFrameOrigin:CGPointMake(bounds.size.width / 2 - [_PMAMTextField frameSize].width / 2, bounds.size.height / 2 + 15)];
[_hourHandLayer setRotationRadians:(360 * (_representedHours + _representedMinutes / 60) / 12) * RADIANS];
[_minuteHandLayer setRotationRadians:(360 * (_representedMinutes + _representedSeconds / 60) / 60) * RADIANS];
[_secondHandLayer setRotationRadians:(360 * _representedSeconds / 60) * RADIANS];
[_hourHandLayer setNeedsDisplay];
[_minuteHandLayer setNeedsDisplay];
[_secondHandLayer setNeedsDisplay];
// [_middleHandLayer setNeedsDisplay];
}
// MARK: Accessors
- (void)setEnabled:(BOOL)shouldEnable
{
shouldEnable = !!shouldEnable;
@@ -205,36 +256,191 @@ var RADIANS = Math.PI / 180;
return;
_isEnabled = shouldEnable;
[self setNeedsLayout];
// FIXME: This is a workaround for an apparent bug in CALayer.
// Without pumping the event loop, the sublayers of _rootLayer
// (the hands) are not redrawn until an event occurs.
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
[self _initHands];
[self setNeedsLayout];
}
// MARK: Mouse actions
- (void)mouseDown:(CPEvent)anEvent
{
if (!_isEnabled)
return;
var currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil];
if ([_secondHandLayer handIsHitAtPoint:currentLocation])
{
_currentHandLayer = _secondHandLayer;
_currentHand = _CPDatePickerClockSeconds;
_currentRepresentedValue = _representedSeconds;
_currentValueShift = 0;
_numberOfUnits = 60;
}
else if ([_minuteHandLayer handIsHitAtPoint:currentLocation])
{
_currentHandLayer = _minuteHandLayer;
_currentHand = _CPDatePickerClockMinutes;
_currentRepresentedValue = _representedMinutes;
_currentValueShift = _representedSeconds / 60;
_numberOfUnits = 60;
}
else if ([_hourHandLayer handIsHitAtPoint:currentLocation])
{
_currentHandLayer = _hourHandLayer;
_currentHand = _CPDatePickerClockHours;
_currentRepresentedValue = _representedHours;
_currentValueShift = _representedMinutes / 60;
_numberOfUnits = 12;
}
else
{
_currentHandLayer = nil;
_currentHand = CPNotFound;
_currentRepresentedValue = CPNotFound;
_currentValueShift = CPNotFound;
_numberOfUnits = CPNotFound;
}
if (_currentHandLayer)
[self trackMouse:anEvent];
}
- (BOOL)tracksMouseOutsideOfFrame
{
return YES;
}
- (BOOL)startTrackingAt:(CGPoint)aPoint
{
_trackingHand = YES;
return YES;
}
- (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
{
var dx = aPoint.x -_bounds.size.width / 2,
dy = _bounds.size.height / 2 - aPoint.y,
angle = (PI_2 - ATAN2(dy,dx) + PI2) % PI2,
value = ROUND(angle * _numberOfUnits / PI2 - _currentValueShift) % _numberOfUnits;
if (value !== _currentRepresentedValue)
{
var movedForward = (_currentRepresentedValue > _numberOfUnits * 3/4) && (value < _numberOfUnits / 4),
movedBackward = (_currentRepresentedValue < _numberOfUnits / 4) && (value > _numberOfUnits * 3/4),
dateValue = [[_datePicker dateValue] copy];
[dateValue _dateWithTimeZone:[_datePicker timeZone]];
switch (_currentHand)
{
case _CPDatePickerClockHours:
if (movedForward || movedBackward)
{
_representedHourIsPM = !_representedHourIsPM;
if (movedForward && !_representedHourIsPM)
// Day++
dateValue.setDate(dateValue.getDate() + 1);
else if (movedBackward && _representedHourIsPM)
// Day--
dateValue.setDate(dateValue.getDate() - 1);
}
dateValue.setHours(value + (_representedHourIsPM ? 12 : 0));
break;
case _CPDatePickerClockMinutes:
if (movedForward)
// Hours++
dateValue.setHours(dateValue.getHours() + 1);
else if (movedBackward)
// Hours--
dateValue.setHours(dateValue.getHours() - 1);
dateValue.setMinutes(value);
break;
case _CPDatePickerClockSeconds:
if (movedForward)
// Minutes++
dateValue.setMinutes(dateValue.getMinutes() + 1);
else if (movedBackward)
// Minutes--
dateValue.setMinutes(dateValue.getMinutes() - 1);
dateValue.setSeconds(value);
break;
}
#if PLATFORM(DOM)
_datePicker._invokedByUserEvent = YES;
#endif
[_datePicker _setDateValue:dateValue timeInterval:[_datePicker timeInterval]];
#if PLATFORM(DOM)
_datePicker._invokedByUserEvent = NO;
#endif
// We have to adapt represented values
_representedHours = dateValue.getHours();
_representedMinutes = dateValue.getMinutes();
_representedSeconds = dateValue.getSeconds();
_representedHourIsPM = (_representedHours > 11);
// Hours are expressed in 24 hours format, we need 12 hours format
_representedHours -= (_representedHourIsPM ? 12 : 0);
switch (_currentHand) {
case _CPDatePickerClockHours:
_currentRepresentedValue = _representedHours;
break;
case _CPDatePickerClockMinutes:
_currentRepresentedValue = _representedMinutes;
break;
case _CPDatePickerClockSeconds:
_currentRepresentedValue = _representedSeconds;
break;
}
[self _updateHands];
}
return YES;
}
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
{
_trackingHand = NO;
}
@end
// MARK: -
@implementation HandLayer : CALayer
{
BOOL _isEnabled @accessors(setter=setEnabled:, getter=isEnabled);
CPImage _image;
CALayer _imageLayer;
float _rotationRadians;
CPImage _image;
HandImageLayer _imageLayer;
float _rotationRadians;
}
#pragma mark -
#pragma mark Init methods
// MARK: Init methods
- (id)initWithSize:(CGSize)aSize
{
if (self = [super init])
{
_isEnabled = YES;
_imageLayer = [CALayer layer];
_imageLayer = [HandImageLayer layer];
_rotationRadians = 0;
[_imageLayer setDelegate:self];
@@ -247,8 +453,7 @@ var RADIANS = Math.PI / 180;
}
#pragma mark -
#pragma mark Setter Getter methods
// MARK: Setter Getter methods
/*!
Set the bounds of the layer. The imageLayer will be at the center of this bounds.
@@ -285,18 +490,6 @@ var RADIANS = Math.PI / 180;
1.0, 1.0)];
}
- (void)setEnabled:(BOOL)shouldEnable
{
shouldEnable = !!shouldEnable;
if (_isEnabled === shouldEnable)
return;
_isEnabled = shouldEnable;
[self setNeedsDisplay];
[_imageLayer setNeedsDisplay];
}
- (void)imageDidLoad:(CPImage)anImage
{
[_imageLayer setNeedsDisplay];
@@ -310,4 +503,109 @@ var RADIANS = Math.PI / 180;
CGContextDrawImage(aContext, [aLayer bounds], _image);
}
- (BOOL)handIsHitAtPoint:(CGPoint)aPoint
{
return (!_isHidden && [_imageLayer hitTest:aPoint] === _imageLayer);
}
- (void)setNeedsDisplay
{
[super setNeedsDisplay];
[_imageLayer setNeedsDisplay];
}
@end
// MARK: -
@implementation HandImageLayer : CALayer
{
CGRect _handBounds;
}
// We have to adapt hitTest so it only takes the hand into account (that's the top half of the image layer)
// We are also sure there's no sublayers
- (CALayer)hitTest:(CGPoint)aPoint
{
if (_isHidden)
return nil;
var point = CGPointApplyAffineTransform(aPoint, _transformToLayer);
return CGRectContainsPoint(_handBounds, point) ? self : nil;
}
- (void)setBounds:(CGRect)aBounds
{
if (CGRectEqualToRect(_bounds, aBounds))
return;
_handBounds = CGRectMakeCopy(aBounds);
_handBounds.size.height = _handBounds.size.height / 2;
[super setBounds:aBounds];
}
@end
// MARK: -
@implementation HoursLayer : CALayer
{
BOOL _drawsHours;
CPFont _font @accessors(property=font);
CPColor _textColor @accessors(property=textColor);
float _radius @accessors(property=radius);
}
- (id)init
{
if (self = [super init])
{
_drawsHours = NO;
}
return self;
}
- (void)setDrawsHours:(BOOL)shouldDrawHours
{
shouldDrawHours = !!shouldDrawHours;
if (_drawsHours === shouldDrawHours)
return;
_drawsHours = shouldDrawHours;
[self setNeedsDisplay];
}
- (void)drawInContext:(CGContext)aContext
{
[super drawInContext:aContext];
if (_drawsHours)
{
var bounds = [self bounds],
centerX = bounds.size.width / 2,
centerY = bounds.size.height / 2;
CGContextSelectFont(aContext, _font);
CGContextSetFillColor(aContext, _textColor);
aContext.textBaseline = @"middle";
aContext.textAlign = @"center";
for (var i = 1, angle = 60.0, x, y; i < 13; i++, angle -= 30.0)
{
x = centerX + _radius * COS(angle * RADIANS);
y = centerY - _radius * SIN(angle * RADIANS);
aContext.fillText(i, x, y);
}
}
}
@end
+280
View File
@@ -0,0 +1,280 @@
/* _CPDatePickerDayView.j
* AppKit
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "_CPDatePickerDayViewTextField.j"
@class CPDatePicker
@implementation _CPDatePickerDayView : CPControl
{
CPDate _date @accessors(property=date);
BOOL _isDisabled;
BOOL _isHighlighted;
BOOL _isSelected;
CPDatePicker _datePicker;
CPTextField _textField;
CPInteger _dayInWeek @accessors(property=dayInWeek);
BOOL _firstSelected @accessors(property=firstSelected);
BOOL _lastSelected @accessors(property=lastSelected);
}
// MARK: Init methods
/*! Create a new instance of _CPDatePickerDayView
@param aFrame
@param aDatePicker
@return a new instance of _CPDatePickerDayView
*/
- (id)initWithFrame:(CGRect)aFrame withDatePicker:(CPDatePicker)aDatePicker
{
if (self = [super initWithFrame:aFrame])
{
[self setHitTests:NO];
_datePicker = aDatePicker;
// FIXME: Beginning with Aristo3, the text field is directly themed based on a new class _CPDatePickerDayViewTextField
if ([self isCSSBased])
{
_textField = [[_CPDatePickerDayViewTextField alloc] initWithFrame:aFrame];
}
else
{
_textField = [[CPTextField alloc] initWithFrame:aFrame];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-alignment"] forThemeAttribute:@"alignment"];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-vertical-alignment"] forThemeAttribute:@"vertical-alignment"];
var contentInset = [_datePicker valueForThemeAttribute:@"tile-content-inset"];
if (contentInset)
[_textField setValue:contentInset forThemeAttribute:@"content-inset"];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:CPThemeStateNormal] forThemeAttribute:@"font" inState:CPThemeStateNormal];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-color" inState:CPThemeStateNormal];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateNormal];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateNormal];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:CPThemeStateSelected] forThemeAttribute:@"font" inState:CPThemeStateSelected];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:CPThemeStateSelected] forThemeAttribute:@"text-color" inState:CPThemeStateSelected];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateSelected] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateSelected];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateSelected] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateSelected];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:CPThemeStateDisabled] forThemeAttribute:@"font" inState:CPThemeStateDisabled];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:CPThemeStateHighlighted] forThemeAttribute:@"font" inState:CPThemeStateHighlighted];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-color" inState:CPThemeStateHighlighted];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateHighlighted];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateHighlighted];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
}
[self addSubview:_textField];
[self setNeedsLayout];
}
return self;
}
// MARK: -
// MARK: Theme methods
/*! Set a theme
*/
- (BOOL)setThemeState:(ThemeState)aState
{
[_textField setThemeState:aState];
[super setThemeState:aState];
}
/*! Unset a theme
*/
- (BOOL)unsetThemeState:(ThemeState)aState
{
[_textField unsetThemeState:aState];
[super unsetThemeState:aState];
}
// MARK: -
// MARK: Getter methods
/*! Select the tile
*/
- (void)setSelected:(BOOL)shouldBeSelected
{
if (_isSelected === shouldBeSelected)
return;
_isSelected = shouldBeSelected;
if (_isSelected)
[self setThemeState:CPThemeStateSelected];
else
[self unsetThemeState:CPThemeStateSelected];
}
/*! Disabled the tile (used for previous and next month tile)
*/
- (void)setDisabled:(BOOL)shouldBeDisabled
{
if (_isDisabled === shouldBeDisabled)
return;
_isDisabled = shouldBeDisabled;
if (_isDisabled)
[self setThemeState:CPThemeStateDisabled];
else
[self unsetThemeState:CPThemeStateDisabled];
}
- (BOOL)isDisabled
{
return _isDisabled;
}
/*! Highlight the tile (used for current day)
*/
- (void)setHighlighted:(BOOL)shouldBeHighlighted
{
if (_isHighlighted === shouldBeHighlighted)
return;
_isHighlighted = shouldBeHighlighted;
if (_isHighlighted)
[self setThemeState:CPThemeStateHighlighted];
else
[self unsetThemeState:CPThemeStateHighlighted];
}
/*! Set the stringValue of the tile
@param aStringValue
*/
- (void)setStringValue:(CPString)aStringValue
{
[_textField setStringValue:aStringValue];
}
// MARK: -
// MARK: Layout methods
/*! Layout the subviews
*/
- (void)layoutSubviews
{
if ([_datePicker isCSSBased])
{
var attributeName = @"bezel-color-calendar";
if (_isSelected)
{
if (_firstSelected && !_lastSelected)
attributeName = @"bezel-color-calendar-left";
else if (_lastSelected && !_firstSelected)
attributeName = @"bezel-color-calendar-right";
else if (!_firstSelected && !_lastSelected)
{
if (_dayInWeek == 0)
attributeName = @"bezel-color-calendar-left";
else if (_dayInWeek == 6)
attributeName = @"bezel-color-calendar-right";
else
attributeName = @"bezel-color-calendar-middle";
}
}
[self setBackgroundColor:[_datePicker valueForThemeAttribute:attributeName inState:[self themeState]]];
return;
}
var bounds = [self bounds];
[_textField sizeToFit];
[_textField setFrameOrigin:CGPointMake(bounds.size.width / 2 - [_textField frameSize].width / 2 + [_datePicker valueForThemeAttribute:@"border-width"], bounds.size.height / 2 - [_textField frameSize].height / 2)];
}
- (void)setFrame:(CGRect)aFrame
{
[super setFrame:aFrame];
[_textField setFrame:CGRectMake(0, 0, aFrame.size.width, aFrame.size.height)];
}
/*! Drawrect
*/
- (void)drawRect:(CGRect)aRect
{
[super drawRect:aRect];
if ([_datePicker isCSSBased])
return;
var themeState = [self themeState],
context = [[CPGraphicsContext currentContext] graphicsPort];
if (themeState.hasThemeState(CPThemeStateSelected))
{
[self setBackgroundColor:[_datePicker valueForThemeAttribute:@"bezel-color-calendar" inState:themeState]];
CGContextSetLineWidth(context, [_datePicker valueForThemeAttribute:@"border-width"]);
CGContextSetStrokeColor(context, [_datePicker valueForThemeAttribute:@"border-color" inState:themeState]);
CGContextAddRect(context, [self bounds]);
CGContextStrokeRect(context, [self bounds]);
}
else
{
// Clear color, because the original color of a tile is handle by his superview
[self setBackgroundColor:[CPColor clearColor]];
}
}
@end
@@ -0,0 +1,48 @@
/* _CPDatePickerDayViewTextField.j
* AppKit
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPTextField.j"
@implementation _CPDatePickerDayViewTextField : CPTextField
+ (CPString)defaultThemeClass
{
return @"datePickerDayViewTextField";
}
+ (CPDictionary)themeAttributes
{
return @{
@"min-size": CGSizeMakeZero(),
@"content-inset": CGInsetMakeZero(),
@"text-color": [CPColor blackColor],
@"text-shadow-color": [CPColor clearColor],
@"text-shadow-offset": CGSizeMakeZero(),
@"font": [CPNull null],
@"vertical-alignment": CPCenterVerticalTextAlignment,
@"alignment": CPCenterTextAlignment,
};
}
@end
@@ -0,0 +1,604 @@
/* _CPDatePickerElementTextField.j
* AppKit
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPTextField.j"
@class CPDatePicker
@class _CPDatePickerElementView
CPDatePickerElementTextFieldBecomeFirstResponder = @"CPDatePickerElementTextFieldBecomeFirstResponder";
CPDatePickerElementTextFieldAMPMChangedNotification = @"CPDatePickerElementTextFieldAMPMChangedNotification";
// Removed hardcoded KeyCodes (CPZeroKeyCode, etc) as they are unreliable across browsers/layouts.
CPMonthDateType = 0;
CPDayDateType = 1;
CPYearDateType = 2;
CPHourDateType = 3;
CPSecondDateType = 4;
CPMinuteDateType = 5;
CPAMPMDateType = 6;
/*! An element textField
*/
@implementation _CPDatePickerElementTextField : CPTextField
{
_CPDatePickerElementTextField _nextTextField @accessors(property=nextTextField);
_CPDatePickerElementTextField _previousTextField @accessors(property=previousTextField);
_CPDatePickerElementView _datePickerElementView @accessors(property=datePickerElementView);
CPDatePicker _datePicker @accessors(setter=setDatePicker:);
int _dateType @accessors(getter=dateType);
int _maxNumber @accessors(getter=maxNumber);
int _minNumber @accessors(getter=minNumber);
BOOL _firstEvent;
CPTimer _timerEdition;
}
+ (CPString)defaultThemeClass
{
return @"datePickerElementTextField";
}
+ (CPDictionary)themeAttributes
{
return @{
@"content-inset": CGInsetMake(1.0, 0.0, 0.0, 0.0),
@"bezel-color": [CPNull null],
@"min-size": CGSizeMakeZero()
};
}
- (id)init
{
if (self = [super init])
{
_firstEvent = YES;
}
return self;
}
/*! @ignore */
- (BOOL)acceptsFirstResponder
{
return [_datePicker isEnabled];
}
/*! Set the dateType of the textField
*/
- (void)setDateType:(int)aDateType
{
_dateType = aDateType;
switch (aDateType)
{
case CPMonthDateType:
_minNumber = 1;
_maxNumber = 12;
break;
case CPDayDateType:
_minNumber = 1;
_maxNumber = 31;
break;
case CPYearDateType:
_minNumber = 0;
_maxNumber = 9999;
break;
case CPHourDateType:
_minNumber = 0;
_maxNumber = 23;
break;
case CPSecondDateType:
_minNumber = 0;
_maxNumber = 59;
break;
case CPMinuteDateType:
_minNumber = 0;
_maxNumber = 59;
break;
}
}
/*! Return the maxNumber of the textField
*/
- (int)maxNumber
{
if (_dateType == CPDayDateType)
return [[_datePicker dateValue] _daysInMonth];
return _maxNumber;
}
/*! Return the maxNumber of the textField depending of the maxDate
*/
- (int)_maxNumberWithMaxDate
{
var maxDate = [_datePicker maxDate],
date = [_datePicker dateValue];
if (maxDate)
{
switch (_dateType)
{
case CPMonthDateType:
if (maxDate.getFullYear() == date.getFullYear())
return maxDate.getMonth();
break;
case CPDayDateType:
if (maxDate.getFullYear() == date.getFullYear() && maxDate.getMonth() == date.getMonth())
return maxDate.getDate();
break;
case CPYearDateType:
return maxDate.getFullYear();
case CPHourDateType:
if (maxDate.getFullYear() == date.getFullYear() && maxDate.getMonth() == date.getMonth() && maxDate.getDate() == date.getDate())
return maxDate.getHours();
break;
case CPSecondDateType:
if (maxDate.getFullYear() == date.getFullYear() && maxDate.getMonth() == date.getMonth() && maxDate.getDate() == date.getDate() && maxDate.getHours() == date.getHours() && maxDate.getMinutes() == date.getMinutes())
return maxDate.getSeconds();
break;
case CPMinuteDateType:
if (maxDate.getFullYear() == date.getFullYear() && maxDate.getMonth() == date.getMonth() && maxDate.getDate() == date.getDate() && maxDate.getHours() == date.getHours())
return maxDate.getMinutes();
break;
}
}
return _maxNumber;
}
/*! Set the stringValue of the textField. This is going to check if there is 2 or 4 letters. If not it adds a space. Check also the maxDate
It's called when the user is editing with the keyboard
@param aStringValue a CPString
*/
- (void)setValueForKeyEvent:(CPEvent)anEvent
{
var keyCode = [anEvent keyCode],
characters = [anEvent characters];
// Check if the event is a deletion
var isDelete = (keyCode === CPDeleteKeyCode || keyCode === CPDeleteForwardKeyCode);
// Check if the event is a numeric input.
// By testing the character string against a regex, we support num-pads and
// international keyboards correctly, rather than relying on keyCode ranges.
var isNumeric = (characters && [characters length] > 0 && /^[0-9]$/.test(characters));
// If it is neither a delete command nor a digit, we ignore it.
if (!isDelete && !isNumeric)
return;
var newValue = [self stringValue].replace(/\s/g, ''),
length = [newValue length];
if (isDelete)
{
[_timerEdition invalidate];
_timerEdition = nil;
// Ensure we don't substring if length is 0
if (length > 0)
newValue = [newValue substringToIndex:(length - 1)];
}
else
{
// Since isNumeric is true, characters is a valid digit string
var eventKeyValue = characters;
if (!_timerEdition)
{
_timerEdition = [CPTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(_timerKeyEvent:) userInfo:nil repeats:NO];
if (_firstEvent || !length)
newValue = eventKeyValue;
else
newValue = parseInt(newValue).toString() + eventKeyValue;
}
else
{
var newFireDate = [CPDate date];
newFireDate.setSeconds(newFireDate.getSeconds() + 2);
[_timerEdition setFireDate:newFireDate];
newValue = parseInt(newValue).toString() + eventKeyValue;
}
}
// Safety check for NaN before comparison
var numericValue = parseInt(newValue);
if (isNaN(numericValue))
numericValue = 0;
if (numericValue > [self _maxNumberWithMaxDate] || ([_datePicker _isAmericanFormat] && _dateType == CPHourDateType && numericValue > 12))
return;
_firstEvent = NO;
[super setObjectValue:newValue];
}
/*!
End of the timer
*/
- (void)_timerKeyEvent:(id)sender
{
var stringValue = [self stringValue];
_timerEdition = nil;
if ([stringValue length])
{
if ([_datePicker _isAmericanFormat] && [self dateType] == CPHourDateType)
{
var isAMHour = [[self superview] _isAMHour];
if (!isAMHour && stringValue != 12)
stringValue = parseInt(stringValue) + 12;
if (stringValue == 12 && !isAMHour)
stringValue = 12;
else if (stringValue == 12)
stringValue = 0;
}
[self setObjectValue:stringValue];
}
}
/*!
We force to end the timer
*/
- (void)_invalidTimer
{
if (_timerEdition)
{
[_timerEdition invalidate];
_timerEdition = nil;
}
}
/*!
We force to end the timer and to update the objectValue of the datePicker
*/
- (void)_endEditing
{
if (_timerEdition)
[_timerEdition invalidate];
_timerEdition = nil;
var objectValue = [self stringValue];
if (![objectValue length])
objectValue = [self objectValue];
if ([_datePicker _isAmericanFormat] && [self dateType] == CPHourDateType)
{
var isAMHour = [[self superview] _isAMHour];
if (!isAMHour && objectValue != 12)
objectValue = parseInt(objectValue) + 12;
if (objectValue == 12 && !isAMHour)
objectValue = 12;
else if (objectValue == 12)
objectValue = 0;
}
[self setObjectValue:objectValue];
}
/*! Set the stringValue of the TextField. Add some zeros of there isn't 2/4 letters in the value. It's called at the end of the editing process
@param aStringValue a CPString
*/
- (void)setStringValue:(CPString)aStringValue
{
if (_dateType == CPYearDateType)
{
while ([aStringValue length] < 4)
aStringValue = "0" + aStringValue;
}
else if (_dateType != CPAMPMDateType)
{
if (_dateType == CPHourDateType && [_datePicker _isAmericanFormat])
{
var value = parseInt(aStringValue);
if (value == 0)
value = 12;
else if (value > 12)
value = value - 12;
aStringValue = value.toString();
}
while ([aStringValue length] < 2)
{
if (_dateType == CPSecondDateType || _dateType == CPMinuteDateType)
aStringValue = @"0" + aStringValue;
else
aStringValue = @" " + aStringValue;
}
}
[super setObjectValue:aStringValue];
}
/*! Set the objectValue. This will update the dateValue of the datePicker also. It's called with the binding of the stepper or arrows
This is not going to update the objectValue of the control !!! It updates the dateValue of the datePicker who's going to update the datePickerTextField if necessary
It's a bit tricky
@param aObjectValue
*/
- (void)setObjectValue:(id)anObjectValue
{
var dateValue = [[_datePicker dateValue] copy],
lengthString = [[self stringValue] length],
objectValue = parseInt(anObjectValue);
switch (_dateType)
{
case CPMonthDateType:
if (objectValue == 0 || !lengthString)
{
[self setStringValue:(dateValue.getMonth() + 1).toString()];
return;
}
var dateNextMonth = [dateValue copy];
dateNextMonth.setDate(1);
dateNextMonth.setMonth(parseInt(anObjectValue) - 1);
var numberDayNextMonth = [dateNextMonth _daysInMonth];
if (numberDayNextMonth < [dateValue _daysInMonth] && dateValue.getDate() > numberDayNextMonth)
[_datePickerElementView setDayDateValue:numberDayNextMonth.toString()];
[super setObjectValue:objectValue];
break;
case CPDayDateType:
if (objectValue == 0 || !lengthString)
{
[self setStringValue:dateValue.getDate().toString()];
return;
}
// if we enter a day that is too high for the current month
// we need to increase the month by one
// if we do not do this, the user input would be silently reset
// very poor user experience
if (parseInt(anObjectValue, 10) > [dateValue _daysInMonth])
{
[_datePickerElementView._textFieldMonth setIntValue:(dateValue.getMonth() + 2)];
[super setObjectValue:objectValue];
return;
}
[super setObjectValue:objectValue];
break;
case CPYearDateType:
if (objectValue == 0 || !lengthString)
{
[self setStringValue:dateValue.getFullYear().toString()];
return;
}
[super setObjectValue:objectValue];
break;
case CPHourDateType:
if (!lengthString)
{
[self setStringValue:dateValue.getHours().toString()];
return;
}
[super setObjectValue:objectValue];
break;
case CPSecondDateType:
if (!lengthString)
{
[self setStringValue:dateValue.getSeconds().toString()];
return;
}
[super setObjectValue:objectValue];
break;
case CPMinuteDateType:
if (!lengthString)
{
[self setStringValue:dateValue.getMinutes().toString()];
return;
}
[super setObjectValue:objectValue];
break;
}
var newDateValue = [_datePickerElementView dateValue],
timeZone = [_datePicker timeZone];
if (timeZone)
{
var secondsFromGMT = [timeZone secondsFromGMTForDate:newDateValue],
secondsFromGMTTimeZone = [timeZone secondsFromGMT];
newDateValue.setSeconds(newDateValue.getSeconds() + secondsFromGMT - secondsFromGMTTimeZone);
}
#if PLATFORM(DOM)
_datePicker._invokedByUserEvent = YES;
#endif
[_datePicker _setDateValue:newDateValue timeInterval:[_datePicker timeInterval]];
#if PLATFORM(DOM)
_datePicker._invokedByUserEvent = NO;
#endif
}
// MARK: -
// MARK: Mouse event
/*! Mouse down event. Launch a notification to notif the new first responder textField
*/
- (void)mouseDown:(CPEvent)anEvent
{
if (![self isEnabled])
return;
[super mouseDown:anEvent];
[[CPNotificationCenter defaultCenter] postNotificationName:CPDatePickerElementTextFieldBecomeFirstResponder object:[[self superview] superview] userInfo:[CPDictionary dictionaryWithObject:self forKey:@"textField"]];
}
// MARK: -
// MARK: Theme functions
/*! Set the theme CPThemeStateSelected
*/
- (void)makeSelectable
{
[self setThemeState:CPThemeStateSelected];
[_datePicker setThemeState:CPThemeStateEditing];
}
/*! Unsert the theme CPThemeStateSelected
*/
- (void)makeDeselectable
{
_firstEvent = YES;
[self unsetThemeState:CPThemeStateSelected];
[_datePicker unsetThemeState:CPThemeStateEditing];
}
// MARK: -
// MARK: Override
/*!
We override this method to get all the time the good width
*/
- (CGSize)_minimumFrameSize
{
var frameSize = [self frameSize],
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
minSize = [self currentValueForThemeAttribute:@"min-size"],
maxSize = [self currentValueForThemeAttribute:@"max-size"],
lineBreakMode = [self lineBreakMode],
text = (_dateType == CPYearDateType) ? @"0000" : (_dateType == CPMonthDateType) ? @"10" : @"00",
textSize = CGSizeMakeCopy(frameSize),
font = [self currentValueForThemeAttribute:@"font"];
textSize.width -= contentInset.left + contentInset.right;
textSize.height -= contentInset.top + contentInset.bottom;
if (_dateType == CPAMPMDateType)
text = [self stringValue];
if (frameSize.width !== 0 &&
![self isBezeled] &&
(lineBreakMode === CPLineBreakByWordWrapping || lineBreakMode === CPLineBreakByCharWrapping))
{
textSize = [text sizeWithFont:font inWidth:textSize.width];
}
else
{
textSize = [text sizeWithFont:font];
// Account for possible fractional pixels at right edge
textSize.width += 1;
}
// Account for possible fractional pixels at bottom edge
textSize.height += 1;
frameSize.height = textSize.height + contentInset.top + contentInset.bottom;
if ([self isBezeled])
{
frameSize.height = MAX(frameSize.height, minSize.height);
if (maxSize.width > 0.0)
frameSize.width = MIN(frameSize.width, maxSize.width);
if (maxSize.height > 0.0)
frameSize.height = MIN(frameSize.height, maxSize.height);
}
else
frameSize.width = textSize.width + contentInset.left + contentInset.right;
frameSize.width = MAX(frameSize.width, minSize.width);
return frameSize;
}
- (CGRect)bezelRectForBounds:(CGRect)bounds
{
return CGRectMakeCopy(bounds);
}
@end
// MARK: -
@implementation _CPDatePickerElementSeparator : CPTextField
+ (CPString)defaultThemeClass
{
return @"datePickerElementSeparator";
}
+ (CPDictionary)themeAttributes
{
return @{
@"content-inset": CGInsetMake(1.0, 0.0, 0.0, 0.0),
@"min-size": CGSizeMakeZero()
};
}
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,306 @@
/* _CPDatePickerHeaderView.j
* AppKit
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPControl.j"
@import "CPTextField.j"
@import "CPButton.j"
@class CPDatePicker
var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"],
CPShortWeekDayNameArrayUS = [@"Su", @"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa"],
CPShortWeekDayNameArrayFr = [@"L", @"M", @"M", @"J", @"V", @"S", @"D"],
CPShortWeekDayNameArrayDe = [@"M", @"D", @"M", @"D", @"F", @"S", @"S"],
CPShortWeekDayNameArrayEs = [@"L", @"M", @"X", @"J", @"V", @"S", @"D"],
CPShortMonthNameArrayEn = [@"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", @"Oct", @"Nov", @"Dec"],
CPShortMonthNameArrayFr = [@"janv.", String.fromCharCode(102, 233, 118, 46), @"mars", @"apr.", @"mai", @"juin", @"juil.", String.fromCharCode(97, 111, 251, 116), @"sept.", @"oct.", @"nov.", String.fromCharCode(100, 233, 99, 46)],
CPShortMonthNameArrayDe = [@"Jan", @"Feb", String.fromCharCode(77, 228, 114), @"Apr", @"Mai", @"Jun", @"Jul", @"Aug", @"Sep", @"Okt", @"Nov", @"Dez"],
CPShortMonthNameArrayEs = [@"ene", @"feb", @"mar", @"abr", @"may", @"jun", @"jul", @"ago", @"sep", @"oct", @"nov", @"dic"];
@implementation _CPDatePickerHeaderView : CPControl
{
CPArray _dayLabels;
CPArray _monthNames;
CPButton _nextButton;
CPButton _previousButton;
CPButton _currentButton;
CPDatePicker _datePicker;
CPDate _date;
CPTextField _title;
}
// MARK: Init methods
/*! Init a new instance of _CPDatePickerHeaderView
@param aFrame
@param aDatePicker
@return a new instance of _CPDatePickerHeaderView
*/
- (id)initWithFrame:(CGRect)aFrame datePicker:(CPDatePicker)aDatePicker delegate:(id)aDelegate
{
self = [super initWithFrame:aFrame];
if (self)
{
_datePicker = aDatePicker;
// Title
_title = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[_title setValue:[_datePicker valueForThemeAttribute:@"title-font" inState:CPThemeStateNormal] forThemeAttribute:@"font" inState:CPThemeStateNormal];
[_title setValue:[_datePicker valueForThemeAttribute:@"title-text-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-color" inState:CPThemeStateNormal];
[_title setValue:[_datePicker valueForThemeAttribute:@"title-text-shadow-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateNormal];
[_title setValue:[_datePicker valueForThemeAttribute:@"title-text-shadow-offset" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateNormal];
[_title setValue:[_datePicker valueForThemeAttribute:@"title-font" inState:CPThemeStateDisabled] forThemeAttribute:@"font" inState:CPThemeStateDisabled];
[_title setValue:[_datePicker valueForThemeAttribute:@"title-text-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
[_title setValue:[_datePicker valueForThemeAttribute:@"title-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled];
[_title setValue:[_datePicker valueForThemeAttribute:@"title-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled];
[self addSubview:_title];
_dayLabels = [CPArray array];
// Days
for (var i = 0, count = [[self _dayNames] count]; i < count; i++)
{
var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[label setValue:[_datePicker valueForThemeAttribute:@"tile-text-alignment"] forThemeAttribute:@"alignment"];
var contentInset = [_datePicker valueForThemeAttribute:@"tile-content-inset"];
if (contentInset)
[label setValue:contentInset forThemeAttribute:@"content-inset"];
[label setValue:[_datePicker valueForThemeAttribute:@"weekday-font" inState:CPThemeStateNormal] forThemeAttribute:@"font" inState:CPThemeStateNormal];
[label setValue:[_datePicker valueForThemeAttribute:@"weekday-text-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-color" inState:CPThemeStateNormal];
[label setValue:[_datePicker valueForThemeAttribute:@"weekday-text-shadow-color" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateNormal];
[label setValue:[_datePicker valueForThemeAttribute:@"weekday-text-shadow-offset" inState:CPThemeStateNormal] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateNormal];
[label setValue:[_datePicker valueForThemeAttribute:@"weekday-font" inState:CPThemeStateDisabled] forThemeAttribute:@"font" inState:CPThemeStateDisabled];
[label setValue:[_datePicker valueForThemeAttribute:@"weekday-text-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
[label setValue:[_datePicker valueForThemeAttribute:@"weekday-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled];
[label setValue:[_datePicker valueForThemeAttribute:@"weekday-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled];
[_dayLabels addObject:label];
[self addSubview:label];
}
// Arrows
var size = [_datePicker valueForThemeAttribute:@"previous-button-size"];
_previousButton = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)];
[_previousButton setButtonType:CPMomentaryChangeButton];
[_previousButton setBordered:NO];
[_previousButton setImage:[_datePicker valueForThemeAttribute:@"arrow-image-left"]];
[_previousButton setAlternateImage:[_datePicker valueForThemeAttribute:@"arrow-image-left-highlighted"]];
[self addSubview:_previousButton];
size = [_datePicker valueForThemeAttribute:@"next-button-size"];
_nextButton = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)];
[_nextButton setButtonType:CPMomentaryChangeButton];
[_nextButton setBordered:NO];
[_nextButton setImage:[_datePicker valueForThemeAttribute:@"arrow-image-right"]];
[_nextButton setAlternateImage:[_datePicker valueForThemeAttribute:@"arrow-image-right-highlighted"]];
[self addSubview:_nextButton];
size = [_datePicker valueForThemeAttribute:@"current-button-size"];
_currentButton = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)];
[_currentButton setButtonType:CPMomentaryChangeButton];
[_currentButton setBordered:NO];
[_currentButton setImage:[_datePicker valueForThemeAttribute:@"circle-image"]];
[_currentButton setAlternateImage:[_datePicker valueForThemeAttribute:@"circle-image-highlighted"]];
[self addSubview:_currentButton];
[_previousButton setTarget:aDelegate];
[_previousButton setAction:@selector(_clickArrowPrevious:)];
[_previousButton setContinuous:YES];
[_nextButton setTarget:aDelegate];
[_nextButton setAction:@selector(_clickArrowNext:)];
[_nextButton setContinuous:YES];
[_currentButton setTarget:aDelegate];
[_currentButton setAction:@selector(_currentMonth:)];
[self setNeedsLayout];
}
return self;
}
// MARK: -
// MARK: Getter Setter methods
/*! Return the day names depending on the CPLocale of the datePicker
@return an array
*/
- (CPArray)_dayNames
{
switch ([[_datePicker locale] objectForKey:CPLocaleLanguageCode])
{
case @"en":
// Check if it's in the american format. If yes the week will begin the sunday
if ([_datePicker _isAmericanFormat])
return CPShortWeekDayNameArrayUS;
else
return CPShortWeekDayNameArrayEn;
break;
case @"es":
return CPShortWeekDayNameArrayEs;
break;
case @"de":
return CPShortWeekDayNameArrayDe;
break;
case @"fr":
return CPShortWeekDayNameArrayFr;
break;
default:
return CPShortWeekDayNameArrayEn;
break;
}
}
/*! Return the month names depending on the CPLocale of the datePicker
@return an array
*/
- (CPArray)_monthNames
{
switch ([[_datePicker locale] objectForKey:CPLocaleLanguageCode])
{
case @"en":
return CPShortMonthNameArrayEn;
break;
case @"es":
return CPShortMonthNameArrayEs;
break;
case @"de":
return CPShortMonthNameArrayDe;
break;
case @"fr":
return CPShortMonthNameArrayFr;
break;
default:
return CPShortMonthNameArrayEn;
break;
}
}
/*! Set the monthDate of the header
@aMonthDate the new monthDate
*/
- (void)setMonthForDate:(CPDate)aMonthDate
{
_date = aMonthDate;
[self setNeedsLayout];
}
/*! Set enabled
@param aBoolean
*/
- (void)setEnabled:(BOOL)aBoolean
{
[_previousButton setEnabled:aBoolean];
[_nextButton setEnabled:aBoolean];
[_currentButton setEnabled:aBoolean];
[_dayLabels makeObjectsPerformSelector:@selector(setEnabled:) withObject:aBoolean];
[_title setEnabled:aBoolean];
}
// MARK: -
// MARK: Layout methods
/*! Layout the subviews
*/
- (void)layoutSubviews
{
var bounds = [self bounds],
dayNames = [self _dayNames],
width = CGRectGetWidth(bounds),
buttonInset = [_datePicker valueForThemeAttribute:@"arrow-inset"],
numberOfLabels = [_dayLabels count],
labelWidth = width / numberOfLabels,
sizeButtonLeft = [[_datePicker valueForThemeAttribute:@"arrow-image-left"] size],
sizeButtonRight = [[_datePicker valueForThemeAttribute:@"arrow-image-right"] size],
sizeButtonCircle = [[_datePicker valueForThemeAttribute:@"circle-image"] size],
sizeTileWidth = [_datePicker valueForThemeAttribute:@"size-tile"].width,
titleInset = [_datePicker valueForThemeAttribute:@"title-inset"],
dayLabelInset = [_datePicker valueForThemeAttribute:@"day-label-inset"];
// Arrows
[_nextButton setFrame:CGRectMake(width - [_nextButton frameSize].width - buttonInset.right, buttonInset.top, sizeButtonRight.width, sizeButtonRight.height)];
[_currentButton setFrame:CGRectMake(CGRectGetMinX([_nextButton frame]) - sizeButtonCircle.width - buttonInset.left - buttonInset.right, buttonInset.top, sizeButtonCircle.width, sizeButtonCircle.height)];
[_previousButton setFrame:CGRectMake(CGRectGetMinX([_currentButton frame]) - sizeButtonLeft.width - buttonInset.left - buttonInset.right, buttonInset.top, sizeButtonLeft.width, sizeButtonLeft.height)];
var firstDayTileX;
// Weekday label
for (var i = 0; i < numberOfLabels; i++)
{
var dayLabel = _dayLabels[i];
[dayLabel setStringValue:dayNames[i]];
if (dayLabelInset) // Beginning with Aristo3
{
var thisWidth = ROUND((i+1) * sizeTileWidth) - ROUND(i * sizeTileWidth);
[dayLabel sizeToFit];
[dayLabel setFrame:CGRectMake(dayLabelInset.left + ROUND(i * sizeTileWidth), dayLabelInset.top, thisWidth, [dayLabel frameSize].height)];
}
else
{
[dayLabel sizeToFit];
[dayLabel setFrameOrigin:CGPointMake(sizeTileWidth * (i + 1) - sizeTileWidth / 2 - [dayLabel frameSize].width / 2, 23)];
if (i == 0)
firstDayTileX = sizeTileWidth * (i + 1) - sizeTileWidth / 2 - [dayLabel frameSize].width / 2;
}
}
// Title
[_title setStringValue:[CPString stringWithFormat:@"%s %i", [self _monthNames][_date.getMonth()], _date.getFullYear()]];
[_title sizeToFit];
if (titleInset) // Beginning with Aristo3
[_title setFrameOrigin:CGPointMake(titleInset.left, titleInset.top)];
else
[_title setFrameOrigin:CGPointMake(firstDayTileX, 6)];
}
@end
@@ -0,0 +1,636 @@
/* _CPDatePickerMonthView.j
* AppKit
*
* Created by Alexandre Wilhelm
* Copyright 2012 <alexandre.wilhelmfr@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPControl.j"
@import "_CPDatePickerDayView.j"
@class CPDatePicker
@global CPSingleDateMode
@global CPRangeDateMode
@implementation _CPDatePickerMonthView : CPControl
{
BOOL _isMonthJustChanged;
CPArray _dayTiles;
CPDate _clickDate;
CPDate _dragDate;
CPDate _date;
CPDate _previousMonth @accessors(property=previousMonth);
CPDate _nextMonth @accessors(property=nextMonth);
CPDatePicker _datePicker;
CPEvent _eventDragged;
CPTimer _timerMonth;
id _delegate;
int _indexDayTile;
}
// MARK: Init methods
/*! Init a _CPDatePickerMonthView
@param aFrame
@param aDatePicker
@return a new _CPDatePickerMonthView
*/
- (id)initWithFrame:(CGRect)aFrame datePicker:(CPDatePicker)aDatePicker delegate:(id)aDelegate
{
if (self = [super initWithFrame:aFrame])
{
_delegate = aDelegate;
_isMonthJustChanged = NO;
_indexDayTile = -1;
_datePicker = aDatePicker;
_dayTiles = [CPArray array];
// Create tiles
for (var i = 0; i < 42; i++)
{
var dayView = [[_CPDatePickerDayView alloc] initWithFrame:CGRectMakeZero() withDatePicker:_datePicker];
[self addSubview:dayView];
[_dayTiles addObject:dayView];
}
[self setNeedsLayout];
}
return self;
}
// MARK: -
// MARK: Getter Setter methods
/*! Set the monthDate of the component
@param aDate
*/
- (void)setMonthForDate:(CPDate)aDate
{
if (_dragDate)
{
if (_dragDate.getMonth() != _date.getMonth())
_isMonthJustChanged = YES;
_date = [_dragDate copy];
}
else
{
_date = [aDate copy];
}
if (![aDate isEqualToDate:[CPDate distantFuture]])
{
// Reset the date to the first day of the month & midnight
_date.setDate(1);
[_date _resetToMidnight];
// There must be a better way to do this.
var firstDay = [_date copy];
firstDay.setDate(1);
// Set the previous and next month date. This is usefull for the tile of the next/previous month
_previousMonth = new Date(firstDay.getTime() - 86400000);
_previousMonth.setDate(1);
_nextMonth = new Date(firstDay.getTime() + (([_date _daysInMonth] + 1) * 86400000));
_nextMonth.setDate(1);
}
[self reloadData];
if (_isMonthJustChanged)
{
var dayTile = [_dayTiles objectAtIndex:_indexDayTile];
if ([dayTile date].getMonth() == _date.getMonth())
{
[self mouseDragged:_eventDragged];
}
else
{
if ([dayTile date].getMonth() - _date.getMonth() == 1 || [dayTile date].getFullYear() - _date.getFullYear() == 1)
_timerMonth = [CPTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(_timerNextMonthEvent:) userInfo:nil repeats:NO];
else
_timerMonth = [CPTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(_timerPreviousMonthEvent:) userInfo:nil repeats:NO];
}
}
}
- (void)monthDate
{
return _date;
}
/*! Return the size of a tile
*/
- (CGSize)tileSize
{
return [_datePicker valueForThemeAttribute:@"size-tile"];
}
/*! Return the first index day of the month
*/
- (int)startOfWeekForDate:(CPDate)aDate
{
var day = aDate.getDay();
// American people begins the week the sunday
if (![_datePicker _isAmericanFormat])
return (day + 6) % 7;
return day;
}
/*! Set enabled
@param aBoolean
*/
- (void)setEnabled:(BOOL)aBoolean
{
[super setEnabled:aBoolean];
[self reloadData];
}
/*! Return the index of tile depending of the giving event
@param anEvent
@return an index
*/
- (CPInteger)indexOfTileForEvent:(CPEvent)anEvent
{
var locationInView = [self convertPoint:[anEvent locationInWindow] fromView:nil],
tileSize = [self tileSize],
borderWidth = [_datePicker valueForThemeAttribute:@"border-width"],
margin = [_datePicker valueForThemeAttribute:@"tile-margin"] || CGSizeMakeZero(),
tileInset = [_datePicker valueForThemeAttribute:@"tile-inset"] || CGInsetMakeZero();
// Get the week row
var rowIndex = FLOOR((locationInView.y - tileInset.top - margin.height) / (tileSize.height + 2 * margin.height + borderWidth)),
columnIndex = FLOOR((locationInView.x - tileInset.left) / (tileSize.width + 2 * margin.width + borderWidth));
columnIndex = MIN(MAX(columnIndex, 0), 6);
rowIndex = MIN(MAX(rowIndex, 0), 5);
var tileIndex = (rowIndex * 7) + columnIndex;
return tileIndex;
}
// MARK: -
// MARK: Reload data
/*! Reload the data
*/
- (void)reloadData
{
if (!_date)
return;
var currentMonth = _date,
startOfMonthDay = [self startOfWeekForDate:currentMonth],
daysInPreviousMonth = [_previousMonth _daysInMonth],
firstDayToShowInPreviousMonth = daysInPreviousMonth - startOfMonthDay,
currentDate = new Date(_previousMonth.getFullYear(), _previousMonth.getMonth(), firstDayToShowInPreviousMonth),
now = [CPDate date],
dateValue = [_datePicker dateValue];
// Update the tiles
for (var i = 0; i < [_dayTiles count]; i++)
{
var dayTile = _dayTiles[i];
// Increment to next day
currentDate.setTime(currentDate.getTime() + 90000000);
[currentDate _resetToMidnight];
var isPresentMonth = (now.getMonth() == currentDate.getMonth()
&& now.getFullYear() == currentDate.getFullYear());
[dayTile setDate:[currentDate copy]];
[dayTile setStringValue:currentDate.getDate()];
[dayTile setDisabled:/*![self isEnabled] ||*/ currentDate.getMonth() !== currentMonth.getMonth() || currentDate < [_datePicker minDate] || currentDate > [_datePicker maxDate]];
[dayTile setHighlighted:isPresentMonth && currentDate.getDate() == now.getDate()];
}
// Select the dates
[self _selectDate:[_datePicker dateValue] timeInterval:[_datePicker timeInterval]];
}
// MARK: -
// MARK: Select methods
/*! Select one date or several date depending of the giving interval
@param aStartDate
@param anInterval;
*/
- (void)_selectDate:(CPDate)aStartDate timeInterval:(CPInteger)anInterval
{
var endDate = [[CPDate alloc] initWithTimeInterval:anInterval sinceDate:aStartDate],
tilesCount = [_dayTiles count];
aStartDate = [aStartDate copy];
[aStartDate _resetToMidnight];
[endDate _resetToMidnight];
var firstSelected = NO;
for (var i = 0; i < tilesCount; i++)
{
var tile = _dayTiles[i],
tileDate = [[tile date] copy],
selected = NO;
[tileDate _resetToMidnight];
if (aStartDate)
selected = tileDate >= aStartDate && tileDate <= endDate;
// Select a tile
[tile setSelected:selected];
// If we are disabled, we have to disable selected tiles so they will appear disabled
[tile setDisabled:[tile isDisabled] || (selected && ![self isEnabled])];
if (selected)
{
if (!firstSelected)
{
firstSelected = YES;
[tile setFirstSelected:YES];
}
else
[tile setFirstSelected:NO];
[tile setLastSelected:NO];
}
else
{
if (firstSelected)
{
firstSelected = NO;
// As there was a first selected and we are now on an unselected tile,
// we are sure that i > 0
[_dayTiles[i-1] setLastSelected:YES];
}
}
}
}
// MARK: -
// MARK: Layout methods
/*! Tile the view
*/
- (void)tile
{
var tileSize = [self tileSize],
width = tileSize.width,
height = tileSize.height,
tilesCount = [_dayTiles count],
borderWidth = [_datePicker valueForThemeAttribute:@"border-width"],
margin = [_datePicker valueForThemeAttribute:@"tile-margin"],
tileInset = [_datePicker valueForThemeAttribute:@"tile-inset"],
thisWidth,
thisX,
dayInWeek,
weekInMonth,
tileFrame,
tileIndex;
// Set the frame of the tiles
for (tileIndex = 0; tileIndex < tilesCount; tileIndex++)
{
dayInWeek = tileIndex % 7;
weekInMonth = (tileIndex - dayInWeek) / 7;
tileFrame;
if (margin) // Beginning with Aristo3
{
thisX = ROUND(dayInWeek * (width + 2 * margin.width));
thisWidth = ROUND((dayInWeek+1) * (width + 2 * margin.width)) - thisX;
tileFrame = CGRectMake(tileInset.left + thisX, tileInset.top + margin.height + weekInMonth * (height + 2 * margin.height), thisWidth + borderWidth, height + borderWidth);
}
else
tileFrame = CGRectMake(dayInWeek * width, weekInMonth * height, width + borderWidth, height + borderWidth);
[_dayTiles[tileIndex] setFrame:tileFrame];
[_dayTiles[tileIndex] setDayInWeek:dayInWeek];
}
[self reloadData];
}
/*! Layout the subviews
*/
- (void)layoutSubviews
{
[super layoutSubviews];
[self tile];
[_dayTiles makeObjectsPerformSelector:@selector(setNeedsLayout)];
}
/*! Draw the component. This draws the border of the tile.
The selected tile are drawed in the drawRect method of the tile. But the unselected tile here.
It avoids some problems with tiles over other tiles (otherwise the color of the tile border would be different).
Rememeber that the first pixel of a tile are over the last pixel of the last tile (because the border)
*/
- (void)drawRect:(CGRect)aRect
{
[super drawRect:aRect];
if ([_datePicker isCSSBased])
{
// We just have to draw the separator (if any)
// No separator color means no separator
var separatorColor = [_datePicker valueForThemeAttribute:@"separator-color"],
separatorHeight = [_datePicker valueForThemeAttribute:@"separator-height"],
separatorMarginWidth = [_datePicker valueForThemeAttribute:@"separator-margin-width"];
if (separatorColor)
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
bounds = [self bounds];
CGContextBeginPath(context);
CGContextSetStrokeColor(context, separatorColor);
CGContextSetLineWidth(context, separatorHeight);
CGContextMoveToPoint(context, separatorMarginWidth, 0.5);
CGContextAddLineToPoint(context, bounds.size.width - separatorMarginWidth, 0.5);
CGContextStrokePath(context);
CGContextClosePath(context);
}
return;
}
var context = [[CPGraphicsContext currentContext] graphicsPort],
width = [self tileSize].width,
height = [self tileSize].height,
isBorderPair = ([_datePicker valueForThemeAttribute:@"border-width"] % 2) == 0;
CGContextBeginPath(context);
CGContextSetStrokeColor(context, [_datePicker valueForThemeAttribute:@"border-color" inState:[_datePicker themeState]]);
CGContextSetLineWidth(context, [_datePicker valueForThemeAttribute:@"border-width"]);
if ([_datePicker isBordered])
{
for (var i = 0; i < 6; i++)
{
var y = i * height;
// Very usefull to avoid to have a line of two pixels instead one
if (!isBorderPair)
y += 0.5;
CGContextMoveToPoint(context, 0, y);
CGContextAddLineToPoint(context, [self bounds].size.width, y);
}
for (var i = 0; i < 7; i++)
{
var x = i * width;
// Very usefull to avoid to have a line of two pixels instead one
if (!isBorderPair)
x += 0.5;
CGContextMoveToPoint(context, x, 0);
CGContextAddLineToPoint(context, x, [self bounds].size.height);
}
}
else
{
var y = 0;
// Very usefull to avoid to have a line of two pixels instead one
if (!isBorderPair)
y += 0.5;
CGContextMoveToPoint(context, 0, y);
CGContextAddLineToPoint(context, [self bounds].size.width, y);
}
CGContextStrokePath(context);
CGContextClosePath(context);
}
// MARK: -
// MARK: Mouse event
/*! Mouse down event
*/
- (void)mouseDown:(CPEvent)anEvent
{
if (![self isEnabled])
return;
var dayTile = [_dayTiles objectAtIndex:[self indexOfTileForEvent:anEvent]],
dateTile = [[dayTile date] copy],
dateValue = [_datePicker dateValue];
_clickDate = [dateTile copy];
_dragDate = nil;
_indexDayTile = -1;
_eventDragged = nil;
#if PLATFORM(DOM)
_datePicker._invokedByUserEvent = YES;
#endif
// Check if we have to change or not the month of the component
if ([dayTile date].getMonth() == _date.getMonth())
{
if ([_datePicker datePickerMode] == CPRangeDateMode && [anEvent modifierFlags] & CPShiftKeyMask)
{
var dateValueAtMidnight = [[_datePicker dateValue] copy];
[dateValueAtMidnight _resetToMidnight];
if (dateTile < dateValueAtMidnight)
{
var interval;
if (dateTile == dateValueAtMidnight)
interval = [_datePicker timeInterval];
else
interval = ([dateValueAtMidnight timeIntervalSinceDate:dateTile] + [_datePicker timeInterval]);
[_datePicker _setDateValue:[self _hoursMinutesSecondsFromDatePickerForDate:dateTile] timeInterval:interval];
}
else if ([[dayTile date] isEqualToDate:dateValueAtMidnight])
{
[_datePicker _setDateValue:[self _hoursMinutesSecondsFromDatePickerForDate:dateTile] timeInterval:0];
}
else
{
[_datePicker _setDateValue:[self _hoursMinutesSecondsFromDatePickerForDate:[dateValueAtMidnight copy]] timeInterval:([dateTile timeIntervalSinceDate:dateValueAtMidnight])];
}
// Be sure to display the good month
[_delegate setDateValue:dateTile];
}
else
{
var minDate = [[_datePicker minDate] copy],
maxDate = [[_datePicker maxDate] copy];
[minDate _resetToMidnight];
[maxDate _resetToLastSeconds];
if (dateTile >= minDate && dateTile <= maxDate)
[_datePicker _setDateValue:[self _hoursMinutesSecondsFromDatePickerForDate:dateTile] timeInterval:0];
}
}
else
{
// Check the year and the month. The year is usefull when changing from Jan to Dec.
if (_date.getMonth() - [dayTile date].getMonth() == 1 || _date.getFullYear() - [dayTile date].getFullYear() == 1)
[_delegate _displayPreviousMonth];
else
[_delegate _displayNextMonth];
}
#if PLATFORM(DOM)
_datePicker._invokedByUserEvent = NO;
#endif
}
/*! Mouse dragged event
*/
- (void)mouseDragged:(CPEvent)anEvent
{
if (![self isEnabled] || !CGRectContainsPoint([self bounds],[self convertPoint:[anEvent locationInWindow] fromView:nil]))
return;
var dayTile = [_dayTiles objectAtIndex:[self indexOfTileForEvent:anEvent]],
dateTile = [[dayTile date] copy],
dateValue = [_datePicker dateValue];
_dragDate = [dateTile copy];
_indexDayTile = [self indexOfTileForEvent:anEvent];
_eventDragged = anEvent;
#if PLATFORM(DOM)
_datePicker._invokedByUserEvent = YES;
#endif
if ([_datePicker datePickerMode] == CPSingleDateMode)
{
// Check if we have to change or not the month of the component
if ([dayTile date].getMonth() == _date.getMonth())
{
[_timerMonth invalidate];
_isMonthJustChanged = NO;
[_datePicker _setDateValue:[self _hoursMinutesSecondsFromDatePickerForDate:dateTile] timeInterval:0];
}
else if (!_isMonthJustChanged)
{
[_timerMonth invalidate];
_isMonthJustChanged = NO;
// Check the year and the month. The year is usefull when changing from Jan to Dec.
if (_date.getMonth() - [dayTile date].getMonth() == 1 || _date.getFullYear() - [dayTile date].getFullYear() == 1)
[_delegate _displayPreviousMonth];
else
[_delegate _displayNextMonth];
}
}
else
{
if (dateTile.getMonth() == _date.getMonth() || !_isMonthJustChanged)
{
[_timerMonth invalidate];
_isMonthJustChanged = NO;
var dateValueAtMidnight = [[_datePicker dateValue] copy];
[dateValueAtMidnight _resetToMidnight];
if (dateTile < _clickDate)
[_datePicker _setDateValue:[self _hoursMinutesSecondsFromDatePickerForDate:dateTile] timeInterval:[_clickDate timeIntervalSinceDate:dateTile]];
else if ([[dayTile date] isEqualToDate:_clickDate])
[_datePicker _setDateValue:[self _hoursMinutesSecondsFromDatePickerForDate:dateTile] timeInterval:0];
else
[_datePicker _setDateValue:[self _hoursMinutesSecondsFromDatePickerForDate:_clickDate] timeInterval:[dateTile timeIntervalSinceDate:dateValueAtMidnight]];
}
}
#if PLATFORM(DOM)
_datePicker._invokedByUserEvent = NO;
#endif
}
- (void)mouseUp:(CPEvent)anEvent
{
[_timerMonth invalidate];
_dragDate = nil;
_clickDate = nil;
_isMonthJustChanged = NO;
_indexDayTile = -1;
_eventDragged = nil;
}
// MARK: -
// MARK: Timer
- (void)_timerNextMonthEvent:(CPEvent)anEvent
{
if (_isMonthJustChanged)
{
_dragDate.setMonth(_date.getMonth() + 1);
[_delegate _displayNextMonth];
}
}
- (void)_timerPreviousMonthEvent:(CPEvent)anEvent
{
if (_isMonthJustChanged)
{
_dragDate.setMonth(_date.getMonth() - 1);
[_delegate _displayPreviousMonth];
}
}
// MARK: -
// MARK: Date methods
- (CPDate)_hoursMinutesSecondsFromDatePickerForDate:(CPDate)aDate
{
var dateValue = [_datePicker dateValue];
aDate.setHours(dateValue.getHours());
aDate.setMinutes(dateValue.getMinutes());
aDate.setSeconds(dateValue.getSeconds());
return aDate;
}
@end
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -69,7 +69,7 @@
aNewObject._controller = self;
aNewObject._key = aKey;
if (aValue !== nil)
if (aValue != nil)
[aNewObject setValue:aValue];
return aNewObject;
@@ -98,7 +98,7 @@
var iter = [[CPSet setWithArray:allKeys] objectEnumerator],
obj;
while ((obj = [iter nextObject]) !== nil)
while ((obj = [iter nextObject]) != nil)
if (![_excludedKeys containsObject:obj])
[array addObject:[self _newObjectWithKey:obj value:nil]];
+26 -14
View File
@@ -515,7 +515,9 @@ var CPDocumentUntitledCount = 0;
alert("There was an error retrieving the document.");
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
var theDelegate = session.delegate;
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
}
else
{
@@ -540,7 +542,9 @@ var CPDocumentUntitledCount = 0;
_writeRequest = nil;
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
var theDelegate = session.delegate;
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
[self _sendDocumentSavedNotification:NO];
}
}
@@ -553,14 +557,15 @@ var CPDocumentUntitledCount = 0;
*/
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
{
var session = aConnection.session;
var session = aConnection.session,
theDelegate = session.delegate;
// READ
if (aConnection == _readConnection)
{
[self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil];
objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, YES, session.contextInfo);
}
else
{
@@ -569,7 +574,7 @@ var CPDocumentUntitledCount = 0;
_writeRequest = nil;
objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, YES, session.contextInfo);
[self _sendDocumentSavedNotification:YES];
}
}
@@ -580,10 +585,11 @@ var CPDocumentUntitledCount = 0;
*/
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
{
var session = aConnection.session;
var session = aConnection.session,
theDelegate = session.delegate;
if (_readConnection == aConnection)
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
else
{
@@ -597,7 +603,7 @@ var CPDocumentUntitledCount = 0;
alert("There was an error saving the document.");
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
[self _sendDocumentSavedNotification:NO];
}
}
@@ -858,21 +864,27 @@ var CPDocumentUntitledCount = 0;
[self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}];
else if ([delegate respondsToSelector:selector])
objj_msgSend(delegate, selector, self, YES, info);
delegate.isa.objj_msgSend3(delegate, selector, self, YES, info);
}
- (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
{
if (aDocument === self && shouldClose)
var theDelegate = context.delegate;
// Only close the document explicitly if there is NO delegate to handle the action.
// If a delegate exists (e.g., the CPWindow), it is responsible for performing the close
// upon receiving the callback below. Calling [self close] here would cause a double-close.
if (aDocument === self && shouldClose && theDelegate == nil)
[self close];
objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context);
if (theDelegate)
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, aDocument, shouldClose, context.context);
}
- (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context
{
if (![self isDocumentEdited])
return [aDelegate respondsToSelector:aSelector] && objj_msgSend(aDelegate, aSelector, self, YES, context);
return [aDelegate respondsToSelector:aSelector] && aDelegate.isa.objj_msgSend3(aDelegate, aSelector, self, YES, context);
_canCloseAlert = [[CPAlert alloc] init];
@@ -901,8 +913,8 @@ var CPDocumentUntitledCount = 0;
if (returnCode === 0)
[self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context];
else
objj_msgSend(delegate, selector, self, returnCode === 2, context);
else if (delegate != null)
delegate.isa.objj_msgSend3(delegate, selector, self, returnCode === 2, context);
_canCloseAlert = nil;
}
+6 -4
View File
@@ -106,7 +106,7 @@ var CPSharedDocumentController = nil;
@param aType the type of the new document
@param shouldDisplay whether to display the document on screen
*/
- (void)openUntitledDocumentOfType:(CPString)aType display:(BOOL)shouldDisplay
- (CPDocument)openUntitledDocumentOfType:(CPString)aType display:(BOOL)shouldDisplay
{
var theDocument = [self makeUntitledDocumentOfType:aType error:nil];
@@ -297,7 +297,7 @@ var CPSharedDocumentController = nil;
var iter = [_documents objectEnumerator],
obj;
while ((obj = [iter nextObject]) !== nil)
while ((obj = [iter nextObject]) != nil)
{
if ([obj isDocumentEdited])
return YES;
@@ -402,8 +402,10 @@ var CPSharedDocumentController = nil;
}
}
if ([context.delegate respondsToSelector:context.selector])
objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
var theDelegate = context.delegate;
if ([theDelegate respondsToSelector:context.selector])
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, self, [[self documents] count] === 0, context.context);
}
@end
+56 -2
View File
@@ -26,6 +26,7 @@
@import "CPPasteboard.j"
@import "CPView.j"
@import "CPWindow_Constants.j"
@import "CPViewAnimation.j"
@class CPWindow // This file is imported by CPWindow.j
@class _CPDOMDataTransferPasteboard
@@ -132,6 +133,10 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
unsigned _dragOperation;
CPTimer _draggingUpdateTimer;
// Animation State
CGPoint _pendingEndLocation;
CPDragOperation _pendingEndOperation;
}
/*
@@ -261,10 +266,19 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
{
var contentView = [scrollView contentView],
bounds = [contentView bounds],
insetBounds = CGRectInset(bounds, 30, 30),
eventLocation = [contentView convertPoint:_draggingLocation fromView:nil],
deltaX = 0,
deltaY = 0;
deltaY = 0,
insetSize = 30;
if ([contentView respondsToSelector:@selector(documentView)] &&
[[contentView documentView] respondsToSelector:@selector(rowHeight)])
{
// Adjust inset bounds based on CPTableView row height
insetSize = MAX(insetSize, [[contentView documentView] rowHeight]);
}
var insetBounds = CGRectInset(bounds, insetSize, insetSize);
if (!CGRectContainsPoint(insetBounds, eventLocation))
{
@@ -316,6 +330,46 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
[_draggingUpdateTimer invalidate];
_draggingUpdateTimer = nil;
// Check if we should slide back (drag failed + slideBack requested)
if (![CPPlatform supportsDragAndDrop] && _shouldSlideBack && anOperation === CPDragOperationNone)
{
// Store state to finalize drag after animation completes
_pendingEndLocation = aLocation;
_pendingEndOperation = anOperation;
var currentFrame = [_draggedWindow frame],
targetFrame = CGRectMake(_startDragLocation.x, _startDragLocation.y, currentFrame.size.width, currentFrame.size.height);
// We use CPViewAnimation. Even though _draggedWindow is a CPWindow,
// CPViewAnimation supports targets that respond to setFrame: (like NSViewAnimation does for NSWindow).
var animation = [[CPViewAnimation alloc] initWithViewAnimations:[
[CPDictionary dictionaryWithObjects:[_draggedWindow, currentFrame, targetFrame]
forKeys:[CPViewAnimationTargetKey, CPViewAnimationStartFrameKey, CPViewAnimationEndFrameKey]]
]];
[animation setAnimationCurve:CPAnimationEaseOut];
[animation setDuration:0.25];
[animation setDelegate:self];
[animation startAnimation];
return;
}
[self _performFinalCleanupWithLocation:aLocation operation:anOperation];
}
- (void)animationDidEnd:(CPAnimation)anAnimation
{
[self _performFinalCleanupWithLocation:_pendingEndLocation operation:_pendingEndOperation];
}
- (void)animationDidStop:(CPAnimation)anAnimation
{
[self _performFinalCleanupWithLocation:_pendingEndLocation operation:_pendingEndOperation];
}
- (void)_performFinalCleanupWithLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation
{
[_draggedView removeFromSuperview];
if (![CPPlatform supportsDragAndDrop])
+99 -6
View File
@@ -28,13 +28,16 @@
@import "CPCompatibility.j"
@import "CGGeometry.j"
@import "CPText.j"
@import "CPTrackingArea.j"
@class CPTextField
@class CPWindow
@class CPGraphicsContext
@global CPApp
@global CPNewlineCharacter
@global CPCarriageReturnCharacter
@global CPEnterCharacter
@typedef DOMEvent
@typedef CPEventType
@@ -63,10 +66,11 @@ var _CPEventPeriodicEventPeriod = 0,
CPWindow _window;
Number _windowNumber;
CPString _characters;
CPString _charactersIgnoringModifiers
CPString _charactersIgnoringModifiers;
BOOL _isARepeat;
unsigned _keyCode;
DOMEvent _DOMEvent;
BOOL _isActionKey;
int _data1;
int _data2;
short _subtype;
@@ -82,6 +86,8 @@ var _CPEventPeriodicEventPeriod = 0,
BOOL _suppressCappuccinoCut;
BOOL _suppressCappuccinoPaste;
#endif
CPTrackingArea _trackingArea;
}
/*!
@@ -105,17 +111,28 @@ var _CPEventPeriodicEventPeriod = 0,
@param unmodCharacters the string of keys pressed without the presence of any modifiers other than Shift
@param repeatKey \c YES if this is caused by the system repeat as opposed to the user pressing the key again
@param code a number associated with the keyboard key of this event
@param isAnActionKey a BOOL indicating whether this key is an action key (e.g. a function key)
@throws CPInternalInconsistencyException if \c anEventType is not a CPKeyDown,
CPKeyUp or CPFlagsChanged
@return the keyboard event
*/
+ (CPEvent)keyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code isActionKey:(BOOL)isAnActionKey
{
return [[self alloc] _initKeyEventWithType:anEventType location:aPoint modifierFlags:modifierFlags
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext
characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code];
characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code isActionKey:isAnActionKey];
}
// for backwards compatibility only
+ (CPEvent)keyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code
{
return [[self alloc] _initKeyEventWithType:anEventType location:aPoint modifierFlags:modifierFlags
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext
characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code isActionKey:NO];
}
/*!
@@ -141,6 +158,27 @@ var _CPEventPeriodicEventPeriod = 0,
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure];
}
/*!
Creates a new mouse tracking event.
@param anEventType the event type
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
@param aTimestamp the time the event occurred
@param aWindowNumber the number of the CPWindow where the event occurred
@param aGraphicsContext the graphics context where the event occurred
@param anEventNumber a number for this event
@param aTrackingArea the tracking area that triggered the event
@throws CPInternalInconsistencyException if an invalid event type is provided
@return the new mouse event
*/
+ (id)enterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
{
return [[self alloc] _initEnterExitEventWithType:anEventType location:aPoint modifierFlags:modifierFlags timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber trackingArea:aTrackingArea];
}
/*!
Creates a new custom event.
@@ -201,10 +239,32 @@ var _CPEventPeriodicEventPeriod = 0,
return self;
}
/* @ignore */
- (id)_initEnterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
{
if ((anEventType != CPMouseEntered) && (anEventType != CPMouseExited) && (anEventType != CPCursorUpdate))
[CPException raise:CPInternalInconsistencyException reason:"Invalid event type"];
if (self = [self _initWithType:anEventType])
{
_location = CGPointCreateCopy(aPoint);
_modifierFlags = modifierFlags;
_timestamp = aTimestamp;
_context = aGraphicsContext;
_eventNumber = anEventNumber;
_trackingArea = aTrackingArea;
_window = [CPApp windowWithWindowNumber:aWindowNumber];
}
return self;
}
/* @ignore */
- (id)_initKeyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code isActionKey:(BOOL)isAnActionKey
{
if (self = [self _initWithType:anEventType])
{
@@ -216,6 +276,7 @@ var _CPEventPeriodicEventPeriod = 0,
_charactersIgnoringModifiers = unmodCharacters;
_isARepeat = isARepeat;
_keyCode = code;
_isActionKey = isAnActionKey;
_windowNumber = aWindowNumber;
}
@@ -236,6 +297,7 @@ var _CPEventPeriodicEventPeriod = 0,
_subtype = aSubtype;
_data1 = aData1;
_data2 = aData2;
_windowNumber = aWindowNumber;
}
return self;
@@ -289,6 +351,14 @@ var _CPEventPeriodicEventPeriod = 0,
return _type;
}
/*!
Returns the subtype of the event.
*/
- (CPEventType)subtype
{
return _subtype;
}
/*!
Returns the event's associated window.
*/
@@ -514,6 +584,21 @@ var _CPEventPeriodicEventPeriod = 0,
return !firstResponderIsText;
}
- (BOOL)_isActionOrCommandEvent
{
// This method is now platform-agnostic. It checks for abstract properties
// of the event, including the _isActionKey flag that was set at creation time.
return (
// Is it a command shortcut?
(_modifierFlags & (CPCommandKeyMask | CPControlKeyMask)) ||
// Is it a key that doesn't produce a character?
([_characters length] === 0) ||
// Was it identified as an action key by the platform-specific layer?
_isActionKey
);
}
/*!
Return YES if this event is a part of processing a browser controlled cut or paste event
where the browser will go ahead and do the work of cutting or pasting within the input
@@ -551,7 +636,7 @@ var _CPEventPeriodicEventPeriod = 0,
*/
+ (void)stopPeriodicEvents
{
if (_CPEventPeriodicEventTimer === nil)
if (_CPEventPeriodicEventTimer == nil)
return;
window.clearTimeout(_CPEventPeriodicEventTimer);
@@ -582,6 +667,14 @@ var _CPEventPeriodicEventPeriod = 0,
}
}
- (CPTrackingArea)trackingArea
{
if ((_type !== CPMouseEntered) && (_type !== CPMouseExited) && (_type !== CPCursorUpdate))
[CPException raise:CPInternalInconsistencyException format:@"You can't call trackingArea for events of type %#x", _type];
return _trackingArea;
}
@end
function _CPEventFirePeriodEvent()
-85
View File
@@ -1,85 +0,0 @@
/*
* CPFlashMovie.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2008, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPObject.j>
/*!
@ingroup appkit
@class CPFlashMovie
CPFlashMovie is used to represent a Flash movie in the Cappuccino framework.
*/
@implementation CPFlashMovie : CPObject
{
CPString _filename;
}
/*!
Creates a new Flash movie with the swf at \c aFileName.
@param aFilename the swf to load
@return the initialized CPFlashMovie
*/
+ (id)flashMovieWithFile:(CPString)aFilename
{
return [[self alloc] initWithFile:aFilename];
}
/*!
Initializes the Flash movie.
@param aFilename the swf to load
@return the initialized CPFlashMovie
*/
- (id)initWithFile:(CPString)aFilename
{
self = [super init];
if (self)
_filename = aFilename;
return self;
}
- (CPString)filename
{
return _filename;
}
@end
var CPFlashMovieFilenameKey = "CPFlashMovieFilenameKey";
@implementation CPFlashMovie (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
_filename = [aCoder decodeObjectForKey:CPFlashMovieFilenameKey];
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_filename forKey:CPFlashMovieFilenameKey];
}
@end
-212
View File
@@ -1,212 +0,0 @@
/*
* CPFlashView.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2008, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPFlashMovie.j"
@import "CPView.j"
var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
/*!
@ingroup appkit
*/
@implementation CPFlashView : CPView
{
CPFlashMovie _flashMovie;
CPDictionary _params;
CPDictionary _paramElements;
#if PLATFORM(DOM)
DOMElement _DOMParamElement;
DOMElement _DOMObjectElement;
#endif
}
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
if (self)
{
#if PLATFORM(DOM)
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
{
_DOMObjectElement = document.createElement(@"object");
_DOMObjectElement.id = [self elementID];
_DOMObjectElement.width = @"100%";
_DOMObjectElement.height = @"100%";
_DOMObjectElement.style.top = @"0px";
_DOMObjectElement.style.left = @"0px";
_DOMObjectElement.type = @"application/x-shockwave-flash";
_DOMParamElement = document.createElement(@"param");
_DOMParamElement.name = @"movie";
_DOMObjectElement.appendChild(_DOMParamElement);
_DOMElement.appendChild(_DOMObjectElement);
}
else
[self _rebuildIEObjects];
#endif
}
return self;
}
- (void)setFlashMovie:(CPFlashMovie)aFlashMovie
{
if (_flashMovie == aFlashMovie)
return;
_flashMovie = aFlashMovie;
#if PLATFORM(DOM)
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
{
_DOMParamElement.value = [aFlashMovie filename];
_DOMObjectElement.data = [aFlashMovie filename];
}
else
[self _rebuildIEObjects];
#endif
}
- (CPFlashMovie)flashMovie
{
return _flashMovie;
}
- (void)setFlashVars:(CPDictionary)aDictionary
{
var varString = @"",
enumerator = [aDictionary keyEnumerator],
key;
if (key = [enumerator nextObject])
varString = [varString stringByAppendingFormat:@"%@=%@", key, [aDictionary objectForKey:key]];
while (key = [enumerator nextObject])
varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]];
if (!_params)
_params = @{};
[_params setObject:varString forKey:@"flashvars"];
[self setParameters:_params];
}
- (CPDictionary)flashVars
{
return [_params objectForKey:@"flashvars"];
}
- (void)setParameters:(CPDictionary)aDictionary
{
#if PLATFORM(DOM)
if (_paramElements && !CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
{
var elements = [_paramElements allValues],
count = [elements count];
for (var i = 0; i < count; i++)
_DOMObjectElement.removeChild([elements objectAtIndex:i]);
}
#endif
if (!_params)
_params = aDictionary;
else
[_params addEntriesFromDictionary:aDictionary];
#if PLATFORM(DOM)
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
{
_paramElements = @{};
var enumerator = [_params keyEnumerator],
key;
while (_DOMObjectElement && (key = [enumerator nextObject]) !== nil)
{
var param = document.createElement(@"param");
param.name = key;
param.value = [_params objectForKey:key];
_DOMObjectElement.appendChild(param);
[_paramElements setObject:param forKey:key];
}
}
else
[self _rebuildIEObjects];
#endif
}
- (CPDictionary)parameters
{
return _params;
}
#if PLATFORM(DOM)
- (void)_rebuildIEObjects
{
_DOMElement.innerHTML = @"";
if (![_flashMovie filename])
return;
var paramString = [CPString stringWithFormat:@"<param name='movie' value='%@' />", [_flashMovie filename]],
paramEnumerator = [_params keyEnumerator],
key;
while ((key = [paramEnumerator nextObject]) !== nil)
paramString = [paramString stringByAppendingFormat:@"<param name='%@' value='%@' />", key, [_params objectForKey:key]];
_DOMObjectElement = document.createElement(@"object");
_DOMElement.appendChild(_DOMObjectElement);
_DOMObjectElement.outerHTML = [CPString stringWithFormat:@"<object id=%@ classid=%@ width=%@ height=%@>%@</object>", [self elementID], IEFlashCLSID, CGRectGetWidth([self bounds]), CGRectGetHeight([self bounds]), paramString];
}
#endif
- (CPString)elementID
{
return @"CPFV_" + [self UID];
}
- (void)mouseMoved:(CPEvent)sommit
{
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
}
- (void)mouseDragged:(CPEvent)anEvent
{
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
}
- (void)mouseDown:(CPEvent)anEvent
{
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
}
- (void)mouseUp:(CPEvent)anEvent
{
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
}
@end
+170 -12
View File
@@ -24,6 +24,9 @@
@import <Foundation/CPBundle.j>
@import "CPView.j"
@import "CPFontDescriptor.j"
@import "_CPObject+Theme.j"
@import "CPControl.j"
CPFontDefaultSystemFontFace = @"Arial, sans-serif";
CPFontDefaultSystemFontSize = 12;
@@ -37,12 +40,15 @@ CPFontCurrentSystemSize = -1;
// For internal use only by this class and subclasses
_CPFontSystemFacePlaceholder = "_CPFontSystemFacePlaceholder";
var _CPFontCache = {},
_CPSystemFontCache = {},
_CPFontSystemFontFace = CPFontDefaultSystemFontFace,
_CPFontSystemFontSize = 12,
_CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
_CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g");
var _CPFontCache = {},
_CPSystemFontCache = {},
_CPFontSystemFontFace = CPFontDefaultSystemFontFace,
_CPFontSystemFontSize = CPFontDefaultSystemFontSize,
_CPFontSystemFontSizeSmall = CPFontDefaultSystemFontSize - 1,
_CPFontSystemFontSizeMini = CPFontDefaultSystemFontSize - 2,
_CPFontFallbackFaces = CPFontDefaultSystemFontFace.split(", "),
_CPFontStripRegExp = new RegExp("(^\\s*[\"']?|[\"']?\\s*$)", "g"),
_CPFontSystemFontFaceSpecified = NO;
#define _CPRealFontSize(aSize) (aSize <= 0 ? _CPFontSystemFontSize : aSize)
@@ -108,7 +114,7 @@ following:
<string>Asap</string>
@endcode
*/
@implementation CPFont : CPObject
@implementation CPFont : CPObject <CPTheme>
{
CPString _name;
float _size;
@@ -122,6 +128,22 @@ following:
CPString _cssString;
}
+ (CPString)defaultThemeClass
{
return @"font";
}
+ (CPDictionary)themeAttributes
{
return @{
@"system-font-face": [CPNull null],
@"system-font-style": [CPNull null],
@"system-font-size-regular": [CPNull null],
@"system-font-size-small": [CPNull null],
@"system-font-size-mini": [CPNull null]
};
}
+ (void)initialize
{
if (self !== [CPFont class])
@@ -133,7 +155,10 @@ following:
systemFontFace = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontFace"];
if (systemFontFace)
{
_CPFontSystemFontFace = _CPFontNormalizedNames(systemFontFace);
_CPFontSystemFontFaceSpecified = YES;
}
var systemFontSize = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPSystemFontSize"];
@@ -141,7 +166,62 @@ following:
systemFontSize = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:@"CPSystemFontSize"];
if (systemFontSize)
{
_CPFontSystemFontSize = systemFontSize;
_CPFontSystemFontFaceSpecified = YES;
}
}
+ (void)initializeSystemFontFromTheme:(CPTheme)aTheme
{
// If something was specified via +initialize (from an Info.plist file), don't do anything
if (_CPFontSystemFontFaceSpecified)
return;
// Reset all system font caches
_CPSystemFontCache = {};
// Now, try to get information from the theme
var systemFontFace = [aTheme valueForAttributeWithName:@"system-font-face" forClass:[CPFont class]];
if (systemFontFace)
{
[self _invalidateSystemFontCache];
_CPFontSystemFontFace = _CPFontNormalizedNames(systemFontFace);
}
var systemFontSize = [aTheme valueForAttributeWithName:@"system-font-size-regular" forClass:[CPFont class]];
if (systemFontSize)
{
[self _invalidateSystemFontCache];
_CPFontSystemFontSize = systemFontSize;
}
systemFontSize = [aTheme valueForAttributeWithName:@"system-font-size-small" forClass:[CPFont class]];
if (systemFontSize)
{
[self _invalidateSystemFontCache];
_CPFontSystemFontSizeSmall = systemFontSize;
}
systemFontSize = [aTheme valueForAttributeWithName:@"system-font-size-mini" forClass:[CPFont class]];
if (systemFontSize)
{
[self _invalidateSystemFontCache];
_CPFontSystemFontSizeMini = systemFontSize;
}
// Is there something to add to the global syle definition ?
var systemFontStyle = [aTheme valueForAttributeWithName:@"system-font-style" forClass:[CPFont class]];
if (systemFontStyle)
{
// Yes, so install it in the DOM Style element
document.getElementsByTagName("STYLE")[0].innerHTML += "\n" + [aTheme setCSSResourcesPathInString:systemFontStyle];
}
}
/*!
@@ -162,7 +242,7 @@ following:
if (normalizedFaces === _CPFontSystemFontFace)
return;
[self _invalidateSystemFontCache]
[self _invalidateSystemFontCache];
_CPFontSystemFontFace = aFace;
}
@@ -176,14 +256,13 @@ following:
+ (CPFont)systemFontForControlSize:(CPControlSize)aSize
{
// TODO These sizes should be themable or made less arbitrary in some other way.
switch (aSize)
{
case CPSmallControlSize:
return [self systemFontOfSize:_CPFontSystemFontSize - 1];
return [self systemFontOfSize:_CPFontSystemFontSizeSmall];
case CPMiniControlSize:
return [self systemFontOfSize:_CPFontSystemFontSize - 2];
return [self systemFontOfSize:_CPFontSystemFontSizeMini];
case CPRegularControlSize:
default:
@@ -314,6 +393,10 @@ following:
_isItalic = isItalic;
_isSystem = isSystem;
_theme = [CPTheme defaultTheme];
_themeState = CPThemeStateNormal;
[self _loadThemeAttributes];
if (isSystem)
{
_name = aName;
@@ -381,6 +464,22 @@ following:
return _CPRealFontSize(_size);
}
/*!
Returns the font size. Cocoa/AppKit compatibility alias for -size.
*/
- (float)pointSize
{
return [self size];
}
/*!
Returns the font name. Cocoa/AppKit compatibility alias for -familyName.
*/
- (CPString)fontName
{
return [self familyName];
}
/*!
Returns the font as a CSS string
*/
@@ -431,8 +530,61 @@ following:
_lineHeight = [metrics objectForKey:@"lineHeight"];
}
- (CPControlSize)controlSizeCorrespondingToFontSize
{
switch (_size)
{
case _CPFontSystemFontSizeSmall:
return CPSmallControlSize;
case _CPFontSystemFontSizeMini:
return CPMiniControlSize;
default:
// If we can't find a corresponding size, return regular control size
return CPRegularControlSize;
}
}
@end
@implementation CPFont(DescriptorAdditions)
- (id)_initWithFontDescriptor:(CPFontDescriptor)fontDescriptor
{
var aName = [fontDescriptor objectForKey: CPFontNameAttribute] ,
aSize = [fontDescriptor pointSize],
isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
return [self _initWithName:aName size:aSize bold:isBold italic:isItalic system:NO];
}
+ (CPFont)fontWithDescriptor:(CPFontDescriptor)fontDescriptor size:(float)aSize
{
var aName = [fontDescriptor objectForKey: CPFontNameAttribute],
isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
return [self _fontWithName:aName size:aSize || [fontDescriptor pointSize] bold:isBold italic:isItalic];
}
- (CPFontDescriptor)fontDescriptor
{
var traits = 0;
if ([self isBold])
traits |= CPFontBoldTrait;
if ([self isItalic])
traits |= CPFontItalicTrait;
return [[CPFontDescriptor fontDescriptorWithName:_name size:_size] fontDescriptorWithSymbolicTraits:traits];
}
@end
var CPFontNameKey = @"CPFontNameKey",
CPFontSizeKey = @"CPFontSizeKey",
CPFontIsBoldKey = @"CPFontIsBoldKey",
@@ -454,7 +606,11 @@ var CPFontNameKey = @"CPFontNameKey",
isItalic = [aCoder decodeBoolForKey:CPFontIsItalicKey],
isSystem = [aCoder decodeBoolForKey:CPFontIsSystemKey];
return [self _initWithName:fontName size:size bold:isBold italic:isItalic system:isSystem];
self = [self _initWithName:fontName size:size bold:isBold italic:isItalic system:isSystem];
[self _decodeThemeObjectsWithCoder:aCoder];
return self;
}
/*!
@@ -468,6 +624,8 @@ var CPFontNameKey = @"CPFontNameKey",
[aCoder encodeBool:_isBold forKey:CPFontIsBoldKey];
[aCoder encodeBool:_isItalic forKey:CPFontIsItalicKey];
[aCoder encodeBool:_isSystem forKey:CPFontIsSystemKey];
[self _encodeThemeObjectsWithCoder:aCoder];
}
@end
+218 -32
View File
@@ -22,9 +22,14 @@
@import <Foundation/CPObject.j>
@import "CPControl.j"
@import "CPFont.j"
@import "CPFontDescriptor.j"
@global CPApp
@class CPFontPanel
@global document
CPItalicFontMask = 1 << 0;
CPBoldFontMask = 1 << 1;
@@ -41,7 +46,20 @@ CPUnitalicFontMask = 1 << 24;
var CPSharedFontManager = nil,
CPFontManagerFactory = Nil;
CPFontManagerFactory = nil,
CPFontPanelFactory = nil;
/*
modifyFont: sender's tag
*/
CPNoFontChangeAction = 0;
CPViaPanelFontAction = 1;
CPAddTraitFontAction = 2;
CPSizeUpFontAction = 3;
CPSizeDownFontAction = 4;
CPHeavierFontAction = 5;
CPLighterFontAction = 6;
CPRemoveTraitFontAction = 7;
/*!
@ingroup appkit
@@ -59,6 +77,8 @@ var CPSharedFontManager = nil,
BOOL _multiple @accessors(getter=isMultiple, setter=setMultiple:);
CPDictionary _activeChange;
unsigned _fontAction;
}
// Getting the Shared Font Manager
@@ -83,6 +103,15 @@ var CPSharedFontManager = nil,
{
CPFontManagerFactory = aClass;
}
/*!
Sets the class that will be used to create the application's
Font panel.
*/
+ (void)setFontPanelFactory:(Class)aClass
{
CPFontPanelFactory = aClass;
}
- (id)init
{
@@ -163,35 +192,6 @@ var CPSharedFontManager = nil,
return ([aFont isBold] ? CPBoldFontMask : 0) | ([aFont isItalic] ? CPItalicFontMask : 0);
}
- (CPFont)convertFont:(CPFont)aFont
{
if (!_activeChange)
return aFont;
var addTraits = [_activeChange valueForKey:@"addTraits"];
if (addTraits)
aFont = [self convertFont:aFont toHaveTrait:addTraits];
return aFont;
}
- (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)addTraits
{
if (!aFont)
return nil;
var shouldBeBold = ([aFont isBold] || (addTraits & CPBoldFontMask)) && !(addTraits & CPUnboldFontMask),
shouldBeItalic = ([aFont isItalic] || (addTraits & CPItalicFontMask)) && !(addTraits & CPUnitalicFontMask),
shouldBeSize = [aFont size];
// XXX On the current platform there will always be a bold/italic version of each font, but still leave
// || aFont in here for future platforms.
aFont = [CPFont _fontWithName:[aFont familyName] size:shouldBeSize bold:shouldBeBold italic:shouldBeItalic] || aFont;
return aFont;
}
- (CPFont)convertFont:(CPFont)aFont toFace:(CPString)aTypeface
{
if (!aFont)
@@ -208,8 +208,13 @@ var CPSharedFontManager = nil,
- (@action)addFontTrait:(id)sender
{
var tag = [sender tag];
_activeChange = tag === nil ? @{} : @{ @"addTraits": tag };
var tag = sender;
if ([sender respondsToSelector:@selector(tag)])
tag = [sender tag];
_activeChange = tag == nil ? @{} : @{ @"addTraits": tag };
_fontAction = CPAddTraitFontAction;
[self sendAction];
}
@@ -219,6 +224,187 @@ var CPSharedFontManager = nil,
return [CPApp sendAction:_action to:_target from:self];
}
/*!
This method open the font panel, create it if necessary.
@param sender The object that sent the message.
*/
- (CPFontPanel)fontPanel:(BOOL)createIt
{
var panel = nil,
panelExists = [CPFontPanelFactory sharedFontPanelExists];
if ((panelExists) || (!panelExists && createIt))
panel = [CPFontPanelFactory sharedFontPanel];
return panel;
}
/*!
Convert a font to have the specified Font traits. The font is unchanged expect for the specified Font traits.
Using CPUnboldFontMask or CPUnitalicFontMask will respectively remove Bold and Italic traits.
@param aFont The font to convert.
@param fontTrait The new font traits mask.
@result The converted font or \c aFont if the conversion failed.
*/
- (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)fontTrait
{
var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
if (fontTrait & CPBoldFontMask)
symbolicTrait |= CPFontBoldTrait;
if (fontTrait & CPItalicFontMask)
symbolicTrait |= CPFontItalicTrait;
if (fontTrait & CPUnboldFontMask) /* FIXME: this only change CPFontSymbolicTrait what about CPFontWeightTrait */
symbolicTrait &= ~CPFontBoldTrait;
if (fontTrait & CPUnitalicFontMask)
symbolicTrait &= ~CPFontItalicTrait;
if (fontTrait & CPExpandedFontMask)
symbolicTrait |= CPFontExpandedTrait;
if (fontTrait & CPSmallCapsFontMask)
symbolicTrait |= CPFontSmallCapsTrait;
if (![attributes containsKey:CPFontTraitsAttribute])
[attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
forKey:CPFontSymbolicTrait]
forKey:CPFontTraitsAttribute];
else
[[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
forKey:CPFontSymbolicTrait];
return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
}
/*!
Convert a font to not have the specified Font traits. The font is unchanged expect for the specified Font traits.
@param aFont The font to convert.
@param fontTrait The font traits mask to remove.
@result The converted font or \c aFont if the conversion failed.
*/
- (CPFont)convertFont:(CPFont)aFont toNotHaveTrait:(CPFontTraitMask)fontTrait
{
var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
if ((fontTrait & CPBoldFontMask) || (fontTrait & CPUnboldFontMask)) /* FIXME: see convertFont:toHaveTrait: about CPFontWeightTrait */
symbolicTrait &= ~CPFontBoldTrait;
if ((fontTrait & CPItalicFontMask) || (fontTrait & CPUnitalicFontMask))
symbolicTrait &= ~CPFontItalicTrait;
if (fontTrait & CPExpandedFontMask)
symbolicTrait &= ~CPFontExpandedTrait;
if (fontTrait & CPSmallCapsFontMask)
symbolicTrait &= ~CPFontSmallCapsTrait;
if (![attributes containsKey:CPFontTraitsAttribute])
[attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
forKey:CPFontSymbolicTrait]
forKey:CPFontTraitsAttribute];
else
[[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
forKey:CPFontSymbolicTrait];
return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
}
/*!
Convert a font to have specified size. The font is unchanged expect for the specified size.
@param aFont The font to convert.
@param aSize The new font size.
@result The converted font or \c aFont if the conversion failed.
*/
- (CPFont)convertFont:(CPFont)aFont toSize:(float)aSize
{
var descriptor = [aFont fontDescriptor];
return [[aFont class] fontWithDescriptor: descriptor size:aSize]
}
- (void)orderFrontFontPanel:(id)sender
{
[[self fontPanel:YES] orderFront:sender];
}
- (void)modifyFont:(id)sender
{
_fontAction = [sender tag];
[self sendAction];
if (_selectedFont)
[self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
}
/*!
This method causes the receiver to send its action message.
@param sender The object that sent the message. (a Font panel)
*/
- (void)modifyFontViaPanel:(id)sender
{
_fontAction = CPViaPanelFontAction;
if (_selectedFont)
[self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
[self sendAction];
}
/*!
Convert a font according to current font changes, provided by the object that initiated the font change.
@param aFont The font to convert.
@result The converted font or \c aFont if the conversion failed.
*/
- (CPFont)convertFont:(CPFont)aFont
{
var newFont = nil;
switch (_fontAction)
{
case CPNoFontChangeAction:
newFont = aFont;
break;
case CPViaPanelFontAction:
newFont = [[self fontPanel:NO] panelConvertFont:aFont];
break;
case CPAddTraitFontAction:
newFont = aFont;
if (!_activeChange)
break;
var addTraits = [_activeChange valueForKey:@"addTraits"];
if (addTraits)
newFont = [self convertFont:aFont toHaveTrait:addTraits];
break;
case CPSizeUpFontAction:
newFont = [self convertFont:aFont toSize:[aFont size] + 1.0]; /* any limit ? */
break;
case CPSizeDownFontAction:
if ([aFont size] > 1)
newFont = [self convertFont:aFont toSize:[aFont size] - 1.0];
/* else CPBeep() :-p */
break;
default:
CPLog.trace(@"-[" + [self className] + " " + _cmd + "] unsupported font action: " + _fontAction + " aFont unchanged");
newFont = aFont;
break;
}
return newFont;
}
@end
var _CPFontDetectSpan,
+6 -5
View File
@@ -70,11 +70,12 @@ CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
{
if (self = [super init])
{
var cgColors = [],
count = [someColors count],
colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB;
for (var i = 0; i < count; i++)
cgColors.push(CGColorCreate(colorSpace, [someColors[i] components]));
var colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB,
cgColors = [someColors arrayByApplyingBlock:function(color)
{
return CGColorCreate(colorSpace, [color components])
}];
_gradient = CGGradientCreateWithColors(colorSpace, cgColors, someLocations);
}
+4 -4
View File
@@ -43,10 +43,10 @@ function CPDrawTiledRects(
if (sides.length != grays.length)
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."];
var colors = [];
for (var i = 0; i < grays.length; ++i)
colors.push([CPColor colorWithCalibratedWhite:grays[i] alpha:1.0]);
var colors = [grays arrayByApplyingBlock:function(gray)
{
return [CPColor colorWithCalibratedWhite:gray alpha:1.0];
}];
return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors);
}
+1 -1
View File
@@ -92,7 +92,7 @@ var CPGraphicsContextCurrent = nil,
@param aGraphicsPort the graphics port to initialize with
@return the initialized context
*/
- (id)initWithGraphicsPort:(CPContext)aGraphicsPort
- (id)initWithGraphicsPort:(CGContext)aGraphicsPort
{
self = [super init];
+460 -7
View File
@@ -26,10 +26,15 @@
@import <Foundation/CPRunLoop.j>
@import <Foundation/CPString.j>
@import <Foundation/CPData.j>
@import <Foundation/CPKeyedArchiver.j>
@import <Foundation/CPKeyedUnarchiver.j>
@import "CGGeometry.j"
@import "CPCompatibility.j"
@import "CPGraphicsContext.j"
@class CPColor
@global document
@protocol CPImageDelegate <CPObject>
@@ -85,9 +90,7 @@ function CPImageInBundle()
if (typeof(arguments[1]) === "number")
{
if (arguments[1] !== nil && arguments[1] !== undefined)
size = CGSizeMake(arguments[1], arguments[2]);
size = CGSizeMake(arguments[1], arguments[2]);
bundle = arguments[3];
}
else if (typeof(arguments[1]) === "object")
@@ -161,7 +164,7 @@ function CPAppKitImage(aFilename, aSize)
- (id)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize
{
// Quietly return nil like in Cocoa, rather than crashing later.
if (aFilename === undefined || aFilename === nil)
if (aFilename == nil)
return nil;
self = [super init];
@@ -246,7 +249,7 @@ function CPAppKitImage(aFilename, aSize)
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
canvas.width = _image.width,
canvas.width = _image.width;
canvas.height = _image.height;
ctx.drawImage(_image, 0, 0);
@@ -275,7 +278,7 @@ function CPAppKitImage(aFilename, aSize)
*/
- (CGSize)size
{
return _size;
return CGSizeMakeCopy(_size);
}
+ (id)imageNamed:(CPString)aName
@@ -446,6 +449,11 @@ function CPAppKitImage(aFilename, aSize)
return NO;
}
- (BOOL)isMaterialIconImage
{
return NO;
}
- (CPString)description
{
var filename = [self filename],
@@ -509,6 +517,229 @@ function CPAppKitImage(aFilename, aSize)
@end
// MARK: -
// MARK: CSS Theming
// The code below adds support for CSS theming with 100% compatibility with current theming system.
// The idea is to extend CPImage (and CPColor) with CSS components and adapt low level UI components to
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
//
// To create a CPImage that uses CSS, simply use the new class method :
// + (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
// where beforeDictionary & afterDictionary are related to ::before & ::after pseudo-elements.
// If you don't need them, just use the simplified class method :
// + (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
//
// Examples :
// regularImageNormal = [CPImage imageWithCSSDictionary:@{
// @"border-color": A3ColorActiveBorder,
// @"border-style": @"solid",
// @"border-width": @"1px",
// @"border-radius": @"50%",
// @"box-sizing": @"border-box",
// @"background-color": A3ColorBackgroundWhite,
// @"transition-duration": @"0.35s",
// @"transition-property": @"all",
// @"transition-timing-function": @"ease"
// }
// size:CGSizeMake(16,16)];
//
// imageSearch = [CPImage imageWithCSSDictionary:@{
// @"background-image": @"url(%%packed.png)",
// @"background-position": @"-16px -32px",
// @"background-repeat": @"no-repeat",
// @"background-size": @"100px 400px"
// }
// size:CGSizeMake(16,16)];
//
// Remark : Please note the special URL of the background image used in this example : url(%%packed.png)
// During theme loading, "%%" will be replaced by the path to the theme blend resources folder.
// Typically, a CSS theme will use some (rare) images all packed together in a single image resource (see packed.png in Aristo3 theme)
//
// Also, please note that if you don't use one of the CSS components, you can either set it to nil (best solution) or to an empty dictionary, like :
// aCssImage = [CPImage imageWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:@{ ... }];
//
// You can use -(BOOL)isCSSBased to determine how to cope with it in your code.
// -(BOOL)hasCSSDictionary, -(BOOL)hasCSSBeforeDictionary and -(BOOL)hasCSSAfterDictionary are convience methods you can use.
//
// Remark : -(DOMElement)applyCSSImageForView is meant to be used by low level UI widgets (like CPImageView and _CPImageAndTextView) to implement CSS theme support.
//
// In some circumstances, you may have to clear a CSS image. You can do this easily by replacing your current image with the special dummy empty CSS image :
// [CPImage dummyCSSImageOfSize:CGSizeMake(someWidth, someHeight)]
@implementation CPImage (CSSTheming)
{
CPDictionary _cssDictionary @accessors(property=cssDictionary);
CPDictionary _cssBeforeDictionary @accessors(property=cssBeforeDictionary);
CPDictionary _cssAfterDictionary @accessors(property=cssAfterDictionary);
CGSize _displaySize;
}
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
{
return [[CPImage alloc] initWithCSSDictionary:aDictionary beforeDictionary:nil afterDictionary:nil size:aSize];
}
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
{
return [[CPImage alloc] initWithCSSDictionary:aDictionary beforeDictionary:beforeDictionary afterDictionary:afterDictionary size:aSize];
}
+ (CPImage)dummyCSSImageOfSize:(CGSize)aSize
{
// This is used to clear a previous CSS image
return [[CPImage alloc] initWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:nil size:aSize];
}
+ (CPImage)imageWithMaterialIconNamed:(CPString)iconName size:(CGSize)size
{
return [_CPMaterialIconImage imageWithIconNamed:iconName size:size];
}
+ (CPImage)imageWithMaterialIconNamed:(CPString)iconName size:(CGSize)size color:(CPColor)color
{
return [_CPMaterialIconImage imageWithIconNamed:iconName size:size color:color];
}
- (id)initWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
{
self = [super init];
if (self)
{
_size = CGSizeMakeCopy(aSize);
_filename = @"CSS image";
_loadStatus = CPImageLoadStatusCompleted;
_cssDictionary = aDictionary;
_cssBeforeDictionary = beforeDictionary;
_cssAfterDictionary = afterDictionary;
_displaySize = CGSizeMakeCopy(aSize);
}
return self;
}
- (BOOL)isCSSBased
{
return !!(_cssDictionary || _cssBeforeDictionary || _cssAfterDictionary);
}
- (BOOL)hasCSSDictionary
{
return ([_cssDictionary count] > 0);
}
- (BOOL)hasCSSBeforeDictionary
{
return ([_cssBeforeDictionary count] > 0);
}
- (BOOL)hasCSSAfterDictionary
{
return ([_cssAfterDictionary count] > 0);
}
- (DOMElement)applyCSSImageForView:(CPView)aView onDOMElement:(DOMElement)aDOMElement styleNode:(DOMElement)aStyleNode previousState:(CPArrayRef)aPreviousStateRef
{
#if PLATFORM(DOM)
// First, restore previous CSS styling before applying the new one
var aPreviousState = @deref(aPreviousStateRef);
for (var i = 0, count = aPreviousState.length; i < count; i++)
aDOMElement.style[aPreviousState[i][0]] = aPreviousState[i][1];
aPreviousState = @[];
// Then apply new CSS styling
[_cssDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
[aPreviousState addObject:@[aKey, aDOMElement.style[aKey]]];
aDOMElement.style[aKey] = anObject;
}];
if (_cssBeforeDictionary || _cssAfterDictionary)
{
// We need to create a unique class name
var styleClassName = @".CP" + [aView UID],
styleContent = @"";
if (_cssBeforeDictionary)
{
styleContent += styleClassName + @"::before { ";
[_cssBeforeDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
styleContent += aKey + ": " + anObject + "; ";
}];
styleContent += "} ";
}
if (_cssAfterDictionary)
{
styleContent += styleClassName + @"::after { ";
[_cssAfterDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
{
styleContent += aKey + ": " + anObject + "; ";
}];
styleContent += "} ";
}
var styleDescription = document.createTextNode(styleContent);
if (!aStyleNode)
{
aStyleNode = document.createElement("style");
aView._DOMElement.insertBefore(aStyleNode, aView._DOMElement.firstChild);
aStyleNode.appendChild(styleDescription);
}
else
{
aStyleNode.replaceChild(styleDescription, aStyleNode.firstChild);
}
aDOMElement.className = @"CP"+[aView UID];
}
else
{
// no before/after so remove aStyleNode if existing
if (aStyleNode)
{
aView._DOMElement.removeChild(aStyleNode);
aStyleNode = nil;
}
}
// Return actualised values
@deref(aPreviousStateRef) = aPreviousState;
return aStyleNode;
#endif
}
- (BOOL)_shouldBeResized
{
return NO;
}
@end
var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
CPImageCSSBeforeDictionaryKey = @"CPImageCSSBeforeDictionaryKey",
CPImageCSSAfterDictionaryKey = @"CPImageCSSAfterDictionaryKey";
// MARK: -
@implementation CPImage (CPCoding)
/*!
@@ -518,7 +749,10 @@ function CPAppKitImage(aFilename, aSize)
*/
- (id)initWithCoder:(CPCoder)aCoder
{
return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
if ([aCoder containsValueForKey:CPImageCSSDictionaryKey])
return [self initWithCSSDictionary:[aCoder decodeObjectForKey:CPImageCSSDictionaryKey] beforeDictionary:[aCoder decodeObjectForKey:CPImageCSSBeforeDictionaryKey] afterDictionary:[aCoder decodeObjectForKey:CPImageCSSAfterDictionaryKey] size:[aCoder decodeSizeForKey:@"CPSize"]];
else
return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
}
/*!
@@ -529,10 +763,218 @@ function CPAppKitImage(aFilename, aSize)
{
[aCoder encodeObject:_filename forKey:@"CPFilename"];
[aCoder encodeSize:_size forKey:@"CPSize"];
// CSS Styling
if ([self isCSSBased])
{
[aCoder encodeObject:_cssDictionary forKey:CPImageCSSDictionaryKey];
[aCoder encodeObject:_cssBeforeDictionary forKey:CPImageCSSBeforeDictionaryKey];
[aCoder encodeObject:_cssAfterDictionary forKey:CPImageCSSAfterDictionaryKey];
}
}
@end
// MARK: -
// MARK: Drawing
@implementation CPImage (Drawing)
- (void)drawAtPoint:(CGPoint)point fromRect:(CPRect)fromRect operation:(CGBlendMode)op fraction:(float)delta
{
if (_loadStatus !== CPImageLoadStatusCompleted)
return;
var context = [CPGraphicsContext currentContext].graphicsPort;
if (!context)
return;
CGContextSaveGState(context);
CGContextSetBlendMode(context, op);
CGContextSetAlpha(context, delta);
context.drawImage(
_image,
fromRect.origin.x,
fromRect.origin.y,
fromRect.size.width,
fromRect.size.height,
point.x,
point.y,
fromRect.size.width,
fromRect.size.height
);
CGContextRestoreGState(context);
}
@end
// MARK: -
@implementation _CPMaterialIconImage : CPImage
{
CPMutableDictionary _cachedColorVersions;
CPColor _cachedInvertedColor;
}
+ (_CPMaterialIconImage)imageWithIconNamed:(CPString)iconName size:(CGSize)size
{
return [[_CPMaterialIconImage alloc] initWithIconName:iconName size:size];
}
+ (_CPMaterialIconImage)imageWithIconNamed:(CPString)iconName size:(CGSize)size color:(CPColor)color
{
return [[_CPMaterialIconImage alloc] initWithIconName:iconName size:size color:color];
}
+ (_CPMaterialIconImage)imageWithIconNamed:(CPString)iconName size:(CGSize)size color:(CPColor)color additionalCSSDictionary:(CPDictionary)additionalCSSDictionary
{
return [[_CPMaterialIconImage alloc] initWithIconName:iconName size:size color:color additionalCSSDictionary:additionalCSSDictionary];
}
- (CPDictionary)_baseMaterialIconCSSDictionaryForIconName:(CPString)iconName size:(CGSize)size
{
return @{
@"width": size.width + @"px",
@"height": size.height + @"px",
@"top": @"0px",
@"left": @"0px",
@"content": @"'" + iconName + @"'",
@"position": @"absolute",
@"z-index": @"300",
@"font-family": @"'Material Icons'",
@"font-weight": @"normal",
@"font-style": @"normal",
@"font-size": MIN(size.width, size.height) + @"px",
@"display": @"inline-block",
@"line-height": @"1",
@"text-transform": @"none",
@"letter-spacing": @"normal",
@"word-wrap": @"normal",
@"white-space": @"nowrap",
@"direction": @"ltr",
@"-webkit-font-smoothing": @"antialiased",
@"text-rendering": @"optimizeLegibility",
@"-moz-osx-font-smoothing": @"grayscale",
@"font-feature-settings": @"'liga'"
};
}
- (_CPMaterialIconImage)initWithIconName:(CPString)iconName size:(CGSize)size
{
return [super initWithCSSDictionary:@{}
beforeDictionary:@{}
afterDictionary:[self _baseMaterialIconCSSDictionaryForIconName:iconName size:size]
size:size];
}
- (_CPMaterialIconImage)initWithIconName:(CPString)iconName size:(CGSize)size color:(CPColor)color
{
var materialIconCSSDictionary = [self _baseMaterialIconCSSDictionaryForIconName:iconName size:size];
[materialIconCSSDictionary setObject:[color cssString] forKey:@"color"];
return [super initWithCSSDictionary:@{}
beforeDictionary:@{}
afterDictionary:materialIconCSSDictionary
size:size];
}
- (void)addRotationEffectWithAngle:(float)angle
{
[self addCSSDictionary:@{
@"transform": @"rotate("+angle+"deg)",
@"transition": @"transform 0.35s ease"
}];
}
- (void)addCSSDictionary:(CPDictionary)additionalCSSDictionary
{
[_cssAfterDictionary addEntriesFromDictionary:additionalCSSDictionary];
}
- (void)setSize:(CGSize)aSize
{
[self _setDisplaySize:aSize];
[super setSize:aSize];
}
- (void)_setDisplaySize:(CGSize)aSize
{
if (CGSizeEqualToSize(_displaySize, aSize))
return;
_displaySize = CGSizeMakeCopy(aSize);
[_cssAfterDictionary setObject:(aSize.width + @"px") forKey:@"width"];
[_cssAfterDictionary setObject:(aSize.height + @"px") forKey:@"height"];
[_cssAfterDictionary setObject:(MIN(aSize.width, aSize.height) + @"px") forKey:@"font-size"];
}
- (BOOL)_shouldBeResized
{
return YES;
}
- (BOOL)isMaterialIconImage
{
return YES;
}
- (_CPMaterialIconImage)invertedImage
{
if (!_cachedInvertedColor)
{
var sourceCSSColor = [_cssAfterDictionary objectForKey:@"color"] || @"rgba(0,0,0,1)",
sourceColor = [CPColor colorWithCSSString:sourceCSSColor];
_cachedInvertedColor = [CPColor colorWithRed:(1-[sourceColor redComponent])
green:(1-[sourceColor greenComponent])
blue:(1-[sourceColor blueComponent])
alpha:[sourceColor alphaComponent]];
}
return [self imageVersionWithColor:_cachedInvertedColor];
}
- (_CPMaterialIconImage)imageVersionWithColor:(CPColor)aColor
{
// We can't just set the color in the cssAfterDictionary as this would not be noticed as a new image,
// so -setImage won't do anything, so no visual refresh won't occur.
// The trick here is to keep in cache a clone of this image for each needed color.
var colorCSSString = [aColor cssString];
if (!_cachedColorVersions)
_cachedColorVersions = @{};
var cachedColorVersion = [_cachedColorVersions objectForKey:colorCSSString];
if (!cachedColorVersion)
{
cachedColorVersion = [self duplicate];
[cachedColorVersion _setCSSColor:colorCSSString];
[_cachedColorVersions setObject:cachedColorVersion forKey:colorCSSString];
}
return cachedColorVersion;
}
- (void)_setCSSColor:(CPString)aCSSColor
{
[_cssAfterDictionary setObject:aCSSColor forKey:@"color"];
}
@end
// MARK: -
@implementation CPThreePartImage : CPObject
{
CPArray _imageSlices;
@@ -673,3 +1115,14 @@ var CPNinePartImageImageSlicesKey = @"CPNinePartImageImageSlicesKey";
}
@end
// MARK: -
@implementation CPImage (Duplication)
- (CPImage)duplicate
{
return [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:self]];
}
@end
+47 -6
View File
@@ -29,6 +29,8 @@
@global CPImagesPboardType
@global appkit_tag_dom_elements
@global document
@typedef CPImageAlignment
CPImageAlignCenter = 0;
CPImageAlignTop = 1;
@@ -98,10 +100,32 @@ var CPImageViewEmptyPlaceholderImage = nil;
- (void)_createDOMImageElement
{
#if PLATFORM(DOM)
if (_DOMImageElement)
return;
var image = [self objectValue],
isCSSBasedImage = [image isCSSBased],
isIMGImageElement = _DOMImageElement && (_DOMImageElement.nodeName == "IMG");
_DOMImageElement = document.createElement("img");
// First, check if we need to destroy a current DOM image element. This is the case if :
// - we have one but not the right one (that is a DIV but needing an IMG, and vice versa)
if (_DOMImageElement)
{
if ((isIMGImageElement && isCSSBasedImage) || (!isIMGImageElement && !isCSSBasedImage))
{
// OK, destroy it
_DOMElement.removeChild(_DOMImageElement);
_DOMImageElement = nil;
// CSS styling cleaning
_cssStylePreviousState = @[];
_cssStyleNode = nil;
}
else
return;
}
_DOMImageElement = document.createElement(isCSSBasedImage ? "div" : "img");
_DOMImageElement.style.position = "absolute";
_DOMImageElement.style.left = "0px";
_DOMImageElement.style.top = "0px";
@@ -150,10 +174,15 @@ var CPImageViewEmptyPlaceholderImage = nil;
var newImage = [self objectValue];
#if PLATFORM(DOM)
if (!_DOMImageElement)
[self _createDOMImageElement];
[self _createDOMImageElement];
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
if ([newImage isCSSBased])
_cssStyleNode = [newImage applyCSSImageForView:self
onDOMElement:_DOMImageElement
styleNode:_cssStyleNode
previousState:@ref(_cssStylePreviousState)];
else
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
#endif
var size = [newImage size];
@@ -417,6 +446,18 @@ var CPImageViewEmptyPlaceholderImage = nil;
#endif
}
#if PLATFORM(DOM)
if ([image isCSSBased] && [image _shouldBeResized])
{
[image _setDisplaySize:CGSizeMake(ROUND(width), ROUND(height))];
_cssStyleNode = [image applyCSSImageForView:self
onDOMElement:_DOMImageElement
styleNode:_cssStyleNode
previousState:@ref(_cssStylePreviousState)];
}
#endif
_imageRect = CGRectMake(x, y, width, height);
if (_hasShadow)
+1
View File
@@ -30,6 +30,7 @@ CPStandardKeyBindings = {
@"@.": @"cancelOperation:",
@"@a": @"selectAll:",
@"@~$v": @"pasteAsPlainText:",
@"^a": @"moveToBeginningOfParagraph:",
@"^$a": @"moveToBeginningOfParagraphAndModifySelection:",
@"^b": @"moveBackward:",
+11 -6
View File
@@ -182,7 +182,7 @@ var CPBindingOperationAnd = 0,
allBindings = [bindingsForObject allKeys],
count = [allBindings count];
while(count--)
while (count--)
{
if ([[anObject class] isBindingExclusive:allBindings[count]])
return NO;
@@ -208,7 +208,11 @@ var CPBindingOperationAnd = 0,
options = [_info objectForKey:CPOptionsKey],
newValue = [destination valueForKeyPath:keyPath];
if (CPIsControllerMarker(newValue))
// give nil values the chance to be transformed to true via CPNegateBoolean (issue #1986)
if ((newValue == nil || CPIsControllerMarker(newValue)) && [options objectForKey:CPValueTransformerNameBindingOption] === CPNegateBooleanTransformerName)
[self setValue:[self transformValue:NO withOptions:options] forBinding:theBinding];
else if (CPIsControllerMarker(newValue))
{
[self raiseIfNotApplicable:newValue forKeyPath:keyPath options:options];
@@ -293,7 +297,7 @@ var CPBindingOperationAnd = 0,
// If the value is nil AND the source doesn't respond to setPlaceholderString: then
// we set the value to the placeholder. Otherwise, we do not want to short cut the process
// of setting the placeholder that is based on the fact that the value is nil.
if ((aValue === undefined || aValue === nil || aValue === [CPNull null])
if ((aValue == nil || aValue === [CPNull null])
&& ![_source respondsToSelector:@selector(setPlaceholderString:)])
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
@@ -653,7 +657,7 @@ var CPBindingOperationAnd = 0,
keyPath = [info objectForKey:CPObservedKeyPathKey],
value = [object valueForKeyPath:keyPath];
if (value === nil || value === undefined)
if (value == nil)
{
[_source setEnabled:NO];
return;
@@ -796,7 +800,7 @@ var CPBindingOperationAnd = 0,
else
value = [theBinding transformValue:value withOptions:options];
if (value === nil || value === undefined)
if (value == nil)
value = @"";
result.value = result.value.replace("%{" + _patternPlaceholder + count + "}@", [value description]);
@@ -857,7 +861,7 @@ var CPBindingOperationAnd = 0,
- (id)initWithName:(CPString)aName
{
if (self = [super init])
_name = aName
_name = aName;
return self;
}
@@ -918,6 +922,7 @@ CPTextColorBinding = @"textColor";
CPTitleBinding = @"title";
CPToolTipBinding = @"toolTip";
CPValueBinding = @"value";
CPAttributedStringBinding = @"attributedString";
CPValueURLBinding = @"valueURL";
CPValuePathBinding = @"valuePath";
CPDataBinding = @"data";
+3 -8
View File
@@ -22,15 +22,10 @@
@import "CPControl.j"
@import "CPWindow_Constants.j"
@import "CPSlider.j"
@global CPApp
@typedef CPTickMarkPosition
CPTickMarkBelow = 0;
CPTickMarkAbove = 1;
CPTickMarkLeft = CPTickMarkAbove;
CPTickMarkRight = CPTickMarkBelow;
@typedef CPLevelIndicatorStyle
CPRelevancyLevelIndicatorStyle = 0;
CPContinuousCapacityLevelIndicatorStyle = 1;
@@ -292,7 +287,7 @@ CPRatingLevelIndicatorStyle = 3;
[self setNeedsLayout];
}
- (void)setWarningValue:(double)warningValue;
- (void)setWarningValue:(double)warningValue
{
if (_warningValue === warningValue)
return;
@@ -301,7 +296,7 @@ CPRatingLevelIndicatorStyle = 3;
[self setNeedsLayout];
}
- (void)setCriticalValue:(double)criticalValue;
- (void)setCriticalValue:(double)criticalValue
{
if (_criticalValue === criticalValue)
return;
+97 -4
View File
@@ -27,6 +27,7 @@
@import "CPKeyValueBinding.j"
@import "CPMenuItem.j"
@import "CALayer.j"
@global CPApp
@@ -164,7 +165,7 @@ var _CPMenuBarVisible = NO,
+ (void)_setOrRemoveMenuBarAttribute:(id)aValue forKey:(id)aKey
{
if (aValue === nil)
if (aValue == nil)
[_CPMenuBarAttributes removeObjectForKey:aKey];
else
[_CPMenuBarAttributes setObject:aValue forKey:aKey];
@@ -268,7 +269,8 @@ var _CPMenuBarVisible = NO,
if (self)
{
_title = aTitle;
_items = [];
// Use CPMutableArray instead of raw JS array for consistency with removeAllItems
_items = [CPMutableArray array];
_autoenablesItems = YES;
_showsStateColumn = YES;
@@ -373,6 +375,10 @@ var _CPMenuBarVisible = NO,
[self willChangeValueForKey:@"items"];
_items = [CPMutableArray array];
[self didChangeValueForKey:@"items"];
// Ensure the main menu updates if cleared
if (self === [CPApp mainMenu] && _CPMenuBarSharedWindow)
[_CPMenuBarSharedWindow setMenu:self];
}
/*!
@@ -389,6 +395,9 @@ var _CPMenuBarVisible = NO,
if ([aMenuItem menu] !== self || !_items)
return;
if (_menuWindow)
[[_menuWindow _menuView] tile];
[aMenuItem setValue:[aMenuItem valueForKey:@"changeCount"] + 1 forKey:@"changeCount"];
[[CPNotificationCenter defaultCenter]
@@ -1016,6 +1025,10 @@ var _CPMenuBarVisible = NO,
// an additional mouse move after all this, but not in other browsers.
// This will be fixed correctly with the coming run loop changes.
[_CPDisplayServer run];
// If layers are updated within the menu callback, the re-draw & re-layout doesn't occur until a mouse move.
// We force the update here.
[CALayer runLoopUpdateLayers];
}
/* @ignore */
@@ -1057,7 +1070,23 @@ var _CPMenuBarVisible = NO,
if ([anEvent _triggersKeyEquivalent:[item keyEquivalent] withModifierMask:[item keyEquivalentModifierMask]])
{
if ([item isEnabled])
{
// Flash the top-level item if this is the Main Menu
if (self === [CPApp mainMenu])
[self _flashItemAtIndex:index];
anEvent._isKeyEquivalent = YES; // prevent the menu keystroke from beeing inserted into textview
[self performActionForItemAtIndex:index];
#if PLATFORM(DOM)
// we are done with this event in cappuccino space. do not let the browser do something weird additionally (e.g. command-o).
// but we must not stop copy/paste events as these can only be handled by the browser at this time even if they are in our menu
// (until we move to CPTextView as the fieleditor)
if (characters != "c" && characters != "x" && characters != "v")
_CPDOMEventStop(anEvent._DOMEvent);
#endif
}
else
{
//beep?
@@ -1067,7 +1096,13 @@ var _CPMenuBarVisible = NO,
}
if ([[item submenu] performKeyEquivalent:anEvent])
{
// Flash the top-level item if a submenu handled the event
if (self === [CPApp mainMenu])
[self _flashItemAtIndex:index];
return YES;
}
}
return NO;
@@ -1131,7 +1166,7 @@ var _CPMenuBarVisible = NO,
}
}
- (void)_menuWithName:(CPString)aName
- (CPMenu)_menuWithName:(CPString)aName
{
if (aName === _name)
return self;
@@ -1147,6 +1182,25 @@ var _CPMenuBarVisible = NO,
return nil;
}
//
/*
@ignore
*/
- (void)_flashItemAtIndex:(int)anIndex
{
// If we are using a native bridge (like a desktop wrapper), let the OS handle the visual feedback.
if ([CPPlatform supportsNativeMainMenu])
return;
[self _highlightItemAtIndex:anIndex];
[self performSelector:@selector(_stopFlashingItem) withObject:nil afterDelay:0.2];
}
- (void)_stopFlashingItem
{
[self _highlightItemAtIndex:CPNotFound];
}
@end
@@ -1218,6 +1272,11 @@ var _CPMenuBarVisible = NO,
postNotificationName:CPMenuDidAddItemNotification
object:self
userInfo:@{ @"CPMenuItemIndex": anIndex }];
// FIX #1222: If this is the main menu, force the shared menu bar window to refresh its layout.
// This ensures new items are positioned correctly (e.g. not pushed to the far right by previous layout states).
if (self === [CPApp mainMenu] && _CPMenuBarSharedWindow)
[_CPMenuBarSharedWindow setMenu:self];
}
- (void)removeObjectFromItemsAtIndex:(CPUInteger)anIndex
@@ -1233,6 +1292,10 @@ var _CPMenuBarVisible = NO,
postNotificationName:CPMenuDidRemoveItemNotification
object:self
userInfo:@{ @"CPMenuItemIndex": anIndex }];
// FIX #1222: Ensure the shared menu bar updates layout when items are removed.
if (self === [CPApp mainMenu] && _CPMenuBarSharedWindow)
[_CPMenuBarSharedWindow setMenu:self];
}
@end
@@ -1293,6 +1356,36 @@ var CPMenuTitleKey = @"CPMenuTitleKey",
@end
// MARK: -
@implementation CPMenu (CSSTheming)
+ (void)setNamedValue:(CPString)aName forKey:(CPString)aKey inAttributes:(CPDictionary)aDictionary forTheme:(CPTheme)aTheme
{
var value = [aTheme valueForAttributeWithName:aName forClass:_CPMenuView];
if (value)
[aDictionary setObject:value forKey:aKey];
else
[aDictionary removeObjectForKey:aKey];
}
+ (void)updateMenuBarAttributesWithTheme:(CPTheme)aTheme
{
var newAttributes = @{};
[CPMenu setNamedValue:@"menu-bar-text-color" forKey:@"CPMenuBarTextColor" inAttributes:newAttributes forTheme:aTheme];
[CPMenu setNamedValue:@"menu-bar-title-color" forKey:@"CPMenuBarTitleColor" inAttributes:newAttributes forTheme:aTheme];
[CPMenu setNamedValue:@"menu-bar-text-shadow-color" forKey:@"CPMenuBarTextShadowColor" inAttributes:newAttributes forTheme:aTheme];
[CPMenu setNamedValue:@"menu-bar-title-shadow-color" forKey:@"CPMenuBarTitleShadowColor" inAttributes:newAttributes forTheme:aTheme];
[CPMenu setNamedValue:@"menu-bar-highlight-color" forKey:@"CPMenuBarHighlightColor" inAttributes:newAttributes forTheme:aTheme];
[CPMenu setNamedValue:@"menu-bar-highlight-text-color" forKey:@"CPMenuBarHighlightTextColor" inAttributes:newAttributes forTheme:aTheme];
[CPMenu setNamedValue:@"menu-bar-highlight-text-shadow-color" forKey:@"CPMenuBarHighlightTextShadowColor" inAttributes:newAttributes forTheme:aTheme];
[CPMenu setMenuBarAttributes:newAttributes];
}
@end
@import "_CPMenuBarWindow.j"
@import "_CPMenuWindow.j"
+67 -27
View File
@@ -25,14 +25,16 @@
@class _CPMenuView
@class CPMenu
@class CPMenuItem
@global CPMenuDidAddItemNotification
@global CPMenuDidChangeItemNotification
@global CPMenuDidRemoveItemNotification
@global document
@implementation _CPMenuBarWindow : CPPanel
{
CPMenu _menu;
CPView _highlightView;
CPArray _menuItemViews;
@@ -130,10 +132,12 @@
- (void)setColor:(CPColor)aColor
{
var targetView = [[CPTheme defaultTheme] valueForAttributeWithName:@"css-based" forClass:CPView] ? [[self contentView] superview] : [self contentView];
if (!aColor)
[[self contentView] setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-background-color" forClass:_CPMenuView]];
[targetView setBackgroundColor:[[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-background-color" forClass:_CPMenuView]];
else
[[self contentView] setBackgroundColor:aColor];
[targetView setBackgroundColor:aColor];
}
- (void)setTextColor:(CPColor)aColor
@@ -278,6 +282,16 @@
[menuItemView setTextColor:_textColor];
[menuItemView setHidden:[item isHidden]];
// If first menu item has tag -1 and if there is a special theme value menu-bar-window-first-item-font,
// set the corresponding font. This is used to set bold on the first item of the menubar (à la Cocoa)
if ((index == 0) && ([item tag] == -1))
{
var firstItemFont = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-first-item-font" forClass:_CPMenuView];
if (firstItemFont)
[item setFont:firstItemFont];
}
[menuItemView synchronizeWithMenuItem];
[contentView addSubview:menuItemView];
@@ -351,17 +365,46 @@
- (CPFont)font
{
[CPFont systemFontOfSize:[CPFont systemFontSize]];
return [CPFont systemFontOfSize:[CPFont systemFontSize]];
}
- (void)tile
{
var bounds = [[self contentView] bounds],
height = CGRectGetHeight(bounds),
x = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-left-margin" forClass:_CPMenuView];
// 1. Layout the Icon (if present)
if (_iconImageView && ![_iconImageView isHidden])
{
var iconFrame = [_iconImageView frame];
iconFrame.origin.x = x;
// Vertically center
iconFrame.origin.y = (height - CGRectGetHeight(iconFrame)) / 2.0;
[_iconImageView setFrame:iconFrame];
x = CGRectGetMaxX(iconFrame) + 6.0; // Spacing between icon and title
}
// 2. Layout the Title (if present)
if (_titleField && [_titleField stringValue] && [[_titleField stringValue] length] > 0)
{
var titleFrame = [_titleField frame];
titleFrame.origin.x = x;
titleFrame.origin.y = (height - CGRectGetHeight(titleFrame)) / 2.0;
[_titleField setFrame:titleFrame];
x = CGRectGetMaxX(titleFrame) + 12.0; // Spacing between title and menu items
}
// 3. Layout the Menu Items
var items = [_menu itemArray],
index = 0,
count = items.length,
x = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-bar-window-left-margin" forClass:_CPMenuView],
y = 0.0,
count = items ? items.length : 0,
isLeftAligned = YES;
for (; index < count; ++index)
@@ -376,11 +419,18 @@
continue;
}
if ([item isHidden])
// Fix for #1742: If a main menu item does not have a submenu, it should not appear in the menu bar.
if ([item isHidden] || ![item submenu])
{
[[item _menuItemView] setHidden:YES];
continue;
}
var menuItemView = [item _menuItemView],
frame = [menuItemView frame];
var menuItemView = [item _menuItemView];
[menuItemView setHidden:NO];
var frame = [menuItemView frame];
if (isLeftAligned)
{
@@ -395,21 +445,6 @@
x = CGRectGetMinX([menuItemView frame]);
}
}
var bounds = [[self contentView] bounds],
titleFrame = [_titleField frame];
if ([_iconImageView isHidden])
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
else
{
var iconFrame = [_iconImageView frame],
iconWidth = CGRectGetWidth(iconFrame),
totalWidth = iconWidth + CGRectGetWidth(titleFrame);
[_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)];
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
}
}
- (void)setFrame:(CGRect)aRect display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
@@ -450,13 +485,18 @@
{
var item = items[index];
if ([item isHidden] || [item isSeparatorItem])
if ([item isHidden] || [item isSeparatorItem] || ![item submenu])
continue;
if (CGRectContainsPoint([self rectForItemAtIndex:index], aPoint))
return index;
}
// If the mouse is within the menu bar bounds but not over an item
// (e.g. dragging far left or right), force the menu to unhighlight.
if (CGRectContainsPoint([[self contentView] bounds], aPoint))
[_menu _highlightItemAtIndex:CPNotFound];
return CPNotFound;
}
+39 -26
View File
@@ -28,6 +28,8 @@
@class CPWindow
@class _CPMenuWindow
@class _CPMenuView
@class CPMenuItem
@global CPApp
@@ -223,7 +225,7 @@ var STICKY_TIME_INTERVAL = 0.4,
_lastGlobalLocation = globalLocation;
// If the item isn't enabled its as if we clicked on nothing.
if (![activeItem isEnabled] || [activeItem _isMenuBarButton])
if ([activeItem _isMenuBarButton])
{
activeItemIndex = CPNotFound;
activeItem = nil;
@@ -352,7 +354,23 @@ var STICKY_TIME_INTERVAL = 0.4,
if ([activeMenuContainer isMenuBar])
newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect));
else
newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect));
{
// New theme attributes to have more precise submenus positioning
var defaultTheme = [CPTheme defaultTheme],
themeDeltaX = [defaultTheme valueForAttributeWithName:@"menu-window-submenu-delta-x" forClass:_CPMenuView],
themeDeltaY = [defaultTheme valueForAttributeWithName:@"menu-window-submenu-delta-y" forClass:_CPMenuView],
themeFirstDeltaY = [defaultTheme valueForAttributeWithName:@"menu-window-submenu-first-level-delta-y" forClass:_CPMenuView],
activeMenuIndex = [_menuContainerStack indexOfObject:activeMenuContainer],
deltaX = themeDeltaX ? themeDeltaX : 0,
deltaY = themeDeltaY ? themeDeltaY : 0;
if (themeFirstDeltaY && (activeMenuIndex == 1) && [_menuContainerStack[0] isMenuBar])
deltaY += themeFirstDeltaY;
newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect)+deltaX, CGRectGetMinY(activeItemRect)+deltaY);
}
newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin];
@@ -553,7 +571,7 @@ var STICKY_TIME_INTERVAL = 0.4,
var iter = [selectorNames objectEnumerator],
obj;
while ((obj = [iter nextObject]) !== nil)
while ((obj = [iter nextObject]) != nil)
{
var aSelector = CPSelectorFromString(obj);
@@ -580,6 +598,8 @@ var STICKY_TIME_INTERVAL = 0.4,
[self selectNextItemBeginningWith:_keyBuffer inMenu:menu];
_lastGlobalLocation = nil;
}
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
}
- (void)selectNextItemBeginningWith:(CPString)characters inMenu:(CPMenu)menu
@@ -587,7 +607,7 @@ var STICKY_TIME_INTERVAL = 0.4,
var iter = [[menu itemArray] objectEnumerator],
obj;
while ((obj = [iter nextObject]) !== nil)
while ((obj = [iter nextObject]) != nil)
{
if ([obj isHidden] || ![obj isEnabled])
continue;
@@ -725,39 +745,32 @@ var STICKY_TIME_INTERVAL = 0.4,
- (void)moveDown:(CPMenu)menu
{
var index = menu._highlightedIndex + 1;
var index = menu._highlightedIndex + 1,
item;
// Search for the next enabled item
while ((index < [menu numberOfItems]) && (item = [menu itemAtIndex:index]) && ([item isSeparatorItem] || [item isHidden] || ![item isEnabled]))
index++;
if (index < [menu numberOfItems])
{
[menu _highlightItemAtIndex:index];
var item = [menu highlightedItem];
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
[self moveDown:menu];
}
else if (menu == [CPApp mainMenu])
[menu _highlightItemAtIndex:0];
}
- (void)moveUp:(CPMenu)menu
{
var index = menu._highlightedIndex - 1;
var index = menu._highlightedIndex - 1,
item;
if (index < 0)
{
if (index != CPNotFound || menu == [CPApp mainMenu])
[menu _highlightItemAtIndex:[menu numberOfItems] - 1];
// Search for the previous enabled item
while ((index >= 0) && (item = [menu itemAtIndex:index]) && ([item isSeparatorItem] || [item isHidden] || ![item isEnabled]))
index--;
return;
}
[menu _highlightItemAtIndex:index];
var item = [menu highlightedItem];
if ([item isSeparatorItem] || [item isHidden] || ![item isEnabled])
[self moveUp:menu];
if (index >= 0)
[menu _highlightItemAtIndex:index];
else if (menu == [CPApp mainMenu])
[menu _highlightItemAtIndex:[menu numberOfItems] - 1];
}
- (void)insertNewline:(CPMenu)menu
+49
View File
@@ -26,6 +26,7 @@
@import "_CPMenuManager.j"
@class _CPMenuView
@class CPMenuItem
var _CPMenuWindowPool = [],
_CPMenuWindowPoolCapacity = 5,
@@ -233,6 +234,19 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
// FIXME: This gets called far too often.
_unconstrainedFrame = CGRectMakeCopy(aFrame);
// If we are a submenu and we are being displayed off the right of the screen,
// we should try and display on the left of our supermenu.
var supermenu = [[self menu] supermenu];
if (supermenu && (CGRectGetMaxX(_unconstrainedFrame) > CGRectGetMaxX(_constraintRect)))
{
var supermenuWindow = supermenu._menuWindow;
if (supermenuWindow)
{
var supermenuFrame = [supermenuWindow frame];
_unconstrainedFrame.origin.x = CGRectGetMinX(supermenuFrame) - CGRectGetWidth(_unconstrainedFrame);
}
}
var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect),
marginInset = [_menuView valueForThemeAttribute:@"menu-window-margin-inset"],
scrollIndicatorHeight = [_menuView valueForThemeAttribute:@"menu-window-scroll-indicator-height"];
@@ -419,6 +433,21 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
@end
// MARK: -
@implementation _CPMenuWindow (CSSTheming)
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
{
[[self contentView] _setThemeIncludingDescendants:aTheme];
[_menuView _setThemeIncludingDescendants:aTheme];
[_menuView tile];
}
@end
// MARK: -
/*
@ignore
*/
@@ -449,6 +478,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
@"menu-bar-window-background-color": [CPNull null],
@"menu-bar-window-background-selected-color": [CPNull null],
@"menu-bar-window-font": [CPNull null],
@"menu-bar-window-first-item-font": [CPNull null],
@"menu-bar-window-height": 30.0,
@"menu-bar-window-margin": 10.0,
@"menu-bar-window-left-margin": 10.0,
@@ -466,6 +496,9 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
@"menu-general-icon-new": [CPNull null],
@"menu-general-icon-save": [CPNull null],
@"menu-general-icon-open": [CPNull null],
@"menu-window-submenu-delta-x": 0.0,
@"menu-window-submenu-delta-y": 0.0,
@"menu-window-submenu-first-level-delta-y": 0.0
};
}
@@ -552,6 +585,7 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
[view setFrameOrigin:CGPointMake(0.0, y)];
[view _setThemeIncludingDescendants:[CPTheme defaultTheme]];
[self addSubview:view];
var size = [view minSize],
@@ -582,3 +616,18 @@ _CPMenuWindowAttachedMenuBackgroundStyle = 2;
}
@end
// MARK: -
@implementation _CPMenuView (CSSTheming)
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
{
[self setTheme:aTheme];
[_menuItemViews makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
for (var i = 0, items = [[self menu] itemArray], count = [items count]; i < count; i++)
[[items[i] _menuItemView] _setThemeIncludingDescendants:aTheme];
}
@end
+27 -7
View File
@@ -505,7 +505,7 @@ CPOffState
if (_submenu)
{
[_submenu setSupermenu:_menu];
[_submenu setTitle:[self title]]
[_submenu setTitle:[self title]];
[self setTarget:_menu];
[self setAction:@selector(submenuAction:)];
@@ -826,7 +826,7 @@ CPControlKeyMask
return [[self menu] highlightedItem] == self;
}
#pragma mark CPObject Overrides
// MARK: CPObject Overrides
/*!
Returns a copy of the item. The copy does not belong If the item has a submenu, it is NOT copied.
@@ -843,7 +843,7 @@ CPControlKeyMask
[item setTarget:_target];
[item setAction:_action];
[item setEnabled:_isEnabled];
[item setHidden:_isHidden]
[item setHidden:_isHidden];
[item setTag:_tag];
[item setState:_state];
[item setImage:_image];
@@ -867,7 +867,7 @@ CPControlKeyMask
return [self copy];
}
#pragma mark Internal
// MARK: Internal
/*
@ignore
@@ -897,6 +897,20 @@ CPControlKeyMask
@end
// MARK: -
@implementation CPMenuItem (CSSTheming)
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
{
[_view _setThemeIncludingDescendants:aTheme];
[_menuItemView _setThemeIncludingDescendants:aTheme];
}
@end
// MARK: -
var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
CPMenuItemTitleKey = @"CPMenuItemTitleKey",
@@ -911,6 +925,9 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
CPMenuItemImageKey = @"CPMenuItemImageKey",
CPMenuItemAlternateImageKey = @"CPMenuItemAlternateImageKey",
CPMenuItemOnStateImageKey = @"CPMenuItemOnStateImageKey",
CPMenuItemOffStateImageKey = @"CPMenuItemOffStateImageKey",
CPMenuItemMixedStateImageKey = @"CPMenuItemMixedStateImageKey",
CPMenuItemSubmenuKey = @"CPMenuItemSubmenuKey",
CPMenuItemMenuKey = @"CPMenuItemMenuKey",
@@ -955,9 +972,9 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
_image = [aCoder decodeObjectForKey:CPMenuItemImageKey];
_alternateImage = [aCoder decodeObjectForKey:CPMenuItemAlternateImageKey];
// CPImage _onStateImage;
// CPImage _offStateImage;
// CPImage _mixedStateImage;
_onStateImage = [aCoder decodeObjectForKey:CPMenuItemOnStateImageKey];
_offStateImage = [aCoder decodeObjectForKey:CPMenuItemOffStateImageKey];
_mixedStateImage = [aCoder decodeObjectForKey:CPMenuItemMixedStateImageKey];
// This order matters because setSubmenu: needs _menu to be around.
_menu = [aCoder decodeObjectForKey:CPMenuItemMenuKey];
@@ -1003,6 +1020,9 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
ENCODE_IFNOT(CPMenuItemImageKey, _image, nil);
ENCODE_IFNOT(CPMenuItemAlternateImageKey, _alternateImage, nil);
ENCODE_IFNOT(CPMenuItemOnStateImageKey, _onStateImage, nil);
ENCODE_IFNOT(CPMenuItemOffStateImageKey, _offStateImage, nil);
ENCODE_IFNOT(CPMenuItemMixedStateImageKey, _mixedStateImage, nil);
ENCODE_IFNOT(CPMenuItemSubmenuKey, _submenu, nil);
ENCODE_IFNOT(CPMenuItemMenuKey, _menu, nil);
+19 -4
View File
@@ -31,7 +31,9 @@
+ (id)view
{
return [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 10.0)];
var themedHeight = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-item-separator-view-height" forClass:_CPMenuItemStandardView];
return [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, (themedHeight ? themedHeight : 10.0))];
}
- (id)initWithFrame:(CGRect)aFrame
@@ -47,12 +49,25 @@
- (void)drawRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
bounds = [self bounds];
bounds = [self bounds],
height = CGRectGetMaxY(bounds),
themedHeight = [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-item-separator-height" forClass:_CPMenuItemStandardView],
lineHeight = themedHeight ? themedHeight : 1.0;
CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
CGContextSetLineWidth(context, lineHeight);
if (!!((height - lineHeight) % 2))
{
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
}
else
{
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)));
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)));
}
CGContextSetStrokeColor(context, [[CPTheme defaultTheme] valueForAttributeWithName:@"menu-item-separator-color" forClass:_CPMenuItemStandardView]);
CGContextStrokePath(context);
+98 -22
View File
@@ -41,6 +41,8 @@
_CPImageAndTextView _imageAndTextView;
_CPImageAndTextView _keyEquivalentView;
CPView _submenuIndicatorView;
BOOL _hasSubmenuIndicatorImage;
}
+ (CPString)defaultThemeClass
@@ -55,6 +57,7 @@
@"menu-item-selection-color": [CPNull null],
@"menu-item-text-shadow-color": [CPNull null],
@"menu-item-text-color": [CPNull null],
@"menu-item-disabled-text-color": [CPColor lightGrayColor],
@"menu-item-default-off-state-image": [CPNull null],
@"menu-item-default-off-state-highlighted-image": [CPNull null],
@"menu-item-default-on-state-image": [CPNull null],
@@ -62,12 +65,17 @@
@"menu-item-default-mixed-state-image": [CPNull null],
@"menu-item-default-mixed-state-highlighted-image": [CPNull null],
@"menu-item-separator-color": [CPNull null],
@"menu-item-separator-height": 1.0,
@"menu-item-separator-view-height": 10.0,
@"left-margin": 3.0,
@"right-margin": 17.0,
@"state-column-width": 14.0,
@"indentation-width": 17.0,
@"vertical-margin": 4.0,
@"vertical-offset": 0.0,
@"right-columns-margin": 30.0,
@"submenu-indicator-image": [CPNull null],
@"submenu-indicator-highlighted-image": [CPNull null]
};
}
@@ -90,6 +98,7 @@
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
[_stateView setImageScaling:CPImageScaleNone];
[_stateView setImageAlignment:CPImageAlignCenter];
[self addSubview:_stateView];
@@ -108,9 +117,24 @@
[self addSubview:_keyEquivalentView];
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
// Do we have a submenu indicator image specified in the theme ?
_hasSubmenuIndicatorImage = !![self valueForThemeAttribute:@"submenu-indicator-image"];
if (_hasSubmenuIndicatorImage)
{
// Yes, then use an imageView
_submenuIndicatorView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
[_submenuIndicatorView setImageAlignment:CPImageAlignCenter];
}
else
{
// No, then use self drawing _CPMenuItemSubmenuIndicatorView
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
}
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
[_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin];
[self addSubview:_submenuIndicatorView];
@@ -124,7 +148,7 @@
- (CPColor)textColor
{
if (![_menuItem isEnabled])
return [CPColor lightGrayColor];
return [self valueForThemeAttribute:@"menu-item-disabled-text-color"];
if (_highlighted)
return [CPColor whiteColor];
@@ -148,11 +172,24 @@
_font = aFont;
}
- (CPFont)font
{
// Menu item font is forced local font or _menuItem font or system font
return _font || [_menuItem font] || [CPFont systemFontOfSize:CPFontCurrentSystemSize];
}
// FIXME: update is called 2 times at each display. Find why and fix.
- (void)update
{
var x = [self valueForThemeAttribute:@"left-margin"] + [_menuItem indentationLevel] * [self valueForThemeAttribute:@"indentation-width"],
height = 0.0,
hasStateColumn = [[_menuItem menu] showsStateColumn];
hasStateColumn = [[_menuItem menu] showsStateColumn],
myFont = [self font],
// When possible, use specific vertical margin/offset value based on font size (which could have been set by control size)
correspondingControlSize = [myFont controlSizeCorrespondingToFontSize],
verticalMargin = [self valueForThemeAttribute:@"vertical-margin" inState:CPControlSizeThemeStates[correspondingControlSize]],
verticalOffset = [self valueForThemeAttribute:@"vertical-offset" inState:CPControlSizeThemeStates[correspondingControlSize]];
if (hasStateColumn)
{
@@ -162,27 +199,32 @@
switch ([_menuItem state])
{
case CPOnState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image"]];
[_stateView setImage:[_menuItem onStateImage] || [self valueForThemeAttribute:@"menu-item-default-on-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
case CPOffState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image"]];
[_stateView setImage:[_menuItem offStateImage] || [self valueForThemeAttribute:@"menu-item-default-off-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
case CPMixedState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image"]];
[_stateView setImage:[_menuItem mixedStateImage] || [self valueForThemeAttribute:@"menu-item-default-mixed-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
default:
break;
}
var stateViewFrameOrigin = [_stateView frameOrigin];
stateViewFrameOrigin.x = x;
[_stateView setFrameOrigin:stateViewFrameOrigin];
x += [self valueForThemeAttribute:@"state-column-width"];
}
else
[_stateView setHidden:YES];
[_imageAndTextView setFont:[_menuItem font] || _font];
[_imageAndTextView setFont:myFont];
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
[_imageAndTextView setImage:[_menuItem image]];
[_imageAndTextView setText:[_menuItem title]];
@@ -195,7 +237,7 @@
imageAndTextViewFrame.origin.x = x;
x += CGRectGetWidth(imageAndTextViewFrame);
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame)); // FIXME: here, height = 0 -> MAX useless
var hasKeyEquivalent = !![_menuItem keyEquivalent],
hasSubmenu = [_menuItem hasSubmenu];
@@ -205,14 +247,14 @@
if (hasKeyEquivalent)
{
[_keyEquivalentView setFont:[_menuItem font] || _font];
[_keyEquivalentView setFont:myFont];
[_keyEquivalentView setVerticalAlignment:CPCenterVerticalTextAlignment];
[_keyEquivalentView setImage:[_menuItem image]];
[_keyEquivalentView setText:[_menuItem keyEquivalentStringRepresentation]];
[_keyEquivalentView setTextColor:[self textColor]];
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)];
[_keyEquivalentView setFrameOrigin:CGPointMake(x, [self valueForThemeAttribute:@"vertical-margin"])];
[_keyEquivalentView setFrameOrigin:CGPointMake(x, verticalMargin)];
[_keyEquivalentView sizeToFit];
var keyEquivalentViewFrame = [_keyEquivalentView frame];
@@ -229,6 +271,14 @@
if (hasSubmenu)
{
if (_hasSubmenuIndicatorImage)
{
var submenuIndicatorImage = [self valueForThemeAttribute:@"submenu-indicator-image" inState:CPControlSizeThemeStates[correspondingControlSize]];
[_submenuIndicatorView setImage:submenuIndicatorImage];
[_submenuIndicatorView setFrameSize:[submenuIndicatorImage size]];
}
[_submenuIndicatorView setHidden:NO];
var submenuViewFrame = [_submenuIndicatorView frame];
@@ -241,9 +291,9 @@
else
[_submenuIndicatorView setHidden:YES];
height += 2.0 * [self valueForThemeAttribute:@"vertical-margin"];
height += 2.0 * verticalMargin;
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0) + verticalOffset;
[_imageAndTextView setFrame:imageAndTextViewFrame];
if (hasStateColumn)
@@ -251,7 +301,7 @@
if (hasKeyEquivalent)
{
keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0);
keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0) + verticalOffset;
[_keyEquivalentView setFrame:keyEquivalentViewFrame];
}
@@ -276,6 +326,8 @@
_highlighted = shouldHighlight;
var correspondingControlSize = [[self font] controlSizeCorrespondingToFontSize];
[_imageAndTextView setTextColor:[self textColor]];
[_keyEquivalentView setTextColor:[self textColor]];
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
@@ -285,13 +337,21 @@
{
[self setBackgroundColor:[self valueForThemeAttribute:@"menu-item-selection-color"]];
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
[_submenuIndicatorView setColor:[self textColor]];
if (_hasSubmenuIndicatorImage)
[_submenuIndicatorView setImage:[self valueForThemeAttribute:@"submenu-indicator-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
else
[_submenuIndicatorView setColor:[self textColor]];
}
else
{
[self setBackgroundColor:nil];
[_imageAndTextView setImage:[_menuItem image]];
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
if (_hasSubmenuIndicatorImage)
[_submenuIndicatorView setImage:[self valueForThemeAttribute:@"submenu-indicator-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
else
[_submenuIndicatorView setColor:[self valueForThemeAttribute:@"submenu-indicator-color"]];
}
if ([[_menuItem menu] showsStateColumn])
@@ -301,15 +361,15 @@
switch ([_menuItem state])
{
case CPOnState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-highlighted-image"]];
[_stateView setImage:[_menuItem onStateImage] || [self valueForThemeAttribute:@"menu-item-default-on-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
case CPOffState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-highlighted-image"]];
[_stateView setImage:[_menuItem offStateImage] || [self valueForThemeAttribute:@"menu-item-default-off-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
case CPMixedState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-highlighted-image"]];
[_stateView setImage:[_menuItem mixedImage] || [self valueForThemeAttribute:@"menu-item-default-mixed-state-highlighted-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
default:
@@ -321,15 +381,15 @@
switch ([_menuItem state])
{
case CPOnState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-on-state-image"]];
[_stateView setImage:[_menuItem onStateImage] || [self valueForThemeAttribute:@"menu-item-default-on-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
case CPOffState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-off-state-image"]];
[_stateView setImage:[_menuItem offStateImage] || [self valueForThemeAttribute:@"menu-item-default-off-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
case CPMixedState:
[_stateView setImage:[self valueForThemeAttribute:@"menu-item-default-mixed-state-image"]];
[_stateView setImage:[_menuItem mixedImage] || [self valueForThemeAttribute:@"menu-item-default-mixed-state-image" inState:CPControlSizeThemeStates[correspondingControlSize]]];
break;
default:
@@ -346,6 +406,22 @@
@end
// MARK: -
@implementation _CPMenuItemStandardView (CSSTheming)
// MARK: Override
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
{
[self setTheme:aTheme];
[[self subviews] makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
}
@end
// MARK: -
@implementation _CPMenuItemSubmenuIndicatorView : CPView
{
CPColor _color;
+25
View File
@@ -252,6 +252,31 @@
@end
// MARK: -
@implementation _CPMenuItemView (CSSTheming)
// MARK: Override
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
{
[self setTheme:aTheme];
[_view _setThemeIncludingDescendants:aTheme];
// Items must also perform this (without this, only the selected item does)
[_imageAndTextView _setThemeIncludingDescendants:aTheme];
[_submenuView _setThemeIncludingDescendants:aTheme];
[[_menuItem view] _setThemeIncludingDescendants:aTheme];
if ([_view respondsToSelector:@selector(update)])
[_view update];
}
@end
// MARK: -
@implementation _CPMenuItemArrowView : CPView
{
CPColor _color;
+132 -7
View File
@@ -27,6 +27,9 @@
@import "CPController.j"
@import "CPKeyValueBinding.j"
@class _CPManagedProxy
@class CPPredicate;
/*!
@class
@@ -47,6 +50,9 @@
BOOL _isEditable;
BOOL _automaticallyPreparesContent;
BOOL _usesLazyFetching @accessors(getter=usesLazyFetching, setter=setUsesLazyFetching:);
BOOL _isUsingManagedProxy;
_CPManagedProxy _managedProxy;
CPCountedSet _observedKeys;
}
@@ -179,6 +185,52 @@
return _automaticallyPreparesContent;
}
/*!
Sets the entity name the controller handles.
@param CPString newEntityName - The new entity name.
*/
- (void)setEntityName:(CPString)newEntityName
{
if (!_managedProxy)
{
_managedProxy = [[_CPManagedProxy alloc] init];
_isUsingManagedProxy = YES;
}
[_managedProxy setEntityName:newEntityName];
}
/*!
Returns the entity name.
@return CPString - The name of the entity.
*/
- (CPString)entityName
{
return [_managedProxy entityName];
}
/*!
Sets the predicate used to fetch content.
@param CPPredicate newPredicate - The fetch predicate.
*/
- (void)setFetchPredicate:(CPPredicate)newPredicate
{
[_managedProxy setFetchPredicate:newPredicate];
}
/*!
Returns the fetch predicate.
@return CPPredicate - The predicate used to fetch content.
*/
- (CPPredicate)fetchPredicate
{
return [_managedProxy fetchPredicate];
}
/*!
Overridden by a subclass that require control over the creation of new objects.
*/
@@ -189,6 +241,7 @@
/*!
Sets the object class when creating new objects.
@param Class - the class of new objects that will be created.
*/
- (void)setObjectClass:(Class)aClass
@@ -331,13 +384,35 @@
*/
- (void)_selectionDidChange
{
if (_selection === undefined || _selection === nil)
if (_selection == nil)
_selection = [[CPControllerSelectionProxy alloc] initWithController:self];
[_selection controllerDidChange];
[self didChangeValueForKey:@"selection"];
}
/*!
@ignore
These two private methods map CPTextField notifications to the CPEditorRegistration protocol
This should be generalized in the future:
The CPEditorRegistrationProtocol can be implemented in all controls that support editing, not just CPTextField.
In CPArrayController there are other cases than selection change when we need to review all editor pending changes. They should be covered, including the selection change, by the wider concept described by the methods commitEditing: (forces to end editing) and discardEditing: (pending changes are lost).
*/
- (void)_objectDidBeginEditing:(CPNotification)notification
{
[self objectDidBeginEditing:[notification object]];
}
/*!
@ignore
*/
- (void) _objectDidEndEditing:(CPNotification)notification
{
[self objectDidEndEditing:[notification object]];
}
/*!
@return id - Returns the keys which are being observed.
*/
@@ -363,7 +438,10 @@
var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey",
CPObjectControllerUsesLazyFetchingKey = @"CPObjectControllerUsesLazyFetchingKey",
CPObjectControllerIsUsingManagedProxyKey = @"CPObjectControllerIsUsingManagedProxyKey",
CPObjectControllerManagedProxyKey = @"CPObjectControllerManagedProxyKey";
@implementation CPObjectController (CPCoding)
@@ -374,12 +452,18 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
if (self)
{
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
objectClass = CPClassFromString(objectClassName);
objectClass = CPClassFromString(objectClassName),
content = [aCoder decodeObjectForKey:CPObjectControllerContentKey];
[self setObjectClass:objectClass || [CPMutableDictionary class]];
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey]];
[self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
[self setUsesLazyFetching:[aCoder decodeBoolForKey:CPObjectControllerUsesLazyFetchingKey]];
_isUsingManagedProxy = [aCoder decodeBoolForKey:CPObjectControllerIsUsingManagedProxyKey];
_managedProxy = [aCoder decodeObjectForKey:CPObjectControllerManagedProxyKey];
if (content != nil)
[self setContent:content];
_observedKeys = [[CPCountedSet alloc] init];
}
@@ -398,6 +482,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
[aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey];
[aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey];
[aCoder encodeBool:[self usesLazyFetching] forKey:CPObjectControllerUsesLazyFetchingKey];
[aCoder encodeBool:_isUsingManagedProxy forKey:CPObjectControllerIsUsingManagedProxyKey];
if (_managedProxy)
[aCoder encodeObject:_managedProxy forKey:CPObjectControllerManagedProxyKey];
}
- (void)awakeFromCib
@@ -703,7 +792,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
}
}
if (value === nil || value.isa && [value isEqual:[CPNull null]])
if (value == nil || value.isa && [value isEqual:[CPNull null]])
value = CPNullMarker;
return value;
@@ -719,7 +808,8 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
var value = [self _controllerMarkerForValues:values];
[_cachedValues setObject:value forKey:theKeyPath];
return value;
// Apple's implementation returns nil instead of CPNullMarker
return value === CPNullMarker ? nil : value;
}
else
return values;
@@ -749,7 +839,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
- (void)setValue:(id)theValue forKey:(CPString)theKeyPath
{
[self setValue:theKeyPath forKeyPath:theKeyPath];
[self setValue:theValue forKeyPath:theKeyPath];
}
- (unsigned)count
@@ -825,3 +915,38 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
}
@end
@implementation _CPManagedProxy : CPObject
{
CPString _entityName @accessors(getter=entityName, setter=setEntityName:);
CPPredicate _fetchPredicate @accessors(getter=fetchPredicate, setter=setFetchPredicate:);
}
@end
var CPManagedProxyEntityNameKey = @"CPManagedProxyEntityNameKey",
CPManagedProxyFetchPredicateKey = @"CPManagedProxyFetchPredicateKey";
@implementation _CPManagedProxy (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super init];
if (self)
{
[self setEntityName:[aCoder decodeObjectForKey:CPManagedProxyEntityNameKey]];
[self setFetchPredicate:[aCoder decodeObjectForKey:CPManagedProxyFetchPredicateKey]];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:[self entityName] forKey:CPManagedProxyEntityNameKey];
[aCoder encodeObject:[self fetchPredicate] forKey:CPManagedProxyFetchPredicateKey];
}
@end
+504 -183
View File
@@ -23,6 +23,7 @@
@import "CPButton.j"
@import "CPTableColumn.j"
@import "CPTableView.j"
@import "CPTreeNode.j"
@global CPApp
@@ -127,16 +128,112 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
@protocol CPOutlineViewDataSource <CPObject>
@optional
/*!
@abstract Invoked when a drag operation concludes over the outline view.
@discussion The data source should incorporate the data from the dragging pasteboard and update its data model.
@param anOutlineView The outline view that is the destination of the drop.
@param info An object that contains information about the dragging session.
@param anItem The item that is the proposed parent for the dropped data. If anItem is nil, the data is to be dropped at the root level.
@param anIndex The index at which to drop the data among the item's children. If you want to drop on anItem, this will be CPOutlineViewDropOnItemIndex (-1).
@return YES if the drop was successful; otherwise, NO.
*/
- (BOOL)outlineView:(CPOutlineView)anOutlineView acceptDrop:(id /*<CPDraggingInfo>*/)info item:(id)anItem childIndex:(CPInteger)anIndex;
/*!
@abstract Asks the data source whether to defer displaying the children of a given item.
@discussion This method is useful for implementing lazy loading of outline view data. Returning NO prevents the outline view from querying for children of anItem, even if it is expandable.
@param anOutlineView The outline view that sent the message.
@param anItem The item being considered for expansion.
@return YES to allow the outline view to query for children of anItem; otherwise, NO. The default is YES.
*/
- (BOOL)outlineView:(CPOutlineView)anOutlineView shouldDeferDisplayingChildrenOfItem:(id)anItem;
/*!
@abstract Invoked when a drag should begin.
@discussion The data source should write the representation of the specified items to the pasteboard.
@param anOutlineView The outline view that is the source of the drag.
@param items An array of items to be dragged.
@param pboard The pasteboard to which the data for the dragged items should be written.
@return YES if the drag should begin; NO to prevent the drag.
*/
- (BOOL)outlineView:(CPOutlineView)anOutlineView writeItems:(CPArray)items toPasteboard:(CPPasteboard)pboard;
/*!
@abstract Used for promised-file dragging.
@discussion When a promised-file drag is dropped, this method is invoked to ask the data source to create the files at the specified destination and return their names.
@param anOutlineView The outline view that was the source of the drag.
@param dropDestination The URL of the directory where the files should be created.
@param items The items that were dragged, representing the promised files.
@return An array of strings containing the names of the files that were created.
*/
- (CPArray)outlineView:(CPOutlineView)anOutlineView namesOfPromisedFilesDroppedAtDestination:(CPURL)dropDestination forDraggedItems:(CPArray)items;
/*!
@abstract Invoked to determine if a drop is allowed at a specified location.
@discussion This method is called repeatedly while the user drags over the outline view. It should return the drag operation that should be performed.
@param anOutlineView The outline view that is the destination of the drag.
@param info An object that contains information about the dragging session.
@param anItem The item that is the proposed parent for the dropped data.
@param anIndex The index at which to drop the data among the item's children. If you want to drop on anItem, this will be CPOutlineViewDropOnItemIndex (-1).
@return A CPDragOperation value that indicates the type of operation to perform.
*/
- (CPDragOperation)outlineView:(CPOutlineView)anOutlineView validateDrop:(id /*<CPDraggingInfo>*/)info proposedItem:(id)anItem proposedChildIndex:(CPInteger)anIndex;
/*!
@abstract Invoked to determine if a drop is allowed at a specified row.
@discussion This is a legacy method from CPTableView. It is recommended to implement outlineView:validateDrop:proposedItem:proposedChildIndex: instead for more precise control in an outline view.
@param anOutlineView The outline view that is the destination of the drag.
@param info An object that contains information about the dragging session.
@param theRow The proposed row for the drop.
@param theOperation The proposed drop operation (CPTableViewDropOn or CPTableViewDropAbove).
@return A CPDragOperation value that indicates the type of operation to perform.
*/
- (CPDragOperation)outlineView:(CPOutlineView)anOutlineView validateDrop:(id /*<CPDraggingInfo>*/)info proposedRow:(int)theRow proposedDropOperation:(CPTableViewDropOperation)theOperation;
/*!
@abstract Used for state preservation.
@discussion This method is called to convert a persistent, serializable object back into a model item.
@param anOutlineView The outline view requesting the item.
@param anObject The persistent object used to identify the model item.
@return The model item corresponding to anObject, or nil if it cannot be found.
*/
- (id)outlineView:(CPOutlineView)anOutlineView itemForPersistentObject:(id)anObject;
/*!
@abstract Returns the data object to be displayed for a given item and column.
@discussion This method is called by the outline view to get the value for each cell. It is required for cell-based outline views.
@param anOutlineView The outline view that sent the message.
@param aTableColumn The column for which the value is requested.
@param anItem The item for the row being displayed.
@return The data object (e.g., a CPString) for the specified item and column.
*/
- (id)outlineView:(CPOutlineView)anOutlineView objectValueforTableColumn:(CPTableColumn)aTableColumn byItem:(id)anItem;
/*!
@abstract Used for state preservation.
@discussion This method is called to convert a model item into a persistent, serializable object (e.g., a string identifier) that can be saved.
@param anOutlineView The outline view requesting the persistent object.
@param anItem The item to be converted.
@return A serializable object that persistently identifies anItem.
*/
- (id)outlineView:(CPOutlineView)anOutlineView persistentObjectForItem:(id)anItem;
/*!
@abstract Sets the data object for a given item and column.
@discussion This method is called when the user edits a cell's value. The data source should update its model with the new value.
@param anOutlineView The outline view that sent the message.
@param anObject The new value.
@param aTableColumn The column that was edited.
@param anItem The item whose value was edited.
*/
- (void)outlineView:(CPOutlineView)anOutlineView setObjectValue:(id)anObject forTableColumn:(CPTableColumn)aTableColumn byItem:(id)anItem;
/*!
@abstract Notifies the data source that the sort descriptors have changed.
@discussion This method is called after the user clicks a column header to change the sort order. The data source should re-sort its data based on the outline view's new 'sortDescriptors' property and then call `reloadData`.
@param anOutlineView The outline view that sent the message.
@param oldDescriptors The previous sort descriptors.
*/
- (void)outlineView:(CPOutlineView)anOutlineView sortDescriptorsDidChange:(CPArray)oldDescriptors;
@end
@@ -215,14 +312,30 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
[self setIndentationPerLevel:16.0];
[self setIndentationMarkerFollowsDataView:YES];
[super setDataSource:[[_CPOutlineViewTableViewDataSource alloc] initWithOutlineView:self]];
[super setDelegate:[[_CPOutlineViewTableViewDelegate alloc] initWithOutlineView:self]];
[super setDataSource:self];
[super setDelegate:self];
[self setDisclosureControlPrototype:[[CPDisclosureButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)]];
}
return self;
}
- (void)_initSubclass
{
_BlockDeselectView = function(view, row, column)
{
[view unsetThemeState:CPThemeStateSelectedDataView];
[_disclosureControlsForRows[row] unsetThemeState:CPThemeStateSelected];
};
_BlockSelectView = function(view, row, column)
{
[view setThemeState:CPThemeStateSelectedDataView];
[_disclosureControlsForRows[row] setThemeState:CPThemeStateSelected];
};
}
/*!
In addition to standard delegation, the outline view also supports data
source delegation. This method sets the data source object. Just like the
@@ -661,6 +774,11 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
[self reloadItem:anItem reloadChildren:NO];
}
- (int)_numberOfRows
{
return _itemsForRows ? _itemsForRows.length : 0;
}
/*!
Reloads the data for a given item and optionally the children.
@@ -669,9 +787,12 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
*/
- (void)reloadItem:(id)anItem reloadChildren:(BOOL)shouldReloadChildren
{
_pendingItemToClean = [];
_itemAddedDuringLastLoading = [];
var previousRowCount = _itemsForRows.length;
if (!!shouldReloadChildren || !anItem)
[self _loadItemInfoForItem:anItem intermediate:NO];
else
@@ -679,7 +800,12 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
[self _cleanPendingItem];
[super reloadData];
// Safely update the table size and force a synchronous layout recalculation
// BEFORE the views are reloaded, avoiding the clipping issue.
if (_itemsForRows.length !== previousRowCount)
[self noteNumberOfRowsChanged];
[super _reloadDataViews];
}
- (void)_reloadItem:(id)anItem
@@ -725,9 +851,20 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
for (var i = [previousItems count] - 1; i >= 0; i--)
{
var item = previousItems[i];
var item = previousItems[i],
found = NO;
if (![children containsObject:item])
// Use strict identity (===) instead of containsObject: (which triggers isEqual:)
for (var j = 0, count = children.length; j < count; j++)
{
if (children[j] === item)
{
found = YES;
break;
}
}
if (!found)
[self _addPendingItem:item];
}
}
@@ -741,7 +878,8 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
var children = itemInfo.children;
for (var i = [children count]; i >= 0; i--)
// Fixed out-of-bounds index (was previously [children count])
for (var i = children.length - 1; i >= 0; i--)
{
var child = children[i];
[self _addPendingItem:child];
@@ -752,7 +890,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
- (void)_cleanPendingItem
{
for (var i = [_pendingItemToClean count]; i >= 0; i--)
for (var i = [_pendingItemToClean count] - 1; i >= 0; i--)
{
var item = _pendingItemToClean[i];
@@ -796,7 +934,8 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
var weight = itemInfo.weight,
descendants = anItem ? [anItem] : [];
[_itemAddedDuringLastLoading addObject:anItem];
if (anItem)
[_itemAddedDuringLastLoading addObject:anItem];
if (itemInfo.isExpanded && [self _sendDataSourceShouldDeferDisplayingChildrenOfItem:anItem])
{
@@ -973,7 +1112,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
var parent = itemInfo.parent;
// Check if the parent is the root item because we never return the actual root item
if (itemInfo[[parent UID]] === _rootItemInfo)
if (parent && _itemInfosForItems[[parent UID]] === _rootItemInfo)
parent = nil;
return parent;
@@ -1308,7 +1447,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
/*!
Reloads all the data of the outlineview.
*/
- (void)reloadData
- (void)_reloadDataViews
{
[self reloadItem:nil reloadChildren:YES];
}
@@ -1340,6 +1479,25 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
else
[super removeTableColumn:aTableColumn];
}
- (void)_addDraggedDataView:(CPView)aDataView toView:(CPView)aSuperview forColumn:(CPInteger)column row:(CPInteger)row offset:(CGPoint)offset
{
var control;
[super _addDraggedDataView:aDataView toView:aSuperview forColumn:column row:row offset:offset];
if (_tableColumns[column] === _outlineTableColumn && (control = _disclosureControlsForRows[row]))
{
var controlFrame = [self frameOfOutlineDisclosureControlAtRow:row];
controlFrame.origin.x -= offset.x;
controlFrame.origin.y -= offset.y;
[control setFrame:controlFrame];
[aSuperview addSubview:control];
}
}
/*!
@ignore
We override this because we need a special behavior for the outline
@@ -1355,65 +1513,19 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
return [super frameOfDataViewAtColumn:aColumn row:aRow];
}
/*!
@ignore
We need to offset the dataview and add the disclosure triangle.
*/
- (CPView)_dragViewForColumn:(CPInteger)theColumnIndex event:(CPEvent)theDragEvent offset:(CGPoint)theDragViewOffset
- (void)_applyToolTipToDataView:(CPView)aDataView forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow
{
var dragView = [[_CPColumnDragView alloc] initWithLineColor:[self gridColor]],
tableColumn = [[self tableColumns] objectAtIndex:theColumnIndex],
defaultRowHeight = [self valueForThemeAttribute:@"default-row-height"],
bounds = CGRectMake(0.0, 0.0, [tableColumn width], CGRectGetHeight([self exposedRect]) + defaultRowHeight),
columnRect = [self rectOfColumn:theColumnIndex],
headerView = [tableColumn headerView],
row = [_exposedRows firstIndex];
while (row !== CPNotFound)
if (_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_toolTipForView_rect_tableColumn_item_mouseLocation_)
{
var dataView = [self _newDataViewForRow:row tableColumn:tableColumn],
dataViewFrame = [self frameOfDataViewAtColumn:theColumnIndex row:row];
var item = [self itemAtRow:aRow],
tooltip = [self _sendDelegateToolTipForView:aDataView rect:[aDataView frame] tableColumn:aTableColumn item:item mouseLocation:CGPointMakeZero()];
// Only one column is ever dragged so we just place the view at
dataViewFrame.origin.x = 0.0;
// Offset by table header height - scroll position
dataViewFrame.origin.y = (CGRectGetMinY(dataViewFrame) - CGRectGetMinY([self exposedRect])) + defaultRowHeight;
[dataView setFrame:dataViewFrame];
[dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
if (tableColumn === _outlineTableColumn)
{
// first inset the dragview
var indentationWidth = ([self levelForRow:row] + 1) * [self indentationPerLevel];
dataViewFrame.origin.x += indentationWidth;
dataViewFrame.size.width -= indentationWidth;
[dataView setFrame:dataViewFrame];
}
[dragView addSubview:dataView];
row = [_exposedRows indexGreaterThanIndex:row];
[aDataView setToolTip:tooltip];
}
else
{
[super _applyToolTipToDataView:aDataView forTableColumn:aTableColumn row:aRow];
}
// Add the column header view
var headerFrame = [headerView frame];
headerFrame.origin = CGPointMakeZero();
var columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
[columnHeaderView setStringValue:[headerView stringValue]];
[columnHeaderView setThemeState:[headerView themeState]];
[dragView addSubview:columnHeaderView];
[dragView setBackgroundColor:[CPColor whiteColor]];
[dragView setAlphaValue:0.7];
[dragView setFrame:bounds];
return dragView;
}
/*!
@@ -1443,7 +1555,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
if (_dropItem)
{
[_dropOperationFeedbackView blink];
[CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO];
[CPTimer scheduledTimerWithTimeInterval:.3 callback:[self expandItem:_dropItem] repeats:NO];
}
};
@@ -1464,7 +1576,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
_shouldRetargetChildIndex = YES;
// set CPTableView's _retargetedDropRow based on retargetedItem and retargetedChildIndex
var retargetedItemInfo = (_retargetedItem !== nil) ? _itemInfosForItems[[_retargetedItem UID]] : _rootItemInfo;
var retargetedItemInfo = (_retargetedItem != nil) ? _itemInfosForItems[[_retargetedItem UID]] : _rootItemInfo;
if (_retargedChildIndex === [retargetedItemInfo.children count])
{
@@ -1539,43 +1651,22 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
@ignore
We need to move the disclosure control too.
*/
- (void)_layoutDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
- (void)_layoutViewsForRowIndexes:(CPIndexSet)rowIndexes columnIndexes:(CPIndexSet)columnIndexes
{
var rowArray = [],
columnArray = [];
[rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
[columns getIndexes:columnArray maxCount:-1 inIndexRange:nil];
var columnIndex = 0,
columnsCount = columnArray.length;
for (; columnIndex < columnsCount; ++columnIndex)
[self _enumerateViewsInRows:rowIndexes columns:columnIndexes usingBlock:function(view, row, column, stop)
{
var column = columnArray[columnIndex],
tableColumn = _tableColumns[column],
tableColumnUID = [tableColumn UID],
dataViewsForTableColumn = _dataViewsForTableColumns[tableColumnUID],
rowIndex = 0,
rowsCount = rowArray.length;
var control;
for (; rowIndex < rowsCount; ++rowIndex)
[view setFrame:[self frameOfDataViewAtColumn:column row:row]];
if (_tableColumns[column] === _outlineTableColumn && (control = _disclosureControlsForRows[row]))
{
var row = rowArray[rowIndex],
dataView = dataViewsForTableColumn[row],
dataViewFrame = [self frameOfDataViewAtColumn:column row:row];
[dataView setFrame:dataViewFrame];
if (tableColumn === _outlineTableColumn)
{
var control = _disclosureControlsForRows[row],
frame = [self frameOfOutlineDisclosureControlAtRow:row];
[control setFrame:frame];
}
var frame = [self frameOfOutlineDisclosureControlAtRow:row];
[control setFrame:frame];
}
}
}];
[self setNeedsDisplay:YES];
}
/*!
@@ -1587,7 +1678,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
var outlineColumn = [[self tableColumns] indexOfObjectIdenticalTo:[self outlineTableColumn]];
if (![columns containsIndex:outlineColumn] || [self outlineTableColumn] === _draggedColumn)
if (![columns containsIndex:outlineColumn] || outlineColumn === _draggedColumnIndex)
return;
var rowArray = [];
@@ -1890,6 +1981,14 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
return _implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_dataViewForTableColumn_item_;
}
- (id)_hitTest:(CPView)aView
{
if ([aView isKindOfClass:[CPDisclosureButton class]])
return aView;
return [super _hitTest:aView];
}
- (BOOL)_delegateRespondsToShouldExpandItem
{
return _implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldExpandItem_;
@@ -1918,43 +2017,28 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
return _implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldSelectItem_;
}
@end
@implementation _CPOutlineViewTableViewDataSource : CPObject
{
CPObject _outlineView;
}
- (id)initWithOutlineView:(CPOutlineView)anOutlineView
{
self = [super init];
if (self)
_outlineView = anOutlineView;
return self;
}
/*** CPTableViewDataSource methods ***/
- (CPInteger)numberOfRowsInTableView:(CPTableView)anOutlineView
{
return _outlineView._itemsForRows.length;
return _itemsForRows.length;
}
- (id)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow
{
return [_outlineView._outlineViewDataSource outlineView:_outlineView objectValueForTableColumn:aTableColumn byItem:_outlineView._itemsForRows[aRow]];
return [_outlineViewDataSource outlineView:self objectValueForTableColumn:aTableColumn byItem:_itemsForRows[aRow]];
}
- (void)tableView:(CPTableView)aTableView setObjectValue:(id)aValue forTableColumn:(CPTableColumn)aColumn row:(CPInteger)aRow
{
if (!(_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_))
if (!(_implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_))
return;
[_outlineView._outlineViewDataSource outlineView:_outlineView setObjectValue:aValue forTableColumn:aColumn byItem:_outlineView._itemsForRows[aRow]];
[_outlineViewDataSource outlineView:self setObjectValue:aValue forTableColumn:aColumn byItem:_itemsForRows[aRow]];
}
- (BOOL)tableView:(CPTableView)aTableColumn writeRowsWithIndexes:(CPIndexSet)theIndexes toPasteboard:(CPPasteboard)thePasteboard
{
if (!(_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_))
if (!(_implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_))
return NO;
var items = [],
@@ -1962,27 +2046,27 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
while (index !== CPNotFound)
{
[items addObject:[_outlineView itemAtRow:index]]
[items addObject:[self itemAtRow:index]];
index = [theIndexes indexGreaterThanIndex:index];
}
return [_outlineView._outlineViewDataSource outlineView:_outlineView writeItems:items toPasteboard:thePasteboard];
return [_outlineViewDataSource outlineView:self writeItems:items toPasteboard:thePasteboard];
}
- (int)_childIndexForDropOperation:(CPTableViewDropOperation)theDropOperation row:(CPInteger)theRow offset:(CGPoint)theOffset
{
if (_outlineView._shouldRetargetChildIndex)
return _outlineView._retargedChildIndex;
if (_shouldRetargetChildIndex)
return _retargedChildIndex;
var childIndex = CPNotFound;
if (theDropOperation === CPTableViewDropAbove)
{
var parentItem = [_outlineView _parentItemForUpperRow:theRow - 1 andLowerRow:theRow atMouseOffset:theOffset],
itemInfo = (parentItem !== nil) ? _outlineView._itemInfosForItems[[parentItem UID]] : _outlineView._rootItemInfo,
var parentItem = [self _parentItemForUpperRow:theRow - 1 andLowerRow:theRow atMouseOffset:theOffset],
itemInfo = (parentItem != nil) ? _itemInfosForItems[[parentItem UID]] : _rootItemInfo,
children = itemInfo.children;
childIndex = [children indexOfObject:[_outlineView itemAtRow:theRow]];
childIndex = [children indexOfObject:[self itemAtRow:theRow]];
if (childIndex === CPNotFound)
childIndex = children.length;
@@ -1993,155 +2077,151 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
return childIndex;
}
- (void)_parentItemForDropOperation:(CPTableViewDropOperation)theDropOperation row:(CPInteger)theRow offset:(CGPoint)theOffset
- (id)_parentItemForDropOperation:(CPTableViewDropOperation)theDropOperation row:(CPInteger)theRow offset:(CGPoint)theOffset
{
if (theDropOperation === CPTableViewDropAbove)
return [_outlineView _parentItemForUpperRow:theRow - 1 andLowerRow:theRow atMouseOffset:theOffset]
return [self _parentItemForUpperRow:theRow - 1 andLowerRow:theRow atMouseOffset:theOffset];
return [_outlineView itemAtRow:theRow];
return [self itemAtRow:theRow];
}
- (CPDragOperation)tableView:(CPTableView)aTableView validateDrop:(id /*< CPDraggingInfo >*/)theInfo
proposedRow:(CPInteger)theRow proposedDropOperation:(CPTableViewDropOperation)theOperation
{
if (!(_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_))
if (!(_implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_))
return CPDragOperationNone;
// Make sure the retargeted item and index are reset
_outlineView._retargetedItem = nil;
_outlineView._shouldRetargetItem = NO;
_retargetedItem = nil;
_shouldRetargetItem = NO;
_outlineView._retargedChildIndex = nil;
_outlineView._shouldRetargetChildIndex = NO;
_retargedChildIndex = nil;
_shouldRetargetChildIndex = NO;
var location = [_outlineView convertPoint:[theInfo draggingLocation] fromView:nil],
var location = [self convertPoint:[theInfo draggingLocation] fromView:nil],
parentItem = [self _parentItemForDropOperation:theOperation row:theRow offset:location],
childIndex = [self _childIndexForDropOperation:theOperation row:theRow offset:location];
return [_outlineView._outlineViewDataSource outlineView:_outlineView validateDrop:theInfo proposedItem:parentItem proposedChildIndex:childIndex];
return [_outlineViewDataSource outlineView:self validateDrop:theInfo proposedItem:parentItem proposedChildIndex:childIndex];
}
- (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id /*<CPDraggingInfo>*/)theInfo row:(CPInteger)theRow dropOperation:(CPTableViewDropOperation)theOperation
{
if (!(_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_))
if (!(_implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_))
return NO;
var location = [_outlineView convertPoint:[theInfo draggingLocation] fromView:nil],
var location = [self convertPoint:[theInfo draggingLocation] fromView:nil],
parentItem = [self _parentItemForDropOperation:theOperation row:theRow offset:location],
childIndex = [self _childIndexForDropOperation:theOperation row:theRow offset:location];
_outlineView._retargetedItem = nil;
_outlineView._shouldRetargetItem = NO;
_retargetedItem = nil;
_shouldRetargetItem = NO;
_outlineView._retargedChildIndex = nil;
_outlineView._shouldRetargetChildIndex = NO;
_retargedChildIndex = nil;
_shouldRetargetChildIndex = NO;
return [_outlineView._outlineViewDataSource outlineView:_outlineView acceptDrop:theInfo item:parentItem childIndex:childIndex];
return [_outlineViewDataSource outlineView:self acceptDrop:theInfo item:parentItem childIndex:childIndex];
}
- (void)tableView:(CPTableView)aTableView sortDescriptorsDidChange:(CPArray)oldSortDescriptors
{
if ((_outlineView._implementedOutlineViewDataSourceMethods &
if ((_implementedOutlineViewDataSourceMethods &
CPOutlineViewDataSource_outlineView_sortDescriptorsDidChange_))
{
[[_outlineView dataSource] outlineView:_outlineView sortDescriptorsDidChange:oldSortDescriptors];
[[self dataSource] outlineView:self sortDescriptorsDidChange:oldSortDescriptors];
}
}
@end
/*** CPTableViewDelegate methods ***/
@implementation _CPOutlineViewTableViewDelegate : CPObject
/*!
@ignore
*/
- (CPString)_sendDelegateToolTipForView:(id)aView rect:(CGRect)aRect tableColumn:(CPTableColumn)aTableColumn item:(id)anItem mouseLocation:(CGPoint)aPoint
{
CPOutlineView _outlineView;
}
if (!(_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_toolTipForView_rect_tableColumn_item_mouseLocation_))
return nil;
- (id)initWithOutlineView:(CPOutlineView)anOutlineView
{
self = [super init];
if (self)
_outlineView = anOutlineView;
return self;
return [_outlineViewDelegate outlineView:self toolTipForView:aView rect:aRect tableColumn:aTableColumn item:anItem mouseLocation:aPoint];
}
- (BOOL)tableView:(CPTableView)theTableView shouldSelectRow:(CPInteger)theRow
{
return SHOULD_SELECT_ITEM(_outlineView, [_outlineView itemAtRow:theRow]);
return SHOULD_SELECT_ITEM(self, [self itemAtRow:theRow]);
}
- (BOOL)selectionShouldChangeInTableView:(CPTableView)theTableView
{
return SELECTION_SHOULD_CHANGE(_outlineView);
return SELECTION_SHOULD_CHANGE(self);
}
- (BOOL)tableView:(CPTableView)aTableView shouldEditTableColumn:(CPTableColumn)aColumn row:(CPInteger)aRow
{
if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldEditTableColumn_item_))
return [_outlineView._outlineViewDelegate outlineView:_outlineView shouldEditTableColumn:aColumn item:[_outlineView itemAtRow:aRow]];
if ((_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldEditTableColumn_item_))
return [_outlineViewDelegate outlineView:self shouldEditTableColumn:aColumn item:[self itemAtRow:aRow]];
return NO;
}
- (float)tableView:(CPTableView)theTableView heightOfRow:(CPInteger)theRow
{
if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_heightOfRowByItem_))
return [_outlineView._outlineViewDelegate outlineView:_outlineView heightOfRowByItem:[_outlineView itemAtRow:theRow]];
if ((_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_heightOfRowByItem_))
return [_outlineViewDelegate outlineView:self heightOfRowByItem:[self itemAtRow:theRow]];
return [theTableView rowHeight];
}
- (void)tableView:(CPTableView)aTableView willDisplayView:(id)aView forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
{
if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_willDisplayView_forTableColumn_item_))
if ((_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_willDisplayView_forTableColumn_item_))
{
var item = [_outlineView itemAtRow:aRowIndex];
[_outlineView._outlineViewDelegate outlineView:_outlineView willDisplayView:aView forTableColumn:aTableColumn item:item];
var item = [self itemAtRow:aRowIndex];
[_outlineViewDelegate outlineView:self willDisplayView:aView forTableColumn:aTableColumn item:item];
}
}
- (void)tableView:(CPTableView)aTableView willRemoveView:(id)aView forTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRowIndex
{
if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_willRemoveView_forTableColumn_item_))
if ((_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_willRemoveView_forTableColumn_item_))
{
var item = [_outlineView itemAtRow:aRowIndex];
[_outlineView._outlineViewDelegate outlineView:_outlineView willRemoveView:aView forTableColumn:aTableColumn item:item];
var item = [self itemAtRow:aRowIndex];
[_outlineViewDelegate outlineView:self willRemoveView:aView forTableColumn:aTableColumn item:item];
}
}
- (BOOL)tableView:(CPTableView)aTableView isGroupRow:(CPInteger)aRow
{
if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_isGroupItem_))
return [_outlineView._outlineViewDelegate outlineView:_outlineView isGroupItem:[_outlineView itemAtRow:aRow]];
if ((_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_isGroupItem_))
return [_outlineViewDelegate outlineView:self isGroupItem:[self itemAtRow:aRow]];
return NO;
}
- (CPMenu)tableView:(CPTableView)aTableView menuForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow
{
if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_menuForTableColumn_item_))
if ((_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_menuForTableColumn_item_))
{
var item = [_outlineView itemAtRow:aRow];
return [_outlineView._outlineViewDelegate outlineView:_outlineView menuForTableColumn:aTableColumn item:item]
var item = [self itemAtRow:aRow];
return [_outlineViewDelegate outlineView:self menuForTableColumn:aTableColumn item:item]
}
// We reimplement CPView menuForEvent: because we can't call it directly. CPTableView implements menuForEvent:
// to call this delegate method.
return [_outlineView menu] || [[_outlineView class] defaultMenu];
return [self menu] || [[self class] defaultMenu];
}
- (CPIndexSet)tableView:(CPTableView)aTableView selectionIndexesForProposedSelection:(CPIndexSet)anIndexSet
{
if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_selectionIndexesForProposedSelection_))
return [_outlineView._outlineViewDelegate outlineView:_outlineView selectionIndexesForProposedSelection:anIndexSet];
if ((_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_selectionIndexesForProposedSelection_))
return [_outlineViewDelegate outlineView:self selectionIndexesForProposedSelection:anIndexSet];
return anIndexSet;
}
- (BOOL)tableView:(CPTableView)aTableView shouldSelectTableColumn:(CPTableColumn)aTableColumn
{
if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldSelectTableColumn_))
return [_outlineView._outlineViewDelegate outlineView:_outlineView shouldSelectTableColumn:aTableColumn];
if ((_implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldSelectTableColumn_))
return [_outlineViewDelegate outlineView:self shouldSelectTableColumn:aTableColumn];
return YES;
}
@@ -2247,8 +2327,8 @@ var CPOutlineViewIndentationPerLevelKey = @"CPOutlineViewIndentationPerLevelKey"
_outlineViewDataSource = [aCoder decodeObjectForKey:CPOutlineViewDataSourceKey];
_outlineViewDelegate = [aCoder decodeObjectForKey:CPOutlineViewDelegateKey];
[super setDataSource:[[_CPOutlineViewTableViewDataSource alloc] initWithOutlineView:self]];
[super setDelegate:[[_CPOutlineViewTableViewDelegate alloc] initWithOutlineView:self]];
[super setDataSource:self];
[super setDelegate:self];
[self _updateIsViewBased];
}
@@ -2287,3 +2367,244 @@ var colorForDisclosureTriangle = function(isSelected, isHighlighted)
? [CPColor colorWithCalibratedWhite:0.4 alpha: 1.0]
: [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0]);
};
@implementation CPOutlineView (CPBindings)
+ (void)initialize
{
if (self !== [CPOutlineView class])
return;
[self exposeBinding:@"content"];
[self exposeBinding:@"selectionIndexPaths"];
[self exposeBinding:@"sortDescriptors"];
}
/*!
Returns the currently selected index paths.
This allows the outline view to be KVC-compliant for `selectionIndexPaths`.
*/
- (CPArray)selectionIndexPaths
{
var indexes = [self selectedRowIndexes],
paths = [CPMutableArray array],
index = [indexes firstIndex];
while (index !== CPNotFound)
{
var item = [self itemAtRow:index];
// Check if the item is a CPTreeNode proxy (which it will be when bound to CPTreeController)
if ([item respondsToSelector:@selector(indexPath)])
[paths addObject:[item indexPath]];
index = [indexes indexGreaterThanIndex:index];
}
return paths;
}
@end
@implementation CPOutlineView (CPBinder)
- (id)content { return nil; }
- (void)setContent:(id)aContent { }
- (void)setSelectionIndexPaths:(CPArray)paths { }
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding === @"content")
return [_CPOutlineViewContentBinder class];
if (aBinding === @"selectionIndexPaths")
return [_CPOutlineViewSelectionIndexPathsBinder class];
return [super _binderClassForBinding:aBinding];
}
@end
// --- Content Binder ---
/*!
_CPOutlineViewContentBinder acts as the CPOutlineViewDataSource when the outline view
is bound to a CPTreeController's arrangedObjects.
*/
@implementation _CPOutlineViewContentBinder : CPBinder
{
CPTreeNode _rootNode;
}
- (void)setValueFor:(CPString)aBinding
{
var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey],
value = [destination valueForKeyPath:keyPath];
if (!value || ![value isKindOfClass:[CPTreeNode class]])
_rootNode = [[CPTreeNode alloc] initWithRepresentedObject:nil];
else
_rootNode = value;
if ([_source dataSource] !== self)
[_source setDataSource:self];
else
[_source reloadData];
}
- (CPTreeNode)rootNode
{
return _rootNode;
}
// -- CPOutlineViewDataSource implementation --
- (id)outlineView:(CPOutlineView)outlineView child:(CPInteger)index ofItem:(id)item
{
var node = item || _rootNode;
return [[node childNodes] objectAtIndex:index];
}
- (BOOL)outlineView:(CPOutlineView)outlineView isItemExpandable:(id)item
{
var node = item || _rootNode;
return ![node isLeaf];
}
- (int)outlineView:(CPOutlineView)outlineView numberOfChildrenOfItem:(id)item
{
var node = item || _rootNode;
return [[node childNodes] count];
}
- (id)outlineView:(CPOutlineView)outlineView objectValueForTableColumn:(CPTableColumn)tableColumn byItem:(id)item
{
var rep = [item respondsToSelector:@selector(representedObject)] ? [item representedObject] : item;
// Dynamically fetch the value using the column's identifier (e.g., "name")
if (rep && [tableColumn identifier] && [tableColumn identifier] !== @"")
return [rep valueForKey:[tableColumn identifier]];
return rep;
}
- (void)outlineView:(CPOutlineView)outlineView setObjectValue:(id)value forTableColumn:(CPTableColumn)tableColumn byItem:(id)item
{
var rep = [item respondsToSelector:@selector(representedObject)] ?[item representedObject] : item;
// Push the inline edit back to the model using the column's identifier
if (rep && [tableColumn identifier] && [tableColumn identifier] !== @"")
[rep setValue:value forKey:[tableColumn identifier]];
}
- (id)content
{
// CPTableView internals probe the binder for its flat content to draw rows.
if (_source && _source._itemsForRows)
return _source._itemsForRows;
return [];
}
@end
// --- Selection Index Paths Binder ---
/*!
_CPOutlineViewSelectionIndexPathsBinder listens for selection changes on the CPOutlineView
and translates the selected rows into CPIndexPaths to push to the CPTreeController.
It also intercepts changes from the CPTreeController and auto-expands the tree to highlight them.
*/
@implementation _CPOutlineViewSelectionIndexPathsBinder : CPBinder
{
BOOL _isSyncingFromModel;
}
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
{
self = [super initWithBinding:aBinding name:aName to:aDestination keyPath:aKeyPath options:options from:aSource];
[[CPNotificationCenter defaultCenter]
addObserver:self
selector:@selector(outlineViewSelectionDidChange:)
name:CPOutlineViewSelectionDidChangeNotification
object:aSource];
return self;
}
+ (void)unbind:(CPString)aBinding forObject:(id)anObject
{
if (aBinding === "selectionIndexPaths")
[[CPNotificationCenter defaultCenter]
removeObserver:self
name:CPOutlineViewSelectionDidChangeNotification
object:anObject];
[super unbind:aBinding forObject:anObject];
}
- (void)setValueFor:(CPString)aBinding
{
// 1. SUPPRESS KVO AT THE VERY TOP to avoid circular updates when expanding parents
_isSyncingFromModel = YES;
var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey],
indexPaths = [destination valueForKeyPath:keyPath] || [],
indexes = [CPMutableIndexSet indexSet];
// 2. Fetch the root node directly from the CPTreeController (destination)
var rootNode = [destination respondsToSelector:@selector(arrangedObjects)] ? [destination arrangedObjects] : nil;
if (rootNode)
{
for (var i = 0, count = [indexPaths count]; i < count; i++)
{
var item = [rootNode descendantNodeAtIndexPath:[indexPaths objectAtIndex:i]];
if (item)
{
var parentsToExpand = [CPMutableArray array],
parent = [item parentNode];
while (parent && parent !== rootNode)
{
[parentsToExpand insertObject:parent atIndex:0];
parent = [parent parentNode];
}
for (var j = 0; j < [parentsToExpand count]; j++)
[_source expandItem:parentsToExpand[j]];
var row = [_source rowForItem:item];
if (row !== CPNotFound && row >= 0)
[indexes addIndex:row];
}
}
}
// Adjust the CPOutlineView selection
[_source selectRowIndexes:indexes byExtendingSelection:NO];
// 3. Re-enable KVO after adjustments are done
_isSyncingFromModel = NO;
}
- (void)outlineViewSelectionDidChange:(CPNotification)note
{
// We only want to push the change back if we aren't currently syncing down from the model
if (_isSyncingFromModel)
return;
// In CPBinder, reverseSetValueFor: takes the name of the property on _source
// it should fetch the updated value from. Since CPOutlineView has the selectionIndexPaths method:
[self reverseSetValueFor:@"selectionIndexPaths"];
}
@end
+5 -4
View File
@@ -22,10 +22,13 @@
@import "CPWindow.j"
@global CPApp
CPOKButton = 1;
CPCancelButton = 0;
CPDocModalWindowMask = 1 << 6;
/*!
@ingroup appkit
@class CPPanel
@@ -51,8 +54,6 @@ CPCancelButton = 0;
@global
@class CPWindow
*/
CPDocModalWindowMask = 1 << 6;
@implementation CPPanel : CPWindow
{
BOOL _becomesKeyOnlyIfNeeded;
@@ -120,8 +121,8 @@ CPDocModalWindowMask = 1 << 6;
}
#pragma mark -
#pragma mark Overrides
// MARK: -
// MARK: Overrides
/*!
@ignore
+4 -69
View File
@@ -44,6 +44,9 @@ CPStringPboardType = @"CPStringPboardType";
CPURLPboardType = @"CPURLPboardType";
CPImagesPboardType = @"CPImagesPboardType";
CPVideosPboardType = @"CPVideosPboardType";
CPRTFPboardType = @"CPRTFPboardType";
_CPSmartPboardType = @"_CPSmartPboardType";
_CPASPboardType = @"_CPASPboardType";
UTF8PboardType = @"public.utf8-plain-text";
@@ -51,8 +54,7 @@ UTF8PboardType = @"public.utf8-plain-text";
CPImagePboardType = @"CPImagePboardType";
var CPPasteboards = nil,
supportsNativePasteboard = NO;
var CPPasteboards = nil;
/*!
@ingroup appkit
@@ -68,8 +70,6 @@ var CPPasteboards = nil,
unsigned _changeCount;
CPString _stateUID;
CPWebScriptObject _nativePasteboard;
}
/*
@@ -83,9 +83,6 @@ var CPPasteboards = nil,
[self setVersion:1.0];
CPPasteboards = @{};
if (typeof window.cpPasteboardWithName !== "undefined")
supportsNativePasteboard = YES;
}
/*!
@@ -128,12 +125,6 @@ var CPPasteboards = nil,
_provided = @{};
_changeCount = 0;
if (supportsNativePasteboard)
{
_nativePasteboard = window.cpPasteboardWithName(aName);
[self _synchronizePasteboard];
}
}
return self;
@@ -163,15 +154,6 @@ var CPPasteboards = nil,
[_owners setObject:anOwner forKey:type];
}
if (_nativePasteboard)
{
var nativeTypes = [types copy];
if ([types containsObject:CPStringPboardType])
nativeTypes.push(UTF8PboardType);
_nativePasteboard.addTypes_(nativeTypes);
}
return ++_changeCount;
}
@@ -182,12 +164,6 @@ var CPPasteboards = nil,
@return the pasteboard's change count
*/
- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner
{
[self _declareTypes:types owner:anOwner updateNativePasteboard:YES];
}
/*! @ignore */
- (unsigned)_declareTypes:(CPArray)types owner:(id)anOwner updateNativePasteboard:(BOOL)shouldUpdate
{
[_types setArray:types];
@@ -201,16 +177,6 @@ var CPPasteboards = nil,
[_owners setObject:anOwner forKey:_types[count]];
}
if (_nativePasteboard && shouldUpdate)
{
var nativeTypes = [types copy];
if ([types containsObject:CPStringPboardType])
nativeTypes.push(UTF8PboardType);
_nativePasteboard.declareTypes_(nativeTypes);
_changeCount = _nativePasteboard.changeCount();
}
return ++_changeCount;
}
@@ -273,7 +239,6 @@ var CPPasteboards = nil,
*/
- (CPArray)types
{
[self _synchronizePasteboard];
return _types;
}
@@ -312,36 +277,6 @@ var CPPasteboards = nil,
return nil;
}
- (void)_synchronizePasteboard
{
if (_nativePasteboard && _nativePasteboard.changeCount() > _changeCount)
{
var nativeTypes = [_nativePasteboard.types() copy];
if ([nativeTypes containsObject:UTF8PboardType])
nativeTypes.push(CPStringPboardType);
[self _declareTypes:nativeTypes owner:self updateNativePasteboard:NO];
_changeCount = _nativePasteboard.changeCount();
}
}
/*! @ignore
method provided for integration with native pasteboard
*/
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
{
if (aType === CPStringPboardType)
{
var string = _nativePasteboard.stringForType_(UTF8PboardType);
[self setString:string forType:CPStringPboardType];
[self setString:string forType:UTF8PboardType];
}
else
[self setString:_nativePasteboard.stringForType_(aType) forType:aType];
}
/*!
Returns the property list for the specified data type
@param aType the requested data type
+48 -16
View File
@@ -45,6 +45,13 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
return "popup-button";
}
+ (CPDictionary)themeAttributes
{
return @{
@"menu-offset": CGSizeMake(0, 0)
};
}
+ (CPSet)keyPathsForValuesAffectingSelectedIndex
{
return [CPSet setWithObject:@"objectValue"];
@@ -361,15 +368,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
*/
- (CPArray)itemTitles
{
var titles = [],
items = [self itemArray],
index = 0,
count = [items count];
for (; index < count; ++index)
titles.push([items[index] title]);
return titles;
return [[self itemArray] arrayByApplyingBlock:function(item)
{
return [item title];
}];
}
/*!
@@ -496,9 +498,17 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
if (index < 0)
{
[self addItemWithTitle:aTitle];
index = [self numberOfItems] - 1;
// this ist to match cocoa where setting an empty string does not add it but simply clears the title
// and sets objectValue to -1
if (aTitle === '')
[self selectItemAtIndex:-1];
else
{
[self addItemWithTitle:aTitle];
index = [self numberOfItems] - 1;
}
}
[self selectItemAtIndex:index];
@@ -682,14 +692,16 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
if ([self pullsDown])
{
var positionedItem = nil,
location = CGPointMake(0.0, CGRectGetMaxY(bounds) - 1);
menuOffset = [self currentValueForThemeAttribute:@"menu-offset"],
location = CGPointMake(menuOffset.width, CGRectGetMaxY(bounds) + menuOffset.height);
}
else
{
var contentRect = [self contentRectForBounds:bounds],
positionedItem = [self selectedItem],
standardLeftMargin = [_CPMenuWindow _standardLeftMargin] + [_CPMenuItemStandardView _standardLeftMargin],
location = CGPointMake(CGRectGetMinX(contentRect) - standardLeftMargin, 0.0);
menuOffset = [self currentValueForThemeAttribute:@"menu-offset"],
location = CGPointMake(CGRectGetMinX(contentRect) - standardLeftMargin + menuOffset.width, menuOffset.height);
minimumWidth += standardLeftMargin;
@@ -852,7 +864,17 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
{
var count = [aValue count],
options = [_info objectForKey:CPOptionsKey],
offset = [self _getInsertNullOffset];
offset = [self _getInsertNullOffset],
selectedBindingInfo = [_source infoForBinding:CPSelectedObjectBinding],
selectedObject = nil;
if (selectedBindingInfo)
{
var destination = [selectedBindingInfo objectForKey:CPObservedObjectKey],
keyPath = [selectedBindingInfo objectForKey:CPObservedKeyPathKey];
selectedObject = [destination valueForKeyPath:keyPath];
}
if (count + offset != [_source numberOfItems])
{
@@ -863,9 +885,19 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
for (var i = 0; i < count; i++)
{
var item = [[CPMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:nil];
[self _setValue:[aValue objectAtIndex:i] forItem:item withOptions:options];
var item = [[CPMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:nil],
itemValue = [aValue objectAtIndex:i];
[self _setValue:itemValue forItem:item withOptions:options];
[_source addItem:item];
// Select this item if it is the one selected by the selected object binding
// This is needed if the selected object binding is set before the items
// from the content binding
if (itemValue === selectedObject)
{
[_source setSelectedIndex:[_source numberOfItems] - 1];
}
}
}
else
+10 -10
View File
@@ -84,8 +84,8 @@ var CPPopoverDelegate_popover_willShow_ = 1 << 0,
}
#pragma mark -
#pragma mark Initialization
// MARK: -
// MARK: Initialization
/*!
Initialize the CPPopover witn default values
@@ -105,8 +105,8 @@ var CPPopoverDelegate_popover_willShow_ = 1 << 0,
}
#pragma mark -
#pragma mark Getters / Setters
// MARK: -
// MARK: Getters / Setters
/*!
Returns the current rect of the popover
@@ -220,8 +220,8 @@ Set the behavior of the CPPopover. It can be:
_implementedDelegateMethods |= CPPopoverDelegate_popover_didClose_;
}
#pragma mark -
#pragma mark Positioning
// MARK: -
// MARK: Positioning
/*!
Show the popover
@@ -298,8 +298,8 @@ Set the behavior of the CPPopover. It can be:
}
#pragma mark -
#pragma mark Action
// MARK: -
// MARK: Action
/*!
Close the popover
@@ -318,8 +318,8 @@ Set the behavior of the CPPopover. It can be:
}
#pragma mark -
#pragma mark Delegates
// MARK: -
// MARK: Delegates
/*! @ignore */
- (BOOL)_popoverWindowShouldClose
+55
View File
@@ -86,6 +86,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
@"spinning-mini-gif": [CPNull null],
@"spinning-small-gif": [CPNull null],
@"spinning-regular-gif": [CPNull null],
@"circular-border-color": [CPNull null],
@"circular-border-size": 1,
@"circular-color": [CPNull null]
};
}
@@ -368,6 +371,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
- (void)drawBar
{
[self setNeedsLayout];
[self setNeedsDisplay:YES];
}
- (CPView)createEphemeralSubviewNamed:(CPString)aName
@@ -413,6 +417,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
{
if (_style == CPProgressIndicatorSpinningStyle)
{
if (!_indeterminate)
return;
// This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle.
[self layoutEphemeralSubviewNamed:"bar-view"
positioned:CPWindowBelow
@@ -438,6 +445,54 @@ var CPProgressIndicatorSpinningStyleColors = [];
[self setBackgroundColor:nil];
}
- (void)drawRect:(CGRect)aRect
{
if (_style == CPProgressIndicatorSpinningStyle && !_indeterminate)
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
rect = CGRectMakeCopy(aRect),
borderSize = [self currentValueForThemeAttribute:@"circular-border-size"];
rect.origin.x += borderSize;
rect.origin.y += borderSize;
rect.size.width = rect.size.width - borderSize * 2;
rect.size.height = rect.size.height - borderSize * 2;
if ([self doubleValue] > [self minValue] && [self doubleValue] < [self maxValue])
{
var midX = CGRectGetMidX(rect),
midY = CGRectGetMidY(rect),
endAngle = Math.PI * 2 * (([self doubleValue] - [self minValue]) / ([self maxValue] - [self minValue])) - Math.PI / 2,
radius = MIN(rect.size.width / 2, rect.size.height / 2);
CGContextBeginPath(context);
CGContextSetLineWidth(context, borderSize);
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
CGContextMoveToPoint(context, midX, midY);
CGContextAddArc(context, midX, midY, radius, 3 * Math.PI / 2, endAngle, YES);
CGContextAddLineToPoint(context, midX, midY);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextStrokePath(context);
}
else if ([self doubleValue] == [self maxValue])
{
CGContextBeginPath(context);
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
CGContextAddEllipseInRect(context, rect);
CGContextClosePath(context);
CGContextFillPath(context);
}
CGContextBeginPath(context);
CGContextSetStrokeColor(context , [self currentValueForThemeAttribute:@"circular-border-color"]);
CGContextSetLineWidth(context, borderSize);
CGContextAddEllipseInRect(context, rect);
CGContextClosePath(context);
CGContextStrokePath(context);
}
}
@end
+113 -17
View File
@@ -29,6 +29,7 @@
@global CPApp
CPRadioImageOffset = 4.0;
/*!
@ingroup appkit
@@ -66,10 +67,19 @@
[[button1 radioGroup] selectedRadio] returns the currently selected
option.
UPDATE 09/2020 : Implementation of modern Cocoa behavior :
As in Cocoa, radio buttons grouping is now automatic.
To be associated in a common group (and so being mutually exclusive),
radio buttons must combine 2 criteria :
- same superview (enclosing view)
- same action
TODO: This first implementation uses "as is" CPRadioGroup. This could
be simplified (no more need for radio group action, for example)
*/
CPRadioImageOffset = 4.0;
@implementation CPRadio : CPButton
{
CPRadioGroup _radioGroup;
@@ -162,6 +172,71 @@ CPRadioImageOffset = 4.0;
[CPApp sendAction:[_radioGroup action] to:[_radioGroup target] from:_radioGroup];
}
- (void)viewDidMoveToSuperview
{
[self _setRadioGroup];
[super viewDidMoveToSuperview];
}
- (void)setAction:(SEL)anAction
{
if (anAction === _action)
return;
[super setAction:anAction];
[self _setRadioGroup];
}
// MARK: Private methods
- (void)_setRadioGroup
{
// Implementation of modern Cocoa behavior : automatic radio group
// If no action is set or no superview, no grouping can be done.
if (![self action] || ![self superview])
{
// If this radio is in a group (size > 1), remove it.
if ([[self radioGroup] size] > 1)
{
[self setRadioGroup:[CPRadioGroup new]];
if ([self state] === CPOnState)
[_radioGroup _setSelectedRadio:self];
}
return;
}
// Search in superview subviews for other radio buttons having the same action.
// Take the one with the radio group having the greatest number of members.
var radioGroup;
for (var i = 0, superviewSubviews = [[self superview] subviews], count = [superviewSubviews count], aSubview, myAction = [self action], radioGroupSize = -1; (i < count); i++)
{
aSubview = superviewSubviews[i];
if ([aSubview isKindOfClass:CPRadio] && (aSubview !== self) && ([aSubview action] === myAction) && ([[aSubview radioGroup] size] > radioGroupSize))
{
radioGroup = [aSubview radioGroup];
radioGroupSize = [radioGroup size];
}
}
if (radioGroup)
[self setRadioGroup:radioGroup];
else
// No other radio buttons to group with found.
// It may be because this radio button was in a radio group and its action was changed.
// If this is the case, we must reisolate it in a new radio group.
if ([_radioGroup size] > 1)
[self setRadioGroup:[CPRadioGroup new]];
if ([self state] === CPOnState)
[_radioGroup _setSelectedRadio:self];
}
@end
var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
@@ -185,21 +260,37 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
[aCoder encodeObject:_radioGroup forKey:CPRadioRadioGroupKey];
}
- (CPImage)image
// MARK: -
// MARK: Override methods from CPButton
- (CPThemeState)_contentVisualState
{
return [self currentValueForThemeAttribute:@"image"];
// Note : Behavior differs from CPButton as title doesn't follow the highlightsBy content flag
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
currentState = [self state];
if ((currentState !== CPOffState) && (_showsStateBy & CPContentsCellMask))
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
return visualState;
}
- (CPImage)alternateImage
- (CPThemeState)_imageVisualState
{
return [self currentValueForThemeAttribute:@"image"];
}
// Note : Behavior differs from CPButton as we don't force "not selected" theme state
// when button is highglighted and selected
- (BOOL)startTrackingAt:(CGPoint)aPoint
{
var startedTracking = [super startTrackingAt:aPoint];
[self highlight:YES];
return startedTracking;
var visualState = [self themeState] || CPThemeStateNormal, // Needed during theme compilation
currentState = [self state];
if (_isHighlighted && (_highlightsBy & CPContentsCellMask))
visualState = visualState.and(CPThemeStateHighlighted);
if ((currentState !== CPOffState) && (_showsStateBy & CPContentsCellMask))
visualState = visualState.and((currentState === CPOnState) ? CPThemeStateSelected : CPButtonStateMixed);
return visualState;
}
@end
@@ -269,9 +360,9 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
- (BOOL)selectRadioWithTag:(int)tag
{
var index = [_radios indexOfObjectPassingTest:function(radio)
{
return [radio tag] === tag;
}];
{
return [radio tag] === tag;
}];
if (index !== CPNotFound)
{
@@ -306,6 +397,11 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
return _radios;
}
- (int)size
{
return [_radios count];
}
- (void)setEnabled:(BOOL)enabled
{
[_radios makeObjectsPerformSelector:@selector(setEnabled:) withObject:enabled];
@@ -316,7 +412,7 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
[_radios makeObjectsPerformSelector:@selector(setHidden:) withObject:hidden];
}
#pragma mark Private
// MARK: Private
- (void)_addRadio:(CPRadio)aRadio
{
+14 -1
View File
@@ -24,6 +24,7 @@
@import <Foundation/CPObjJRuntime.j>
@import "CPEvent.j"
@import "CPCursor.j"
@class CPKeyBinding
@class CPMenu
@@ -200,6 +201,18 @@ CPDeleteForwardKeyCode = 46;
[_nextResponder performSelector:_cmd withObject:anEvent];
}
/*!
Notifies the receiver that the mouse entered the receiver's area and that it can adapt the cursor.
@param anEvent contains information about the exit
*/
- (void)cursorUpdate:(CPEvent)anEvent
{
if (_nextResponder)
[_nextResponder performSelector:_cmd withObject:anEvent];
else
[[CPCursor arrowCursor] set];
}
/*!
Notifies the receiver that the mouse scroll wheel has moved.
@param anEvent information about the scroll
@@ -384,7 +397,7 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey",
- (void)encodeWithCoder:(CPCoder)aCoder
{
// This will come out nil on the other side with decodeObjectForKey:
if (_nextResponder !== nil)
if (_nextResponder != nil)
[aCoder encodeConditionalObject:_nextResponder forKey:CPResponderNextResponderKey];
[aCoder encodeObject:_menu forKey:CPResponderMenuKey];
+11 -6
View File
@@ -34,7 +34,7 @@
SEL _predicateAction @accessors(property=action);
}
#pragma mark public methods
// MARK: public methods
/*!
@ingroup appkit
@class CPPredicateEditor
@@ -251,7 +251,7 @@
return tree;
}
#pragma mark Set the Predicate
// MARK: Set the Predicate
- (void)setObjectValue:(id)objectValue
{
@@ -269,6 +269,8 @@
_currentAnimation = nil;
_sendAction = NO;
var rows = [];
if (predicate != nil)
{
if ((_nestingMode == CPRuleEditorNestingModeSimple || _nestingMode == CPRuleEditorNestingModeCompound)
@@ -276,10 +278,13 @@
predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:[CPArray arrayWithObject:predicate]];
var row = [self _rowObjectFromPredicate:predicate];
if (row != nil)
[_boundArrayOwner setValue:[CPArray arrayWithObject:row] forKey:_boundArrayKeyPath];
[rows addObject:row];
}
[_boundArrayOwner setValue:rows forKey:_boundArrayKeyPath];
[self setAnimation:animation];
}
@@ -369,7 +374,7 @@
return row;
}
#pragma mark Get the predicate
// MARK: Get the predicate
- (void)_updatePredicate
{
@@ -447,7 +452,7 @@
return CPAndPredicateType;
}
#pragma mark Control delegate
// MARK: Control delegate
- (void)_sendRuleAction
{
@@ -482,7 +487,7 @@
}
*/
#pragma mark RuleEditor delegate methods
// MARK: RuleEditor delegate methods
- (int)_queryNumberOfChildrenOfItem:(id)rowItem withRowType:(CPRuleEditorRowType)type
{
@@ -471,9 +471,11 @@ CPTransformableAttributeType = 1800;
var value;
if (attributeType >= CPInteger16AttributeType && attributeType <= CPFloatAttributeType)
value = [aView intValue];
value = [aView floatValue];
else if (attributeType == CPBooleanAttributeType)
value = [aView state];
else if (attributeType == CPDateAttributeType)
value = [aView dateValue];
else
value = [aView stringValue];
@@ -487,9 +489,9 @@ CPTransformableAttributeType = 1800;
- (id)copy
{
var views = [CPArray array];
var views = [CPArray array],
copy = [[[self class] alloc] init];
var copy = [[[self class] alloc] init];
[copy _setTemplateType:_templateType];
[copy _setOptions:_predicateOptions];
[copy _setModifier:_predicateModifier];
+167 -60
View File
@@ -80,6 +80,35 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
@n An ordered to-many relation containing the display values for the row.
@n@n @c @@"criteria"
@n An ordered to-many relation containing the criteria for the row.
@n@n
Localization & Positional Reordering
@n
CPRuleEditor supports complete localization of menu items and grammatical positional reordering (sentence structure layout adjustment) via strings resource files (.strings) or custom programmatic CPDictionary tables.
@n@n
Since sentence structures vary significantly across languages, the editor can dynamically reposition views (such as popups, static labels, and text fields) from left to right to form grammatically correct sentences.
@n@n
Formatting Keys (English representation):
@n @c %[%]@@
@n Represents a popup button displaying its selected value (e.g. @c %[firstName]@@).
@n @c %@@
@n Represents an editable text input field.
@n Static text represents a literal label placed directly inside the formatting key.
@n@n
Example English format key:
@n @c "%[firstName]@ %[is equal to]@ %@"
@n@n
Translation Patterns (Target language):
@n Positional specifiers such as @c %1$@@, @c %2$@@, @c %3$@@ dictate the visual order of views from left to right.
@n Bracketed values inside positional specifiers (e.g. @c %1$[Nombre]@@) define the localized title for popup selection items.
@n Literal text outside the specifiers (such as @c "y" or @c "und") is automatically instantiated as static text labels positioned between controls.
@n@n
Example translations:
@n@n
Spanish (Reorders to: [1: Name] y [3: Value] [2: are equal]):
@n @c "%[firstName]@ %[is equal to]@ %@" = "%1$[Nombre]@ y %3$@ %2$[son iguales]@";
@n@n
German (Reorders to: [1: First Name] und [3: Value] [2: are equal]):
@n @c "%[firstName]@ %[is equal to]@ %@" = "%1$[Vorname]@ und %3$@ %2$[sind gleich]@";
*/
@implementation CPRuleEditor : CPControl
@@ -127,7 +156,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
BOOL _isKeyDown;
BOOL _nestingModeDidChange;
_CPRuleEditorLocalizer _standardLocalizer @accessors(property=standardLocalizer);
_CPRuleEditorLocalizer _standardLocalizer;
CPDictionary _itemsAndValuesToAddForRowType;
}
@@ -146,7 +175,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
@"slice-top-border-color": [CPNull null],
@"slice-bottom-border-color": [CPNull null],
@"slice-last-bottom-border-color": [CPNull null],
@"font": [CPNull null],
@"font": [CPFont systemFontOfSize:12],
@"font-color": [CPNull null],
@"add-image": [CPNull null],
@"remove-image": [CPNull null],
@@ -157,7 +186,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self !== nil)
if (self)
{
_slices = [[CPMutableArray alloc] init];
@@ -207,8 +236,34 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPRuleEditorItemPBoardType,nil]];
[_boundArrayOwner addObserver:self forKeyPath:_boundArrayKeyPath options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:boundArrayContext];
[[CPNotificationCenter defaultCenter] addObserver:self
selector:@selector(_ruleEditorLocalizerDidLoad:)
name:@"_CPRuleEditorLocalizerDidLoadNotification"
object:nil];
}
- (void)_ruleEditorLocalizerDidLoad:(CPNotification)aNotification
{
if ([aNotification object] === [self standardLocalizer])
{
// Defer execution to the next run loop cycle so that any active slice
// insertions have fully completed and are present in the `_slices` array.
[[CPRunLoop mainRunLoop] performBlock:function() {
var count = [_slices count];
for (var i = 0; i < count; i++)
{
var slice = [_slices objectAtIndex:i];
[slice _reconfigureSubviews];
[slice _updateButtonVisibilities]; // Force updates on row button tooltips
}
[self _updatePredicate];
[self _sendRuleAction];
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
}
}
/*! @endcond */
/*!
@@ -384,7 +439,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
*/
- (CPDictionary)formattingDictionary
{
return [_standardLocalizer dictionary];
return [[self standardLocalizer] dictionary];
}
/*!
@@ -396,6 +451,9 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
*/
- (void)setFormattingDictionary:(CPDictionary)dictionary
{
if (_standardLocalizer == nil)
_standardLocalizer = [_CPRuleEditorLocalizer new];
[_standardLocalizer setDictionary:dictionary];
_stringsFilename = nil;
}
@@ -419,7 +477,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
*/
- (void)setFormattingStringsFilename:(CPString)stringsFilename
{
if (_standardLocalizer === nil)
if (_standardLocalizer == nil)
_standardLocalizer = [_CPRuleEditorLocalizer new];
if (_stringsFilename !== stringsFilename)
@@ -427,19 +485,32 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
// Convert an empty string to nil
_stringsFilename = stringsFilename || nil;
if (stringsFilename !== nil)
if (stringsFilename != nil)
{
if (![stringsFilename hasSuffix:@".strings"])
stringsFilename = stringsFilename + @".strings";
var path = [[CPBundle mainBundle] pathForResource:stringsFilename];
if (path !== nil)
if (path != nil)
[_standardLocalizer loadContentOfURL:[CPURL URLWithString:path]];
}
}
}
- (_CPRuleEditorLocalizer)standardLocalizer
{
if (_standardLocalizer == nil)
_standardLocalizer = [_CPRuleEditorLocalizer new];
return _standardLocalizer;
}
- (void)setStandardLocalizer:(_CPRuleEditorLocalizer)aLocalizer
{
_standardLocalizer = aLocalizer;
}
/*!
@name Providing Data
*/
@@ -465,7 +536,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
*/
- (void)setCriteria:(CPArray)criteria andDisplayValues:(CPArray)values forRowAtIndex:(int)rowIndex
{
if (criteria === nil || values === nil)
if (criteria == nil || values == nil)
[CPException raise:CPInvalidArgumentException reason:_cmd + @". criteria and values parameters must not be nil."];
if (rowIndex < 0 || rowIndex >= [self numberOfRows])
@@ -538,7 +609,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
if ([self rowTypeForRow:current_index] === CPRuleEditorRowTypeCompound)
{
var candidate = [[self _rowCacheForIndex:current_index] rowObject],
subObjects = [[self _subrowObjectsOfObject:candidate] _representedObject];
subObjects = [self _subrowObjectsOfObject:candidate]; // Standard direct array query
if ([subObjects indexOfObjectIdenticalTo:targetObject] !== CPNotFound)
return current_index;
@@ -605,7 +676,7 @@ TODO: implement
for (var i = rowIndex + 1; i < count; i++)
{
var candidate = [[self _rowCacheForIndex:i] rowObject],
indexInSubrows = [[subobjects _representedObject] indexOfObjectIdenticalTo:candidate];
indexInSubrows = [subobjects indexOfObjectIdenticalTo:candidate]; // Standard direct array query
if (indexInSubrows !== CPNotFound)
{
@@ -774,7 +845,7 @@ TODO: implement
while (current_index !== CPNotFound)
{
var rowObject = [[self _rowCacheForIndex:current_index] rowObject],
relativeChildIndex = [[subrows _representedObject] indexOfObjectIdenticalTo:rowObject];
relativeChildIndex = [subrows indexOfObjectIdenticalTo:rowObject]; // Standard direct array query
if (relativeChildIndex !== CPNotFound)
[childsIndexes addIndex:relativeChildIndex];
@@ -831,7 +902,6 @@ TODO: implement
for (i = 0; i < count; i++)
{
var item = [items objectAtIndex:i],
//var displayValue = [self _queryValueForItem:item inRow:aRow]; Ask the delegate or get cached value ?.
displayValue = [[self displayValuesForRow:aRow] objectAtIndex:i],
predpart = [self _sendDelegateRuleEditorPredicatePartsForCriterion:item withDisplayValue:displayValue inRow:aRow];
@@ -849,10 +919,11 @@ TODO: implement
return nil;
var current_index = [subrowsIndexes firstIndex];
while (current_index !== CPNotFound)
{
var subpredicate = [self predicateForRow:current_index];
if (subpredicate !== nil)
if (subpredicate != nil)
[subpredicates addObject:subpredicate];
current_index = [subrowsIndexes indexGreaterThanIndex:current_index];
@@ -888,33 +959,33 @@ TODO: implement
modifier = [predicateParts objectForKey:CPRuleEditorPredicateComparisonModifier],
selector = CPSelectorFromString([predicateParts objectForKey:CPRuleEditorPredicateCustomSelector]);
if (lhs === nil)
if (lhs == nil)
{
CPLogConsole(@"missing left expression in predicate parts dictionary");
return NULL;
}
if (rhs === nil)
if (rhs == nil)
{
CPLogConsole(@"missing right expression in predicate parts dictionary");
return NULL;
}
if (selector === nil && operator === nil)
if (selector == nil && operator == nil)
{
CPLogConsole(@"missing operator and selector in predicate parts dictionary");
return NULL;
}
if (modifier === nil)
if (modifier == nil)
CPLogConsole(@"missing modifier in predicate parts dictionary. Setting default: CPDirectPredicateModifier");
if (options === nil)
if (options == nil)
CPLogConsole(@"missing options in predicate parts dictionary. Setting default: CPCaseInsensitivePredicateOption");
try
{
if (selector !== nil)
if (selector != nil)
predicate = [CPComparisonPredicate predicateWithLeftExpression:lhs
rightExpression:rhs
customSelector:selector
@@ -1167,7 +1238,7 @@ TODO: implement
- (BOOL)_wantsRowAnimations
{
return (_currentAnimation !== nil);
return (_currentAnimation != nil);
}
- (void)_updateButtonVisibilities
@@ -1233,7 +1304,7 @@ TODO: implement
return shouldHide;
}
#pragma mark Rows management
// MARK: Rows management
- (id)_rowCacheForIndex:(int)index
{
@@ -1278,23 +1349,28 @@ TODO: implement
while (current_index !== CPNotFound)
{
var parentIndex = [self parentRowForRow:current_index],
subrowsIndexes = [self subrowIndexesForRow:parentIndex];
if ([subrowsIndexes count] === 1)
var parentIndex = [self parentRowForRow:current_index];
// If the row has a valid parent in the editor (i.e. not a root row)
if (parentIndex !== -1)
{
if (parentIndex !== -1)
return [CPIndexSet indexSetWithIndex:0];
var subrowsIndexes = [self subrowIndexesForRow:parentIndex];
var childlessGranPa = [self _childlessParentsIfSlicesWereDeletedAtIndexes:[CPIndexSet indexSetWithIndex:parentIndex]];
[childlessParents addIndexes:childlessGranPa];
// If deleting this row leaves the parent with no remaining child rows
if ([subrowsIndexes count] === 1)
{
[childlessParents addIndex:parentIndex];
// Recursively check if deleting this parent row leaves the grandparent childless
var childlessGranPa = [self _childlessParentsIfSlicesWereDeletedAtIndexes:[CPIndexSet indexSetWithIndex:parentIndex]];
[childlessParents addIndexes:childlessGranPa];
}
}
current_index = [indexes indexGreaterThanIndex:current_index];
}
return childlessParents;
// (id)-[RuleEditor _includeSubslicesForSlicesAtIndexes:]
}
- (CPIndexSet)_includeSubslicesForSlicesAtIndexes:(CPIndexSet)indexes
@@ -1356,8 +1432,25 @@ TODO: implement
if ([self rowTypeForRow:row] === type && itemIndex < [aCriteria count])
{
var crit = [aCriteria objectAtIndex:itemIndex];
[current_criterions addObject:crit];
// Verify that this row's parent path matches the path currently being built
var pathMatches = true;
for (var p = 0; p < itemIndex; p++)
{
var criterionA = [aCriteria objectAtIndex:p],
criterionB = [items objectAtIndex:p];
if (criterionA !== criterionB && (typeof criterionA.isEqual !== "function" || ![criterionA isEqual:criterionB]))
{
pathMatches = false;
break;
}
}
if (pathMatches)
{
var crit = [aCriteria objectAtIndex:itemIndex];
[current_criterions addObject:crit];
}
}
}
@@ -1473,7 +1566,7 @@ TODO: implement
return row;
}
#pragma mark Key value observing
// MARK: Key value observing
- (void)_startObservingRowObjectsRecursively:(CPArray)rowObjects
{
@@ -1753,7 +1846,7 @@ TODO: implement
{
var subpredicate = [self predicateForRow:current_index];
if (subpredicate !== nil)
if (subpredicate != nil)
[subpredicates addObject:subpredicate];
current_index = [subindexes indexGreaterThanIndex:current_index];
@@ -1774,7 +1867,12 @@ TODO: implement
- (_CPRuleEditorViewSliceRow)_createNewSliceWithFrame:(CGRect)frame ruleEditorView:(CPRuleEditor)editor
{
return [[_CPRuleEditorViewSliceRow alloc] initWithFrame:frame ruleEditorView:editor];
var slice = [[_CPRuleEditorViewSliceRow alloc] initWithFrame:frame ruleEditorView:editor];
// Ensure the slice resizes with the editor
[slice setAutoresizingMask:CPViewWidthSizable];
return slice;
}
- (void)_reconfigureSubviewsAnimate:(BOOL)animate
@@ -1795,7 +1893,7 @@ TODO: implement
startRect = [aslice frame],
startIndex = [aslice rowIndex] - 1;
if ([aslice superview] === nil)
if ([aslice superview] == nil)
{
startRect = CGRectMake(0, startIndex * _sliceHeight, CGRectGetWidth(startRect), _sliceHeight);
[aslice _reconfigureSubviews];
@@ -1919,17 +2017,17 @@ TODO: implement
- (CPString)_toolTipForAddCompoundRowButton
{
return [_standardLocalizer localizedStringForString:@"Add compound row"];
return [[self standardLocalizer] localizedStringForString:@"Add compound row"];
}
- (CPString)_toolTipForAddSimpleRowButton
{
return [_standardLocalizer localizedStringForString:@"Add row"];
return [[self standardLocalizer] localizedStringForString:@"Add row"];
}
- (CPString)_toolTipForDeleteRowButton
{
return [_standardLocalizer localizedStringForString:@"Delete row"];
return [[self standardLocalizer] localizedStringForString:@"Delete row"];
}
- (void)_updateSliceIndentations
@@ -2121,7 +2219,7 @@ TODO: implement
return;
var point = [self convertPoint:[event locationInWindow] fromView:nil],
view = [_slices objectAtIndex:FLOOR(point.y / _sliceHeight)];
view = [_slices objectAtIndex:FLOOR(MAX(0, point.y) / _sliceHeight)];
if ([self _dragShouldBeginFromMouseDown:view])
[self _performDragForSlice:view withEvent:event];
@@ -2129,7 +2227,7 @@ TODO: implement
- (BOOL)_dragShouldBeginFromMouseDown:(CPView)view
{
return (([self nestingMode] === CPRuleEditorNestingModeList || [view rowIndex] !== 0) && _editable && [view isKindOfClass:[_CPRuleEditorViewSliceRow class]] && _draggingRows === nil);
return (([self nestingMode] === CPRuleEditorNestingModeList || [view rowIndex] !== 0) && _editable && [view isKindOfClass:[_CPRuleEditorViewSliceRow class]] && _draggingRows == nil);
}
- (BOOL)_performDragForSlice:(id)slice withEvent:(CPEvent)event
@@ -2244,33 +2342,41 @@ TODO: implement
- (BOOL)performDragOperation:(id /*< CPDraggingInfo >*/)info
{
var aboveInsertIndexCount = 0,
object,
removeIndex;
var object;
var rowObjects = [_rowCache valueForKey:@"rowObject"],
index = [_draggingRows lastIndex];
var parentRowIndex = [self parentRowForRow:index], // first index of draggingrows
var firstDraggingIndex = [_draggingRows firstIndex],
parentRowIndex = [self parentRowForRow:firstDraggingIndex],
parentRowObject = (parentRowIndex === -1) ? _boundArrayOwner : [[self _rowCacheForIndex:parentRowIndex] rowObject],
insertIndex = _subviewIndexOfDropLine;
while (index !== CPNotFound)
{
if (index >= insertIndex)
// Identify if this row's parent is also inside the dragging set.
// If it is, we skip it because it will move automatically with its parent.
var parentOfCurrent = [self parentRowForRow:index];
if (parentOfCurrent !== -1 && [_draggingRows containsIndex:parentOfCurrent])
{
removeIndex = index + aboveInsertIndexCount;
aboveInsertIndexCount += 1;
}
else
{
removeIndex = index;
insertIndex -= 1;
index = [_draggingRows indexLessThanIndex:index];
continue;
}
object = [rowObjects objectAtIndex:removeIndex];
[self removeRowAtIndex:removeIndex];
[[self _subrowObjectsOfObject:parentRowObject] insertObject:object atIndex:insertIndex - parentRowIndex - 1];
object = [rowObjects objectAtIndex:index];
// Find the current live index of the object inside the editor
var cache = [self _searchCacheForRowObject:object];
var liveIndex = [_rowCache indexOfObjectIdenticalTo:cache];
if (liveIndex !== CPNotFound)
{
if (liveIndex < insertIndex)
insertIndex -= 1;
[self removeRowAtIndex:liveIndex];
[[self _subrowObjectsOfObject:parentRowObject] insertObject:object atIndex:insertIndex - parentRowIndex - 1];
}
index = [_draggingRows indexLessThanIndex:index];
}
@@ -2280,6 +2386,7 @@ TODO: implement
return YES;
}
- (CPIndexSet)_draggingTypes
{
return [CPIndexSet indexSetWithIndex:CPDragOperationMove];
@@ -2321,7 +2428,7 @@ TODO: implement
- (void)_postRowCountChangedNotificationOfType:(CPString)notificationName indexes:indexes
{
var userInfo = indexes === nil ? @{} : @{ "indexes": indexes };
var userInfo = indexes == nil ? @{} : @{ "indexes": indexes };
[[CPNotificationCenter defaultCenter] postNotificationName:notificationName object:self userInfo:userInfo];
}
@@ -2386,7 +2493,7 @@ TODO: implement
var criteria = [self criteriaForRow:aRow];
indexofCriterion = [criteria indexOfObject:criterion];
if (parentItem !== nil
if (parentItem != nil
&& indexofCriterion !== CPNotFound
&& indexofCriterion < [criteria count] - 1)
{
@@ -2469,7 +2576,7 @@ var CPRuleEditorAlignmentGridWidthKey = @"CPRuleEditorAlignmentGridWidth",
- (id)initWithCoder:(CPCoder)coder
{
self = [super initWithCoder:coder];
if (self !== nil)
if (self)
{
[self setFormattingStringsFilename:[coder decodeObjectForKey:CPRuleEditorStringsFilenameKey]];
_alignmentGridWidth = [coder decodeFloatForKey:CPRuleEditorAlignmentGridWidthKey];
@@ -2553,7 +2660,7 @@ var CriteriaKey = @"criteria",
- (id)initWithCoder:(CPCoder)coder
{
self = [super init];
if (self !== nil)
if (self)
{
subrows = [coder decodeObjectForKey:SubrowsKey];
criteria = [coder decodeObjectForKey:CriteriaKey];
@@ -131,7 +131,7 @@
{
var title = [self title];
if (title !== nil)
if (title != nil)
return title;
return [self templateView];
+234 -4
View File
@@ -40,7 +40,7 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)
- (void)reloadIfNeeded
{
if (connection !== nil) // Connection waiting
if (connection != nil) // Connection waiting
{
connection = nil;
@@ -51,7 +51,7 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)rawString
{
if (connection !== nil && rawString !== nil)
if (connection != nil && rawString != nil)
[self loadContent:rawString];
connection = nil;
@@ -77,21 +77,251 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+)
}
_dictionary = [CPDictionary dictionaryWithDictionary:dict];
// Post notification to let the rule editor know the translation dictionary is ready
[[CPNotificationCenter defaultCenter] postNotificationName:@"_CPRuleEditorLocalizerDidLoadNotification" object:self];
}
- (CPString)localizedStringForString:(CPString)aString
{
[self reloadIfNeeded];
if (_dictionary !== nil && aString !== nil)
if (_dictionary != nil && aString != nil)
{
var localized = [_dictionary objectForKey:aString];
if (localized !== nil)
if (localized != nil)
return localized;
}
return aString;
}
// MARK: - Formatting & Reordering Helpers
- (CPString)_englishRepresentationForView:(id)aView
{
if ([aView isKindOfClass:[CPPopUpButton class]])
{
var selectedItem = [aView selectedItem];
if (selectedItem)
{
var originalTitle = selectedItem._originalTitle;
// Fallback: If not cached directly, inspect representedObject payload dictionary
if (!originalTitle)
{
var rep = [selectedItem representedObject];
if (rep && typeof rep === "object" && [rep respondsToSelector:@selector(objectForKey:)])
{
originalTitle = [rep objectForKey:@"value"];
}
else if (rep && typeof rep === "string")
{
originalTitle = rep;
}
}
if (!originalTitle)
{
originalTitle = [selectedItem title];
}
return "%[" + originalTitle + "]@";
}
return "%[]@";
}
else if ([aView isKindOfClass:[CPTextField class]] && ![aView isEditable])
{
return aView._originalText || [aView stringValue];
}
else
{
return "%@";
}
}
- (CPString)formattingKeyForViews:(CPArray)views
{
var keyParts = [];
var count = [views count];
for (var i = 0; i < count; i++)
{
var view = [views objectAtIndex:i];
[keyParts addObject:[self _englishRepresentationForView:view]];
}
return [keyParts componentsJoinedByString:@" "];
}
- (void)localizeMenuItemsForViews:(CPArray)views
{
var count = [views count];
for (var i = 0; i < count; i++)
{
var view = [views objectAtIndex:i];
if ([view isKindOfClass:[CPPopUpButton class]])
{
var menuItems = [view itemArray];
var menuItemsCount = [menuItems count];
var selectedItem = [view selectedItem];
for (var j = 0; j < menuItemsCount; j++)
{
var item = [menuItems objectAtIndex:j];
if (!item._originalTitle)
{
var rep = [item representedObject];
if (rep && typeof rep === "object" && [rep respondsToSelector:@selector(objectForKey:)])
{
item._originalTitle = [rep objectForKey:@"value"];
}
else
{
item._originalTitle = [item title];
}
}
// Temporarily select item to generate formatting key context
[view selectItem:item];
var tempKey = [self formattingKeyForViews:views];
var tempPattern = [self localizedStringForString:tempKey];
if (tempPattern !== tempKey)
{
var regex = /%(\d+)\$(?:\[([^\]]+)\])?@/g;
var match;
while ((match = regex.exec(tempPattern)) !== null)
{
var position = parseInt(match[1], 10) - 1;
var translatedValue = match[2];
if (position === i && translatedValue)
{
[item setTitle:translatedValue];
break;
}
}
}
else
{
[item setTitle:item._originalTitle];
}
}
if (selectedItem)
{
[view selectItem:selectedItem];
}
}
}
}
- (CPArray)localizeAndReorderViews:(CPArray)views
{
var key = [self formattingKeyForViews:views];
var localizedPattern = [self localizedStringForString:key];
if (localizedPattern === key)
{
var count = [views count];
for (var i = 0; i < count; i++)
{
var originalView = [views objectAtIndex:i];
if ([originalView isKindOfClass:[CPPopUpButton class]])
{
var selectedItem = [originalView selectedItem];
if (selectedItem && selectedItem._originalTitle)
{
[selectedItem setTitle:selectedItem._originalTitle];
}
}
else if ([originalView respondsToSelector:@selector(setStringValue:)] && originalView._originalText)
{
[originalView setStringValue:originalView._originalText];
if ([originalView isKindOfClass:[CPTextField class]] && ![originalView isEditable])
{
var font = [originalView font] || [CPFont systemFontOfSize:[CPFont systemFontSize]],
size = [originalView._originalText sizeWithFont:font];
[originalView setFrameSize:CGSizeMake(size.width + 4, CGRectGetHeight([originalView frame]))];
}
}
}
return views;
}
var newViews = [CPMutableArray array];
var regex = /%(\d+)\$(?:\[([^\]]+)\])?@/g;
var lastIndex = 0;
var match;
while ((match = regex.exec(localizedPattern)) !== null)
{
var literalText = localizedPattern.substring(lastIndex, match.index);
// Only add a label if there are actual non-whitespace characters (like 'y')
if (literalText.length > 0 && /\S/.test(literalText))
{
var label = [CPTextField labelWithTitle:literalText];
[newViews addObject:label];
}
var position = parseInt(match[1], 10) - 1;
var translatedValue = match[2];
if (position >= 0 && position < [views count])
{
var originalView = [views objectAtIndex:position];
if (translatedValue !== undefined && translatedValue !== null)
{
if ([originalView isKindOfClass:[CPPopUpButton class]])
{
var selectedItem = [originalView selectedItem];
if (selectedItem)
{
if (!selectedItem._originalTitle)
{
selectedItem._originalTitle = [selectedItem title];
}
[selectedItem setTitle:translatedValue];
}
}
else if ([originalView respondsToSelector:@selector(setStringValue:)])
{
[originalView setStringValue:translatedValue];
// Recalculate frame size if it is a static CPTextField to avoid visual clipping
if ([originalView isKindOfClass:[CPTextField class]] && ![originalView isEditable])
{
var font = [originalView font] || [CPFont systemFontOfSize:[CPFont systemFontSize]],
size = [translatedValue sizeWithFont:font];
[originalView setFrameSize:CGSizeMake(size.width + 4, CGRectGetHeight([originalView frame]))];
}
}
}
[newViews addObject:originalView];
}
lastIndex = regex.lastIndex;
}
if (lastIndex < localizedPattern.length)
{
var literalText = localizedPattern.substring(lastIndex);
// Only add a label if there are actual non-whitespace characters
if (literalText.length > 0 && /\S/.test(literalText))
{
var label = [CPTextField labelWithTitle:literalText];
[newViews addObject:label];
}
}
return newViews;
}
@end
@@ -29,7 +29,6 @@
CGRect _animationTargetRect @accessors(property=_animationTargetRect);
BOOL _selected @accessors(getter=_isSelected, setter=_setSelected:);
BOOL _lastSelected @accessors(getter=_isLastSelected, setter=_setLastSelected:);
CPColor _backgroundColor @accessors(property=backgroundColor);
BOOL _editable @accessors(getter=isEditable, setter=setEditable:);
}
+55 -22
View File
@@ -114,8 +114,32 @@
- (CPMenuItem)_createMenuItemWithTitle:(CPString )title
{
title = [[_ruleEditor standardLocalizer] localizedStringForString:title];
return [[CPMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""];
var originalTitle = title;
var localizedTitle = [[_ruleEditor standardLocalizer] localizedStringForString:title];
var item = [[CPMenuItem alloc] initWithTitle:localizedTitle action:nil keyEquivalent:@""];
// Cache the raw English title for pattern-matching
item._originalTitle = originalTitle;
return item;
}
- (CPTextField)_createStaticTextFieldWithStringValue:(CPString)text
{
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()],
ruleEditorFont = [_ruleEditor font],
font = [CPFont fontWithName:[ruleEditorFont familyName] size:[ruleEditorFont size] + 2],
localizedText = [[_ruleEditor standardLocalizer] localizedStringForString:text],
size = [localizedText sizeWithFont:font];
[textField setFrameSize:CGSizeMake(size.width + 4, [_ruleEditor rowHeight])];
[textField setValue:font forThemeAttribute:@"font"];
[textField setValue:[_ruleEditor _verticalAlignment] forThemeAttribute:@"vertical-alignment"];
[textField setStringValue:localizedText];
// Cache the raw English text for pattern-matching
textField._originalText = text;
return textField;
}
- (CPPopUpButton)_createPopUpButtonWithItems:(CPArray)itemsArray selectedItemIndex:(int)index
@@ -142,22 +166,6 @@
return [CPMenuItem separatorItem];
}
- (CPTextField)_createStaticTextFieldWithStringValue:(CPString)text
{
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()],
ruleEditorFont = [_ruleEditor font],
font = [CPFont fontWithName:[ruleEditorFont familyName] size:[ruleEditorFont size] + 2],
localizedText = [[_ruleEditor standardLocalizer] localizedStringForString:text],
size = [localizedText sizeWithFont:font];
[textField setFrameSize:CGSizeMake(size.width + 4, [_ruleEditor rowHeight])];
[textField setValue:font forThemeAttribute:@"font"];
[textField setValue:[_ruleEditor _verticalAlignment] forThemeAttribute:@"vertical-alignment"];
[textField setStringValue:localizedText];
return textField;
}
- (void)_addOption:(id)sender
{
if (_rowIndex == [_ruleEditor numberOfRows] - 1)
@@ -209,8 +217,7 @@
- (void)_reconfigureSubviews
{
var ruleItems,
criteria,
var criteria,
repObject,
menuItem,
ruleView,
@@ -335,6 +342,28 @@
[_correspondingRuleItems setArray:ruleItems];
// Localize drop-down options in context and reorder/insert intermediate labels natively
var localizer = [_ruleEditor standardLocalizer];
if (localizer)
{
[localizer localizeMenuItemsForViews:_ruleOptionViews];
_ruleOptionViews = [localizer localizeAndReorderViews:_ruleOptionViews];
}
// Rebuild frame configurations to match the new localized layout order
[_ruleOptionFrames removeAllObjects];
[_ruleOptionInitialViewFrames removeAllObjects];
var newCount = [_ruleOptionViews count];
for (var i = 0; i < newCount; i++)
{
var view = [_ruleOptionViews objectAtIndex:i],
frame = [view frame];
[_ruleOptionFrames addObject:frame];
[_ruleOptionInitialViewFrames addObject:frame];
}
if (!_editable)
[self _updateEnabledStateForSubviews];
@@ -366,11 +395,12 @@
count = [_ruleOptionViews count],
sliceFrame = [self frame],
image = [_ruleEditor _imageAdd],
imageSize = image ? [image size] : CGSizeMake(0,0),
buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - [image size].width - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - [image size].height) / 2 - 1, [image size].width, [image size].height);
buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - imageSize.width - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - imageSize.height) / 2 - 1, imageSize.width, imageSize.height);
[_addButton setFrame:buttonFrame];
buttonFrame.origin.x -= [image size].width + [self _rowButtonsInterviewHorizontalPadding];
buttonFrame.origin.x -= imageSize.width + [self _rowButtonsInterviewHorizontalPadding];
[_subtractButton setFrame:buttonFrame];
if (widthChanged)
@@ -410,6 +440,9 @@
{
[_addButton setHidden:[_ruleEditor _shouldHideAddButtonForSlice:self]];
[_subtractButton setHidden:[_ruleEditor _shouldHideSubtractButtonForSlice:self]];
[_addButton setToolTip:[_ruleEditor _toolTipForAddSimpleRowButton]];
[_subtractButton setToolTip:[_ruleEditor _toolTipForDeleteRowButton]];
}
- (void)_configurePlusButtonByRowType:(CPRuleEditorRowType)type
+5
View File
@@ -35,6 +35,11 @@
CPArray _allowedFileTypes @accessors(property=allowedFileTypes);
}
+ (CPURL)proposedFileURLWithDocumentName:(CPString)aDocumentName
{
return [CPURL URLWithString:aDocumentName];
}
+ (id)savePanel
{
return [[CPSavePanel alloc] init];
+376 -77
View File
@@ -29,10 +29,23 @@
@import "CPClipView.j"
@import "CPScroller.j"
@import "CPView.j"
@import "CPRulerView.j"
@class CPTableView
@class CPRulerView
#define SHOULD_SHOW_CORNER_VIEW() (_scrollerStyle === CPScrollerStyleLegacy && _verticalScroller && ![_verticalScroller isHidden])
@protocol CPScrollViewDelegate <CPObject>
@optional
- (void)scrollViewWillScroll:(CPScrollView)aScrollView;
- (void)scrollViewDidScroll:(CPScrollView)aScrollView;
@end
/*! @ignore */
var _isBrowserUsingOverlayScrollers = function()
{
@@ -77,6 +90,19 @@ var _isBrowserUsingOverlayScrollers = function()
#endif
};
var TIMER_INTERVAL = 0.2,
CPScrollViewDelegate_scrollViewWillScroll_ = 1 << 0,
CPScrollViewDelegate_scrollViewDidScroll_ = 1 << 1,
CPScrollViewFadeOutTime = 1.3;
var CPScrollViewWillStartLiveScrollNotification = @"CPScrollViewWillStartLiveScrollNotification",
CPScrollViewDidLiveScrollNotification = @"CPScrollViewDidLiveScrollNotification",
CPScrollViewDidEndLiveScrollNotification = @"CPScrollViewDidEndLiveScrollNotification";
var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
CPScrollerStyleGlobalChangeNotification = @"CPScrollerStyleGlobalChangeNotification";
/*!
@ingroup appkit
@class CPScrollView
@@ -85,53 +111,50 @@ var _isBrowserUsingOverlayScrollers = function()
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,
CPScrollViewFadeOutTime = 1.3;
var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
CPScrollerStyleGlobalChangeNotification = @"CPScrollerStyleGlobalChangeNotification";
@implementation CPScrollView : CPView
{
CPClipView _contentView;
CPClipView _headerClipView;
CPView _cornerView;
CPView _bottomCornerView;
CPClipView _contentView;
CPClipView _headerClipView;
CPView _cornerView;
CPView _bottomCornerView;
id _delegate;
CPTimer _scrollTimer;
id <CPScrollViewDelegate> _delegate;
CPTimer _scrollTimer;
BOOL _hasVerticalScroller;
BOOL _hasHorizontalScroller;
BOOL _autohidesScrollers;
BOOL _hasVerticalScroller;
BOOL _hasHorizontalScroller;
BOOL _autohidesScrollers;
CPScroller _verticalScroller;
CPScroller _horizontalScroller;
CPScroller _verticalScroller;
CPScroller _horizontalScroller;
CPInteger _recursionCount;
CPInteger _implementedDelegateMethods;
CPInteger _recursionCount;
CPInteger _implementedDelegateMethods;
float _verticalLineScroll;
float _verticalPageScroll;
float _horizontalLineScroll;
float _horizontalPageScroll;
float _verticalLineScroll;
float _verticalPageScroll;
float _horizontalLineScroll;
float _horizontalPageScroll;
CPBorderType _borderType;
CPBorderType _borderType;
CPTimer _timerScrollersHide;
CPTimer _timerScrollersHide;
int _scrollerStyle;
int _scrollerKnobStyle;
int _scrollerStyle;
int _scrollerKnobStyle;
// Ruler Support
BOOL _hasVerticalRuler;
BOOL _hasHorizontalRuler;
BOOL _rulersVisible;
CPRulerView _verticalRuler;
CPRulerView _horizontalRuler;
}
#pragma mark -
#pragma mark Class methods
// MARK: -
// MARK: Class methods
+ (void)initialize
{
@@ -140,7 +163,7 @@ var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
var globalValue = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPScrollersGlobalStyle"];
if (globalValue === nil || globalValue === -1)
if (globalValue == nil || globalValue === -1)
CPScrollerStyleGlobal = _isBrowserUsingOverlayScrollers() ? CPScrollerStyleOverlay : CPScrollerStyleLegacy
else
CPScrollerStyleGlobal = globalValue;
@@ -159,33 +182,65 @@ var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
};
}
/*! Deprecated
*/
+ (CGSize)contentSizeForFrameSize:(CGSize)frameSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
{
var bounds = [self _insetBounds:CGRectMake(0.0, 0.0, frameSize.width, frameSize.height) borderType:borderType],
scrollerWidth = [CPScroller scrollerWidth];
return [self contentSizeForFrameSize:frameSize
horizontalScrollerClass:hFlag ? [CPScroller class] : nil
verticalScrollerClass:vFlag ? [CPScroller class] : nil
borderType:borderType
controlSize:CPRegularControlSize
scrollerStyle:CPScrollerStyleGlobal];
}
if (hFlag)
bounds.size.height -= scrollerWidth;
+ (CGSize)contentSizeForFrameSize:(CGSize)frameSize
horizontalScrollerClass:(Class)horizontalScrollerClass
verticalScrollerClass:(Class)verticalScrollerClass
borderType:(CPBorderType)borderType
controlSize:(CPControlSize)controlSize
scrollerStyle:(CPScrollerStyle)scrollerStyle
{
var bounds = [self _insetBounds:CGRectMake(0.0, 0.0, frameSize.width, frameSize.height) borderType:borderType];
if (vFlag)
bounds.size.width -= scrollerWidth;
if (horizontalScrollerClass)
bounds.size.height -= [horizontalScrollerClass scrollerWidthInStyle:scrollerStyle];
if (verticalScrollerClass)
bounds.size.width -= [verticalScrollerClass scrollerWidthForControlSize:scrollerStyle];
return bounds.size;
}
/*! Deprecated
*/
+ (CGSize)frameSizeForContentSize:(CGSize)contentSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
{
return [self frameSizeForContentSize:contentSize
horizontalScrollerClass:hFlag ? [CPScroller class] : nil
verticalScrollerClass:vFlag ? [CPScroller class] : nil
borderType:borderType
controlSize:CPRegularControlSize
scrollerStyle:CPScrollerStyleGlobal];
}
+ (CGSize)frameSizeForContentSize:(CGSize)contentSize
horizontalScrollerClass:(Class)horizontalScrollerClass
verticalScrollerClass:(Class)verticalScrollerClass
borderType:(CPBorderType)borderType
controlSize:(CPControlSize)controlSize
scrollerStyle:(CPScrollerStyle)scrollerStyle
{
var bounds = [self _insetBounds:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) borderType:borderType],
widthInset = contentSize.width - bounds.size.width,
heightInset = contentSize.height - bounds.size.height,
frameSize = CGSizeMake(contentSize.width + widthInset, contentSize.height + heightInset),
scrollerWidth = [CPScroller scrollerWidth];
frameSize = CGSizeMake(contentSize.width + widthInset, contentSize.height + heightInset);
if (hFlag)
frameSize.height += scrollerWidth;
frameSize.height += [horizontalScrollerClass scrollerWidthInStyle:scrollerStyle];
if (vFlag)
frameSize.width += scrollerWidth;
frameSize.width += [verticalScrollerClass scrollerWidthForControlSize:scrollerStyle];
return frameSize;
}
@@ -213,7 +268,7 @@ var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
/*!
Get the system wide scroller style.
*/
+ (int)globalScrollerStyle
+ (CPScrollerStyle)globalScrollerStyle
{
return CPScrollerStyleGlobal;
}
@@ -223,15 +278,15 @@ var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
@param aStyle the scroller style to set all scroller views to use (CPScrollerStyleLegacy or CPScrollerStyleOverlay)
*/
+ (void)setGlobalScrollerStyle:(int)aStyle
+ (void)setGlobalScrollerStyle:(CPScrollerStyle)aStyle
{
CPScrollerStyleGlobal = aStyle;
[[CPNotificationCenter defaultCenter] postNotificationName:CPScrollerStyleGlobalChangeNotification object:nil];
}
#pragma mark -
#pragma mark Initialization
// MARK: -
// MARK: Initialization
- (id)initWithFrame:(CGRect)aFrame
{
@@ -261,6 +316,10 @@ var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
_scrollerKnobStyle = CPScrollerKnobStyleDefault;
[self setScrollerStyle:CPScrollerStyleGlobal];
_hasVerticalRuler = NO;
_hasHorizontalRuler = NO;
_rulersVisible = NO;
_delegate = nil;
_scrollTimer = nil;
_implementedDelegateMethods = 0;
@@ -270,8 +329,8 @@ var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
}
#pragma mark -
#pragma mark Getters / Setters
// MARK: -
// MARK: Getters / Setters
/*!
The delegate of the scroll view
@@ -296,7 +355,7 @@ Notifies the delegate when the scroll view has finished scrolling.
@endcode
*/
- (void)setDelegate:(id)aDelegate
- (void)setDelegate:(id <CPScrollViewDelegate>)aDelegate
{
if (aDelegate === _delegate)
return;
@@ -304,7 +363,7 @@ Notifies the delegate when the scroll view has finished scrolling.
_delegate = aDelegate;
_implementedDelegateMethods = 0;
if (_delegate === nil)
if (_delegate == nil)
return;
if ([_delegate respondsToSelector:@selector(scrollViewWillScroll:)])
@@ -314,7 +373,7 @@ Notifies the delegate when the scroll view has finished scrolling.
_implementedDelegateMethods |= CPScrollViewDelegate_scrollViewDidScroll_;
}
- (int)scrollerStyle
- (CPScrollerStyle)scrollerStyle
{
return _scrollerStyle;
}
@@ -325,7 +384,7 @@ Notifies the delegate when the scroll view has finished scrolling.
- CPScrollerStyleLegacy: Standard scrollers like Windows or Mac OS X prior to 10.7
- CPScrollerStyleOverlay: scrollers like those in Mac OS X 10.7+
*/
- (void)setScrollerStyle:(int)aStyle
- (void)setScrollerStyle:(CPScrollerStyle)aStyle
{
if (_scrollerStyle === aStyle)
return;
@@ -522,8 +581,8 @@ Notifies the delegate when the scroll view has finished scrolling.
{
var bounds = [self _insetBounds];
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, MAX(CGRectGetWidth(bounds), [CPScroller scrollerWidthInStyle:_scrollerStyle] + 1), [CPScroller scrollerWidthInStyle:_scrollerStyle])]];
[[self horizontalScroller] setFrameSize:CGSizeMake(CGRectGetWidth(bounds), [CPScroller scrollerWidthInStyle:_scrollerStyle])];
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, MAX(CGRectGetWidth(bounds), [_horizontalScroller scrollerWidth] + 1), [_horizontalScroller scrollerWidth])]];
[[self horizontalScroller] setFrameSize:CGSizeMake(CGRectGetWidth(bounds), [_horizontalScroller scrollerWidth])];
}
[self reflectScrolledClipView:_contentView];
@@ -587,8 +646,8 @@ Notifies the delegate when the scroll view has finished scrolling.
{
var bounds = [self _insetBounds];
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, [CPScroller scrollerWidthInStyle:_scrollerStyle], MAX(CGRectGetHeight(bounds), [CPScroller scrollerWidthInStyle:_scrollerStyle] + 1))]];
[[self verticalScroller] setFrameSize:CGSizeMake([CPScroller scrollerWidthInStyle:_scrollerStyle], CGRectGetHeight(bounds))];
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, [_verticalScroller scrollerWidth], MAX(CGRectGetHeight(bounds), [_verticalScroller scrollerWidth] + 1))]];
[[self verticalScroller] setFrameSize:CGSizeMake([_verticalScroller scrollerWidth], CGRectGetHeight(bounds))];
}
[self reflectScrolledClipView:_contentView];
@@ -749,8 +808,105 @@ Notifies the delegate when the scroll view has finished scrolling.
}
#pragma mark -
#pragma mark Privates
// MARK: -
// MARK: Rulers
- (BOOL)hasHorizontalRuler
{
return _hasHorizontalRuler;
}
- (void)setHasHorizontalRuler:(BOOL)shouldHaveHorizontalRuler
{
if (_hasHorizontalRuler === shouldHaveHorizontalRuler)
return;
_hasHorizontalRuler = shouldHaveHorizontalRuler;
if (_hasHorizontalRuler && !_horizontalRuler)
{
_horizontalRuler = [[CPRulerView alloc] initWithScrollView:self orientation:CPRulerOrientationHorizontal];
}
[self tile];
}
- (BOOL)hasVerticalRuler
{
return _hasVerticalRuler;
}
- (void)setHasVerticalRuler:(BOOL)shouldHaveVerticalRuler
{
if (_hasVerticalRuler === shouldHaveVerticalRuler)
return;
_hasVerticalRuler = shouldHaveVerticalRuler;
if (_hasVerticalRuler && !_verticalRuler)
{
_verticalRuler = [[CPRulerView alloc] initWithScrollView:self orientation:CPRulerOrientationVertical];
}
[self tile];
}
- (BOOL)rulersVisible
{
return _rulersVisible;
}
- (void)setRulersVisible:(BOOL)areRulersVisible
{
if (_rulersVisible === areRulersVisible)
return;
_rulersVisible = areRulersVisible;
[self tile];
}
- (CPRulerView)horizontalRulerView
{
return _horizontalRuler;
}
- (void)setHorizontalRulerView:(CPRulerView)aRulerView
{
if (_horizontalRuler === aRulerView)
return;
[_horizontalRuler removeFromSuperview];
_horizontalRuler = aRulerView;
if (_horizontalRuler)
[self addSubview:_horizontalRuler];
[self tile];
}
- (CPRulerView)verticalRulerView
{
return _verticalRuler;
}
- (void)setVerticalRulerView:(CPRulerView)aRulerView
{
if (_verticalRuler === aRulerView)
return;
[_verticalRuler removeFromSuperview];
_verticalRuler = aRulerView;
if (_verticalRuler)
[self addSubview:_verticalRuler];
[self tile];
}
// MARK: -
// MARK: Privates
/* @ignore */
- (void)_updateScrollerStyle
@@ -904,8 +1060,8 @@ Notifies the delegate when the scroll view has finished scrolling.
bottomCornerFrame.origin.x = CGRectGetMinX(verticalFrame);
bottomCornerFrame.origin.y = CGRectGetMaxY(verticalFrame);
bottomCornerFrame.size.width = [CPScroller scrollerWidthInStyle:_scrollerStyle];
bottomCornerFrame.size.height = [CPScroller scrollerWidthInStyle:_scrollerStyle];
bottomCornerFrame.size.width = [_verticalScroller scrollerWidth];
bottomCornerFrame.size.height = [_horizontalScroller scrollerWidth];
return bottomCornerFrame;
}
@@ -946,6 +1102,8 @@ Notifies the delegate when the scroll view has finished scrolling.
[self _sendDelegateMessages];
[_contentView scrollToPoint:contentBounds.origin];
[[CPNotificationCenter defaultCenter] postNotificationName:CPScrollViewDidLiveScrollNotification object:self];
}
/* @ignore */
@@ -984,6 +1142,8 @@ Notifies the delegate when the scroll view has finished scrolling.
[_contentView scrollToPoint:contentBounds.origin];
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
[[CPNotificationCenter defaultCenter] postNotificationName:CPScrollViewDidLiveScrollNotification object:self];
}
/* @ignore */
@@ -994,6 +1154,7 @@ Notifies the delegate when the scroll view has finished scrolling.
if (!_scrollTimer)
{
[[CPNotificationCenter defaultCenter] postNotificationName:CPScrollViewWillStartLiveScrollNotification object:self];
[self _scrollViewWillScroll];
_scrollTimer = [CPTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(_scrollViewDidScroll) userInfo:nil repeats:YES];
}
@@ -1032,6 +1193,8 @@ Notifies the delegate when the scroll view has finished scrolling.
[_contentView scrollToPoint:constrainedOrigin];
[_headerClipView scrollToPoint:CGPointMake(constrainedOrigin.x, 0.0)];
[[CPNotificationCenter defaultCenter] postNotificationName:CPScrollViewDidLiveScrollNotification object:self];
if (extraX || extraY)
[enclosingScrollView _respondToScrollWheelEventWithDeltaX:extraX deltaY:extraY];
}
@@ -1051,6 +1214,8 @@ Notifies the delegate when the scroll view has finished scrolling.
if (_implementedDelegateMethods & CPScrollViewDelegate_scrollViewDidScroll_)
[_delegate scrollViewDidScroll:self];
[[CPNotificationCenter defaultCenter] postNotificationName:CPScrollViewDidEndLiveScrollNotification object:self];
}
/*! @ignore*/
@@ -1061,18 +1226,15 @@ Notifies the delegate when the scroll view has finished scrolling.
#pragma mark -
#pragma mark Utilities
// MARK: -
// MARK: Utilities
/*!
Lays out the scroll view's components.
*/
- (void)tile
{
// yuck.
// RESIZE: tile->setHidden AND refl
// Outside Change: refl->tile->setHidden AND refl
// scroll: refl.
[self reflectScrolledClipView:_contentView];
}
/*!
@@ -1116,9 +1278,44 @@ Notifies the delegate when the scroll view has finished scrolling.
contentFrame.origin.y += headerClipViewHeight;
contentFrame.size.height -= headerClipViewHeight;
// Adjust content view based on horizontal / vertical ruler presence
var showHorizontalRuler = _rulersVisible && _hasHorizontalRuler && _horizontalRuler,
showVerticalRuler = _rulersVisible && _hasVerticalRuler && _verticalRuler;
var horizRulerThickness = showHorizontalRuler ? ([_horizontalRuler respondsToSelector:@selector(ruleThickness)] ? [_horizontalRuler ruleThickness] : 16.0) : 0.0,
vertRulerThickness = showVerticalRuler ? ([_verticalRuler respondsToSelector:@selector(ruleThickness)] ? [_verticalRuler ruleThickness] : 24.0) : 0.0;
if (showHorizontalRuler)
{
if ([_horizontalRuler superview] !== self)
[self addSubview:_horizontalRuler];
[_horizontalRuler setHidden:NO];
}
else if (_horizontalRuler)
{
[_horizontalRuler setHidden:YES];
}
if (showVerticalRuler)
{
if ([_verticalRuler superview] !== self)
[self addSubview:_verticalRuler];
[_verticalRuler setHidden:NO];
}
else if (_verticalRuler)
{
[_verticalRuler setHidden:YES];
}
contentFrame.origin.y += horizRulerThickness;
contentFrame.size.height -= horizRulerThickness;
contentFrame.origin.x += vertRulerThickness;
contentFrame.size.width -= vertRulerThickness;
var difference = CGSizeMake(CGRectGetWidth(documentFrame) - CGRectGetWidth(contentFrame), CGRectGetHeight(documentFrame) - CGRectGetHeight(contentFrame)),
verticalScrollerWidth = [CPScroller scrollerWidthInStyle:[_verticalScroller style]],
horizontalScrollerHeight = [CPScroller scrollerWidthInStyle:[_horizontalScroller style]],
verticalScrollerWidth = [_verticalScroller scrollerWidth],
horizontalScrollerHeight = [_horizontalScroller scrollerWidth],
hasVerticalScroll = difference.height > 0.0,
hasHorizontalScroll = difference.width > 0.0,
shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll),
@@ -1208,6 +1405,7 @@ Notifies the delegate when the scroll view has finished scrolling.
[_contentView setFrame:contentFrame];
[_headerClipView setFrame:[self _headerClipViewFrame]];
[[_headerClipView documentView] setNeedsDisplay:YES];
if (SHOULD_SHOW_CORNER_VIEW())
{
[_cornerView setFrame:[self _cornerViewFrame]];
@@ -1222,6 +1420,37 @@ Notifies the delegate when the scroll view has finished scrolling.
[[self bottomCornerView] setBackgroundColor:[self currentValueForThemeAttribute:@"bottom-corner-color"]];
}
// Position and redraw rulers to track viewport updates
if (showHorizontalRuler)
{
[_horizontalRuler setFrame:CGRectMake(
CGRectGetMinX(contentFrame),
CGRectGetMinY(contentFrame) - horizRulerThickness,
CGRectGetWidth(contentFrame),
horizRulerThickness
)];
if ([_horizontalRuler respondsToSelector:@selector(updateRuler)])
[_horizontalRuler updateRuler];
else
[_horizontalRuler setNeedsDisplay:YES];
}
if (showVerticalRuler)
{
[_verticalRuler setFrame:CGRectMake(
CGRectGetMinX(contentFrame) - vertRulerThickness,
CGRectGetMinY(contentFrame),
vertRulerThickness,
CGRectGetHeight(contentFrame)
)];
if ([_verticalRuler respondsToSelector:@selector(updateRuler)])
[_verticalRuler updateRuler];
else
[_verticalRuler setNeedsDisplay:YES];
}
--_recursionCount;
}
@@ -1246,7 +1475,7 @@ Notifies the delegate when the scroll view has finished scrolling.
}
if (_timerScrollersHide)
[_timerScrollersHide invalidate]
[_timerScrollersHide invalidate];
_timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
}
@@ -1262,8 +1491,8 @@ Notifies the delegate when the scroll view has finished scrolling.
return [_contentView documentVisibleRect];
}
#pragma mark -
#pragma mark Overrides
// MARK: -
// MARK: Overrides
- (void)_removeObservers
@@ -1384,8 +1613,8 @@ Notifies the delegate when the scroll view has finished scrolling.
y = maxY - 1.5;
CGContextMoveToPoint(context, maxX - 1.0, y);
CGContextAddLineToPoint(context, minX + 2.0, y);
CGContextMoveToPoint(maxX - 1.0, y);
CGContextAddLineToPoint(minX + 2.0, y);
x = minX + 0.5;
@@ -1503,6 +1732,51 @@ Notifies the delegate when the scroll view has finished scrolling.
@end
// MARK: -
@implementation CPScrollView (FirstResponder)
// Those 4 next methods are needed to (un)set CPThemeStateFirstResponder based on content view
- (void)viewWillMoveToWindow:(CPWindow)aWindow
{
[super viewWillMoveToWindow:aWindow];
[self _stopObservingFirstResponderForWindow:[self window]];
if (aWindow)
[self _startObservingFirstResponderForWindow:aWindow];
}
- (void)_startObservingFirstResponderForWindow:(CPWindow)aWindow
{
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_firstResponderDidChange:) name:_CPWindowDidChangeFirstResponderNotification object:aWindow];
}
- (void)_stopObservingFirstResponderForWindow:(CPWindow)aWindow
{
[[CPNotificationCenter defaultCenter] removeObserver:self name:_CPWindowDidChangeFirstResponderNotification object:aWindow];
}
- (void)_firstResponderDidChange:(CPNotification)aNotification
{
var responder = [[self window] firstResponder],
// FIXME: We add focus ring only on table views right now. When focus ring management will be added, this must be adapted.
shouldAddFocusRing = [responder isKindOfClass:[CPTableView class]],
found;
while (!(found = (responder === self)) && responder)
responder = [responder superview];
if (found && shouldAddFocusRing)
[self setThemeState:CPThemeStateFirstResponder];
else
[self unsetThemeState:CPThemeStateFirstResponder];
}
@end
// MARK: -
var CPScrollViewContentViewKey = @"CPScrollViewContentView",
CPScrollViewHeaderClipViewKey = @"CPScrollViewHeaderClipViewKey",
@@ -1519,7 +1793,14 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
CPScrollViewBottomCornerViewKey = @"CPScrollViewBottomCornerViewKey",
CPScrollViewBorderTypeKey = @"CPScrollViewBorderTypeKey",
CPScrollViewScrollerStyleKey = @"CPScrollViewScrollerStyleKey",
CPScrollViewScrollerKnobStyleKey = @"CPScrollViewScrollerKnobStyleKey";
CPScrollViewScrollerKnobStyleKey = @"CPScrollViewScrollerKnobStyleKey",
// Ruler Coding Keys
CPScrollViewHasVRulerKey = @"CPScrollViewHasVRuler",
CPScrollViewHasHRulerKey = @"CPScrollViewHasHRuler",
CPScrollViewRulersVisibleKey = @"CPScrollViewRulersVisible",
CPScrollViewVRulerKey = @"CPScrollViewVRuler",
CPScrollViewHRulerKey = @"CPScrollViewHRuler";
@implementation CPScrollView (CPCoding)
@@ -1554,6 +1835,14 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
_cornerView = [aCoder decodeObjectForKey:CPScrollViewCornerViewKey];
_bottomCornerView = [aCoder decodeObjectForKey:CPScrollViewBottomCornerViewKey];
// Ruler decoding
_hasVerticalRuler = [aCoder decodeBoolForKey:CPScrollViewHasVRulerKey];
_hasHorizontalRuler = [aCoder decodeBoolForKey:CPScrollViewHasHRulerKey];
_rulersVisible = [aCoder decodeBoolForKey:CPScrollViewRulersVisibleKey];
_verticalRuler = [aCoder decodeObjectForKey:CPScrollViewVRulerKey];
_horizontalRuler = [aCoder decodeObjectForKey:CPScrollViewHRulerKey];
_delegate = nil;
_scrollTimer = nil;
_implementedDelegateMethods = 0;
@@ -1575,6 +1864,8 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
*/
- (void)awakeFromCib
{
[super awakeFromCib];
[self _updateScrollerStyle];
[self _updateCornerAndHeaderView];
}
@@ -1605,6 +1896,14 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
[aCoder encodeInt:_scrollerStyle forKey:CPScrollViewScrollerStyleKey];
[aCoder encodeInt:_scrollerKnobStyle forKey:CPScrollViewScrollerKnobStyleKey];
// Ruler encoding
[aCoder encodeBool:_hasVerticalRuler forKey:CPScrollViewHasVRulerKey];
[aCoder encodeBool:_hasHorizontalRuler forKey:CPScrollViewHasHRulerKey];
[aCoder encodeBool:_rulersVisible forKey:CPScrollViewRulersVisibleKey];
[aCoder encodeObject:_verticalRuler forKey:CPScrollViewVRulerKey];
[aCoder encodeObject:_horizontalRuler forKey:CPScrollViewHRulerKey];
}
@end
+48 -38
View File
@@ -23,12 +23,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../Foundation/Foundation.h"
@import "CPAnimation.j"
@import "CPControl.j"
@import "CPWindow_Constants.j"
@import "CPViewAnimation.j"
@import "CPWindow_Constants.j"
@global CPApp
@@ -50,11 +49,6 @@ CPNoScrollerParts = 0;
CPOnlyScrollerArrows = 1;
CPAllScrollerParts = 2;
/*!
@ingroup appkit
@class CPScroller
*/
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
NAMES_FOR_PARTS = {},
PARTS_FOR_NAMES = {};
@@ -67,6 +61,7 @@ NAMES_FOR_PARTS[CPScrollerKnobSlot] = @"knob-slot";
NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
@typedef CPScrollerStyle
CPScrollerStyleLegacy = 0;
CPScrollerStyleOverlay = 1;
@@ -78,6 +73,11 @@ CPThemeStateScrollViewLegacy = CPThemeState("scroller-style-legacy");
CPThemeStateScrollerKnobLight = CPThemeState("scroller-knob-light");
CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
/*!
@ingroup appkit
@class CPScroller
*/
@implementation CPScroller : CPControl
{
CPUsableScrollerParts _usableParts;
@@ -101,8 +101,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
}
#pragma mark -
#pragma mark Class methods
// MARK: -
// MARK: Class methods
+ (CPString)defaultThemeClass
{
@@ -134,7 +134,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
/*!
Returns the CPScroller's width for a CPRegularControlSize.
*/
+ (float)scrollerWidthInStyle:(int)aStyle
+ (float)scrollerWidthInStyle:(CPScrollerStyle)aStyle
{
if (!_CACHED_THEME_SCROLLER)
_CACHED_THEME_SCROLLER = [[self alloc] init];
@@ -166,8 +166,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
}
#pragma mark -
#pragma mark Initialization
// MARK: -
// MARK: Initialization
- (id)initWithFrame:(CGRect)aFrame
{
@@ -202,13 +202,13 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
}
#pragma mark -
#pragma mark Getters / Setters
// MARK: -
// MARK: Getters / Setters
/*!
Returns the scroller's style
*/
- (void)style
- (int)style
{
return _style;
}
@@ -217,7 +217,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
Set the scroller's control size
@param aStyle the scroller style: CPScrollerStyleLegacy or CPScrollerStyleOverlay
*/
- (void)setStyle:(id)aStyle
- (void)setStyle:(CPScrollerStyle)aStyle
{
if (_style != nil && _style === aStyle)
return;
@@ -226,6 +226,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
if (_style === CPScrollerStyleLegacy)
{
_allowFadingOut = NO;
[self fadeIn];
[self setThemeState:CPThemeStateScrollViewLegacy];
}
@@ -257,8 +258,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
*/
- (void)setKnobProportion:(float)aProportion
{
if (!_IS_NUMERIC(aProportion))
[CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric"];
if (!CPIsNumeric(aProportion))
[CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric, was: "+aProportion];
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
@@ -267,8 +268,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
}
#pragma mark -
#pragma mark Privates
// MARK: -
// MARK: Privates
/*! @ignore */
- (void)_adjustScrollerSize
@@ -293,8 +294,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
}
#pragma mark -
#pragma mark Utilities
// MARK: -
// MARK: Utilities
- (CGRect)rectForPart:(CPScrollerPart)aPart
{
@@ -316,7 +317,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
// The ordering of these tests is important. We check the knob and
// page rects first since they may overlap with the arrows.
if (![self hasThemeState:CPThemeStateSelected])
if (![self hasThemeState:CPThemeStateSelected] && ![self hasThemeState:CPThemeStateScrollViewLegacy])
return CPScrollerNoPart;
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
@@ -380,9 +381,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
knobVerticalInset = knobInset.top + knobInset.bottom,
knobWidth = width - knobInset.left - knobInset.right,
knobHeight = MAX(minimumKnobLength, (slotSize * _knobProportion)),
knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight) * [self floatValue];
knobHeight = MAX(minimumKnobLength, ((slotSize - knobVerticalInset) * _knobProportion)),
knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight - knobVerticalInset) * [self floatValue] + knobInset.top;
_partRects[CPScrollerDecrementPage] = CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
_partRects[CPScrollerKnob] = CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
@@ -409,9 +411,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
knobWidth = MAX(minimumKnobLength, (slotSize * _knobProportion)),
knobHorizontalInset = knobInset.left + knobInset.right,
knobWidth = MAX(minimumKnobLength, ((slotSize - knobHorizontalInset) * _knobProportion)),
knobHeight = height - knobInset.top - knobInset.bottom,
knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth) * [self floatValue];
knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth - knobHorizontalInset) * [self floatValue] + knobInset.left;
_partRects[CPScrollerDecrementPage] = CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
_partRects[CPScrollerKnob] = CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
@@ -460,15 +463,12 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
*/
- (void)fadeOut
{
if ([self hasThemeState:CPThemeStateScrollViewLegacy])
return;
[_animationScroller startAnimation];
}
#pragma mark -
#pragma mark Drawing
// MARK: -
// MARK: Drawing
/*!
Draws the specified arrow and sets the highlight.
@@ -702,8 +702,8 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
}
#pragma mark -
#pragma mark Overrides
// MARK: -
// MARK: Overrides
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
{
@@ -759,18 +759,28 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
if ([self isHidden] || ![self isEnabled] || !_isMouseOver)
return;
_allowFadingOut = YES;
_allowFadingOut = (_style !== CPScrollerStyleLegacy);
_isMouseOver = NO;
if (_timerFadeOut)
[_timerFadeOut invalidate];
_timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
if ([self hasThemeState:CPThemeStateScrollViewLegacy])
[self unsetThemeState:CPThemeStateSelected];
else
_timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
}
- (float)scrollerWidth
{
if (_style == CPScrollerStyleLegacy)
return [self valueForThemeAttribute:@"scroller-width" inState:CPThemeStateScrollViewLegacy];
#pragma mark -
#pragma mark Delegates
return [self currentValueForThemeAttribute:@"scroller-width"];
}
// MARK: -
// MARK: Delegates
- (void)animationDidEnd:(CPAnimation)animation
{
+376 -64
View File
@@ -24,8 +24,12 @@
@import "CPMenu.j"
@import "CPMenuItem.j"
@import "CPTextField.j"
@import "CPAnimationContext.j"
@import "CPViewAnimator.j"
@import "CPArrayController.j"
@class CPUserDefaults
@class CALayer
@global CPApp
@@ -36,8 +40,6 @@ CPSearchFieldNoRecentsMenuItemTag = 1003;
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
var RECENT_SEARCH_PREFIX = @" ";
/*!
@ingroup appkit
@class CPSearchField
@@ -60,6 +62,9 @@ var RECENT_SEARCH_PREFIX = @" ";
BOOL _sendsSearchStringImmediately;
BOOL _canResignFirstResponder;
CPTimer _partialStringTimer;
CPView _contentView;
BOOL _isBecomingFirstResponder;
}
+ (CPString)defaultThemeClass
@@ -73,10 +78,24 @@ var RECENT_SEARCH_PREFIX = @" ";
@"image-search": [CPNull null],
@"image-find": [CPNull null],
@"image-cancel": [CPNull null],
@"image-cancel-pressed": [CPNull null]
@"image-cancel-pressed": [CPNull null],
@"image-search-inset" : CGInsetMake(0, 0, 0, 5),
@"image-cancel-inset" : CGInsetMake(0, 5, 0, 0),
@"search-button-rect-function": [CPNull null],
@"layout-function": [CPNull null],
@"search-right-margin": 2,
@"search-menu-offset": CGPointMake(10, -4)
};
}
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding === CPPredicateBinding)
return [_CPSearchFieldPredicateBinder class];
return [super _binderClassForBinding:aBinding];
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
@@ -87,10 +106,6 @@ var RECENT_SEARCH_PREFIX = @" ";
_recentsAutosaveName = nil;
[self _init];
#if PLATFORM(DOM)
_cancelButton._DOMElement.style.cursor = "default";
_searchButton._DOMElement.style.cursor = "default";
#endif
}
return self;
@@ -117,11 +132,12 @@ var RECENT_SEARCH_PREFIX = @" ";
[self resetSearchButton];
_canResignFirstResponder = YES;
_isBecomingFirstResponder = NO;
}
#pragma mark -
#pragma mark Override observers
// MARK: -
// MARK: Override observers
- (void)_removeObservers
{
@@ -177,7 +193,7 @@ var RECENT_SEARCH_PREFIX = @" ";
- (void)resetSearchButton
{
var button = [self searchButton],
searchButtonImage = (_searchMenuTemplate === nil) ? [self valueForThemeAttribute:@"image-search"] : [self valueForThemeAttribute:@"image-find"];
searchButtonImage = (_searchMenuTemplate == nil) ? [self currentValueForThemeAttribute:@"image-search"] : [self currentValueForThemeAttribute:@"image-find"];
[button setBordered:NO];
[button setImageScaling:CPImageScaleAxesIndependently];
@@ -224,8 +240,8 @@ var RECENT_SEARCH_PREFIX = @" ";
var button = [self cancelButton];
[button setBordered:NO];
[button setImageScaling:CPImageScaleAxesIndependently];
[button setImage:[self valueForThemeAttribute:@"image-cancel"]];
[button setAlternateImage:[self valueForThemeAttribute:@"image-cancel-pressed"]];
[button setImage:[self currentValueForThemeAttribute:@"image-cancel"]];
[button setAlternateImage:[self currentValueForThemeAttribute:@"image-cancel-pressed"]];
[button setAutoresizingMask:CPViewMinXMargin];
[button setTarget:self];
[button setAction:@selector(cancelOperation:)];
@@ -246,8 +262,9 @@ var RECENT_SEARCH_PREFIX = @" ";
if (_searchButton)
{
var searchBounds = [self searchButtonRectForBounds:bounds];
leftOffset = CGRectGetMaxX(searchBounds) + 2;
var searchBounds = [self searchButtonRectForBounds:bounds],
rightMargin = [self _potentialCurrentValueForThemeAttribute:@"search-right-margin"];
leftOffset = CGRectGetMaxX(searchBounds) + rightMargin;
}
if (_cancelButton)
@@ -266,9 +283,18 @@ var RECENT_SEARCH_PREFIX = @" ";
*/
- (CGRect)searchButtonRectForBounds:(CGRect)rect
{
var size = [[self valueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero();
var themedRectFunction = [self _potentialCurrentValueForThemeAttribute:@"search-button-rect-function"];
return CGRectMake(5, (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
if (themedRectFunction)
// There's a theme defined positioning function, just use it
return objj_eval("("+themedRectFunction+")")(self, rect);
else
{
var size = [[self _potentialCurrentValueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero(),
inset = [self _potentialCurrentValueForThemeAttribute:@"image-search-inset"];
return CGRectMake(inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
}
}
/*!
@@ -278,9 +304,10 @@ var RECENT_SEARCH_PREFIX = @" ";
*/
- (CGRect)cancelButtonRectForBounds:(CGRect)rect
{
var size = [[self valueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero();
var size = [[self _potentialCurrentValueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero(),
inset = [self _potentialCurrentValueForThemeAttribute:@"image-cancel-inset"];
return CGRectMake(CGRectGetWidth(rect) - size.width - 5, (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
return CGRectMake(CGRectGetWidth(rect) - size.width + inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
}
// Managing Menu Templates
@@ -384,10 +411,9 @@ var RECENT_SEARCH_PREFIX = @" ";
*/
- (void)setRecentSearches:(CPArray)searches
{
var max = MIN([self maximumRecents], [searches count]),
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
var max = MIN([self maximumRecents], [searches count]);
_recentSearches = searches;
_recentSearches = [searches subarrayWithRange:CPMakeRange(0, max)];
[self _autosaveRecentSearchList];
}
@@ -472,6 +498,7 @@ var RECENT_SEARCH_PREFIX = @" ";
- (BOOL)sendAction:(SEL)anAction to:(id)anObject
{
[self selectAll:nil];
[super sendAction:anAction to:anObject];
[_partialStringTimer invalidate];
@@ -482,7 +509,7 @@ var RECENT_SEARCH_PREFIX = @" ";
- (void)_addStringToRecentSearches:(CPString)string
{
if (string === nil || string === @"" || [_recentSearches containsObject:string])
if (string == nil || string === @"" || [_recentSearches containsObject:string])
return;
var searches = [CPMutableArray arrayWithArray:_recentSearches];
@@ -529,19 +556,16 @@ var RECENT_SEARCH_PREFIX = @" ";
}
/*!
Provides the common case items for a recent searches menu. If there are not recent searches,
displays a single disabled item:
No Recent Searches
Provides the common case items for a recent searches menu.
If there are 1 more recent searches, it displays:
Recent Searches
recent search 1
recent search 2
etc.
Clear
---------------------
Clear Recent Searches
Recent Searches
recent search 1
recent search 2
etc.
If you wish to add items before or after the template, you can. If you put items
before, a separator will automatically be placed before the default template item.
@@ -566,6 +590,15 @@ var RECENT_SEARCH_PREFIX = @" ";
var template = [[CPMenu alloc] init],
item;
item = [[CPMenuItem alloc] initWithTitle:@"Clear"
action:@selector(_searchFieldClearRecents:)
keyEquivalent:@""];
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
[item setTarget:self];
[template addItem:item];
[self _addSeparatorToMenu:template];
item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches"
action:nil
keyEquivalent:@""];
@@ -580,32 +613,20 @@ var RECENT_SEARCH_PREFIX = @" ";
[item setTarget:self];
[template addItem:item];
item = [[CPMenuItem alloc] initWithTitle:@"Clear Recent Searches"
action:@selector(_searchFieldClearRecents:)
keyEquivalent:@""];
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
[item setTarget:self];
[template addItem:item];
item = [[CPMenuItem alloc] initWithTitle:@"No Recent Searches"
action:nil
keyEquivalent:@""];
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
[item setEnabled:NO];
[template addItem:item];
return template;
}
- (void)_updateSearchMenu
{
if (_searchMenuTemplate === nil)
if (_searchMenuTemplate == nil)
return;
var menu = [[CPMenu alloc] init],
countOfRecents = [_recentSearches count],
numberOfItems = [_searchMenuTemplate numberOfItems];
[menu setAutoenablesItems:NO];
for (var i = 0; i < numberOfItems; i++)
{
var item = [[_searchMenuTemplate itemAtIndex:i] copy];
@@ -615,9 +636,6 @@ var RECENT_SEARCH_PREFIX = @" ";
case CPSearchFieldRecentsTitleMenuItemTag:
if (countOfRecents === 0)
continue;
if ([menu numberOfItems] > 0)
[self _addSeparatorToMenu:menu];
break;
case CPSearchFieldRecentsMenuItemTag:
@@ -626,8 +644,7 @@ var RECENT_SEARCH_PREFIX = @" ";
for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
{
// RECENT_SEARCH_PREFIX is a hack until CPMenuItem -setIndentationLevel works
var recentItem = [[CPMenuItem alloc] initWithTitle:RECENT_SEARCH_PREFIX + [_recentSearches objectAtIndex:recentIndex]
var recentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:recentIndex]
action:itemAction
keyEquivalent:[item keyEquivalent]];
[item setTarget:self];
@@ -641,9 +658,6 @@ var RECENT_SEARCH_PREFIX = @" ";
if (countOfRecents === 0)
continue;
if ([menu numberOfItems] > 0)
[self _addSeparatorToMenu:menu];
[item setAction:@selector(_searchFieldClearRecents:)];
[item setTarget:self];
break;
@@ -651,9 +665,6 @@ var RECENT_SEARCH_PREFIX = @" ";
case CPSearchFieldNoRecentsMenuItemTag:
if (countOfRecents !== 0)
continue;
if ([menu numberOfItems] > 0)
[self _addSeparatorToMenu:menu];
break;
}
@@ -670,6 +681,7 @@ var RECENT_SEARCH_PREFIX = @" ";
{
var separator = [CPMenuItem separatorItem];
[separator setEnabled:NO];
[separator setTag:CPSearchFieldRecentsTitleMenuItemTag];
[aMenu addItem:separator];
}
@@ -691,7 +703,8 @@ var RECENT_SEARCH_PREFIX = @" ";
return;
var aFrame = [[self superview] convertRect:[self frame] toView:nil],
location = CGPointMake(aFrame.origin.x + 10, aFrame.origin.y + aFrame.size.height - 4);
offset = [self currentValueForThemeAttribute:@"search-menu-offset"],
location = CGPointMake(aFrame.origin.x + offset.x, aFrame.origin.y + aFrame.size.height + offset.y);
var anEvent = [CPEvent mouseEventWithType:CPRightMouseDown location:location modifierFlags:0 timestamp:[[CPApp currentEvent] timestamp] windowNumber:[[self window] windowNumber] context:nil eventNumber:1 clickCount:1 pressure:0];
@@ -707,15 +720,19 @@ var RECENT_SEARCH_PREFIX = @" ";
- (void)cancelOperation:(id)sender
{
[self setObjectValue:@""];
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
// If something was entered, the search field must deactivate itself (else, do nothing)
if ([[self stringValue] length] > 0)
{
[self setObjectValue:@""];
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
[self _updateCancelButtonVisibility];
[[self window] makeFirstResponder:[self window]];
}
}
- (void)_searchFieldSearch:(id)sender
{
var searchString = [[sender title] substringFromIndex:[RECENT_SEARCH_PREFIX length]];
var searchString = [sender title];
if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
[self _addStringToRecentSearches:searchString];
@@ -760,17 +777,266 @@ var RECENT_SEARCH_PREFIX = @" ";
- (void)_loadRecentSearchList
{
var name = [self recentsAutosaveName];
if (name === nil)
if (name == nil)
return;
var list = [[CPUserDefaults standardUserDefaults] objectForKey:name];
if (list !== nil)
if (list != nil)
_recentSearches = list;
}
@end
// MARK: -
@implementation CPSearchField (ThemingAdditions)
// Overwrite CPTextField method to permit themed layout functions
- (void)layoutSubviews
{
if (!_contentView)
{
// Search for the CPImageAndTextView subview of mine
for (var i = 0, subviews = [self subviews], nb = [subviews count]; (!_contentView && (i < nb)); i++)
if ([subviews[i] isKindOfClass:_CPImageAndTextView])
_contentView = subviews[i];
}
var bezelColor = [self currentValueForThemeAttribute:@"bezel-color"];
if ([bezelColor isCSSBased])
{
// CSS Styling
// We don't need bezelView as we apply CSS styling directly on the search view itself
[self _setBackgroundColor:bezelColor];
if (!_contentView)
_contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:nil];
}
else
{
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
[bezelView setBackgroundColor:bezelColor];
if (!_contentView)
_contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
}
if (_contentView)
{
[_contentView setHidden:(_stringValue && _stringValue.length > 0) && [self hasThemeState:CPThemeStateEditing]];
[_contentView setText:[self hasThemeState:CPTextFieldStatePlaceholder] ? [self placeholderString] : _stringValue];
[_contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
[_contentView setFont:[self font]]; //[self currentValueForThemeAttribute:@"font"]];
[_contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]];
[_contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]];
[_contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
[_contentView setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
[_contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
}
if (_isEditing)
[self _setCSSStyleForInputElement];
[self updateSearchButton];
[self updateTrackingAreas];
}
// As CPTextField redefines setBackgroundColor, we have to bypass it
- (void)_setBackgroundColor:(CPColor)aColor
{
var grannyMethod = class_getInstanceMethod([[self superclass] superclass], @selector(setBackgroundColor:)),
grannyImplementation = method_getImplementation(grannyMethod);
grannyImplementation(self, @selector(setBackgroundColor:), aColor);
}
- (void)_layoutSubviews
{
var themedLayoutFunction = [self currentValueForThemeAttribute:@"layout-function"];
if (themedLayoutFunction)
// There's a theme defined layout function, just use it
objj_eval("("+themedLayoutFunction+")")(self);
else
// Call directly the completion handler
[self themedLayoutFunctionCompletionHandler];
}
- (void)updateSearchButton
{
[_searchButton setImage:(_searchMenuTemplate ? [self currentValueForThemeAttribute:@"image-find"] : [self currentValueForThemeAttribute:@"image-search"])];
[self updateTrackingAreas];
}
- (void)themedLayoutFunctionCompletionHandler
{
if (_isBecomingFirstResponder)
{
_isBecomingFirstResponder = NO;
// Call the real _becomeFirstKeyResponder to finish setting the input element
[super _becomeFirstKeyResponder];
// For some (unknown yet) reason, input field doesn't focus and text field visual state is not updated
var element = [self _inputElement];
element.focus();
[self layoutSubviews];
[CALayer runLoopUpdateLayers]; // Thank you @daboe01 for suggesting adding this
}
else
[self layoutSubviews];
}
// We override CPTextField method in order to delay the display of the input element until the end of the animation
- (BOOL)_becomeFirstKeyResponder
{
// As we have to return a result, we check if response could be NO
if (![self _isWithinUsablePlatformRect] || ![self isEditable])
return NO;
// We are now sure that response will be YES
_isBecomingFirstResponder = YES;
// We have to do this now in order to avoid running conditions messing things among multiple textfields
_stringValue = [self stringValue];
var element = [self _inputElement];
element.value = _stringValue;
[self _layoutSubviews];
return YES;
}
- (void)textDidEndEditing:(CPNotification)note
{
if ([note object] != self)
return;
[self _layoutSubviews];
[super textDidEndEditing:note];
}
- (void)_windowDidResignKey:(CPNotification)aNotification
{
// When the window resigns key, if the search field is empty, it must cancel first responder
if ([[self stringValue] length] == 0)
[[self window] makeFirstResponder:[self window]];
[super _windowDidResignKey:aNotification];
[self _layoutSubviews];
}
- (void)setPlaceholderString:(CPString)aStringValue
{
[super setPlaceholderString:aStringValue];
[self _layoutContent];
}
- (void)_layoutContent
{
[_searchButton setFrame:[self searchButtonRectForBounds:[self bounds]]];
[_contentView setFrame:[self contentRectForBounds:[self bounds]]];
[_cancelButton setFrame:[self cancelButtonRectForBounds:[self bounds]]];
[self updateTrackingAreas];
}
- (void)setFrameSize:(CGSize)aSize
{
[super setFrameSize:aSize];
[self _layoutContent];
}
- (id)_potentialCurrentValueForThemeAttribute:(CPString)aName
{
if (_isBecomingFirstResponder)
return [self valueForThemeAttribute:aName inState:[self themeState].and(CPThemeStateEditing)];
else
return [self currentValueForThemeAttribute:aName];
}
- (void)unbind:(CPString)aBinding
{
[super unbind:aBinding];
// our CPPredicateBinding binder adds a binding to the value.
// this private binding has to be also removed
if (aBinding === CPPredicateBinding)
[[[self class] _binderClassForBinding:aBinding] unbind:CPValueBinding forObject:self];
}
@end
// MARK: -
@implementation CPSearchField (CPTrackingArea)
{
CPTrackingArea _searchButtonTrackingArea;
CPTrackingArea _cancelButtonTrackingArea;
}
- (void)updateTrackingAreas
{
if (_searchButtonTrackingArea)
{
[self removeTrackingArea:_searchButtonTrackingArea];
_searchButtonTrackingArea = nil;
}
if (_cancelButtonTrackingArea)
{
[self removeTrackingArea:_cancelButtonTrackingArea];
_cancelButtonTrackingArea = nil;
}
if (_searchButton)
{
_searchButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_searchButton frame]
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
owner:self
userInfo:@{ @"isButton": YES }];
[self addTrackingArea:_searchButtonTrackingArea];
}
if (_cancelButton && ![_cancelButton isHidden])
{
_cancelButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_cancelButton frame]
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
owner:self
userInfo:@{ @"isButton": YES }];
[self addTrackingArea:_cancelButtonTrackingArea];
}
[super updateTrackingAreas];
}
- (void)cursorUpdate:(CPEvent)anEvent
{
if ([[[anEvent trackingArea] userInfo] objectForKey:@"isButton"])
[[CPCursor arrowCursor] set];
else
[super cursorUpdate:anEvent];
}
@end
// MARK: -
var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
@@ -823,3 +1089,49 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
}
@end
@implementation _CPSearchFieldPredicateBinder : CPBinder
{
CPArrayController _controller;
CPString _predicateFormat;
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
if (aBinding === CPPredicateBinding)
{
var options = [_info objectForKey:CPOptionsKey];
_controller = [_info objectForKey:CPObservedObjectKey];
_predicateFormat = [options objectForKey:"CPPredicateFormat"];
[_source bind:CPValueBinding toObject:self withKeyPath:"searchFieldValue" options:nil];
}
}
- (void)setSearchFieldValue:(CPString)aValue
{
var destination = [_info objectForKey:CPObservedObjectKey],
keyPath = [_info objectForKey:CPObservedKeyPathKey];
[self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
if (aValue)
{
var values = @[],
formatString = _predicateFormat.replace(/\$value/g, function(x) {[values addObject:aValue]; return "%@";});
[_controller setFilterPredicate:[CPPredicate predicateWithFormat:formatString argumentArray:values]];
}
else
[_controller setFilterPredicate:nil];
[self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
}
- (CPString)searchFieldValue
{
return [_source stringValue];
}
@end
+257 -192
View File
@@ -42,7 +42,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
@implementation CPSegmentedControl : CPControl
{
CPArray _segments;
CPArray _segments @accessors(getter=segments);
CPArray _themeStates;
int _selectedSegment;
@@ -87,6 +87,8 @@ CPSegmentSwitchTrackingMomentary = 2;
_selectedSegment = -1;
_trackingMode = CPSegmentSwitchTrackingSelectOne;
_trackingHighlighted = NO;
_trackingSegment = -1;
}
return self;
@@ -107,7 +109,54 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)selectedTag
{
return [_segments[_selectedSegment] tag];
return [[_segments objectAtIndex:_selectedSegment] tag];
}
/*! @ignore */
- (void)setSegments:(CPArray)segments
{
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self segmentCount])]];
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [segments count])]];
}
/*! @ignore */
- (void)insertSegments:(CPArray)segments atIndexes:(CPIndexSet)indices
{
if ([segments count] == 0)
return;
var newStates = @[],
count = [indices count];
while (count--)
[newStates addObject:CPThemeStateNormal];
[_segments insertObjects:segments atIndexes:indices];
[_themeStates insertObjects:newStates atIndexes:indices];
if (_selectedSegment >= [indices firstIndex])
_selectedSegment += [indices count];
}
/*! @ignore */
- (void)removeSegmentsAtIndexes:(CPIndexSet)indices
{
if ([indices count] == 0)
return;
[indices enumerateIndexesUsingBlock:function(idx, stop)
{
[[_segments objectAtIndex:idx] setSelected:NO];
}];
if ([indices containsIndex:_selectedSegment])
_selectedSegment = -1;
else if ([indices lastIndex] < _selectedSegment)
_selectedSegment -= [indices count];
[_segments removeObjectsAtIndexes:indices];
[_themeStates removeObjectsAtIndexes:indices];
}
// Specifying the number of segments
@@ -117,41 +166,32 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setSegmentCount:(unsigned)aCount
{
if (_segments.length == aCount)
var prevCount = [_segments count];
if (aCount == prevCount)
return;
var height = CGRectGetHeight([self bounds]),
dividersBefore = MAX(0, _segments.length - 1),
dividersAfter = MAX(0, aCount - 1);
if (_segments.length < aCount)
if (aCount > prevCount)
{
for (var index = _segments.length; index < aCount; ++index)
{
_segments[index] = [[_CPSegmentItem alloc] init];
_themeStates[index] = CPThemeStateNormal;
}
}
else if (aCount < _segments.length)
{
_segments.length = aCount;
_themeStates.length = aCount;
}
var count = aCount - prevCount,
segments = @[];
if (_selectedSegment >= _segments.length)
while (count--)
[segments addObject:[[_CPSegmentItem alloc] init]];
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(prevCount, aCount - prevCount)]];
}
else
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(aCount, prevCount - aCount)]];
[self _updateSelectionIfNeeded];
[self tileWithChangedSegment:MAX(MIN(prevCount, aCount) - 1, 0)];
}
- (void)_updateSelectionIfNeeded
{
if (_selectedSegment >= [self segmentCount])
_selectedSegment = -1;
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
frame = [self frame],
widthOfAllSegments = 0,
dividerExtraSpace = ([_segments count] - 1) * thickness;
for (var i = 0; i < [_segments count]; i++)
widthOfAllSegments += [_segments[i] width];
[self setFrameSize:CGSizeMake(widthOfAllSegments + dividerExtraSpace, frame.size.height)];
[self tileWithChangedSegment:0];
}
/*!
@@ -159,7 +199,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (unsigned)segmentCount
{
return _segments.length;
return [_segments count];
}
// Specifying Selected Segment
@@ -171,7 +211,20 @@ CPSegmentSwitchTrackingMomentary = 2;
- (void)setSelectedSegment:(unsigned)aSegment
{
// setSelected:forSegment throws the exception for us (if necessary)
[self setSelected:YES forSegment:aSegment];
if (_selectedSegment == aSegment)
return;
if (aSegment == -1)
{
var count = [self segmentCount];
while (count--)
[self setSelected:NO forSegment:count];
_selectedSegment = -1;
}
else
[self setSelected:YES forSegment:aSegment];
}
/*!
@@ -189,8 +242,8 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
if (_segments[index].tag == aTag)
for (; index < [_segments count]; ++index)
if ([[_segments objectAtIndex:index] tag] == aTag)
{
[self setSelectedSegment:index];
@@ -204,8 +257,8 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
if (_segments[index].label == aLabel)
for (; index < [_segments count]; ++index)
if ([[_segments objectAtIndex:index] label] == aLabel)
{
[self setSelectedSegment:index];
@@ -216,7 +269,7 @@ CPSegmentSwitchTrackingMomentary = 2;
}
// Specifying Tracking Mode
/*! @ignore */
- (BOOL)isTracking
{
@@ -234,7 +287,7 @@ CPSegmentSwitchTrackingMomentary = 2;
var index = 0,
selected = NO;
for (; index < _segments.length; ++index)
for (; index < [self segmentCount]; ++index)
if ([_segments[index] selected])
if (selected)
[self setSelected:NO forSegment:index];
@@ -246,7 +299,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{
var index = 0;
for (; index < _segments.length; ++index)
for (; index < [self segmentCount]; ++index)
if ([_segments[index] selected])
[self setSelected:NO forSegment:index];
}
@@ -269,7 +322,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
{
[_segments[aSegment] setWidth:aWidth];
[[_segments objectAtIndex:aSegment] setWidth:aWidth];
[self tileWithChangedSegment:aSegment];
}
@@ -280,7 +333,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (float)widthForSegment:(unsigned)aSegment
{
return [_segments[aSegment] width];
return [[_segments objectAtIndex:aSegment] width];
}
/*!
@@ -291,7 +344,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
{
[_segments[aSegment] setImage:anImage];
[[_segments objectAtIndex:aSegment] setImage:anImage];
[self tileWithChangedSegment:aSegment];
}
@@ -303,7 +356,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPImage)imageForSegment:(unsigned)aSegment
{
return [_segments[aSegment] image];
return [[_segments objectAtIndex:aSegment] image];
}
/*!
@@ -314,7 +367,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
{
[_segments[aSegment] setLabel:aLabel];
[[_segments objectAtIndex:aSegment] setLabel:aLabel];
[self tileWithChangedSegment:aSegment];
}
@@ -326,7 +379,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPString)labelForSegment:(unsigned)aSegment
{
return [_segments[aSegment] label];
return [[_segments objectAtIndex:aSegment] label];
}
/*!
@@ -337,7 +390,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
{
[_segments[aSegment] setMenu:aMenu];
[[_segments objectAtIndex:aSegment] setMenu:aMenu];
}
/*!
@@ -347,7 +400,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CPMenu)menuForSegment:(unsigned)aSegment
{
return [_segments[aSegment] menu];
return [[_segments objectAtIndex:aSegment] menu];
}
/*!
@@ -359,7 +412,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
{
var segment = _segments[aSegment];
var segment = [_segments objectAtIndex:aSegment];
// If we're already in this state, bail.
if ([segment selected] == isSelected)
@@ -376,7 +429,7 @@ CPSegmentSwitchTrackingMomentary = 2;
_selectedSegment = aSegment;
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1 && oldSelectedSegment < _segments.length)
{
[_segments[oldSelectedSegment] setSelected:NO];
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
@@ -399,7 +452,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isSelectedForSegment:(unsigned)aSegment
{
return [_segments[aSegment] selected];
return [[_segments objectAtIndex:aSegment] selected];
}
/*!
@@ -410,10 +463,12 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(unsigned)aSegment
{
if ([_segments[aSegment] enabled] === shouldBeEnabled)
var segment = [_segments objectAtIndex:aSegment];
if ([segment enabled] === shouldBeEnabled)
return;
[_segments[aSegment] setEnabled:shouldBeEnabled];
[segment setEnabled:shouldBeEnabled];
if (shouldBeEnabled)
_themeStates[aSegment] = _themeStates[aSegment].without(CPThemeStateDisabled);
@@ -431,7 +486,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (BOOL)isEnabledForSegment:(unsigned)aSegment
{
return [_segments[aSegment] enabled];
return [[_segments objectAtIndex:aSegment] enabled];
}
/*!
@@ -441,7 +496,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
{
[_segments[aSegment] setTag:aTag];
[[_segments objectAtIndex:aSegment] setTag:aTag];
}
/*!
@@ -450,7 +505,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (int)tagForSegment:(unsigned)aSegment
{
return [_segments[aSegment] tag];
return [[_segments objectAtIndex:aSegment] tag];
}
// Drawings
@@ -461,7 +516,7 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
{
if(aSegment < _themeStates.length)
if (aSegment < _themeStates.length)
{
if (shouldHighlight)
_themeStates[aSegment] = _themeStates[aSegment].and(CPThemeStateHighlighted);
@@ -475,14 +530,12 @@ CPSegmentSwitchTrackingMomentary = 2;
- (float)_leftOffsetForSegment:(unsigned)segment
{
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
if (segment == 0)
return bezelInset.left;
return [self currentValueForThemeAttribute:@"bezel-inset"].left;
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
return [self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness;
}
- (unsigned)_indexOfLastSegment
@@ -508,15 +561,16 @@ CPSegmentSwitchTrackingMomentary = 2;
}
else if (aName === "right-segment-bezel")
{
return CGRectMake(CGRectGetWidth([self bounds]) - contentInset.right,
bezelInset.top,
contentInset.right,
height);
// we have to FLOOR the coordinates to prevent a 1px glitch in Safari
return CGRectMake(FLOOR(CGRectGetWidth([self bounds]) - contentInset.right),
FLOOR(bezelInset.top),
FLOOR(contentInset.right),
FLOOR(height));
}
else if (aName.indexOf("segment-bezel") === 0)
{
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
frame = CGRectCreateCopy([_segments[segment] frame]);
frame = CGRectCreateCopy([self frameForSegment:segment]);
if (segment === 0)
{
@@ -524,7 +578,7 @@ CPSegmentSwitchTrackingMomentary = 2;
frame.size.width -= contentInset.left;
}
if (segment === _segments.length - 1)
if (segment === [self segmentCount] - 1)
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
return frame;
@@ -532,7 +586,7 @@ CPSegmentSwitchTrackingMomentary = 2;
else if (aName.indexOf("divider-bezel") === 0)
{
var segment = parseInt(aName.substring("divider-bezel-".length), 10),
width = [self widthForSegment:segment],
width = CGRectGetWidth([self frameForSegment:segment]),
left = [self _leftOffsetForSegment:segment],
thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
@@ -551,14 +605,19 @@ CPSegmentSwitchTrackingMomentary = 2;
- (CPView)createEphemeralSubviewNamed:(CPString)aName
{
if ([aName hasPrefix:@"segment-content"])
return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
{
var view = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
[view _setUsesSingleLineMode:YES];
return view;
}
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
}
- (void)layoutSubviews
{
if (_segments.length <= 0)
if ([self segmentCount] <= 0)
return;
var themeState = _themeStates[0],
@@ -646,9 +705,8 @@ CPSegmentSwitchTrackingMomentary = 2;
if (i == count - 1)
continue;
var borderState = _themeStates[i].and(_themeStates[i + 1]);
borderState = (borderState.hasThemeState(CPThemeStateSelected) && !borderState.hasThemeState(CPThemeStateHighlighted)) ? CPThemeStateSelected : CPThemeStateNormal;
var borderState = _themeStates[i].and(_themeStates[i + 1]);
borderState = isDisabled ? borderState.and(CPThemeStateDisabled) : borderState;
@@ -678,60 +736,33 @@ CPSegmentSwitchTrackingMomentary = 2;
{
}
- (void)tileWithChangedSegment:(unsigned)aSegment
/*! @ignore */
- (void)tile
{
if (aSegment >= _segments.length)
[self tileWithChangedSegment:0];
}
/*! @ignore */
- (void)tileWithChangedSegment:(CPInteger)aSegment
{
var segmentCount = [self segmentCount];
// Corner case: when segmentCount == 0 and aSegment == 0, we do not return here because we still need to set the new frameSize bellow.
if (aSegment < 0 || (segmentCount > 0 && aSegment >= segmentCount))
return;
var segment = _segments[aSegment],
segmentWidth = [segment width],
themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
font = [self font];
var width = 0;
if (!segmentWidth)
if (segmentCount > 0)
{
if ([segment image] && [segment label])
segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.image)
segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
else if (segment.label)
segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
else
segmentWidth = 0.0;
// Invalidate frames for segments on the right. They will be lazily computed by -frameForSegment:.
for (var i = aSegment; i < segmentCount; i++)
[_segments[i] setFrame:CGRectMakeZero()];
width = CGRectGetMaxX([self frameForSegment:(segmentCount - 1)]);
}
var delta = segmentWidth - CGRectGetWidth([segment frame]);
if (!delta)
{
[self setNeedsLayout];
[self setNeedsDisplay:YES];
return;
}
// Update control size
var frame = [self frame];
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
// Update segment width
[segment setWidth:segmentWidth];
[segment setFrame:[self frameForSegment:aSegment]];
// Update following segments widths
var index = aSegment + 1;
for (; index < _segments.length; ++index)
{
[_segments[index] frame].origin.x += delta;
[self drawSegmentBezel:index highlight:NO];
[self drawSegment:index highlight:NO];
}
[self drawSegmentBezel:aSegment highlight:NO];
[self drawSegment:aSegment highlight:NO];
[self setFrameSize:CGSizeMake(width, CGRectGetHeight([self frame]))];
[self setNeedsLayout];
[self setNeedsDisplay:YES];
@@ -743,29 +774,58 @@ CPSegmentSwitchTrackingMomentary = 2;
*/
- (CGRect)frameForSegment:(unsigned)aSegment
{
return [self bezelFrameForSegment:aSegment];
var segment = [_segments objectAtIndex:aSegment],
frame = [segment frame];
if (CGRectEqualToRect(frame, CGRectMakeZero()))
{
frame = [self bezelFrameForSegment:aSegment];
[segment setFrame:frame];
}
return frame;
}
- (CGRect)bezelFrameForSegment:(unsigned)aSegment
{
var height = [self currentValueForThemeAttribute:@"min-size"].height,
bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
var left = [self _leftOffsetForSegment:aSegment],
top = [self currentValueForThemeAttribute:@"bezel-inset"].top,
width = [self widthForSegment:aSegment],
left = [self _leftOffsetForSegment:aSegment];
height = [self currentValueForThemeAttribute:@"min-size"].height;
return CGRectMake(left, bezelInset.top, width, height);
if (width == 0)
{
var themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
contentInsetWidth = contentInset.left + contentInset.right,
segment = _segments[aSegment],
label = [segment label],
image = [segment image];
// add 1 pixel to account for possible fractional pixels at right edge
width = (label ? [label sizeWithFont:[self font]].width + 1 : 4.0) + (image ? [image size].width : 0) + contentInsetWidth;
}
return CGRectMake(left, top, width, height);
}
- (CGRect)contentFrameForSegment:(unsigned)aSegment
{
var height = [self currentValueForThemeAttribute:@"min-size"].height,
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
width = [self widthForSegment:aSegment],
width = CGRectGetWidth([self frameForSegment:aSegment]),
left = [self _leftOffsetForSegment:aSegment];
return CGRectMake(left + contentInset.left, contentInset.top, width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
}
- (CGSize)_minimumFrameSize
{
// The current width is always the minimum width.
return CGSizeMake(CGRectGetWidth([self frame]), [self currentValueForThemeAttribute:@"min-size"].height);
}
/*!
Returns the segment that is hit by the specified point.
@param aPoint the point to test for a segment hit
@@ -774,19 +834,19 @@ CPSegmentSwitchTrackingMomentary = 2;
- (unsigned)testSegment:(CGPoint)aPoint
{
var location = [self convertPoint:aPoint fromView:nil],
count = _segments.length;
count = [self segmentCount];
while (count--)
if (CGRectContainsPoint([_segments[count] frame], aPoint))
if (CGRectContainsPoint([self frameForSegment:count], aPoint))
return count;
if (_segments.length)
if ([self segmentCount])
{
var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
var adjustedLastFrame = CGRectCreateCopy([self frameForSegment:(_segments.length - 1)]);
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
if (CGRectContainsPoint(adjustedLastFrame, aPoint))
return _segments.length - 1;
return [self segmentCount] - 1;
}
return -1;
@@ -814,64 +874,68 @@ CPSegmentSwitchTrackingMomentary = 2;
var type = [anEvent type],
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
if (type == CPLeftMouseUp)
switch (type)
{
if (_trackingSegment == -1)
case CPLeftMouseUp:
if (_trackingSegment === CPNotFound)
return;
if (_trackingSegment === [self testSegment:location])
{
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
{
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
// With ANY, _selectedSegment means last pressed.
_selectedSegment = _trackingSegment;
}
else
[self setSelected:YES forSegment:_trackingSegment];
[self sendAction:[self action] to:[self target]];
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
{
[self setSelected:NO forSegment:_trackingSegment];
_selectedSegment = CPNotFound;
}
}
[self drawSegmentBezel:_trackingSegment highlight:NO];
_trackingSegment = CPNotFound;
return;
if (_trackingSegment === [self testSegment:location])
{
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
case CPLeftMouseDown:
var trackingSegment = [self testSegment:location];
if (trackingSegment > CPNotFound && [self isEnabledForSegment:trackingSegment])
{
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
// With ANY, _selectedSegment means last pressed.
_selectedSegment = _trackingSegment;
_trackingHighlighted = YES;
_trackingSegment = trackingSegment;
[self drawSegmentBezel:_trackingSegment highlight:YES];
}
else
[self setSelected:YES forSegment:_trackingSegment];
[self sendAction:[self action] to:[self target]];
break;
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
case CPLeftMouseDragged:
if (_trackingSegment === CPNotFound)
return;
var highlighted = [self testSegment:location] === _trackingSegment;
if (highlighted != _trackingHighlighted)
{
[self setSelected:NO forSegment:_trackingSegment];
_trackingHighlighted = highlighted;
_selectedSegment = -1;
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
}
}
[self drawSegmentBezel:_trackingSegment highlight:NO];
_trackingSegment = -1;
return;
}
if (type == CPLeftMouseDown)
{
var trackingSegment = [self testSegment:location];
if (trackingSegment > -1 && [self isEnabledForSegment:trackingSegment])
{
_trackingHighlighted = YES;
_trackingSegment = trackingSegment;
[self drawSegmentBezel:_trackingSegment highlight:YES];
}
}
else if (type == CPLeftMouseDragged)
{
if (_trackingSegment == -1)
return;
var highlighted = [self testSegment:location] === _trackingSegment;
if (highlighted != _trackingHighlighted)
{
_trackingHighlighted = highlighted;
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
}
break;
}
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
@@ -881,7 +945,7 @@ CPSegmentSwitchTrackingMomentary = 2;
{
[super setFont:aFont];
[self tileWithChangedSegment:0];
[self tile];
}
@end
@@ -913,7 +977,7 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
else
_selectedSegment = -1;
_selectedSegment = CPNotFound;
if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
_trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
@@ -921,23 +985,24 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
_trackingMode = CPSegmentSwitchTrackingSelectOne;
// Here we update the themeStates array for each segments to know if there are selected or not
for (var i = 0; i < _segments.length; i++)
for (var i = 0; i < [self segmentCount]; i++)
_themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
// We do this in a second loop because it relies on all the themeStates being set first
for (var i = 0; i < _segments.length; i++)
[self tileWithChangedSegment:i];
[self tile];
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
dividerExtraSpace = ([_segments count] - 1) * thickness,
difference = MAX(originalWidth - [self frame].size.width - dividerExtraSpace, 0.0),
remainingWidth = FLOOR(difference / _segments.length),
remainingWidth = FLOOR(difference / [self segmentCount]),
widthOfAllSegments = 0;
for (var i = 0; i < _segments.length; i++)
// We do this in a second loop because it relies on all the themeStates being set first
for (var i = 0; i < [self segmentCount]; i++)
{
[self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
widthOfAllSegments += [_segments[i] width];
var frame = [_segments[i] frame];
frame.size.width += remainingWidth;
widthOfAllSegments += CGRectGetWidth(frame);
}
// Here we handle the leftovers pixel, and we will add one pixel to each segment cell till we have the same size as the originalSize.
@@ -945,16 +1010,16 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
var leftOversPixel = originalWidth - (widthOfAllSegments + dividerExtraSpace);
// Make sure we don't make an out of range
if (leftOversPixel < _segments.length - 1)
if (leftOversPixel < [self segmentCount] - 1)
{
for (var i = 0; i < leftOversPixel; i++)
{
[self setWidth:[_segments[i] width] + 1 forSegment:i];
[_segments[i] frame].size.width += 1;
}
}
[self setFrameSize:CGSizeMake(originalWidth, [self frame].size.height)];
[self tileWithChangedSegment:0];
[self setFrameSize:CGSizeMake(originalWidth, CGRectGetHeight([self frame]))];
[self tile];
}
return self;
+808 -150
View File
File diff suppressed because it is too large Load Diff
+12 -11
View File
@@ -32,6 +32,7 @@
@end
@global document
var CPSoundDelegate_sound_didFinishPlaying_ = 1 << 1;
@@ -65,8 +66,8 @@ CPSoundPlayBackStatePause = 2;
unsigned _implementedDelegateMethods;
}
#pragma mark -
#pragma mark Initialization
// MARK: -
// MARK: Initialization
- (id)init
{
@@ -146,8 +147,8 @@ CPSoundPlayBackStatePause = 2;
}
#pragma mark -
#pragma mark Delegate methods
// MARK: -
// MARK: Delegate methods
/*!
Sets the sound's delegate.
@@ -165,8 +166,8 @@ CPSoundPlayBackStatePause = 2;
_implementedDelegateMethods |= CPSoundDelegate_sound_didFinishPlaying_;
}
#pragma mark -
#pragma mark Events listener
// MARK: -
// MARK: Events listener
/*! @ignore
*/
@@ -198,8 +199,8 @@ CPSoundPlayBackStatePause = 2;
}
#pragma mark -
#pragma mark Media controls
// MARK: -
// MARK: Media controls
/*!
Play the sound.
@@ -323,8 +324,8 @@ CPSoundPlayBackStatePause = 2;
_audioTag.volume = aVolume;
}
#pragma mark -
#pragma mark Accessors
// MARK: -
// MARK: Accessors
/*!
Returns the duration in seconds of the sound.
@@ -363,4 +364,4 @@ CPSoundPlayBackStatePause = 2;
[_delegate sound:self didFinishPlaying:finishedPlaying];
}
@end
@end
+1038 -605
View File
File diff suppressed because it is too large Load Diff
+388
View File
@@ -0,0 +1,388 @@
/*
* CPSplitViewController.j
*
* Created by Daniel Boehringer on September 2, 2025.
* Copyright (c) 2025 Daniel Boehringer. All rights reserved.
*
* 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 "CPViewController.j"
@import "CPSplitView.j"
var CPSplitViewControllerAutomaticDimension = -1.0;
/**
* A container view controller that manages two or more child view
* controllers in a split view interface.
*
* This class provides a controller-level abstraction for CPSplitView,
* managing the addition, removal, and arrangement of view controllers
* through CPSplitViewItem instances.
*
* This implementation was synthesized with the assistance of an LLM,
* directed by the author.
*/
@implementation CPSplitViewController : CPViewController <CPSplitViewDelegate>
{
/** @private The underlying split view that arranges the child views. */
CPSplitView _splitView;
/** @private An array of CPSplitViewItem objects managed by this controller. */
CPMutableArray _splitViewItems;
/** @private The minimum thickness for sidebars to be displayed inline. */
CPNumber _minimumThicknessForInlineSidebars;
}
// MARK: - Initialization
- (id)init
{
if (self = [super init])
{
_splitViewItems = [CPMutableArray array];
_minimumThicknessForInlineSidebars = 20.0;
}
return self;
}
// MARK: - View Lifecycle
- (void)loadView
{
if (!_splitView)
{
_splitView = [[CPSplitView alloc] initWithFrame:CGRectMake(0,0,400,400)];
[_splitView setDelegate:self];
}
[self setView:_splitView];
}
- (void)viewDidLoad
{
[super viewDidLoad];
for (var i = 0; i < [_splitViewItems count]; i++)
{
var viewController = [[_splitViewItems objectAtIndex:i] viewController];
[[self splitView] addArrangedSubview:[viewController view]];
}
}
// MARK: - Accessors
/**
* Returns the CPSplitView instance managed by the controller.
*
* @returns {CPSplitView} The split view.
*/
- (CPSplitView)splitView
{
return _splitView;
}
/**
* Sets a custom split view for the controller.
*
* @param {CPSplitView} splitView The custom split view to use.
*/
- (void)setSplitView:(CPSplitView)splitView
{
if (_splitView !== splitView)
{
_splitView = splitView;
[_splitView setDelegate:self];
if ([self isViewLoaded])
[self setView:_splitView];
}
}
/**
* Returns the array of split view items.
*
* @returns {CPArray} The array of CPSplitViewItem objects.
*/
- (CPArray)splitViewItems
{
return _splitViewItems;
}
/**
* Sets the array of split view items, replacing any existing items.
*
* @param {CPArray} splitViewItems An array of CPSplitViewItem objects.
*/
- (void)setSplitViewItems:(CPArray)splitViewItems
{
// Remove all existing items
while ([_splitViewItems count] > 0)
{
[self removeSplitViewItem:[_splitViewItems lastObject]];
}
// Add new items
for (var i = 0; i < [splitViewItems count]; i++)
{
[self addSplitViewItem:[splitViewItems objectAtIndex:i]];
}
}
// MARK: - Managing Split View Items
/**
* Adds a split view item to the end of the split view.
*
* @param {CPSplitViewItem} splitViewItem The split view item to add.
*/
- (void)addSplitViewItem:(CPSplitViewItem)splitViewItem
{
[self insertSplitViewItem:splitViewItem atIndex:[_splitViewItems count]];
}
/**
* Inserts a split view item at a specific index.
*
* @param {CPSplitViewItem} splitViewItem The split view item to insert.
* @param {CPInteger} index The zero-based index at which to insert the item.
*/
- (void)insertSplitViewItem:(CPSplitViewItem)splitViewItem atIndex:(CPInteger)index
{
[splitViewItem _setSplitViewController:self];
[_splitViewItems insertObject:splitViewItem atIndex:index];
[self addChildViewController:[splitViewItem viewController]];
if ([self isViewLoaded])
[[self splitView] insertArrangedSubview:[[splitViewItem viewController] view] atIndex:index];
}
/**
* Removes the specified split view item.
*
* @param {CPSplitViewItem} splitViewItem The split view item to remove.
*/
- (void)removeSplitViewItem:(CPSplitViewItem)splitViewItem
{
var viewController = [splitViewItem viewController];
if ([self isViewLoaded])
[[viewController view] removeFromSuperview];
[viewController removeFromParentViewController];
[splitViewItem _setSplitViewController:nil];
[_splitViewItems removeObject:splitViewItem];
}
/**
* Retrieves the split view item associated with a given view controller.
*
* @param {CPViewController} viewController The view controller to find.
* @returns {CPSplitViewItem | null} The corresponding split view item, or nil if not found.
*/
- (CPSplitViewItem)splitViewItemForViewController:(CPViewController)viewController
{
for (var i = 0; i < [_splitViewItems count]; i++)
{
var item = [_splitViewItems objectAtIndex:i];
if ([item viewController] === viewController)
return item;
}
return nil;
}
// MARK: - Managing Sidebars and Inspectors
/**
* Toggles the collapsed state of the first split view item, typically a sidebar.
*
* @param {id} sender The object that initiated the action.
*/
- (void)toggleSidebar:(id)sender
{
if ([_splitViewItems count] > 0)
{
var sidebarItem = [_splitViewItems objectAtIndex:0];
[sidebarItem setCollapsed:![sidebarItem isCollapsed]];
}
}
/**
* Toggles the collapsed state of the last split view item, typically an inspector.
*
* @param {id} sender The object that initiated the action.
*/
- (void)toggleInspector:(id)sender
{
if ([_splitViewItems count] > 1)
{
var inspectorItem = [_splitViewItems lastObject];
[inspectorItem setCollapsed:![inspectorItem isCollapsed]];
}
}
/**
* Returns the minimum thickness for sidebars to be displayed inline.
*
* @returns {CPNumber} The minimum thickness.
*/
- (CPNumber)minimumThicknessForInlineSidebars
{
return _minimumThicknessForInlineSidebars;
}
// MARK: - CPSplitViewDelegate Methods
// Note: A more complete implementation would forward these delegate methods
// to a separate delegate property on the CPSplitViewController itself.
// For now, these are stubbed to demonstrate where they would be handled.
- (BOOL)splitView:(CPSplitView)splitView canCollapseSubview:(CPView)subview
{
// Default behavior: allow all subviews to be collapsed.
return YES;
}
- (CGFloat)splitView:(CPSplitView)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(CPInteger)dividerIndex
{
return proposedMinimumPosition;
}
- (CGFloat)splitView:(CPSplitView)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(CPInteger)dividerIndex
{
return proposedMaximumPosition;
}
- (CGRect)splitView:(CPSplitView)splitView effectiveRect:(CGRect)proposedEffectiveRect forDrawnRect:(CGRect)drawnRect ofDividerAtIndex:(CPInteger)dividerIndex
{
// Default behavior: return the proposed rectangle.
// This can be overridden to provide a larger or custom hit area for the divider.
return proposedEffectiveRect;
}
- (void)splitViewDidResizeSubviews:(CPNotification)notification
{
// Can be used to respond to user-initiated resizing.
}
@end
/**
* An object that manages a view controller within a CPSplitViewController.
*
* A CPSplitViewItem acts as a wrapper around a CPViewController,
* maintaining properties like its collapsed state within the parent
* split view controller.
*/
@implementation CPSplitViewItem : CPObject
{
/** @private The view controller managed by this item. */
CPViewController _viewController;
/** @private A boolean indicating whether the item is collapsed. */
BOOL _isCollapsed;
/** @private A weak reference to the owning split view controller. */
CPSplitViewController _splitViewController;
}
// MARK: - Class Methods
/**
* Creates and returns a new split view item with the specified view controller.
*
* @param {CPViewController} viewController The view controller for the new item.
* @returns {instancetype} A new CPSplitViewItem instance.
*/
+ (instancetype)splitViewItemWithViewController:(CPViewController)viewController
{
return [[self alloc] initWithViewController:viewController];
}
// MARK: - Initialization
/**
* Initializes a new split view item with the specified view controller.
*
* @param {CPViewController} viewController The view controller for the new item.
* @returns {id} The initialized CPSplitViewItem instance.
*/
- (id)initWithViewController:(CPViewController)viewController
{
if (self = [super init])
{
_viewController = viewController;
_isCollapsed = NO;
}
return self;
}
// MARK: - Accessors
/**
* Returns the view controller associated with the item.
*
* @returns {CPViewController} The associated view controller.
*/
- (CPViewController)viewController
{
return _viewController;
}
/**
* Returns a boolean value indicating whether the item is collapsed.
*
* @returns {BOOL} YES if the item is collapsed, otherwise NO.
*/
- (BOOL)isCollapsed
{
return _isCollapsed;
}
/**
* Sets the collapsed state of the item.
* When collapsed, the view controller's view is hidden.
*
* @param {BOOL} shouldCollapse YES to collapse the item, NO to expand it.
*/
- (void)setCollapsed:(BOOL)shouldCollapse
{
if (_isCollapsed === shouldCollapse)
return;
_isCollapsed = shouldCollapse;
[[_viewController view] setHidden:shouldCollapse];
}
/**
* Returns the split view controller that owns this item.
*
* @returns {CPSplitViewController | null} The parent split view controller.
*/
- (CPSplitViewController)splitViewController
{
return _splitViewController;
}
/**
* @private
* Sets the owning split view controller. This method is for internal use by
* CPSplitViewController.
*
* @param {CPSplitViewController} splitViewController The parent controller.
*/
- (void)_setSplitViewController:(CPSplitViewController)splitViewController
{
_splitViewController = splitViewController;
}
@end
+815
View File
@@ -0,0 +1,815 @@
/*
* CPStackView.j
* AppKit
*
* Created by Daniel Boehringer.
* Copyright 2025, Cappuccino Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* PLACEHOLDER IMPLEMENTATION — READ BEFORE USE OR MODIFICATION.
*
* This class lays out views by direct, procedural arithmetic. It has no
* constraint solver. It cannot compress, expand, or negotiate space among
* views the way NSStackView does; it only places views at their existing
* frame size, in order, separated by fixed spacing.
*
* Known, accepted limitations:
* - `distribution` is stored but has no effect on layout. Fill,
* FillEqually, FillProportionally, and EqualSpacing are unimplemented.
* - Center-gravity views are clamped against the Leading edge only; if
* Leading + Center + Trailing content overflows the container, Center
* views can overlap Trailing views instead of compressing.
* - `visibilityPriority:forView:` only supports the two extreme values
* (MustHold / NotVisible). Intermediate priorities are accepted but
* have no defined effect.
* - No guarantee is made of correctness beyond what a single manual
* test (Tests/Manual/CPStackViewTest) exercises: orientation, the
* three gravity areas, alignment switching, spacing, and hidden-view
* detachment. Insertion, removal, custom spacing, and the CPCoding
* archive path are implemented but not verified by that test.
*
* This exists to give AppKit a working CPStackView symbol now, not to be
* a durable design. It is expected to be replaced by a constraint-solver
* based implementation (Kiwi.js) when time permits. Do not build on its
* internal layout algorithm as if it were a stable foundation.
*/
@import "CPView.j"
@import <Foundation/CPMapTable.j>
// MARK: -
// MARK: Minimal local type definitions
//
// These types support this file only. They are not shared with the rest
// of AppKit. A future constraint-solver based Auto Layout engine will
// replace them. Numeric values match the equivalent Cocoa constants
// (NSUserInterfaceLayoutOrientation, NSLayoutAttribute) so that a later,
// solver-based CPLayoutAttribute can reuse these numbers without a
// renumbering pass.
@typedef CPUserInterfaceLayoutOrientation
CPUserInterfaceLayoutOrientationHorizontal = 0;
CPUserInterfaceLayoutOrientationVertical = 1;
@typedef CPLayoutAttribute
CPLayoutAttributeLeft = 1;
CPLayoutAttributeRight = 2;
CPLayoutAttributeTop = 3;
CPLayoutAttributeBottom = 4;
CPLayoutAttributeLeading = 5;
CPLayoutAttributeTrailing = 6;
CPLayoutAttributeWidth = 7;
CPLayoutAttributeHeight = 8;
CPLayoutAttributeCenterX = 9;
CPLayoutAttributeCenterY = 10;
@typedef CPEdgeInsets
/*!
Creates a CPEdgeInsets. Argument order matches Cocoa's NSEdgeInsetsMake
(top, left, bottom, right). Storage reuses the existing CGInset struct,
whose field order is (top, right, bottom, left).
*/
function CPEdgeInsetsMake(top, left, bottom, right)
{
return CGInsetMake(top, right, bottom, left);
}
function CPEdgeInsetsEqualToEdgeInsets(lhsInsets, rhsInsets)
{
return CGInsetEqualToInset(lhsInsets, rhsInsets);
}
// Gravity Areas
@typedef CPStackViewGravity
CPStackViewGravityTop = 1;
CPStackViewGravityLeading = 1;
CPStackViewGravityCenter = 2;
CPStackViewGravityBottom = 3;
CPStackViewGravityTrailing = 3;
// Distribution (Deprecated in modern macOS, but kept for compatibility/logic)
@typedef CPStackViewDistribution
CPStackViewDistributionGravityAreas = 0;
CPStackViewDistributionFill = 1;
CPStackViewDistributionFillEqually = 2;
CPStackViewDistributionFillProportionally = 3;
CPStackViewDistributionEqualSpacing = 4;
CPStackViewDistributionEqualCentering = 5;
// Visibility Priority
@typedef CPStackViewVisibilityPriority
CPStackViewVisibilityPriorityMustHold = 1000.0;
CPStackViewVisibilityPriorityNotVisible = 0.0;
var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
/*!
@ingroup appkit
@class CPStackView
CPStackView arranges an array of views horizontally or vertically and updates
their placement and sizing when the window size changes.
Unlike a simple list, CPStackView supports "Gravity Areas" (Leading, Center, Trailing),
allowing you to pin groups of views to specific sections of the layout.
*/
@implementation CPStackView : CPView
{
CPUserInterfaceLayoutOrientation _orientation;
CPLayoutAttribute _alignment;
CPStackViewDistribution _distribution;
float _spacing;
CPEdgeInsets _edgeInsets;
BOOL _detachesHiddenViews;
// View Storage by Gravity
CPMutableArray _viewsLeading;
CPMutableArray _viewsCenter;
CPMutableArray _viewsTrailing;
// Internal cache of all arranged subviews to maintain order for hittesting/iterating
CPMutableArray _arrangedSubviews;
// Custom Spacing storage
CPMapTable _customSpacings;
// Visibility Priorities
CPMapTable _visibilityPriorities;
}
// MARK: -
// MARK: Initialization
+ (CPStackView)stackViewWithViews:(CPArray)views
{
var stackView = [[CPStackView alloc] initWithFrame:CGRectMakeZero()];
for (var i = 0, count = [views count]; i < count; i++)
[stackView addView:views[i] inGravity:CPStackViewGravityLeading];
return stackView;
}
- (id)initWithFrame:(CGRect)aFrame
{
if (self = [super initWithFrame:aFrame])
{
_orientation = CPUserInterfaceLayoutOrientationHorizontal;
_alignment = CPLayoutAttributeCenterY; // Default alignment
_distribution = CPStackViewDistributionGravityAreas;
_spacing = 8.0; // Default Cocoa spacing
_edgeInsets = CPEdgeInsetsMake(0, 0, 0, 0);
_detachesHiddenViews = YES;
_viewsLeading = [[CPMutableArray alloc] init];
_viewsCenter = [[CPMutableArray alloc] init];
_viewsTrailing = [[CPMutableArray alloc] init];
_arrangedSubviews = [[CPMutableArray alloc] init];
_customSpacings = [[CPMapTable alloc] init];
_visibilityPriorities = [[CPMapTable alloc] init];
}
return self;
}
// MARK: -
// MARK: Configuration
/*!
The horizontal or vertical layout direction of the stack view.
*/
- (CPUserInterfaceLayoutOrientation)orientation
{
return _orientation;
}
- (void)setOrientation:(CPUserInterfaceLayoutOrientation)anOrientation
{
if (_orientation === anOrientation)
return;
_orientation = anOrientation;
// Reset default alignment based on new orientation if needed,
// though usually developer sets alignment explicitly.
// If switching to Vertical, CenterY makes less sense, usually CenterX.
if (_orientation === CPUserInterfaceLayoutOrientationVertical)
{
if (_alignment === CPLayoutAttributeCenterY)
_alignment = CPLayoutAttributeCenterX;
}
else
{
if (_alignment === CPLayoutAttributeCenterX)
_alignment = CPLayoutAttributeCenterY;
}
[self setNeedsLayout:YES];
}
/*!
The view alignment within the stack view.
Common values:
Horizontal: CPLayoutAttributeTop, CPLayoutAttributeBottom, CPLayoutAttributeCenterY, CPLayoutAttributeHeight (fill)
Vertical: CPLayoutAttributeLeading, CPLayoutAttributeTrailing, CPLayoutAttributeCenterX, CPLayoutAttributeWidth (fill)
*/
- (CPLayoutAttribute)alignment
{
return _alignment;
}
- (void)setAlignment:(CPLayoutAttribute)anAlignment
{
if (_alignment === anAlignment)
return;
_alignment = anAlignment;
[self setNeedsLayout:YES];
}
/*!
The distribution mode for the stack view.
@note Not yet applied to layout. All views are laid out at their
existing frame size regardless of this value, pending the
constraint-solver based layout engine. The value is stored and
returned so client code can read back what was set.
*/
- (CPStackViewDistribution)distribution
{
return _distribution;
}
- (void)setDistribution:(CPStackViewDistribution)aDistribution
{
if (_distribution === aDistribution)
return;
_distribution = aDistribution;
[self setNeedsLayout:YES];
}
/*!
The minimum spacing, in points, between adjacent views in the stack view.
*/
- (float)spacing
{
return _spacing;
}
- (void)setSpacing:(float)aSpacing
{
if (_spacing === aSpacing)
return;
_spacing = aSpacing;
[self setNeedsLayout:YES];
}
/*!
The geometric padding, in points, inside the stack view, surrounding its views.
*/
- (CPEdgeInsets)edgeInsets
{
return _edgeInsets;
}
- (void)setEdgeInsets:(CPEdgeInsets)insets
{
if (CPEdgeInsetsEqualToEdgeInsets(_edgeInsets, insets))
return;
_edgeInsets = insets;
[self setNeedsLayout:YES];
}
/*!
A Boolean value that indicates whether the stack view removes hidden views from its view hierarchy.
*/
- (BOOL)detachesHiddenViews
{
return _detachesHiddenViews;
}
- (void)setDetachesHiddenViews:(BOOL)shouldDetach
{
if (_detachesHiddenViews === shouldDetach)
return;
_detachesHiddenViews = shouldDetach;
[self setNeedsLayout:YES];
}
// MARK: -
// MARK: Managing Views in Gravity Areas
- (CPArray)_containerForGravity:(CPStackViewGravity)gravity
{
if (gravity === CPStackViewGravityCenter)
return _viewsCenter;
else if (gravity === CPStackViewGravityTrailing) // or Bottom
return _viewsTrailing;
return _viewsLeading; // Leading or Top
}
/*!
Rebuilds _arrangedSubviews from the three gravity containers, in
Leading, Center, Trailing order. Call after any change to a gravity
container so _arrangedSubviews stays a correct, single source of truth
for ordering, rather than an incrementally and separately maintained
(and error-prone) copy.
*/
- (void)_rebuildArrangedSubviews
{
_arrangedSubviews = [[CPMutableArray alloc] init];
[_arrangedSubviews addObjectsFromArray:_viewsLeading];
[_arrangedSubviews addObjectsFromArray:_viewsCenter];
[_arrangedSubviews addObjectsFromArray:_viewsTrailing];
}
/*!
Adds a view to the end of the stack view gravity area.
*/
- (void)addView:(CPView)aView inGravity:(CPStackViewGravity)gravity
{
var container = [self _containerForGravity:gravity];
// Check if view is already in a container
if ([_arrangedSubviews containsObject:aView])
[self removeView:aView];
[container addObject:aView];
[self _rebuildArrangedSubviews];
// Add as actual subview
if ([aView superview] !== self)
[self addSubview:aView];
[self setNeedsLayout:YES];
}
/*!
Adds a view to a stack view gravity area at a specified index position.
*/
- (void)insertView:(CPView)aView atIndex:(CPInteger)index inGravity:(CPStackViewGravity)gravity
{
var container = [self _containerForGravity:gravity];
if ([_arrangedSubviews containsObject:aView])
[self removeView:aView];
if (index >= [container count])
[container addObject:aView];
else
[container insertObject:aView atIndex:index];
[self _rebuildArrangedSubviews];
if ([aView superview] !== self)
[self addSubview:aView];
[self setNeedsLayout:YES];
}
/*!
Specifies an array of views for a specified gravity area in the stack view, replacing any previous views in that area.
*/
- (void)setViews:(CPArray)views inGravity:(CPStackViewGravity)gravity
{
var container = [self _containerForGravity:gravity];
// Remove old views from superview
for (var i = 0; i < [container count]; i++)
[container[i] removeFromSuperview];
[container removeAllObjects];
for (var i = 0; i < [views count]; i++)
{
var newView = views[i];
[container addObject:newView];
[self addSubview:newView];
}
[self _rebuildArrangedSubviews];
[self setNeedsLayout:YES];
}
/*!
Removes a specified view from the stack view.
*/
- (void)removeView:(CPView)aView
{
if (![_arrangedSubviews containsObject:aView])
return;
[_viewsLeading removeObject:aView];
[_viewsCenter removeObject:aView];
[_viewsTrailing removeObject:aView];
[self _rebuildArrangedSubviews];
[aView removeFromSuperview];
[self setNeedsLayout:YES];
}
/*!
Returns the array of views in the specified gravity area in the stack view.
*/
- (CPArray)viewsInGravity:(CPStackViewGravity)gravity
{
return [[self _containerForGravity:gravity] copy];
}
/*!
The array of views arranged by the stack view.
*/
- (CPArray)arrangedSubviews
{
return [_arrangedSubviews copy];
}
/*!
Adds the specified view to the end of the arranged subviews list.
(Defaults to Leading gravity if not specified).
*/
- (void)addArrangedSubview:(CPView)view
{
[self addView:view inGravity:CPStackViewGravityLeading];
}
/*!
Removes the provided view from the stacks array of arranged subviews.
*/
- (void)removeArrangedSubview:(CPView)view
{
[self removeView:view];
}
// MARK: -
// MARK: Custom Spacing
- (float)customSpacingAfterView:(CPView)aView
{
var val = [_customSpacings objectForKey:aView];
if (val)
return [val floatValue];
return CPStackViewSpacingUseDefault;
}
- (void)setCustomSpacing:(float)spacing afterView:(CPView)aView
{
if (spacing === CPStackViewSpacingUseDefault)
[_customSpacings removeObjectForKey:aView];
else
[_customSpacings setObject:spacing forKey:aView];
[self setNeedsLayout:YES];
}
- (float)_spacingAfterView:(CPView)aView
{
var custom = [self customSpacingAfterView:aView];
if (custom !== CPStackViewSpacingUseDefault)
return custom;
return _spacing;
}
// MARK: -
// MARK: Visibility Priority
- (void)setVisibilityPriority:(float)priority forView:(CPView)aView
{
[_visibilityPriorities setObject:priority forKey:aView];
if (priority === CPStackViewVisibilityPriorityNotVisible)
{
[aView setHidden:YES];
}
else if (priority === CPStackViewVisibilityPriorityMustHold)
{
[aView setHidden:NO];
}
// Note: Intermediate priorities require complex constraint logic
// or a multi-pass layout system to determine fitting, which is
// simplified here to basic Hidden/Visible states.
[self setNeedsLayout:YES];
}
- (float)visibilityPriorityForView:(CPView)aView
{
var val = [_visibilityPriorities objectForKey:aView];
if (val)
return [val floatValue];
return CPStackViewVisibilityPriorityMustHold;
}
// MARK: -
// MARK: Layout
- (void)resizeSubviewsWithOldSize:(CGSize)oldSize
{
[self layoutSubviews];
}
- (void)layoutSubviews
{
if (_orientation === CPUserInterfaceLayoutOrientationVertical)
[self _layoutVertical];
else
[self _layoutHorizontal];
}
- (void)_layoutHorizontal
{
var bounds = [self bounds],
availWidth = CGRectGetWidth(bounds) - _edgeInsets.left - _edgeInsets.right,
availHeight = CGRectGetHeight(bounds) - _edgeInsets.top - _edgeInsets.bottom,
currentX = _edgeInsets.left;
// 1. Layout Leading Views
currentX = [self _layoutViews:_viewsLeading startOffset:currentX availableOrthogonalSize:availHeight direction:1];
// 2. Layout Trailing Views
// We layout backwards from the right
var startRight = CGRectGetWidth(bounds) - _edgeInsets.right;
[self _layoutViews:_viewsTrailing startOffset:startRight availableOrthogonalSize:availHeight direction:-1];
// 3. Layout Center Views
if ([_viewsCenter count] > 0)
{
// Calculate total width of center stack
var centerStackWidth = 0.0;
for (var i = 0; i < [_viewsCenter count]; i++)
{
var view = _viewsCenter[i];
if (_detachesHiddenViews && [view isHidden]) continue;
centerStackWidth += CGRectGetWidth([view frame]);
if (i < [_viewsCenter count] - 1)
centerStackWidth += [self _spacingAfterView:view];
}
var centerStart = (CGRectGetWidth(bounds) / 2.0) - (centerStackWidth / 2.0);
// Clamp to prevent overlap with Leading (simplified collision logic)
// ideally stack view compresses views, but here we just shift/clip
if (centerStart < currentX)
centerStart = currentX;
[self _layoutViews:_viewsCenter startOffset:centerStart availableOrthogonalSize:availHeight direction:1];
}
}
- (void)_layoutVertical
{
var bounds = [self bounds],
availWidth = CGRectGetWidth(bounds) - _edgeInsets.left - _edgeInsets.right,
availHeight = CGRectGetHeight(bounds) - _edgeInsets.top - _edgeInsets.bottom,
currentY = _edgeInsets.top;
// 1. Layout Top (Leading) Views
currentY = [self _layoutViews:_viewsLeading startOffset:currentY availableOrthogonalSize:availWidth direction:1];
// 2. Layout Bottom (Trailing) Views
var startBottom = CGRectGetHeight(bounds) - _edgeInsets.bottom;
[self _layoutViews:_viewsTrailing startOffset:startBottom availableOrthogonalSize:availWidth direction:-1];
// 3. Layout Center Views
if ([_viewsCenter count] > 0)
{
var centerStackHeight = 0.0;
for (var i = 0; i < [_viewsCenter count]; i++)
{
var view = _viewsCenter[i];
if (_detachesHiddenViews && [view isHidden]) continue;
centerStackHeight += CGRectGetHeight([view frame]);
if (i < [_viewsCenter count] - 1)
centerStackHeight += [self _spacingAfterView:view];
}
var centerStart = (CGRectGetHeight(bounds) / 2.0) - (centerStackHeight / 2.0);
if (centerStart < currentY)
centerStart = currentY;
[self _layoutViews:_viewsCenter startOffset:centerStart availableOrthogonalSize:availWidth direction:1];
}
}
// Helper to layout a specific array of views in one direction
// Returns the ending offset
- (float)_layoutViews:(CPArray)views startOffset:(float)offset availableOrthogonalSize:(float)orthoSize direction:(int)dir
{
var cursor = offset;
var isVert = (_orientation === CPUserInterfaceLayoutOrientationVertical);
// If direction is -1 (Trailing/Bottom), we iterate backwards
// However, the standard behavior for trailing gravity is that the *last* view added is at the *end*.
// Leading: [A] [B] ->
// Trailing: -> [C] [D] (where D is rightmost)
// To support Trailing logic: We start at Right Edge, move left by Width(D), place D, move left by Spacing...
var count = [views count];
if (count === 0) return cursor;
// If direction is negative (Trailing), we process list in reverse order to stack them from edge inwards
var i = (dir === 1) ? 0 : count - 1;
var limit = (dir === 1) ? count : -1;
var step = (dir === 1) ? 1 : -1;
// Spacing is applied as a gap *before* placing an element (except the
// first placed one), rather than trailing off the end after the last
// element. This keeps the returned cursor at the true content edge,
// with no phantom spacing past the final view.
var hasPlacedAny = false;
var pendingSpacing = 0;
for (; i !== limit; i += step)
{
var view = views[i];
if (_detachesHiddenViews && [view isHidden])
continue;
if (hasPlacedAny)
cursor += (dir === 1) ? pendingSpacing : -pendingSpacing;
var viewFrame = [view frame];
var viewSizePrimary = isVert ? CGRectGetHeight(viewFrame) : CGRectGetWidth(viewFrame);
// Handle Alignment (Orthogonal Axis)
var orthoPos = 0.0;
var viewOrthoSize = isVert ? CGRectGetWidth(viewFrame) : CGRectGetHeight(viewFrame);
// Apply Stretch/Fill Alignment
if (isVert)
{
// Vertical Stack, dealing with Width
if (_alignment === CPLayoutAttributeWidth || _alignment === CPLayoutAttributeLeading || _alignment === CPLayoutAttributeTrailing)
{
// Note: CPLayoutAttributeLeading/Trailing in this context implies filling width usually,
// or aligning to edges. Let's assume Width/Fill for Leading/Trailing/Left/Right
// in this simplified implementation, or strictly left/right.
if (_alignment === CPLayoutAttributeWidth || _alignment === CPLayoutAttributeLeft || _alignment === CPLayoutAttributeLeading)
{
// Fill width if explicit, or just align left
if (_alignment === CPLayoutAttributeWidth) viewOrthoSize = orthoSize;
orthoPos = _edgeInsets.left;
}
else if (_alignment === CPLayoutAttributeRight || _alignment === CPLayoutAttributeTrailing)
{
orthoPos = _edgeInsets.left + (orthoSize - viewOrthoSize);
}
else // CenterX
{
orthoPos = _edgeInsets.left + (orthoSize - viewOrthoSize) / 2.0;
}
}
else // Default CenterX
{
orthoPos = _edgeInsets.left + (orthoSize - viewOrthoSize) / 2.0;
}
}
else
{
// Horizontal Stack, dealing with Height
if (_alignment === CPLayoutAttributeHeight || _alignment === CPLayoutAttributeTop || _alignment === CPLayoutAttributeBottom)
{
if (_alignment === CPLayoutAttributeHeight)
{
viewOrthoSize = orthoSize;
orthoPos = _edgeInsets.top;
}
else if (_alignment === CPLayoutAttributeTop)
{
orthoPos = _edgeInsets.top;
}
else if (_alignment === CPLayoutAttributeBottom)
{
orthoPos = _edgeInsets.top + (orthoSize - viewOrthoSize);
}
else // CenterY
{
orthoPos = _edgeInsets.top + (orthoSize - viewOrthoSize) / 2.0;
}
}
else // Default CenterY
{
orthoPos = _edgeInsets.top + (orthoSize - viewOrthoSize) / 2.0;
}
}
// Calculate Position
var originX = 0.0, originY = 0.0;
var sizeW = 0.0, sizeH = 0.0;
if (isVert)
{
// Vertical
sizeH = viewSizePrimary;
sizeW = viewOrthoSize;
originX = orthoPos;
if (dir === 1) {
originY = cursor;
cursor += sizeH;
} else {
cursor -= sizeH;
originY = cursor;
}
}
else
{
// Horizontal
sizeW = viewSizePrimary;
sizeH = viewOrthoSize;
originY = orthoPos;
if (dir === 1) {
originX = cursor;
cursor += sizeW;
} else {
cursor -= sizeW;
originX = cursor;
}
}
[view setFrame:CGRectMake(originX, originY, sizeW, sizeH)];
pendingSpacing = [self _spacingAfterView:view];
hasPlacedAny = true;
}
return cursor;
}
// MARK: -
// MARK: CPCoding
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
_orientation = [aCoder decodeIntForKey:@"CPStackViewOrientation"];
_alignment = [aCoder decodeIntForKey:@"CPStackViewAlignment"];
_distribution = [aCoder decodeIntForKey:@"CPStackViewDistribution"];
_spacing = [aCoder decodeFloatForKey:@"CPStackViewSpacing"];
_edgeInsets = [aCoder decodeObjectForKey:@"CPStackViewEdgeInsets"]; // Assuming CPEdgeInsets supports obj coding or manual decode
if (!_edgeInsets) _edgeInsets = CPEdgeInsetsMake(0,0,0,0);
_detachesHiddenViews = [aCoder decodeBoolForKey:@"CPStackViewDetachesHiddenViews"];
_viewsLeading = [aCoder decodeObjectForKey:@"CPStackViewViewsLeading"] || [];
_viewsCenter = [aCoder decodeObjectForKey:@"CPStackViewViewsCenter"] || [];
_viewsTrailing = [aCoder decodeObjectForKey:@"CPStackViewViewsTrailing"] || [];
// Rebuild arranged subviews cache
[self _rebuildArrangedSubviews];
_customSpacings = [aCoder decodeObjectForKey:@"CPStackViewCustomSpacings"] || [[CPMapTable alloc] init];
_visibilityPriorities = [[CPMapTable alloc] init]; // usually not persisted
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeInt:_orientation forKey:@"CPStackViewOrientation"];
[aCoder encodeInt:_alignment forKey:@"CPStackViewAlignment"];
[aCoder encodeInt:_distribution forKey:@"CPStackViewDistribution"];
[aCoder encodeFloat:_spacing forKey:@"CPStackViewSpacing"];
[aCoder encodeObject:_edgeInsets forKey:@"CPStackViewEdgeInsets"];
[aCoder encodeBool:_detachesHiddenViews forKey:@"CPStackViewDetachesHiddenViews"];
[aCoder encodeObject:_viewsLeading forKey:@"CPStackViewViewsLeading"];
[aCoder encodeObject:_viewsCenter forKey:@"CPStackViewViewsCenter"];
[aCoder encodeObject:_viewsTrailing forKey:@"CPStackViewViewsTrailing"];
[aCoder encodeObject:_customSpacings forKey:@"CPStackViewCustomSpacings"];
}
@end
+15 -14
View File
@@ -42,8 +42,8 @@
CPButton _buttonUp;
}
#pragma mark -
#pragma mark Initialization
// MARK: -
// MARK: Initialization
/*!
Initializes a CPStepper with given values.
@@ -147,8 +147,8 @@
[self _sizeToFit];
}
#pragma mark -
#pragma mark Superclass overrides
// MARK: -
// MARK: Superclass overrides
/*!
Set if the CPStepper is enabled or not.
@@ -188,12 +188,12 @@
[_buttonUp setFrame:upFrame];
[_buttonDown setFrame:downFrame];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPButtonStateBezelStyleRoundRect]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRoundRect]];
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRoundRect]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPButtonStateBezelStyleRoundRect]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled, CPButtonStateBezelStyleRoundRect]];
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted, CPButtonStateBezelStyleRoundRect]];
}
- (void)_sizeToFit
@@ -230,8 +230,8 @@
[super setDoubleValue:aValue];
}
#pragma mark -
#pragma mark Actions
// MARK: -
// MARK: Actions
/*! @ignore */
- (IBAction)_buttonDidClick:(id)aSender
@@ -266,8 +266,8 @@
}
#pragma mark -
#pragma mark Theming
// MARK: -
// MARK: Theming
+ (CPString)defaultThemeClass
{
@@ -323,6 +323,7 @@ var CPStepperMinValue = @"CPStepperMinValue",
_autorepeat = [aCoder decodeBoolForKey:CPStepperAutorepeat];
[self _init];
[self setEnabled:[self isEnabled]];
}
return self;
+55 -9
View File
@@ -24,11 +24,15 @@
@import "CGGeometry.j"
@import "CPPlatformString.j"
@import "CPFont.j"
@import "CPCompatibility.j"
var CPStringSizeWithFontInWidthCache = {};
var CPStringSizeWithFontInWidthCache = [],
CPStringSizeWithFontHeightCache = [],
CPStringSizeMeasuringContext;
CPStringSizeCachingEnabled = YES;
CPCanvasStringSizingIsFunctional = NO;
@implementation CPString (CPStringDrawing)
@@ -53,20 +57,62 @@ CPStringSizeCachingEnabled = YES;
return [self sizeWithFont:aFont inWidth:NULL];
}
+ (void) initialize
{
if ([self class] != [CPString class])
return;
#if PLATFORM(DOM)
if (CPFeatureIsCompatible(CPHTMLCanvasFeature))
{
if (!CPStringSizeMeasuringContext)
CPStringSizeMeasuringContext = CGBitmapGraphicsContextCreate();
// This is to make sure that Canvas based string sizing is functional before we use it.
// Unfortunately, as of today canvas sizing is not funtional any more. Neither in Chrome nor in FF
CPCanvasStringSizingIsFunctional = NO;
}
#endif
}
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
{
if (!CPStringSizeCachingEnabled)
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
var size;
var cacheKey = self + [aFont cssString] + aWidth,
size = CPStringSizeWithFontInWidthCache[cacheKey];
#if PLATFORM(DOM)
var sizeCacheForFont = CPStringSizeWithFontInWidthCache[self];
if (size === undefined)
{
if (sizeCacheForFont === undefined)
sizeCacheForFont = CPStringSizeWithFontInWidthCache[self] = [];
var cssString = [aFont cssString],
cacheKey = cssString + '_' + (aWidth ? aWidth : '0');
size = sizeCacheForFont[cacheKey];
if (size !== undefined && sizeCacheForFont.hasOwnProperty(cacheKey))
return CGSizeMakeCopy(size);
if (!CPCanvasStringSizingIsFunctional || aWidth)
size = [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
CPStringSizeWithFontInWidthCache[cacheKey] = size;
else
{
if (CPPlatformHasBug(CPTextSizingAlwaysNeedsSetFontBug) || CPStringSizeMeasuringContext.font !== cssString)
CPStringSizeMeasuringContext.font = cssString;
var fontHeight = CPStringSizeWithFontHeightCache[cssString];
if (fontHeight === undefined)
fontHeight = CPStringSizeWithFontHeightCache[cssString] = [aFont defaultLineHeightForFont];
size = CGSizeMake(CPStringSizeMeasuringContext.measureText(self).width, fontHeight);
}
sizeCacheForFont[cacheKey] = size;
#else
size = CGSizeMake(0, 0);
#endif
return CGSizeMakeCopy(size);
}
+583 -97
View File
@@ -34,6 +34,9 @@ CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
CPNoTabsNoBorder = 6; //Displays no tabs and no border.
@class _CPTabViewBox
@class _CPSegmentedControl
var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
CPTabViewShouldSelectTabViewItemSelector = 1 << 2,
CPTabViewWillSelectTabViewItemSelector = 1 << 3,
@@ -63,10 +66,11 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
{
CPArray _items;
CPSegmentedControl _tabs;
CPBox _box;
_CPSegmentedControl _tabs;
_CPTabViewBox _box;
CPView _placeholderView @accessors;
CPNumber _selectedIndex;
CPTabViewItem _selectedTabViewItem;
CPTabViewType _type;
CPFont _font;
@@ -75,13 +79,26 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
unsigned _delegateSelectors;
}
+ (CPString)defaultThemeClass
{
return @"tab-view";
}
+ (CPDictionary)themeAttributes
{
return @{
@"nib2cib-adjustment-frame": [CPNull null],
@"should-center-on-border": NO,
@"box-content-inset": CGInsetMakeZero()
};
}
- (id)initWithFrame:(CGRect)aFrame
{
if (self = [super initWithFrame:aFrame])
{
_items = [CPArray array];
[self _init];
_selectedTabViewItem = nil;
[self setTabViewType:CPTopTabsBezelBorder];
}
@@ -90,19 +107,30 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
- (void)_init
{
_selectedIndex = CPNotFound;
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
_tabs = [[_CPSegmentedControl alloc] initWithFrame:CGRectMakeZero()];
[_tabs setTabView:self];
[_tabs setHitTests:NO];
[_tabs setSegments:[CPArray array]];
[_tabs setAction:@selector(_reflectSelectedTab:)];
[_tabs setTarget:self];
var height = [_tabs valueForThemeAttribute:@"min-size"].height;
[_tabs setFrameSize:CGSizeMake(0, height)];
_box = [[CPBox alloc] initWithFrame:[self bounds]];
[self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
_box = [[_CPTabViewBox alloc] initWithFrame:[self bounds]];
[_box setTabView:self];
[_box setContentInset:[self currentValueForThemeAttribute:@"box-content-inset"]];
[_box setContentViewMargins:CGSizeMakeZero()];
[self addSubview:_box];
[self addSubview:_tabs];
_placeholderView = nil;
}
- (CPArray)items
{
return [_tabs segments];
}
// Adding and Removing Tabs
@@ -112,7 +140,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
{
[self insertTabViewItem:aTabViewItem atIndex:[_items count]];
[self insertTabViewItem:aTabViewItem atIndex:[self numberOfTabViewItems]];
}
/*!
@@ -122,15 +150,24 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPUInteger)anIndex
{
[_items insertObject:aTabViewItem atIndex:anIndex];
[self _insertTabViewItems:[aTabViewItem] atIndexes:[CPIndexSet indexSetWithIndex:anIndex] canUpdateSelectedTab:YES];
}
[self _updateItems];
[self _repositionTabs];
- (void)_insertTabViewItems:(CPArray)tabViewItems atIndexes:(CPIndexSet)indexes canUpdateSelectedTab:(BOOL)canUpdateSelectedTab
{
var prevItemsCount = [self numberOfTabViewItems];
[aTabViewItem _setTabView:self];
[_tabs insertSegments:tabViewItems atIndexes:indexes];
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
[self tileWithChangedItem:[tabViewItems firstObject]];
[self _reverseSetContent];
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
// Do not allow empty selection if selection bindings are not enabled.
if (prevItemsCount == 0 && [self numberOfTabViewItems] > 0 && ![self _isSelectionBinded] && canUpdateSelectedTab)
[self _selectTabViewItemAtIndex:0];
}
/*!
@@ -139,23 +176,39 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
{
var count = [_items count];
for (var i = 0; i < count; i++)
{
if ([_items objectAtIndex:i] === aTabViewItem)
{
[_items removeObjectAtIndex:i];
break;
}
}
var idx = [[self items] indexOfObjectIdenticalTo:aTabViewItem];
[self _updateItems];
[self _repositionTabs];
if (idx == CPNotFound)
return;
[_tabs removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndex:idx]];
[aTabViewItem _setTabView:nil];
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
[self tileWithChangedItem:nil];
[self _didRemoveTabViewItem:aTabViewItem atIndex:idx];
[self _reverseSetContent];
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
}
- (void)_didRemoveTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPInteger)idx
{
// If the selection is managed by bindings, let the binder do that.
if ([self _isSelectionBinded])
return;
if (_selectedTabViewItem == aTabViewItem)
{
var didSelect = NO;
if (idx > 0)
didSelect = [self selectTabViewItemAtIndex:idx - 1];
else if ([self numberOfTabViewItems] > 0)
didSelect = [self selectTabViewItemAtIndex:0];
if (didSelect == NO)
_selectedTabViewItem == nil;
}
}
// Accessing Tabs
@@ -166,7 +219,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
{
return [_items indexOfObjectIdenticalTo:aTabViewItem];
return [[self items] indexOfObjectIdenticalTo:aTabViewItem];
}
/*!
@@ -176,11 +229,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
{
for (var index = [_items count]; index >= 0; index--)
if ([[_items[index] identifier] isEqual:anIdentifier])
return index;
return CPNotFound;
return [[self items] indexOfObjectPassingTest:function(item, idx, stop)
{
return [[item identifier] isEqual:anIdentifier];
}];
}
/*!
@@ -189,7 +241,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (unsigned)numberOfTabViewItems
{
return [_items count];
return [[self items] count];
}
/*!
@@ -198,7 +250,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (CPTabViewItem)tabViewItemAtIndex:(CPUInteger)anIndex
{
return [_items objectAtIndex:anIndex];
return [[self items] objectAtIndex:anIndex];
}
/*!
@@ -207,7 +259,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (CPArray)tabViewItems
{
return [_items copy]; // Copy?
return [[self items] copy]; // Copy?
}
// Selecting a Tab
@@ -217,7 +269,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)selectFirstTabViewItem:(id)aSender
{
if ([_items count] === 0)
if ([self numberOfTabViewItems] === 0)
return; // throw?
[self selectTabViewItemAtIndex:0];
@@ -229,10 +281,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)selectLastTabViewItem:(id)aSender
{
if ([_items count] === 0)
if ([self numberOfTabViewItems] === 0)
return; // throw?
[self selectTabViewItemAtIndex:[_items count] - 1];
[self selectTabViewItemAtIndex:[self numberOfTabViewItems] - 1];
}
/*!
@@ -241,12 +293,12 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)selectNextTabViewItem:(id)aSender
{
if (_selectedIndex === CPNotFound)
if (_selectedTabViewItem == nil)
return;
var nextIndex = _selectedIndex + 1;
var nextIndex = [self indexOfTabViewItem:_selectedTabViewItem] + 1;
if (nextIndex === [_items count])
if (nextIndex === [self numberOfTabViewItems])
// does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:)
return;
@@ -259,10 +311,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (void)selectPreviousTabViewItem:(id)aSender
{
if (_selectedIndex === CPNotFound)
if (_selectedTabViewItem == nil)
return;
var previousIndex = _selectedIndex - 1;
var previousIndex = [self indexOfTabViewItem:_selectedTabViewItem] - 1;
if (previousIndex < 0)
return; // does nothing. See above.
@@ -285,36 +337,70 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
*/
- (BOOL)selectTabViewItemAtIndex:(CPUInteger)anIndex
{
if (anIndex === _selectedIndex)
return;
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
if (![self _selectTabViewItemAtIndex:anIndex])
return NO;
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
[_tabs selectSegmentWithTag:anIndex];
[self _setSelectedIndex:anIndex];
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
[self _reverseSetSelectedIndex];
return YES;
}
// Like selectTabViewItemAtIndex: but without bindings interaction
- (BOOL)_selectTabViewItemAtIndex:(CPUInteger)anIndex
{
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
if (aTabViewItem == _selectedTabViewItem)
return NO;
if (![self _sendDelegateShouldSelectTabViewItem:aTabViewItem])
return NO;
[self _sendDelegateWillSelectTabViewItem:aTabViewItem];
[_tabs setSelectedSegment:anIndex];
_selectedTabViewItem = aTabViewItem;
[self _loadTabViewItem:aTabViewItem];
[self _sendDelegateDidSelectTabViewItem:aTabViewItem];
return YES;
}
- (void)_loadTabViewItem:(CPTabViewItem)aTabViewItem
{
var controller = [aTabViewItem viewController];
if (controller != nil && ![controller isViewLoaded])
{
[controller loadViewWithCompletionHandler:function(view, error)
{
if (error != nil)
{
CPLog.warn("Could not load the view for item " + aTabViewItem + ". " + error);
}
else if (view != nil)
{
[aTabViewItem setView:view];
if ([self selectedTabViewItem] == aTabViewItem)
[self _displayItemView:view];
}
}];
}
else
{
[self _displayItemView:[aTabViewItem view]];
}
}
/*!
Returns the current item being displayed.
@return the tab view item currenly being displayed by the receiver
*/
- (CPTabViewItem)selectedTabViewItem
{
if (_selectedIndex != CPNotFound)
return [_items objectAtIndex:_selectedIndex];
return nil;
return _selectedTabViewItem;
}
// Modifying the font
@@ -375,6 +461,14 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
[self setNeedsLayout];
}
- (void)tileWithChangedItem:(CPTabViewItem)aTabViewItem
{
var segment = aTabViewItem ? [self indexOfTabViewItem:aTabViewItem] : 0;
[_tabs tileWithChangedSegment:segment];
[self setNeedsLayout];
}
- (void)layoutSubviews
{
// Even if CPTabView's autoresizesSubviews is NO, _tabs and _box has to be laid out.
@@ -387,12 +481,11 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
{
var aFrame = [self frame],
segmentedHeight = CGRectGetHeight([_tabs frame]),
origin = _type === CPTopTabsBezelBorder ? segmentedHeight / 2 : 0;
borderWidth = [self currentValueForThemeAttribute:@"should-center-on-border"] ? [_box borderWidth] : 0,
origin = _type === CPTopTabsBezelBorder ? (segmentedHeight - borderWidth) / 2 : 0;
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame), CGRectGetHeight(aFrame) - (segmentedHeight - borderWidth) / 2)];
[self _updateItems];
[self _repositionTabs];
}
}
@@ -453,10 +546,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
- (void)mouseDown:(CPEvent)anEvent
{
var segmentIndex = [_tabs testSegment:[_tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
if (segmentIndex != CPNotFound && [self selectTabViewItemAtIndex:segmentIndex])
[_tabs trackSegment:anEvent];
[_tabs trackSegment:anEvent];
}
- (void)_repositionTabs
@@ -470,30 +560,248 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
}
- (void)_setSelectedIndex:(CPNumber)index
- (void)_displayItemView:(CPView)aView
{
_selectedIndex = index;
[self _setContentViewFromItem:[_items objectAtIndex:_selectedIndex]];
[_box setContentView:aView];
}
- (void)_setContentViewFromItem:(CPTabViewItem)anItem
- (void)_reflectSelectedTab:(id)aSender
{
[_box setContentView:[anItem view]];
[self selectTabViewItemAtIndex:[_tabs selectedSegment]];
}
- (void)_updateItems
{
var count = [_items count];
[_tabs setSegmentCount:count];
// DELEGATE METHODS
for (var i = 0; i < count; i++)
- (BOOL)_sendDelegateShouldSelectTabViewItem:(CPTabViewItem)aTabViewItem
{
if (_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector)
return [_delegate tabView:self shouldSelectTabViewItem:aTabViewItem];
return YES;
}
- (void)_sendDelegateWillSelectTabViewItem:(CPTabViewItem)aTabViewItem
{
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
}
- (void)_sendDelegateDidSelectTabViewItem:(CPTabViewItem)aTabViewItem
{
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
}
- (void)_sendDelegateTabViewDidChangeNumberOfTabViewItems
{
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
}
@end
@implementation CPTabView (BindingSupport)
+ (Class)_binderClassForBinding:(CPString)aBinding
{
if (aBinding == CPContentBinding)
return [_CPTabViewContentBinder class];
else if (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding)
return [_CPTabViewSelectionBinder class];
return [super _binderClassForBinding:aBinding];
}
+ (BOOL)isBindingExclusive:(CPString)aBinding
{
return (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding);
}
- (void)_reverseSetContent
{
var theBinder = [self binderForBinding:CPContentBinding];
[theBinder reverseSetValueFor:@"items"];
}
- (void)_reverseSetSelectedIndex
{
var theBinder = [self binderForBinding:CPSelectionIndexesBinding];
if (theBinder != nil)
[theBinder reverseSetValueFor:@"selectionIndexes"];
else
{
[_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
[_tabs setTag:i forSegment:i];
theBinder = [self binderForBinding:CPSelectedIndexBinding];
[theBinder reverseSetValueFor:@"selectedIndex"];
}
}
- (CPBinder)binderForBinding:(CPString)aBinding
{
var cls = [[self class] _binderClassForBinding:aBinding];
return [cls getBinding:aBinding forObject:self];
}
- (BOOL)_isSelectionBinded
{
return [self binderForBinding:CPSelectionIndexesBinding] || [self binderForBinding:CPSelectedIndexBinding];
}
- (void)setItems:(CPArray)tabViewItems
{
if ([tabViewItems isEqualToArray:[_tabs segments]])
return;
[[self items] makeObjectsPerformSelector:@selector(_setTabView:) withObject:nil];
[_tabs setSegments:tabViewItems];
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
[self tileWithChangedItem:nil];
// Update the selection because setSegments: did remove all previous segments AND the selection.
[_tabs setSelectedSegment:[self indexOfTabViewItem:_selectedTabViewItem]];
// should we send delegate methods in bindings mode ?
//[self _delegateTabViewDidChangeNumberOfTabViewItems:self];
}
- (void)_deselectAll
{
[_tabs setSelectedSegment:-1];
_selectedTabViewItem = nil;
}
- (void)_displayPlaceholder:(CPString)aPlaceholder
{
if (_placeholderView == nil)
{
_placeholderView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[textField setTag:1000];
[textField setTextColor:[CPColor whiteColor]];
[textField setFont:[CPFont boldFontWithName:@"Geneva" size:18 italic:YES]];
[_placeholderView addSubview:textField];
}
if (_selectedIndex === CPNotFound)
[self selectFirstTabViewItem:self];
var textField = [_placeholderView viewWithTag:1000];
[textField setStringValue:aPlaceholder];
[textField sizeToFit];
var boxBounds = [_box bounds],
textFieldBounds = [textField bounds],
origin = CGPointMake(CGRectGetWidth(boxBounds)/2 - CGRectGetWidth(textFieldBounds)/2, CGRectGetHeight(boxBounds)/2 - CGRectGetHeight(textFieldBounds));
[textField setFrameOrigin:origin];
[self _displayItemView:_placeholderView];
}
// MARK: -
// MARK: Override
/*!
Enabled controls accept first mouse by default.
*/
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
{
return YES;
}
@end
var _CPTabViewContentBinderNull = @"NO CONTENT";
@implementation _CPTabViewContentBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:_CPTabViewContentBinderNull forMarker:CPNullMarker isDefault:YES];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
[_source setItems:@[]];
[_source _setPlaceholderView:aValue];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
[_source setItems:aValue];
}
- (id)valueForBinding:(CPString)aBinding
{
return [_source items];
}
@end
var _CPTabViewSelectionBinderMultipleValues = @"Multiple Selection",
_CPTabViewSelectionBinderNoSelection = @"No Selection";
@implementation _CPTabViewSelectionBinder : CPBinder
{
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
{
[super _updatePlaceholdersWithOptions:options];
[self _setPlaceholder:_CPTabViewSelectionBinderMultipleValues forMarker:CPMultipleValuesMarker isDefault:YES];
[self _setPlaceholder:_CPTabViewSelectionBinderNoSelection forMarker:CPNoSelectionMarker isDefault:YES];
}
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
{
if (aMarker == CPNoSelectionMarker || aMarker == CPNullMarker)
[_source _deselectAll];
[_source _displayPlaceholder:aValue];
}
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
{
if (aBinding == CPSelectionIndexesBinding)
{
if (aValue == nil || [aValue count] == 0)
{
[_source _deselectAll];
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
}
else if ([aValue count] > 1)
[_source _displayPlaceholder:_CPTabViewSelectionBinderMultipleValues];
else if ([aValue firstIndex] < [_source numberOfTabViewItems])
[_source _selectTabViewItemAtIndex:[aValue firstIndex]];
}
else if (aBinding == CPSelectedIndexBinding)
{
if (aValue == CPNotFound)
{
[_source _deselectAll];
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
}
else if (aValue < [_source numberOfTabViewItems])
[_source _selectTabViewItemAtIndex:aValue];
}
}
- (id)valueForBinding:(CPString)aBinding
{
if (aBinding == CPSelectionIndexesBinding)
{
var result = [CPIndexSet indexSet],
idx = [_source indexOfTabViewItem:[_source selectedTabViewItem]];
if (idx !== CPNotFound)
[result addIndex:idx];
return result;
}
else if (aBinding == CPSelectedIndexBinding)
return [_source indexOfTabViewItem:[_source selectedTabViewItem]];
}
@end
@@ -515,12 +823,13 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
_font = [aCoder decodeObjectForKey:CPTabViewFontKey];
[_tabs setFont:_font];
_items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
[_items makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
var items = [aCoder decodeObjectForKey:CPTabViewItemsKey] || [CPArray array];
[self _insertTabViewItems:items atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [items count])] canUpdateSelectedTab:NO];
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
self.selectOnAwake = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
_selectedTabViewItem = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
_type = [aCoder decodeIntForKey:CPTabViewTypeKey];
}
@@ -529,14 +838,23 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
- (void)awakeFromCib
{
[super awakeFromCib];
// This cannot be run in initWithCoder because it might call selectTabViewItem:, which is
// not safe to call before the views of the tab views items are fully decoded.
[self _updateItems];
if (self.selectOnAwake)
if (_selectedTabViewItem)
{
[self selectTabViewItem:self.selectOnAwake];
delete self.selectOnAwake;
var idx = [self indexOfTabViewItem:_selectedTabViewItem];
if (idx !== CPNotFound)
{
// Temporarily set the selected item to not selected.
// It allows the initial selection to be made correctly.
_selectedTabViewItem = nil;
[self selectTabViewItemAtIndex:idx];
}
}
var type = _type;
@@ -558,9 +876,7 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
[aCoder encodeObject:_items forKey:CPTabViewItemsKey];
var selected = [self selectedTabViewItem];
if (selected)
[aCoder encodeObject:selected forKey:CPTabViewSelectedItemKey];
[aCoder encodeConditionalObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
[aCoder encodeInt:_type forKey:CPTabViewTypeKey];
[aCoder encodeObject:_font forKey:CPTabViewFontKey];
@@ -569,3 +885,173 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
}
@end
// MARK: -
@implementation CPTabView (CSSTheming)
// MARK: Override
- (void)_setThemeIncludingDescendants:(CPTheme)aTheme
{
[self setTheme:aTheme];
[[self subviews] makeObjectsPerformSelector:@selector(_setThemeIncludingDescendants:) withObject:aTheme];
// Items must also perform this (without this, only the selected item does)
for (var i = 0, allItems = [self items], count = allItems.length; i < count; i++)
if (allItems[i] != _selectedTabViewItem)
[[allItems[i] view] _setThemeIncludingDescendants:aTheme];
}
@end
// MARK: -
@implementation _CPTabViewBox : CPBox
{
CPTabView _tabView @accessors(property=tabView);
CGInset _contentInset @accessors(property=contentInset);
}
// MARK: -
// MARK: Override
- (id)initWithFrame:(CGRect)aFrame
{
if (self = [super initWithFrame:aFrame])
{
[self setBoxType:CPBoxPrimary];
}
return self;
}
- (CPView)hitTest:(CGPoint)aPoint
{
// Here we check if we have clicked on the segmentedControl of the tabView or not
// If YES, the CPBox should not handle the click
var segmentIndex = [_tabView._tabs testSegment:[_tabView._tabs convertPoint:aPoint fromView:[self superview]]];
if (segmentIndex != CPNotFound)
return nil;
return [super hitTest:aPoint];
}
// Some CPBox overrides because CPTabView use of CPBox differs from legacy CPBox regarding layout.
- (void)setContentView:(CPView)aView
{
if (aView === _contentView)
return;
if (_contentInset)
[aView setFrame:CGRectInsetByInset([self bounds], _contentInset)];
[aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
// A nil contentView is allowed (tested in Cocoa 2013-02-22).
if (!aView)
[_contentView removeFromSuperview];
else if (_contentView)
[_boxView replaceSubview:_contentView with:aView];
else
[_boxView addSubview:aView];
_contentView = aView;
[self refreshDisplay];
}
- (void)sizeToFit
{
var offset = [self _titleHeightOffset],
size = [self frameSize];
[_boxView setFrame:CGRectMake(0, offset[1], size.width, size.height - offset[0])];
if (!_contentView)
return;
var boxSize = [_boxView frameSize];
if (_contentInset)
[_contentView setFrame:CGRectMake(_contentInset.left, _contentInset.top, boxSize.width - _contentInset.left - _contentInset.right, boxSize.height - _contentInset.top - _contentInset.bottom)];
}
@end
# pragma mark -
// This subclass of CPSegmentedControl implements specific behaviour needed by CPTabView, which
// differs in some ways from normal CPSegmentedControl habits :
//
// - all items are enabled
// - when you select a tab (mouse down, holding) and drag upon another tab, the second one reacts
// (showing a pushed state) and can then be selected by releasing the mouse over it.
// - a delegate has the opportunity to accept or not the selection of an item
@implementation _CPSegmentedControl : CPSegmentedControl
{
CPTabView _tabView @accessors(property=tabView);
}
- (void)trackSegment:(CPEvent)anEvent
{
var type = [anEvent type],
location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
currentSegment = [self testSegment:location];
switch (type)
{
case CPLeftMouseUp:
if (_trackingSegment === CPNotFound)
return;
if ((_trackingSegment !== _selectedSegment) &&
[_tabView _sendDelegateShouldSelectTabViewItem:[_tabView tabViewItemAtIndex:_trackingSegment]])
{
[self setSelected:YES forSegment:_trackingSegment];
_selectedSegment = _trackingSegment;
[self sendAction:[self action] to:[self target]];
}
[self drawSegmentBezel:_trackingSegment highlight:NO];
_trackingSegment = CPNotFound;
return;
case CPLeftMouseDown:
if (currentSegment > CPNotFound)
{
_trackingSegment = currentSegment;
[self drawSegmentBezel:_trackingSegment highlight:YES];
}
break;
case CPLeftMouseDragged:
if (_trackingSegment !== currentSegment)
{
if (_trackingSegment > CPNotFound)
[self drawSegmentBezel:_trackingSegment highlight:NO];
_trackingSegment = currentSegment;
if (_trackingSegment > CPNotFound)
[self drawSegmentBezel:_trackingSegment highlight:YES];
}
break;
}
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
}
@end
+102 -10
View File
@@ -24,6 +24,7 @@
@import "CPView.j"
@class CPTabView
@class CPViewController
/*
The tab is currently selected.
@@ -53,14 +54,34 @@ CPPressedTab = 2;
*/
@implementation CPTabViewItem : CPObject
{
id _identifier;
CPString _label;
id _identifier;
CPString _label;
CPInteger _tag @accessors(property=tag);
CPView _view;
CPView _auxiliaryView;
CPView _view;
CPView _auxiliaryView;
CPTabView _tabView;
unsigned _tabState; // Looks like it is not yet implemented
CPTabView _tabView;
unsigned _tabState; // Looks like it is not yet implemented
CPImage _image @accessors(property=image);
CPViewController _viewController @accessors(getter=viewController);
BOOL _enabled @accessors(property=enabled);
BOOL _selected @accessors(property=selected);
CGRect _tabRect @accessors(property=frame);
float _width @accessors(property=width);
}
/*
*/
+ (CPTabViewItem)tabViewItemWithViewController:(CPViewController)aViewController
{
var item = [[CPTabViewItem alloc] init];
[item setViewController:aViewController];
return item;
}
- (id)init
@@ -68,6 +89,19 @@ CPPressedTab = 2;
return [self initWithIdentifier:@""];
}
- (void)_init
{
_tag = 0;
_viewController = nil;
_image = nil;
_tabState = 0;
_tabView = nil;
_enabled = YES;
_selected = NO;
_tabRect = CGRectMakeZero();
_width = 0;
}
/*!
Initializes the tab view item with the specified identifier.
@return the initialized CPTabViewItem
@@ -76,8 +110,12 @@ CPPressedTab = 2;
{
self = [super init];
if (self)
_identifier = anIdentifier;
[self _init];
_identifier = anIdentifier;
_label = nil;
_view = nil;
//_auxiliaryView = nil;
return self;
}
@@ -89,8 +127,11 @@ CPPressedTab = 2;
*/
- (void)setLabel:(CPString)aLabel
{
if ([aLabel isEqualToString:_label])
return;
_label = aLabel;
[_tabView setNeedsLayout];
[_tabView tileWithChangedItem:self];
}
/*!
@@ -101,6 +142,28 @@ CPPressedTab = 2;
return _label;
}
// Working With Images
/*!
Sets the CPTabViewItem's image.
@param anImage the image for the item
*/
- (void)setImage:(CPImage)anImage
{
if ([anImage isEqual:_image])
return;
_image = anImage;
[_tabView tileWithChangedItem:self];
}
/*!
Returns the CPTabViewItem's image
*/
- (CPImage)image
{
return _image;
}
// Checking the Tab Display State
/*!
Returns the tab's current state.
@@ -140,7 +203,7 @@ CPPressedTab = 2;
_view = aView;
if ([_tabView selectedTabViewItem] == self)
[_tabView _setContentViewFromItem:self];
[_tabView _displayItemView:_view];
}
/*!
@@ -148,6 +211,9 @@ CPPressedTab = 2;
*/
- (CPView)view
{
if (!_view && _viewController)
return [_viewController view]; // The view controller loads here.
return _view;
}
@@ -186,10 +252,32 @@ CPPressedTab = 2;
_tabView = aView;
}
/*!
Sets the specified view controller for the tab view item.
@param aViewController an instance of CPViewController.
*/
- (void)setViewController:(CPViewController)aViewController
{
_viewController = aViewController;
var identifier = [aViewController cibName],
title = [_viewController title];
if (identifier)
_identifier = identifier;
if (title)
[self setLabel:title];
if ([_tabView selectedTabViewItem] == self)
[_tabView _loadTabViewItem:self];
}
@end
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
CPTabViewItemLabelKey = "CPTabViewItemLabelKey",
CPTabViewItemImageKey = "CPTabViewItemImageKey",
CPTabViewItemViewKey = "CPTabViewItemViewKey",
CPTabViewItemAuxViewKey = "CPTabViewItemAuxViewKey";
@@ -202,8 +290,11 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
if (self)
{
[self _init];
_identifier = [aCoder decodeObjectForKey:CPTabViewItemIdentifierKey];
_label = [aCoder decodeObjectForKey:CPTabViewItemLabelKey];
_image = [aCoder decodeObjectForKey:CPTabViewItemImageKey];
_view = [aCoder decodeObjectForKey:CPTabViewItemViewKey];
_auxiliaryView = [aCoder decodeObjectForKey:CPTabViewItemAuxViewKey];
@@ -216,6 +307,7 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
{
[aCoder encodeObject:_identifier forKey:CPTabViewItemIdentifierKey];
[aCoder encodeObject:_label forKey:CPTabViewItemLabelKey];
[aCoder encodeObject:_image forKey:CPTabViewItemImageKey];
[aCoder encodeObject:_view forKey:CPTabViewItemViewKey];
[aCoder encodeObject:_auxiliaryView forKey:CPTabViewItemAuxViewKey];
+84 -19
View File
@@ -180,7 +180,7 @@ CPTableColumnUserResizingMask = 1 << 1;
columns = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(index, [tableView._exposedColumns lastIndex] - index + 1)];
// FIXME: Would be faster with some sort of -setNeedsDisplayInColumns: that updates a dirtyTableColumnForDisplay cache; then marked columns would relayout their data views at display time.
[tableView _layoutDataViewsInRows:rows columns:columns];
[tableView _layoutViewsForRowIndexes:rows columnIndexes:columns];
[tableView tile];
if (!_disableResizingPosting)
@@ -287,7 +287,7 @@ CPTableColumnUserResizingMask = 1 << 1;
if (width < [self minWidth])
[self setMinWidth:width];
else if (width > [self maxWidth])
[self setMaxWidth:width]
[self setMaxWidth:width];
if (_width !== width)
[self setWidth:width];
@@ -393,8 +393,8 @@ CPTableColumnUserResizingMask = 1 << 1;
*/
- (void)setDataView:(CPView)aView
{
if (_dataView)
_dataViewData = nil;
if (_dataView === aView)
return;
[aView setThemeState:CPThemeStateTableDataView];
@@ -499,7 +499,7 @@ CPTableColumnUserResizingMask = 1 << 1;
*/
- (void)setHidden:(BOOL)shouldBeHidden
{
shouldBeHidden = !!shouldBeHidden
shouldBeHidden = !!shouldBeHidden;
if (_isHidden === shouldBeHidden)
return;
@@ -537,6 +537,14 @@ CPTableColumnUserResizingMask = 1 << 1;
return _headerToolTip;
}
/*!
@ignore
*/
- (CPString)description
{
return [CPString stringWithFormat:"<%@ 0x%@ identifier=%@>", [self className], [CPString stringWithHash:[self UID]], [self identifier]];
}
@end
@implementation CPTableColumnValueBinder : CPBinder
@@ -546,11 +554,22 @@ CPTableColumnUserResizingMask = 1 << 1;
- (void)setValueFor:(CPString)aBinding
{
var tableView = [_source tableView],
column = [[tableView tableColumns] indexOfObjectIdenticalTo:_source],
rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [tableView numberOfRows])],
columnIndexes = [CPIndexSet indexSetWithIndex:column];
newNumberOfRows = [tableView _numberOfRows];
[tableView reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
if ([tableView numberOfRows] == newNumberOfRows)
{
var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, newNumberOfRows)],
column = [[tableView tableColumns] indexOfObjectIdenticalTo:_source],
columnIndexes = [CPIndexSet indexSetWithIndex:column];
// Reloads objectValues only, not the views.
// FIXME: reload data for all rows or just rows intersecting exposed rows ?
[tableView _reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
}
else
{
[tableView reloadData];
}
}
- (CPSortDescriptor)_defaultSortDescriptorPrototype
@@ -574,7 +593,7 @@ CPTableColumnUserResizingMask = 1 << 1;
{
var options = [_info objectForKey:CPOptionsKey],
optionValue = [options objectForKey:CPCreatesSortDescriptorBindingOption];
return optionValue === nil ? YES : [optionValue boolValue];
return optionValue == nil ? YES : [optionValue boolValue];
}
@end
@@ -590,7 +609,7 @@ CPTableColumnUserResizingMask = 1 << 1;
}
/*!
Binds the receiver to an object.
Binds the receiver to an object. Note that unlike Cocoa, this works only *after* the receiver has been added to a \c CPTableView.
@param CPString aBinding - The binding you wish to make. Typically CPValueBinding.
@param id anObject - The object to bind the receiver to.
@@ -620,6 +639,53 @@ CPTableColumnUserResizingMask = 1 << 1;
}
}
/*!
@ignore
This method will return the object at a row in the first found CPArray in the key path
that is divided in a first and second part.
The first part is never a combined key path. The second part can be a combined key path.
If this optimization is not done we will create an array with the valueForKeyPath value on each row and then pick
the wanted value for the row and throw away all the other rows. It is much more effective to first
pick the row and then do the valueForKeyPath on the rest of the key path.
When the second part is depleated it will stop the search for a CPArray and return the
current object for the first part. The second part will then be nil.
The secondPartRef will always be updated with the rest of the key path that can be applied
to the returned object.
It will stop the search if the object is nil
*/
- (CPValueCoding)_firstObjectInArrayUsingKeyPathFirstPart:(CPString)firstPart secondPart:(CPStringRef)secondPartRef sourceObject:(CPValueCoding)source forRow:(unsigned)aRow
{
var firstValue = [source valueForKeyPath:firstPart];
if (firstValue == nil)
return firstValue;
if ([firstValue isKindOfClass:CPArray])
return [firstValue objectAtIndex:aRow];
var secondPart = @deref(secondPartRef);
if (secondPart == nil)
return firstValue;
var dotIndex = secondPart.indexOf(".");
if (dotIndex === CPNotFound)
{
firstPart = secondPart;
@deref(secondPartRef) = nil;
}
else
{
firstPart = secondPart.substring(0, dotIndex);
@deref(secondPartRef) = secondPart.substring(dotIndex + 1);
}
return [self _firstObjectInArrayUsingKeyPathFirstPart:firstPart secondPart:secondPartRef sourceObject:firstValue forRow:aRow];
}
/*!
@ignore
*/
@@ -636,7 +702,7 @@ CPTableColumnUserResizingMask = 1 << 1;
bindingInfo = binding._info,
destination = [bindingInfo objectForKey:CPObservedObjectKey],
keyPath = [bindingInfo objectForKey:CPObservedKeyPathKey],
dotIndex = keyPath.lastIndexOf("."),
dotIndex = keyPath.indexOf("."),
value;
if (dotIndex === CPNotFound)
@@ -651,17 +717,16 @@ CPTableColumnUserResizingMask = 1 << 1;
The optimization is to get the array and access the value directly. This
turns the operation into a single access regardless of how long the model
array is.
array is or how long the key path is.
*/
var firstPart = keyPath.substring(0, dotIndex),
secondPart = keyPath.substring(dotIndex + 1),
firstValue = [destination valueForKeyPath:firstPart];
secondPart = keyPath.substring(dotIndex + 1);
if ([firstValue isKindOfClass:CPArray])
value = [[firstValue objectAtIndex:aRow] valueForKeyPath:secondPart];
else
value = [[firstValue valueForKeyPath:secondPart] objectAtIndex:aRow];
value = [self _firstObjectInArrayUsingKeyPathFirstPart:firstPart secondPart:@ref(secondPart) sourceObject:destination forRow:aRow];
if (secondPart != nil)
value = [value valueForKeyPath:secondPart];
}
value = [binding transformValue:value withOptions:[bindingInfo objectForKey:CPOptionsKey]];
+414 -365
View File
File diff suppressed because it is too large Load Diff
+910 -676
View File
File diff suppressed because it is too large Load Diff
+337 -1
View File
@@ -5,6 +5,13 @@
* Created by Alexander Ljungberg.
* Copyright 2010, WireLoad, LLC.
*
* additions from
*
* Daniel Boehringer on 8/02/2014.
* Copyright Daniel Boehringer on 8/02/2014.
*
*
*
* 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
@@ -20,6 +27,30 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/Foundation.j>
@import "CPPasteboard.j"
@import "CPView.j"
@global CPStringPboardType
@global _CPSmartPboardType
@class CPAttributedString
@class _CPRTFParser
@protocol CPTextDelegate <CPObject>
@optional
- (BOOL)textShouldBeginEditing:(CPText)aTextObject;
- (BOOL)textShouldEndEditing:(CPText)aTextObject;
- (void)textDidBeginEditing:(CPNotification)aNotification;
- (void)textDidChange:(CPNotification)aNotification;
- (void)textDidEndEditing:(CPNotification)aNotification;
@end
CPParagraphSeparatorCharacter = 0x2029;
CPLineSeparatorCharacter = 0x2028;
CPEnterCharacter = "\u0003";
CPBackspaceCharacter = "\u0008";
CPTabCharacter = "\u0009";
@@ -29,6 +60,7 @@ CPCarriageReturnCharacter = "\u000d";
CPBackTabCharacter = "\u0019";
CPDeleteCharacter = "\u007f";
@typedef CPTextMovement
CPIllegalTextMovement = 0;
CPOtherTextMovement = 0;
CPReturnTextMovement = 16;
@@ -38,4 +70,308 @@ CPLeftTextMovement = 19;
CPRightTextMovement = 20;
CPUpTextMovement = 21;
CPDownTextMovement = 22;
CPCancelTextMovement = 23;
CPCancelTextMovement = 23;
@typedef CPWritingDirection
CPWritingDirectionNatural = -1;
CPWritingDirectionLeftToRight = 0;
CPWritingDirectionRightToLeft = 1;
@typedef CPTextAlignment
CPLeftTextAlignment = 0;
CPRightTextAlignment = 1;
CPCenterTextAlignment = 2;
CPJustifiedTextAlignment = 3;
CPNaturalTextAlignment = 4;
@typedef CPUnderlineStyle
CPUnderlineStyleNone = 0;
CPUnderlineStyleSingle = 1;
CPUnderlineStyleThick = 2;
CPUnderlineStyleDouble = 3;
CPUnderlineStylePatternSolid = 4;
CPUnderlineStylePatternDot = 5;
CPUnderlineStylePatternDash = 6;
CPUnderlineStylePatternDashDot = 7;
CPUnderlineStylePatternDashDotDot = 8;
CPUnderlineStyleByWord = 9;
/*
CPText notifications
*/
CPTextDidBeginEditingNotification = @"CPTextDidBeginEditingNotification";
CPTextDidChangeNotification = @"CPTextDidChangeNotification";
CPTextDidEndEditingNotification = @"CPTextDidEndEditingNotification";
/*
CPTextView Notifications
*/
CPTextViewDidChangeSelectionNotification = @"CPTextViewDidChangeSelectionNotification";
CPTextViewDidChangeTypingAttributesNotification = @"CPTextViewDidChangeTypingAttributesNotification";
/*
FIXME: move these to CPAttributed string
Make use of attributed keys in AppKit
*/
CPFontAttributeName = @"CPFontAttributeName";
CPForegroundColorAttributeName = @"CPForegroundColorAttributeName";
CPBackgroundColorAttributeName = @"CPBackgroundColorAttributeName";
CPShadowAttributeName = @"CPShadowAttributeName";
CPUnderlineStyleAttributeName = @"CPUnderlineStyleAttributeName";
CPSuperscriptAttributeName = @"CPSuperscriptAttributeName";
CPBaselineOffsetAttributeName = @"CPBaselineOffsetAttributeName";
CPAttachmentAttributeName = @"CPAttachmentAttributeName";
CPLigatureAttributeName = @"CPLigatureAttributeName";
CPKernAttributeName = @"CPKernAttributeName";
@implementation CPText : CPView
{
BOOL _isEditable @accessors(getter=isEditable, setter=setEditable:);
BOOL _isSelectable @accessors(getter=isSelectable, setter=setSelectable:);
BOOL _isRichText @accessors(getter=isRichText, setter=setRichText:);
}
- (void)setSelectable:(BOOL)flag
{
[self willChangeValueForKey:@"selectable"];
_isSelectable = flag;
[self didChangeValueForKey:@"selectable"];
if (!flag)
[self setEditable:flag];
}
- (void)setEditable:(BOOL)flag
{
[self willChangeValueForKey:@"editable"];
_isEditable = flag;
[self didChangeValueForKey:@"editable"];
if (flag)
[self setSelectable:flag];
}
- (void)changeFont:(id)sender
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)copy:(id)sender
{
var selectedRange = [self selectedRange];
if (selectedRange.length < 1)
return;
var pasteboard = [CPPasteboard generalPasteboard];
// put plain representation on the pasteboad unconditionally
[pasteboard declareTypes:[CPStringPboardType] owner:nil];
[pasteboard setString:[[self stringValue] substringWithRange:selectedRange] forType:CPStringPboardType];
}
- (id)_plainStringForPasting
{
return [[CPPasteboard generalPasteboard] stringForType:CPStringPboardType];
}
- (id)_stringForPasting
{
var pasteboard = [CPPasteboard generalPasteboard],
dataForPasting = [pasteboard stringForType:CPRTFPboardType],
stringForPasting = [pasteboard stringForType:CPStringPboardType],
attributedStringData = [pasteboard stringForType:_CPASPboardType];
if ([self isRichText] && attributedStringData)
return [CPKeyedUnarchiver unarchiveObjectWithData:[CPData dataWithRawString:attributedStringData]];
if (dataForPasting || [stringForPasting hasPrefix:"{\\rtf1\\ansi"])
stringForPasting = [[_CPRTFParser new] parseRTF:dataForPasting ? dataForPasting : stringForPasting];
if (![self isRichText] && [stringForPasting isKindOfClass:[CPAttributedString class]])
stringForPasting = stringForPasting._string;
return stringForPasting;
}
- (BOOL)_shouldUseSmartPasting
{
return parseInt([[CPPasteboard generalPasteboard] stringForType:_CPSmartPboardType], 10) > 0 || NO;
}
- (void)paste:(id)sender
{
var stringForPasting = [self _stringForPasting];
if (stringForPasting)
[self insertText:stringForPasting];
}
- (void)copyFont:(id)sender
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)delete:(id)sender
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (CPFont)font:(CPFont)aFont
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return nil;
}
- (BOOL)isHorizontallyResizable
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return NO;
}
- (BOOL)isRulerVisible
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return NO;
}
- (BOOL)isVerticallyResizable
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return NO;
}
- (CGSize)maxSize
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return CGSizeMake(0,0);
}
- (CGSize)minSize
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return CGSizeMake(0,0);
}
- (void)pasteFont:(id)sender
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)replaceCharactersInRange:(CPRange)aRange withString:(CPString)aString
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)scrollRangeToVisible:(CPRange)aRange
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)selectedAll:(id)sender
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (CPRange)selectedRange
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return CPMakeRange(CPNotFound, 0);
}
- (void)setFont:(CPFont)aFont
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)setFont:(CPFont)aFont range:(CPRange)aRange
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)setHorizontallyResizable:(BOOL)flag
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)setMaxSize:(CGSize)aSize
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)setMinSize:(CGSize)aSize
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)setString:(CPString)aString
{
[self replaceCharactersInRange:CPMakeRange(0, [[self string] length]) withString:aString];
}
- (void)setUsesFontPanel:(BOOL)flag
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)setVerticallyResizable:(BOOL)flag
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (CPString)string
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return nil;
}
- (void)underline:(id)sender
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (BOOL)usesFontPanel
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
return NO;
}
@end
var CPTextViewIsEditableKey = @"CPTextViewIsEditableKey",
CPTextViewIsSelectableKey = @"CPTextViewIsSelectableKey",
CPTextViewIsRichTextKey = @"CPTextViewIsRichTextKey";
@implementation CPText (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super initWithCoder:aCoder];
if (self)
{
[self setSelectable:[aCoder decodeBoolForKey:CPTextViewIsSelectableKey]];
[self setEditable:[aCoder decodeBoolForKey:CPTextViewIsEditableKey]];
[self setRichText:[aCoder decodeBoolForKey:CPTextViewIsRichTextKey]];
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeBool:_isEditable forKey:CPTextViewIsEditableKey];
[aCoder encodeBool:_isSelectable forKey:CPTextViewIsSelectableKey];
[aCoder encodeBool:_isRichText forKey:CPTextViewIsRichTextKey];
}
@end
+444 -93
View File
@@ -28,11 +28,13 @@
@import "_CPImageAndTextView.j"
@class CPPasteboard
@class CPScrollView
@global CPApp
@global CPStringPboardType
@global CPCursor
@global document
@protocol CPTextFieldDelegate <CPControlTextEditingDelegate>
@@ -65,11 +67,15 @@ var CPTextFieldDOMCurrentElement = nil,
CPTextFieldCachedSelectStartFunction = nil,
CPTextFieldCachedDragFunction = nil,
CPTextFieldBlurHandler = nil,
CPTextFieldInputFunction = nil;
CPTextFieldInputFunction = nil,
CPTexFieldCurrentCSSSelectableField = nil,
CPTextFieldLastValidationFailureEvent = nil,
CPTextFieldLastValidationFailureString = nil,
CPTextFieldLastValidationFailureField = nil,
CPTextFieldLastValidationFailureResult = NO;
var CPSecureTextFieldCharacter = "\u2022";
function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resigning, didBlurRef)
{
if (owner && domElement != inputElement.parentNode)
@@ -77,20 +83,16 @@ function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resig
var ownerWindow = [owner window];
if (!resigning && [ownerWindow isKeyWindow])
if (!resigning && [ownerWindow isKeyWindow] && [ownerWindow firstResponder] === owner)
{
/*
Browsers blur text fields when a click occurs anywhere outside the text field. That is normal for browsers, but in Cocoa the key view retains focus unless the click target accepts first responder. So if we lost focus but were not told to resign and our window is still key, restore focus,
but only if the text field is completely within the browser window. If we restore focus when it
is off screen, the entire body scrolls out of our control.
Previously we had code here which would force the input to regain focus if the input lost focus without the CPTextField having actually lost first responder status. This typically happened because the user clicked away from the input in the browser, without clicking on something that could become the first responder. In the browser, clicking outside of an input blurs it, but in Cocoa and Cappuccino it does not (unless you actually click something that will become the first responder).
That refocusing code has now been removed because we simply prevent the default action on clicks in the browser instead, which combined with the fix in 58d5d7d7, successfully prevents unintentional focus loss in (at least) Safari 9.1.1, Chrome 51 and Safari for iOS 9.3 when you click outside of a text field.
Now we can still lose focus unexpectedly: this is when the 'done' button is tapped on the virtual keyboard of a mobile device. In this case we actually do want to resign first responder status, because that is what the done button should do (and if we did not the keyboard would go away and then immediately come back which looks dumb and isn't what the user wanted).
*/
if ([owner _isWithinUsablePlatformRect])
{
window.setTimeout(function()
{
inputElement.focus();
}, 0.0);
}
[ownerWindow makeFirstResponder:nil];
}
CPTextFieldHandleBlur(anEvent, @ref(owner));
@@ -173,7 +175,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
+ (CPTextField)textFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth theme:(CPTheme)aTheme
{
var textField = [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, aWidth, 29.0)];
var minSize = aTheme ? [aTheme valueForAttributeWithName:@"min-size" forClass:CPTextField] : CGSizeMake(0,0),
textField = [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, aWidth, minSize.height)];
[textField setTheme:aTheme];
[textField setStringValue:aStringValue];
@@ -194,7 +197,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
+ (CPTextField)roundedTextFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth theme:(CPTheme)aTheme
{
var textField = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, aWidth, 29.0)];
var minSize = aTheme ? [aTheme valueForAttributeWithName:@"min-size" forClass:CPTextField] : CGSizeMake(0,0),
textField = [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, aWidth, minSize.height)];
[textField setTheme:aTheme];
[textField setStringValue:aStringValue];
@@ -235,12 +239,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
@"bezel-inset": CGInsetMakeZero(),
@"content-inset": CGInsetMake(1.0, 0.0, 0.0, 0.0),
@"bezel-color": [CPNull null],
@"min-size": CGSizeMake(0, 29),
@"background-inset": CGInsetMakeZero()
};
}
#pragma mark -
#pragma mark Control Size
// MARK: -
// MARK: Control Size
- (void)setControlSize:(CPControlSize)aControlSize
{
@@ -251,7 +257,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
#pragma mark -
// MARK: -
#if PLATFORM(DOM)
- (DOMElement)_inputElement
@@ -330,12 +336,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
characters:nil
charactersIgnoringModifiers:nil
isARepeat:NO
keyCode:nil];
keyCode:nil
isActionKey:NO];
[CPTextFieldInputOwner keyUp:cappEvent];
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
}
};
CPTextFieldDOMPasswordInputElement.oninput = CPTextFieldInputFunction;
CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurHandler;
@@ -369,14 +376,12 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self setPlaceholderString:@""];
_sendActionOn = CPKeyUpMask | CPKeyDownMask;
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
}
return self;
}
#pragma mark Controlling Editability and Selectability
// MARK: Controlling Editability and Selectability
/*!
Sets whether or not the receiver text field can be edited. If NO, any
@@ -397,7 +402,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
else
[self unsetThemeState:CPThemeStateEditable];
// We only allow first responder status if the field is enable, and editable or selectable.
// We only allow first responder status if the field is enabled, and editable or selectable.
if (!(shouldBeEditable && ![self isSelectable]) && [[self window] firstResponder] === self)
[[self window] makeFirstResponder:nil];
@@ -405,6 +410,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self setThemeState:CPThemeStateEditable];
else
[self unsetThemeState:CPThemeStateEditable];
[self updateTrackingAreas];
}
/*!
@@ -426,6 +433,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
// We only allow first responder status if the field is enabled.
if (!shouldBeEnabled && [[self window] firstResponder] === self)
[[self window] makeFirstResponder:nil];
[self updateTrackingAreas];
}
/*!
@@ -435,6 +444,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)setSelectable:(BOOL)aFlag
{
_isSelectable = aFlag;
[self updateTrackingAreas];
}
/*!
@@ -555,9 +566,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
Sets the background color, which is shown for non-bezeled text fields with drawsBackground set to YES
@param aColor The background color
*/
- (void)setTextFieldBackgroundColor:(CPColor)aColor
- (void)setBackgroundColor:(CPColor)aColor
{
if (_textFieldBackgroundColor == aColor)
if (_backgroundColor == aColor)
return;
_textFieldBackgroundColor = aColor;
@@ -569,7 +580,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
/*!
Returns the background color.
*/
- (CPColor)textFieldBackgroundColor
- (CPColor)backgroundColor
{
return _textFieldBackgroundColor;
}
@@ -643,6 +654,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
_stringValue = [self stringValue];
#if PLATFORM(DOM)
[self _setCSSStyleForInputElement];
@@ -664,30 +676,86 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
CPTextFieldInputOwner = self;
window.setTimeout(function()
[[CPRunLoop mainRunLoop] performBlock:function()
{
/*
setTimeout handlers are not guaranteed to fire in the order they were initiated. This can cause a race condition when several windows with text fields are opened quickly, resulting in several instances of this timeout function being fired, perhaps out of order. So we have to check that by the time this function is fired, CPTextFieldInputOwner has not been changed to another text field in the meantime.
*/
if (CPTextFieldInputOwner !== self)
return;
// This will prevent to jump to the focused element
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
element.focus();
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
// Select the text if the textfield became first responder through keyboard interaction
if (!_willBecomeFirstResponderByClick)
{
[self _selectText:self immediately:YES];
}
else
{
var x = [self convertPointFromBase:[[CPApp currentEvent] locationInWindow]].x,
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
text = [self stringValue],
font = [self font];
switch ([self alignment]) {
case CPCenterTextAlignment:
var contentWidth = [self bounds].size.width - contentInset.left - contentInset.right,
textWidth = [text sizeWithFont:font].width;
x -= (contentWidth - textWidth) / 2 + contentInset.left;
break;
case CPRightTextAlignment:
var contentWidth = [self bounds].size.width - contentInset.left - contentInset.right,
textWidth = [text sizeWithFont:font].width;
x -= (contentWidth - textWidth) + contentInset.left;
break;
default: // CPLeftTextAlignment, CPJustifiedTextAlignment, CPNaturalTextAlignment
x -= contentInset.left;
break;
}
var position = [CPPlatformString charPositionOfString:text withFont:font forPoint:CGPointMake(x, 0)];
[self setSelectedRange:CPMakeRange(position, 0)];
}
_willBecomeFirstResponderByClick = NO;
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
}, 0.0);
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
#endif
return YES;
}
/*!
Set the selection css style for the DOM element of the textField
@ignore
*/
- (void)_setEnableCSSSelection:(BOOL)shouldEnable
{
#if PLATFORM (DOM)
if (CPTexFieldCurrentCSSSelectableField)
CPTexFieldCurrentCSSSelectableField._DOMElement.style[CPBrowserStyleProperty(@"user-select")] = @"none";
CPTexFieldCurrentCSSSelectableField = self;
_DOMElement.style[CPBrowserStyleProperty(@"user-select")] = shouldEnable ? @"text" : @"none";
#endif
}
/*!
Set the css style for the input element of the textField
@ignore
@@ -697,7 +765,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
#if PLATFORM(DOM)
var element = [self _inputElement],
font = [self currentValueForThemeAttribute:@"font"],
font = [self font],
lineHeight = [font defaultLineHeightForFont],
contentRect = [self contentRectForBounds:[self bounds]],
verticalAlign = [self currentValueForThemeAttribute:"vertical-alignment"],
@@ -741,6 +809,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
element.style.textAlign = "right";
break;
case CPNaturalTextAlignment:
element.style.textAlign = "";
break;
default:
element.style.textAlign = "left";
}
@@ -750,7 +822,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
element.style.zIndex = 1000;
element.style.top = topPoint;
element.style.lineHeight = ROUND(lineHeight) + "px";
element.style.height = isTextArea ? CGRectGetHeight(contentRect) + "px" : ROUND(lineHeight) + "px";;
element.style.height = isTextArea ? CGRectGetHeight(contentRect) + "px" : ROUND(lineHeight) + "px";
element.style.width = CGRectGetWidth(contentRect) + "px";
element.style.left = left + "px";
element.style.verticalAlign = "top";
@@ -783,7 +855,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
// even if the value has not changed.
if ([self _valueIsValid:newValue] === NO)
{
// This will prevent to jump to the focused element
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
element.focus();
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
return NO;
}
}
@@ -792,9 +870,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
// When we are no longer the first responder we don't worry about the key status of our window anymore.
[self _setObserveWindowKeyNotifications:NO];
[self _resignFirstKeyResponder];
if ([[self window] isKeyWindow])
[self _resignFirstKeyResponder];
_isEditing = NO;
if ([self isEditable])
{
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:@{"CPTextMovement": [self _currentTextMovement]}]];
@@ -832,12 +912,18 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (!CPTextFieldInputDidBlur)
CPTextFieldBlurHandler();
CPTextFieldInputDidBlur = NO;
CPTextFieldInputResigning = NO;
if (element.parentNode == _DOMElement)
element.parentNode.removeChild(element);
// Previosly, we unflagged CPTextFieldInputDidBlur and CPTextFieldInputResigning before
// the call to removeChild. This resulted in DOM exceptions in Chrome under certain conditions.
// See https://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node
// for why we need to unflag CPTextFieldInputDidBlur and CPTextFieldInputResigning
// only after removing the element.
CPTextFieldInputDidBlur = NO;
CPTextFieldInputResigning = NO;
CPTextFieldInputIsActive = NO;
if (document.attachEvent)
@@ -915,7 +1001,26 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
var acceptInvalidValue = NO;
if (_implementedDelegateMethods & CPTextFieldDelegate_control_didFailToFormatString_errorDescription_)
acceptInvalidValue = [_delegate control:self didFailToFormatString:aValue errorDescription:error];
{
var currentEvent = [CPApp currentEvent];
if (currentEvent &&
CPTextFieldLastValidationFailureField === self &&
CPTextFieldLastValidationFailureString === aValue &&
CPTextFieldLastValidationFailureEvent === currentEvent)
{
acceptInvalidValue = CPTextFieldLastValidationFailureResult;
}
else
{
acceptInvalidValue = [_delegate control:self didFailToFormatString:aValue errorDescription:error];
CPTextFieldLastValidationFailureField = self;
CPTextFieldLastValidationFailureString = aValue;
CPTextFieldLastValidationFailureEvent = currentEvent;
CPTextFieldLastValidationFailureResult = acceptInvalidValue;
}
}
if (acceptInvalidValue === NO)
return NO;
@@ -954,14 +1059,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
}
- (void)mouseMoved:(CPEvent)anEvent
{
[super mouseMoved:anEvent];
[self _updateCursorForEvent:anEvent];
}
- (void)mouseDown:(CPEvent)anEvent
{
if (![self isEnabled])
return [[self nextResponder] mouseDown:anEvent];
// Don't track! (ever?)
if ([self isEditable] && [self isEnabled])
{
@@ -970,6 +1072,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
else if ([self isSelectable])
{
[self _setEnableCSSSelection:YES];
if (document.attachEvent)
{
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
@@ -995,7 +1098,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
CPTextFieldCachedSelectStartFunction = nil
CPTextFieldCachedSelectStartFunction = nil;
CPTextFieldCachedDragFunction = nil;
}
@@ -1011,6 +1114,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
}
- (void)rightMouseDown:(CPEvent)anEvent
{
if ([self menuForEvent:anEvent] || [[self nextResponder] isKindOfClass:CPView])
[super rightMouseDown:anEvent];
else
[[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
}
- (void)mouseDragged:(CPEvent)anEvent
{
if (![self isEnabled] || !([self isSelectable] || [self isEditable]))
@@ -1039,9 +1150,16 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)keyDown:(CPEvent)anEvent
{
if (!([self isEnabled] && [self isEditable]))
// Has to be enabled, and it also has to be editable or selectable.
if (![self isEnabled] || !([self isEditable] || [self isSelectable]))
return;
if ([self isEditable] && !_isEditing)
{
_isEditing = YES;
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
}
// CPTextField uses an HTML input element to take the input so we need to
// propagate the dom event so the element is updated. This has to be done
// before interpretKeyEvents: though so individual commands have a chance
@@ -1161,7 +1279,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)textDidFocus:(CPNotification)note
{
// this looks to prevent false propagation of notifications for other objects
if ([note object] != self)
if ([note object] !== self)
return;
if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidFocus_)
@@ -1190,7 +1308,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return;
if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidBeginEditing_)
[_delegate controlTextDidBeginEditing:[[CPNotification alloc] initWithName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}]]
[_delegate controlTextDidBeginEditing:[[CPNotification alloc] initWithName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}]];
[super textDidBeginEditing:note];
}
@@ -1207,27 +1325,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[_delegate controlTextDidEndEditing:note];
}
- (void)_updateCursorForEvent:(CPEvent)anEvent
{
var frame = CGRectMakeCopy([self frame]),
contentInset = [self currentValueForThemeAttribute:@"content-inset"];
frame = [[self superview] convertRectToBase:CGRectInsetByInset(frame, contentInset)];
if ([self isEnabled] && ([self isSelectable] || [self isEditable]) && CGRectContainsPoint(frame, [anEvent locationInWindow]))
{
#if PLATFORM(DOM)
self._DOMElement.style.cursor = "text";
#endif
}
else
{
#if PLATFORM(DOM)
self._DOMElement.style.cursor = "default";
#endif
}
}
/*!
Returns the string in the text field.
*/
@@ -1299,7 +1396,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
// If there is a formatter, make sure the object value can be formatted successfully
var formattedString = [self hasThemeState:CPThemeStateEditing] ? [formatter editingStringForObjectValue:aValue] : [formatter stringForObjectValue:aValue];
if (formattedString === nil)
if (formattedString == nil)
{
var value = nil;
@@ -1309,7 +1406,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
value = undefined;
[super setObjectValue:value];
_stringValue = (value === nil || value === undefined) ? @"" : String(value);
_stringValue = (value == nil) ? @"" : String(value);
}
else
_stringValue = formattedString;
@@ -1396,7 +1493,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
lineBreakMode = [self lineBreakMode],
text = (_stringValue || @" "),
textSize = CGSizeMakeCopy(frameSize),
font = [self currentValueForThemeAttribute:@"font"];
font = [self font];
textSize.width -= contentInset.left + contentInset.right;
textSize.height -= contentInset.top + contentInset.bottom;
@@ -1463,9 +1560,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (immediately)
element.select();
else
window.setTimeout(function() { element.select(); }, 0);
[[CPRunLoop mainRunLoop] performBlock:function(){ element.select(); } argument:nil order:0 modes:[CPDefaultRunLoopMode]];
}
else if (wind !== nil && [wind makeFirstResponder:self])
else if (wind != nil && [wind makeFirstResponder:self])
[self _selectText:sender immediately:immediately];
}
else
@@ -1475,7 +1572,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
#else
// Even if we can't actually select the text we need to preserve the first
// responder side effect.
if (wind !== nil && [wind firstResponder] !== self)
if (wind != nil && [wind firstResponder] !== self)
[wind makeFirstResponder:self];
#endif
}
@@ -1747,7 +1844,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self _didEdit];
}
#pragma mark Setting the Delegate
// MARK: Setting the Delegate
- (void)setDelegate:(id <CPTextFieldDelegate>)aDelegate
{
@@ -1758,7 +1855,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
_implementedDelegateMethods = 0;
if ([_delegate respondsToSelector:@selector(control:didFailToFormatString:errorDescription:)])
_implementedDelegateMethods |= CPTextFieldDelegate_control_didFailToFormatString_errorDescription_
_implementedDelegateMethods |= CPTextFieldDelegate_control_didFailToFormatString_errorDescription_;
if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)])
_implementedDelegateMethods |= CPTextFieldDelegate_controlTextDidBeginEditing_;
@@ -1800,6 +1897,12 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (aName === "bezel-view")
return [self bezelRectForBounds:[self bounds]];
else if (aName === "background-view")
{
var backgroundInset = [self currentValueForThemeAttribute:@"background-inset"];
return CGRectInsetByInset([self bounds], backgroundInset);
}
else if (aName === "content-view")
return [self contentRectForBounds:[self bounds]];
@@ -1816,6 +1919,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return view;
}
else if (aName === "background-view")
{
var view = [[CPView alloc] initWithFrame:CGRectMakeZero()];
[view setHitTests:NO];
return view;
}
else
{
var view = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
@@ -1830,16 +1941,37 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
- (void)layoutSubviews
{
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
var bezelColor = [self currentValueForThemeAttribute:@"bezel-color"];
if ([bezelColor isCSSBased])
{
// CSS Styling
// We don't need bezelView as we apply CSS styling directly on the text field view itself
if (bezelView)
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
// We need to call [super setBackgroundColor:] as we have redefined it here
[super setBackgroundColor:bezelColor];
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:nil],
backgroundView = [self layoutEphemeralSubviewNamed:@"background-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
[backgroundView setBackgroundColor:(_drawsBackground ? _textFieldBackgroundColor : [CPColor clearColor])];
}
else
{
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
positioned:CPWindowBelow
relativeToEphemeralSubviewNamed:@"content-view"];
[bezelView setBackgroundColor:bezelColor];
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
positioned:CPWindowAbove
relativeToEphemeralSubviewNamed:@"bezel-view"];
}
if (contentView)
{
@@ -1860,7 +1992,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[contentView setText:string];
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
[contentView setFont:[self font]];
[contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]];
[contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]];
[contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
@@ -1872,6 +2004,43 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
[self _setCSSStyleForInputElement];
}
// MARK: Overrides
/*!
Sets the font of the receiver.
@param aFont - A CPFont object.
*/
- (void)setFont:(CPFont)aFont
{
if ([self currentValueForThemeAttribute:@"font"] === aFont)
return;
// Apply the font to the default/normal state
[self setValue:aFont forThemeAttribute:@"font"];
// Apply to standard editing and border states
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeStateEditing];
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeStateBezeled];
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeStateBordered];
[self setValue:aFont forThemeAttribute:@"font" inState:CPTextFieldStateRounded];
// Use CPThemeState() function to create composite states instead of array literals
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeState(CPTextFieldStateRounded, CPThemeStateEditing)];
// Apply across all standard control size states
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeStateControlSizeRegular];
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeStateControlSizeSmall];
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeStateControlSizeMini];
// Apply to table data view states (ensuring Interface Builder-style lists respect the font)
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeStateTableDataView];
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeState(CPThemeStateTableDataView, CPThemeStateSelectedDataView)];
[self setValue:aFont forThemeAttribute:@"font" inState:CPThemeState(CPThemeStateTableDataView, CPThemeStateSelectedDataView, CPThemeStateFirstResponder, CPThemeStateKeyWindow)];
[self layoutSubviews];
}
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
{
var count = objects.length,
@@ -1888,7 +2057,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
}
}
#pragma mark Overrides
// MARK: Overrides
/*!
Sets the text color of the receiver.
@@ -1900,8 +2069,28 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
// We don't want to change the text-color of the placeHolder of the textField
var placeholderColor = [self valueForThemeAttribute:@"text-color" inState:CPTextFieldStatePlaceholder];
[super setTextColor:aColor];
// If the text field is a cell based table data view, we need to fix the color for all possible states
if ([self hasThemeState:CPThemeStateTableDataView])
{
[self setTextColor:aColor inThemeStates:[CPThemeStateTableDataView]];
[self setTextColor:aColor inThemeStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView]];
[self setTextColor:aColor inThemeStates:[CPThemeStateTableDataView, CPThemeStateSelectedDataView, CPThemeStateFirstResponder, CPThemeStateKeyWindow]];
}
else
{
if ([self hasThemeState:CPTextFieldStateRounded])
{
[self setTextColor:aColor inThemeStates:[CPTextFieldStateRounded]];
[self setTextColor:aColor inThemeStates:[CPTextFieldStateRounded, CPThemeStateEditing]];
}
[self setTextColor:aColor inThemeStates:[CPThemeStateNormal]];
[self setTextColor:aColor inThemeStates:[CPThemeStateEditing]];
}
[self setValue:placeholderColor forThemeAttribute:@"text-color" inState:CPTextFieldStatePlaceholder];
[self layoutSubviews];
}
- (void)viewDidHide
@@ -1937,7 +2126,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
return YES;
}
#pragma mark Private
// MARK: Private
- (BOOL)_isWithinUsablePlatformRect
{
@@ -1950,17 +2139,55 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
if (!wind)
return NO;
var scrollView = [self enclosingScrollView],
previousContentViewBoundsOrigin;
// Here we scroll to the textField, otherwise the textField could not be in the usable platformRect
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
var frame = [self convertRectToBase:[self contentRectForBounds:[self bounds]]],
usableRect = [[wind platformWindow] usableContentFrame];
frame.origin = [wind convertBaseToGlobal:frame.origin];
// Here we restore the previous scrolling posiition
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
return (CGRectGetMinX(frame) >= CGRectGetMinX(usableRect) &&
CGRectGetMaxX(frame) <= CGRectGetMaxX(usableRect) &&
CGRectGetMinY(frame) >= CGRectGetMinY(usableRect) &&
CGRectGetMaxY(frame) <= CGRectGetMaxY(usableRect));
}
/*!
@ignore
*/
- (CGPoint)_scrollToVisibleRectAndReturnPreviousOrigin
{
var scrollView = [self enclosingScrollView],
previousContentViewBoundsOrigin;
// Here we scroll to the textField, otherwise the textField could not be in the usable platformRect
if ([scrollView isKindOfClass:[CPScrollView class]])
{
previousContentViewBoundsOrigin = CGPointMakeCopy([[scrollView contentView] boundsOrigin]);
if (![[self superview] scrollRectToVisible:[self frame]])
previousContentViewBoundsOrigin = nil;
}
return previousContentViewBoundsOrigin;
}
/*!
@ignore
*/
- (void)_restorePreviousScrollingOrigin:(CGPoint)scrollingOrigin
{
if (scrollingOrigin)
[[[self enclosingScrollView] contentView] setBoundsOrigin:scrollingOrigin];
}
@end
var secureStringForString = function(aString)
@@ -2005,8 +2232,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
[self setSelectable:[aCoder decodeBoolForKey:CPTextFieldIsSelectableKey]];
[self setDrawsBackground:[aCoder decodeBoolForKey:CPTextFieldDrawsBackgroundKey]];
[self setTextFieldBackgroundColor:[aCoder decodeObjectForKey:CPTextFieldBackgroundColorKey]];
[self setBackgroundColor:[aCoder decodeObjectForKey:CPTextFieldBackgroundColorKey]];
[self setLineBreakMode:[aCoder decodeIntForKey:CPTextFieldLineBreakModeKey]];
[self setAlignment:[aCoder decodeIntForKey:CPTextFieldAlignmentKey]];
@@ -2016,6 +2242,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
[self _setUsesSingleLineMode:[aCoder decodeBoolForKey:CPTextFieldUsesSingleLineMode]];
[self _setWraps:[aCoder decodeBoolForKey:CPTextFieldWraps]];
[self _setScrolls:[aCoder decodeBoolForKey:CPTextFieldScrolls]];
[self updateTrackingAreas];
}
return self;
@@ -2050,6 +2277,56 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
@implementation _CPTextFieldValueBinder : CPBinder
+ (void)unbind:(CPString)aBinding forObject:(id)anObject
{
var theBinding = [self getBinding:aBinding forObject:anObject],
notificationCenter = [CPNotificationCenter defaultCenter];
if (theBinding)
{
[notificationCenter removeObserver:[theBinding._info objectForKey:CPObservedObjectKey]
name:CPControlTextDidBeginEditingNotification
object:anObject];
[notificationCenter removeObserver:[theBinding._info objectForKey:CPObservedObjectKey]
name:CPControlTextDidEndEditingNotification
object:anObject];
[super unbind:aBinding forObject:anObject];
}
}
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
{
self = [super initWithBinding:aBinding
name:aName
to:aDestination
keyPath:aKeyPath
options:options
from:aSource];
var notificationCenter = [CPNotificationCenter defaultCenter];
// This gives us support for the CPEditorRegistration informal protocol
if ([aDestination respondsToSelector:@selector(_objectDidBeginEditing:)])
{
[notificationCenter addObserver:aDestination
selector:@selector(_objectDidBeginEditing:)
name:CPControlTextDidBeginEditingNotification
object:aSource];
}
if ([aDestination respondsToSelector:@selector(_objectDidEndEditing:)])
{
[notificationCenter addObserver:aDestination
selector:@selector(_objectDidEndEditing:)
name:CPControlTextDidEndEditingNotification
object:aSource];
}
return self;
}
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
{
[super _updatePlaceholdersWithOptions:options];
@@ -2082,7 +2359,8 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
newValue = [self valueForBinding:aBinding],
value = [destination valueForKeyPath:keyPath];
if (CPIsControllerMarker(value) && newValue === nil) return;
if (CPIsControllerMarker(value) && newValue == nil)
return;
newValue = [self reverseTransformValue:newValue withOptions:options];
@@ -2110,3 +2388,76 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
}
@end
@implementation CPTextField (CPTrackingArea)
{
CPTrackingArea _textFieldTrackingArea;
}
- (void)updateTrackingAreas
{
if (_textFieldTrackingArea)
{
[self removeTrackingArea:_textFieldTrackingArea];
_textFieldTrackingArea = nil;
}
if ([self isEnabled] && (_isEditable || _isSelectable))
{
var myBounds = CGRectMakeCopy([self bounds]),
contentInset = [self currentValueForThemeAttribute:@"content-inset"];
_textFieldTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectInsetByInset(myBounds, contentInset)
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
owner:self
userInfo:nil];
[self addTrackingArea:_textFieldTrackingArea];
}
[super updateTrackingAreas];
}
- (void)cursorUpdate:(CPEvent)anEvent
{
[[CPCursor IBeamCursor] set];
}
@end
// MARK: -
@implementation CPTextField (TableDataView)
// We overide here _CPObject+Theme setValue:forThemeAttribute as CPTextField can be used as tableView data view
// So, when outside a table data view, setValue:forThemeAttribute should store the value with the CPThemeStateNormal (default behavior)
// When inside a table data view, it should store the value with the CPThemeStateTableDataView. If not, the value won't be used if the
// theme defined a value for this attribute for state CPThemeStateTableDataView
- (void)setValue:(id)aValue forThemeAttribute:(CPString)aName
{
[super setValue:aValue forThemeAttribute:aName];
[super setValue:aValue forThemeAttribute:aName inState:CPThemeStateTableDataView];
}
@end
// MARK: -
@implementation CPTextField (Deprecated)
- (void)setTextFieldBackgroundColor:(CPColor)aColor
{
CPLog.error("[CPTextField setTextFieldBackgroundColor:] is deprecated, use [CPTextField setBackgroundColor:] instead.");
[self setBackgroundColor:aColor];
}
- (CPColor)textFieldBackgroundColor
{
CPLog.info("[CPTextField textFieldBackgroundColor] is deprecated, use [CPTextField backgroundColor] instead.");
return [self backgroundColor];
}
@end
+315
View File
@@ -0,0 +1,315 @@
/*
* CPFontDescriptor.j
* AppKit
*
* Created by Emmanuel Maillard on 07/03/10.
* Copyright Emmanuel Maillard 2010.
*
* 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>
/*
Font descriptor dictionary keys
*/
/*
CPFontNameAttribute contains a CPString that specified the font name
(may be an name list like: 'Marker Felt, Lucida Grande, Helvetica')
*/
CPFontNameAttribute = @"CPFontNameAttribute";
/*
CPFontSizeAttribute contains a CPString that specified the font size
(as a float value)
*/
CPFontSizeAttribute = @"CPFontSizeAttribute";
/*
CPFontTraitsAttribute a CPDictionary that contains font traits keys
(CPFontSymbolicTrait or CPFontWeightTrait)
*/
CPFontTraitsAttribute = @"CPFontTraitsAttribute";
// Font traits dictionary keys
/*
CPFontSymbolicTrait a CPNumber that contains CPFontFamilyClass and
typeface information flags.
*/
CPFontSymbolicTrait = @"CPFontSymbolicTrait";
/*
CPFontWeightTrait
We use CPString with CSS string values for font weight
(normal | bold | bolder | lighter | 100 | 200 | 300 | 400
| 500 | 600 | 700 | 800 | 900)
NOTE: Cocoa compatibility issue: NSFontWeightTrait are NSNumber for
font weight (from -1.0 to 1.0, 0.0 for normal weight).
*/
CPFontWeightTrait = @"CPFontWeightTrait";
/*
CPFontFamilyClass
*/
CPFontUnknownClass = 0 << 28;
CPFontOldStyleSerifsClass = 1 << 28;
CPFontTransitionalSerifsClass = 2 << 28;
CPFontModernSerifsClass = 3 << 28;
CPFontClarendonSerifsClass = 4 << 28;
CPFontSlabSerifsClass = 5 << 28;
CPFontFreeformSerifsClass = 7 << 28;
CPFontSansSerifClass = 8 << 28;
CPFontSerifClass = (CPFontOldStyleSerifsClass | CPFontTransitionalSerifsClass |
CPFontModernSerifsClass | CPFontClarendonSerifsClass |
CPFontSlabSerifsClass | CPFontFreeformSerifsClass);
CPFontFamilyClassMask = 0xF0000000;
/*
Typeface information
*/
CPFontItalicTrait = 1 << 0;
CPFontBoldTrait = 1 << 1;
CPFontExpandedTrait = 1 << 5; /* TODO: CCS 3 font-stretch */
CPFontCondensedTrait = 1 << 6;
CPFontSmallCapsTrait = 1 << 7;
/*!
@ingroup appkit
@class CPFontDescriptor
*/
@implementation CPFontDescriptor : CPObject
{
CPDictionary _attributes;
}
/*!
Returns a font descriptor with the specified attributes.
@param attributes a dictionary that describe the desired font descriptor
@return the requested font descriptor
*/
+ (CPFontDescriptor)fontDescriptorWithFontAttributes:(CPDictionary)attributes
{
return [[CPFontDescriptor alloc] initWithFontAttributes:attributes];
}
/*!
Returns a font descriptor with the specified name and size.
@param fontName the name of the font
@param aSize the size of the font (in points)
@return the requested font descriptor
*/
+ (CPFontDescriptor)fontDescriptorWithName:(CPString)fontName size:(float)size
{
return [[CPFontDescriptor alloc] initWithFontAttributes:[CPDictionary dictionaryWithObjects:[fontName, [CPString stringWithString:size + '']] forKeys:[CPFontNameAttribute,CPFontSizeAttribute]]];
}
/*!
Initialize a font descriptor with the specified attributes.
@param attributes a dictionary that describe the desired font descriptor
@return the requested font descriptor
*/
- (id)initWithFontAttributes:(CPDictionary)attributes
{
if (self = [super init])
{
_attributes = [[CPMutableDictionary alloc] init];
if (attributes)
[_attributes addEntriesFromDictionary:attributes];
}
return self;
}
/*!
Returns a new font descriptor that is the same as the receiver but with the
specified attributes taking precedence over the existing ones.
@param attributes a dictionary that describe the desired font descriptor
@return the new font descriptor
*/
- (CPFontDescriptor)fontDescriptorByAddingAttributes:(CPDictionary)attributes
{
var attrib = [_attributes copy];
[attrib addEntriesFromDictionary:attributes];
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
}
/*!
Returns a new font descriptor that is the same as the receiver but with the specified size taking precedence over the existing ones.
@param aSize the new size
@return the new font descriptor
*/
- (CPFontDescriptor)fontDescriptorWithSize:(float)aSize
{
var attrib = [_attributes copy];
[attrib setObject:[CPString stringWithString:aSize + ''] forKey:CPFontSizeAttribute];
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
}
/*!
Returns a new font descriptor that is the same as the receiver but with
the specified symbolic traits taking precedence over the existing ones.
@param symbolicTraits the desired new symbolic traits
@return the new font descriptor
*/
- (CPFontDescriptor)fontDescriptorWithSymbolicTraits:(CPFontSymbolicTraits)symbolicTraits
{
var attrib = [_attributes copy];
if ([attrib objectForKey:CPFontTraitsAttribute])
[[attrib objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTraits]
forKey:CPFontSymbolicTrait];
else
[attrib setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTraits]
forKey:CPFontSymbolicTrait] forKey:CPFontTraitsAttribute];
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
}
- (id)objectForKey:(id)aKey
{
return [_attributes objectForKey:aKey];
}
- (CPDictionary)fontAttributes
{
return _attributes;
}
- (float)pointSize
{
var value = [_attributes objectForKey:CPFontSizeAttribute];
return value ? [value floatValue] : 0.0;
}
- (CPFontSymbolicTraits)symbolicTraits
{
var traits = [_attributes objectForKey:CPFontTraitsAttribute];
return (traits && [traits objectForKey:CPFontSymbolicTrait]) ? [[traits objectForKey:CPFontSymbolicTrait] unsignedIntValue] : 0;
}
@end
var CPFontDescriptorAttributesKey = @"CPFontDescriptorAttributesKey";
@implementation CPFontDescriptor (CPCoding)
/*!
Initializes the font descriptor from a coder.
@param aCoder the coder from which to read the font descriptor data
@return the initialized font
*/
- (id)initWithCoder:(CPCoder)aCoder
{
return [self initWithFontAttributes:[aCoder decodeObjectForKey:CPFontDescriptorAttributesKey]];
}
/*!
Writes the font descriptor to a coder.
@param aCoder the coder to which the data will be written
*/
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:_attributes forKey:CPFontDescriptorAttributesKey];
}
@end
var _wrapNameRegEx = new RegExp(/(\w+\s+\w+)(,*)/g);
/*
Helper methods to CPFont for generating CSS font style
*/
@implementation CPFontDescriptor (CPFontCSSHelper)
- (CPString)fontStyleCSSString
{
return [self symbolicTraits] & CPFontItalicTrait ? @"italic" : @"normal";
}
- (CPString)fontWeightCSSString
{
var traitsAttributes = [_attributes objectForKey:CPFontTraitsAttribute];
if (traitsAttributes)
{
/* give preference to CPFontWeightTrait */
if ([traitsAttributes objectForKey:CPFontWeightTrait])
return [traitsAttributes objectForKey:CPFontWeightTrait];
/* else fallback to facetype symbolic traits */
if ([self symbolicTraits] & CPFontBoldTrait)
return @"bold";
}
return @"normal";
}
- (CPString)fontSizeCSSString
{
return [_attributes objectForKey:CPFontSizeAttribute] ? [[_attributes objectForKey:CPFontSizeAttribute] intValue] + "px" : @"";
}
- (CPString)fontFamilyCSSString
{
var aName = @"";
if ([_attributes objectForKey:CPFontNameAttribute])
aName += [_attributes objectForKey:CPFontNameAttribute].replace(_wrapNameRegEx, '"$1"$2');
var symbolicTraits = [self symbolicTraits];
if (symbolicTraits)
{
if ((symbolicTraits & CPFontFamilyClassMask) & CPFontSansSerifClass)
aName += @", sans-serif";
else if ((symbolicTraits & CPFontFamilyClassMask) & CPFontSerifClass)
aName += @", serif";
}
return aName;
}
- (CPString)fontVariantCSSString
{
if ([self symbolicTraits] & CPFontSmallCapsTrait)
return @"small-caps";
return @"normal";
}
- (CPString)cssString
{
return [CPString stringWithString:[self fontStyleCSSString] + " "
+ [self fontVariantCSSString] + " "
+ [self fontWeightCSSString] + " "
+ [self fontSizeCSSString] + " "
+ [self fontFamilyCSSString]];
}
@end

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