Guest User

Untitled

a guest
Apr 24th, 2018
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.97 KB | None | 0 0
  1.  
  2. /*
  3. * CPTextField.j
  4. * AppKit
  5. *
  6. * Created by Francisco Tolmasky.
  7. * Copyright 2008, 280 North, Inc.
  8. *
  9. * Modified by Erik Aigner (Nov 21, 2009) based on Randall Luecke's
  10. * (July 21, 2009) approach to add support for wrapping editable CPTextFields.
  11. * Updated to work with latest version and added support for enter key.
  12. * Call setWraps:YES and then setLineBreakMode:CPLineBreakByWordWrapping
  13. *
  14. * This library is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU Lesser General Public
  16. * License as published by the Free Software Foundation; either
  17. * version 2.1 of the License, or (at your option) any later version.
  18. *
  19. * This library is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  22. * Lesser General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU Lesser General Public
  25. * License along with this library; if not, write to the Free Software
  26. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  27. */
  28.  
  29. @import "CPControl.j"
  30. @import "CPStringDrawing.j"
  31. @import "CPCompatibility.j"
  32.  
  33. #include "CoreGraphics/CGGeometry.h"
  34. #include "Platform/Platform.h"
  35. #include "Platform/DOM/CPDOMDisplayServer.h"
  36.  
  37.  
  38. CPLineBreakByWordWrapping = 0;
  39. CPLineBreakByCharWrapping = 1;
  40. CPLineBreakByClipping = 2;
  41. CPLineBreakByTruncatingHead = 3;
  42. CPLineBreakByTruncatingTail = 4;
  43. CPLineBreakByTruncatingMiddle = 5;
  44.  
  45. CPTextFieldSquareBezel = 0; /*! A textfield bezel with a squared corners. */
  46. CPTextFieldRoundedBezel = 1; /*! A textfield bezel with rounded corners. */
  47.  
  48. CPTextFieldDidFocusNotification = @"CPTextFieldDidFocusNotification";
  49. CPTextFieldDidBlurNotification = @"CPTextFieldDidBlurNotification";
  50.  
  51. #if PLATFORM(DOM)
  52.  
  53. var CPTextFieldDOMInputElement = nil,
  54. CPTextFieldDOMPasswordInputElement = nil,
  55. CPTextFieldDOMStandardInputElement = nil,
  56. CPTextFieldInputOwner = nil,
  57. CPTextFieldTextDidChangeValue = nil,
  58. CPTextFieldInputResigning = NO,
  59. CPTextFieldInputDidBlur = NO,
  60. CPTextFieldInputIsActive = NO,
  61. CPTextFieldCachedSelectStartFunction = nil,
  62. CPTextFieldCachedDragFunction = nil,
  63.  
  64. CPTextFieldBlurFunction = nil,
  65. CPTextFieldKeyUpFunction = nil,
  66. CPTextFieldKeyPressFunction = nil,
  67. CPTextFieldKeyDownFunction = nil;
  68.  
  69. #endif
  70.  
  71. var CPSecureTextFieldCharacter = "\u2022";
  72.  
  73. @implementation CPString (CPTextFieldAdditions)
  74.  
  75. /*!
  76. Returns the string (\c self).
  77. */
  78. - (CPString)string
  79. {
  80. return self;
  81. }
  82.  
  83. @end
  84.  
  85. CPTextFieldStateRounded = CPThemeState("rounded");
  86. CPTextFieldStatePlaceholder = CPThemeState("placeholder");
  87.  
  88. /*!
  89. @ingroup appkit
  90. This control displays editable text in a Cappuccino application.
  91. */
  92. @implementation CPTextField : CPControl
  93. {
  94. BOOL _isEditing;
  95.  
  96. BOOL _isEditable;
  97. BOOL _isSelectable;
  98. BOOL _isSecure;
  99.  
  100. BOOL _drawsBackground;
  101.  
  102. CPColor _textFieldBackgroundColor;
  103.  
  104. id _placeholderString;
  105.  
  106. id _delegate;
  107.  
  108. CPString _textDidChangeValue;
  109.  
  110. // NS-style Display Properties
  111. CPTextFieldBezelStyle _bezelStyle;
  112. BOOL _isBordered;
  113. CPControlSize _controlSize;
  114. BOOL _wraps; // support for textarea
  115. }
  116.  
  117. + (CPTextField)textFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth
  118. {
  119. return [self textFieldWithStringValue:aStringValue placeholder:aPlaceholder width:aWidth theme:[CPTheme defaultTheme]];
  120. }
  121.  
  122. + (CPTextField)textFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth theme:(CPTheme)aTheme
  123. {
  124. var textField = [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, aWidth, 29.0)];
  125.  
  126. [textField setTheme:aTheme];
  127. [textField setStringValue:aStringValue];
  128. [textField setPlaceholderString:aPlaceholder];
  129. [textField setBordered:YES];
  130. [textField setBezeled:YES];
  131. [textField setEditable:YES];
  132.  
  133. [textField sizeToFit];
  134.  
  135. return textField;
  136. }
  137.  
  138. + (CPTextField)roundedTextFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth
  139. {
  140. return [self roundedTextFieldWithStringValue:aStringValue placeholder:aPlaceholder width:aWidth theme:[CPTheme defaultTheme]];
  141. }
  142.  
  143. + (CPTextField)roundedTextFieldWithStringValue:(CPString)aStringValue placeholder:(CPString)aPlaceholder width:(float)aWidth theme:(CPTheme)aTheme
  144. {
  145. var textField = [[CPTextField alloc] initWithFrame:CGRectMake(0.0, 0.0, aWidth, 29.0)];
  146.  
  147. [textField setTheme:aTheme];
  148. [textField setStringValue:aStringValue];
  149. [textField setPlaceholderString:aPlaceholder];
  150. [textField setBezelStyle:CPTextFieldRoundedBezel];
  151. [textField setBordered:YES];
  152. [textField setBezeled:YES];
  153. [textField setEditable:YES];
  154.  
  155. [textField sizeToFit];
  156.  
  157. return textField;
  158. }
  159.  
  160. + (CPTextField)labelWithTitle:(CPString)aTitle
  161. {
  162. return [self labelWithTitle:aTitle theme:[CPTheme defaultTheme]];
  163. }
  164.  
  165. + (CPTextField)labelWithTitle:(CPString)aTitle theme:(CPTheme)aTheme
  166. {
  167. var textField = [[self alloc] init];
  168.  
  169. [textField setStringValue:aTitle];
  170. [textField sizeToFit];
  171.  
  172. return textField;
  173. }
  174.  
  175. + (CPString)themeClass
  176. {
  177. return "textfield";
  178. }
  179.  
  180. + (id)themeAttributes
  181. {
  182. return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), nil]
  183. forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]];
  184. }
  185.  
  186. /* @ignore */
  187. #if PLATFORM(DOM)
  188. - (DOMElement)_inputElement
  189. {
  190. if (!CPTextFieldDOMInputElement)
  191. {
  192. if(_wraps) {
  193. CPTextFieldDOMInputElement = document.createElement("textarea");
  194. CPTextFieldDOMInputElement.style.resize = "none";
  195. CPTextFieldDOMInputElement.style.overflow = "hidden";
  196. }
  197. else {
  198. CPTextFieldDOMInputElement = document.createElement("input");
  199. }
  200.  
  201. CPTextFieldDOMInputElement.style.position = "absolute";
  202. CPTextFieldDOMInputElement.style.border = "0px";
  203. CPTextFieldDOMInputElement.style.padding = "0px";
  204. CPTextFieldDOMInputElement.style.margin = "0px";
  205. CPTextFieldDOMInputElement.style.whiteSpace = "pre";
  206. CPTextFieldDOMInputElement.style.background = "transparent";
  207. CPTextFieldDOMInputElement.style.outline = "none";
  208.  
  209.  
  210. CPTextFieldBlurFunction = function(anEvent)
  211. {
  212. if (CPTextFieldInputOwner && CPTextFieldInputOwner._DOMElement != CPTextFieldDOMInputElement.parentNode)
  213. return;
  214.  
  215. if (!CPTextFieldInputResigning)
  216. {
  217. [[CPTextFieldInputOwner window] makeFirstResponder:nil];
  218. return;
  219. }
  220.  
  221. CPTextFieldHandleBlur(anEvent, CPTextFieldDOMInputElement);
  222. CPTextFieldInputDidBlur = YES;
  223.  
  224. return true;
  225. }
  226.  
  227. CPTextFieldKeyDownFunction = function(aDOMEvent)
  228. {
  229. CPTextFieldTextDidChangeValue = [CPTextFieldInputOwner stringValue];
  230.  
  231. aDOMEvent = aDOMEvent || window.event;
  232.  
  233. if (aDOMEvent.keyCode == CPReturnKeyCode || aDOMEvent.keyCode == CPTabKeyCode)
  234. CPTextFieldKeyPressFunction(aDOMEvent);
  235.  
  236. return true;
  237. }
  238.  
  239. CPTextFieldKeyPressFunction = function(aDOMEvent)
  240. {
  241. aDOMEvent = aDOMEvent || window.event;
  242.  
  243. CPTextFieldKeyUpFunction();
  244.  
  245. if (aDOMEvent.keyCode == CPReturnKeyCode || aDOMEvent.keyCode == CPTabKeyCode)
  246. {
  247. if (aDOMEvent.preventDefault)
  248. aDOMEvent.preventDefault();
  249. if (aDOMEvent.stopPropagation)
  250. aDOMEvent.stopPropagation();
  251. aDOMEvent.cancelBubble = true;
  252.  
  253. var owner = CPTextFieldInputOwner;
  254.  
  255. if (aDOMEvent && aDOMEvent.keyCode == CPReturnKeyCode)
  256. {
  257. if (owner._isEditing)
  258. {
  259. owner._isEditing = NO;
  260. [owner textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:owner userInfo:nil]];
  261. }
  262.  
  263. if(_wraps) {
  264. var _input = [self _inputElement];
  265. _input.value = _input.value + "\n";
  266. }
  267.  
  268. [owner sendAction:[owner action] to:[owner target]];
  269.  
  270. if(!_wraps) {
  271. [owner selectText:nil];
  272. }
  273. }
  274. else if (aDOMEvent && aDOMEvent.keyCode == CPTabKeyCode)
  275. {
  276. if (!aDOMEvent.shiftKey)
  277. [[owner window] selectNextKeyView:owner];
  278. else
  279. [[owner window] selectPreviousKeyView:owner];
  280. }
  281. }
  282.  
  283. [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
  284. }
  285.  
  286. CPTextFieldKeyUpFunction = function()
  287. {
  288. [CPTextFieldInputOwner _setStringValue:CPTextFieldDOMInputElement.value];
  289.  
  290. if ([CPTextFieldInputOwner stringValue] !== CPTextFieldTextDidChangeValue)
  291. {
  292. if (!CPTextFieldInputOwner._isEditing)
  293. {
  294. CPTextFieldInputOwner._isEditing = YES;
  295. [CPTextFieldInputOwner textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:CPTextFieldInputOwner userInfo:nil]];
  296. }
  297.  
  298. [CPTextFieldInputOwner textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:CPTextFieldInputOwner userInfo:nil]];
  299. CPTextFieldTextDidChangeValue = [CPTextFieldInputOwner stringValue];
  300. }
  301.  
  302. [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
  303. }
  304.  
  305. CPTextFieldHandleBlur = function(anEvent)
  306. {
  307. var owner = CPTextFieldInputOwner;
  308. CPTextFieldInputOwner = nil;
  309.  
  310. [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
  311. }
  312.  
  313. if (document.attachEvent)
  314. {
  315. CPTextFieldDOMInputElement.attachEvent("on" + CPDOMEventKeyUp, CPTextFieldKeyUpFunction);
  316. CPTextFieldDOMInputElement.attachEvent("on" + CPDOMEventKeyDown, CPTextFieldKeyDownFunction);
  317. CPTextFieldDOMInputElement.attachEvent("on" + CPDOMEventKeyPress, CPTextFieldKeyPressFunction);
  318. }
  319. else
  320. {
  321. CPTextFieldDOMInputElement.addEventListener(CPDOMEventKeyUp, CPTextFieldKeyUpFunction, NO);
  322. CPTextFieldDOMInputElement.addEventListener(CPDOMEventKeyDown, CPTextFieldKeyDownFunction, NO);
  323. CPTextFieldDOMInputElement.addEventListener(CPDOMEventKeyPress, CPTextFieldKeyPressFunction, NO);
  324. }
  325.  
  326. //FIXME make this not onblur
  327. CPTextFieldDOMInputElement.onblur = CPTextFieldBlurFunction;
  328.  
  329. CPTextFieldDOMStandardInputElement = CPTextFieldDOMInputElement;
  330. }
  331.  
  332. if (CPFeatureIsCompatible(CPInputTypeCanBeChangedFeature))
  333. {
  334. if ([self isSecure])
  335. CPTextFieldDOMInputElement.type = "password";
  336. else
  337. CPTextFieldDOMInputElement.type = "text";
  338.  
  339. return CPTextFieldDOMInputElement;
  340. }
  341.  
  342. if ([self isSecure])
  343. {
  344. if (!CPTextFieldDOMPasswordInputElement)
  345. {
  346. CPTextFieldDOMPasswordInputElement = document.createElement("input");
  347. CPTextFieldDOMPasswordInputElement.style.position = "absolute";
  348. CPTextFieldDOMPasswordInputElement.style.border = "0px";
  349. CPTextFieldDOMPasswordInputElement.style.padding = "0px";
  350. CPTextFieldDOMPasswordInputElement.style.margin = "0px";
  351. CPTextFieldDOMPasswordInputElement.style.whiteSpace = "pre";
  352. CPTextFieldDOMPasswordInputElement.style.background = "transparent";
  353. CPTextFieldDOMPasswordInputElement.style.outline = "none";
  354. CPTextFieldDOMPasswordInputElement.type = "password";
  355.  
  356. CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyUp, CPTextFieldKeyUpFunction);
  357. CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyDown, CPTextFieldKeyDownFunction);
  358. CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyPress, CPTextFieldKeyPressFunction);
  359.  
  360. CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurFunction;
  361. }
  362.  
  363. CPTextFieldDOMInputElement = CPTextFieldDOMPasswordInputElement;
  364. }
  365. else
  366. {
  367. CPTextFieldDOMInputElement = CPTextFieldDOMStandardInputElement;
  368. }
  369.  
  370. return CPTextFieldDOMInputElement;
  371. }
  372. #endif
  373.  
  374. - (id)initWithFrame:(CGRect)aFrame
  375. {
  376. self = [super initWithFrame:aFrame];
  377.  
  378. if (self)
  379. {
  380. [self setStringValue:@""];
  381. [self setPlaceholderString:@""];
  382.  
  383. _sendActionOn = CPKeyUpMask | CPKeyDownMask;
  384.  
  385. [self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
  386. }
  387.  
  388. return self;
  389. }
  390.  
  391. #pragma mark Controlling Editability and Selectability
  392.  
  393. /*!
  394. Sets whether or not the receiver text field can be edited
  395. */
  396. - (void)setEditable:(BOOL)shouldBeEditable
  397. {
  398. _isEditable = shouldBeEditable;
  399. }
  400.  
  401. /*!
  402. Returns \c YES if the textfield is currently editable by the user.
  403. */
  404. - (BOOL)isEditable
  405. {
  406. return _isEditable;
  407. }
  408.  
  409. /*!
  410. Sets whether the field's text is selectable by the user.
  411. @param aFlag \c YES makes the text selectable
  412. */
  413. - (void)setSelectable:(BOOL)aFlag
  414. {
  415. _isSelectable = aFlag;
  416. }
  417.  
  418. /*!
  419. Returns \c YES if the field's text is selectable by the user.
  420. */
  421. - (BOOL)isSelectable
  422. {
  423. return _isSelectable;
  424. }
  425.  
  426. /*!
  427. Sets whether the field's text is secure.
  428. @param aFlag \c YES makes the text secure
  429. */
  430. - (void)setSecure:(BOOL)aFlag
  431. {
  432. _isSecure = aFlag;
  433. }
  434.  
  435. /*!
  436. Returns \c YES if the field's text is secure (password entry).
  437. */
  438. - (BOOL)isSecure
  439. {
  440. return _isSecure;
  441. }
  442.  
  443. // Setting the Bezel Style
  444. /*!
  445. Sets whether the textfield will have a bezeled border.
  446. @param shouldBeBezeled \c YES means the textfield will draw a bezeled border
  447. */
  448. - (void)setBezeled:(BOOL)shouldBeBezeled
  449. {
  450. if (shouldBeBezeled)
  451. [self setThemeState:CPThemeStateBezeled];
  452. else
  453. [self unsetThemeState:CPThemeStateBezeled];
  454. }
  455.  
  456. /*!
  457. Returns \c YES if the textfield draws a bezeled border.
  458. */
  459. - (BOOL)isBezeled
  460. {
  461. return [self hasThemeState:CPThemeStateBezeled];
  462. }
  463.  
  464. /*!
  465. Sets the textfield's bezel style.
  466. @param aBezelStyle the constant for the desired bezel style
  467. */
  468. - (void)setBezelStyle:(CPTextFieldBezelStyle)aBezelStyle
  469. {
  470. var shouldBeRounded = aBezelStyle === CPTextFieldRoundedBezel;
  471.  
  472. if (shouldBeRounded)
  473. [self setThemeState:CPTextFieldStateRounded];
  474. else
  475. [self unsetThemeState:CPTextFieldStateRounded];
  476. }
  477.  
  478. /*!
  479. Returns the textfield's bezel style.
  480. */
  481. - (CPTextFieldBezelStyle)bezelStyle
  482. {
  483. if ([self hasThemeState:CPTextFieldStateRounded])
  484. return CPTextFieldRoundedBezel;
  485.  
  486. return CPTextFieldSquareBezel;
  487. }
  488.  
  489. /*!
  490. Sets whether the textfield will have a border drawn.
  491. @param shouldBeBordered \c YES makes the textfield draw a border
  492. */
  493. - (void)setBordered:(BOOL)shouldBeBordered
  494. {
  495. if (shouldBeBordered)
  496. [self setThemeState:CPThemeStateBordered];
  497. else
  498. [self unsetThemeState:CPThemeStateBordered];
  499. }
  500.  
  501. /*!
  502. Returns \c YES if the textfield has a border.
  503. */
  504. - (BOOL)isBordered
  505. {
  506. return [self hasThemeState:CPThemeStateBordered];
  507. }
  508.  
  509. /*!
  510. Sets whether the textfield will have a background drawn.
  511. @param shouldDrawBackground \c YES makes the textfield draw a background
  512. */
  513. - (void)setDrawsBackground:(BOOL)shouldDrawBackground
  514. {
  515. if (_drawsBackground == shouldDrawBackground)
  516. return;
  517.  
  518. _drawsBackground = shouldDrawBackground;
  519.  
  520. [self setNeedsLayout];
  521. [self setNeedsDisplay:YES];
  522. }
  523.  
  524. /*!
  525. Returns \c YES if the textfield draws a background.
  526. */
  527. - (BOOL)drawsBackground
  528. {
  529. return _drawsBackground;
  530. }
  531.  
  532. /*!
  533. Sets the background color, which is shown for non-bezeled text fields with drawsBackground set to YES
  534. @param aColor The background color
  535. */
  536. - (void)setTextFieldBackgroundColor:(CPColor)aColor
  537. {
  538. if (_textFieldBackgroundColor == aColor)
  539. return;
  540.  
  541. _textFieldBackgroundColor = aColor;
  542.  
  543. [self setNeedsLayout];
  544. [self setNeedsDisplay:YES];
  545. }
  546.  
  547. /*!
  548. Returns the background color.
  549. */
  550. - (CPColor)textFieldBackgroundColor
  551. {
  552. return _textFieldBackgroundColor;
  553. }
  554.  
  555. /* @ignore */
  556. - (BOOL)acceptsFirstResponder
  557. {
  558. return [self isEditable] && [self isEnabled];
  559. }
  560.  
  561. /* @ignore */
  562. - (BOOL)becomeFirstResponder
  563. {
  564. #if PLATFORM(DOM)
  565. if (CPTextFieldInputOwner && [CPTextFieldInputOwner window] !== [self window])
  566. [[CPTextFieldInputOwner window] makeFirstResponder:nil];
  567. #endif
  568.  
  569. [self setThemeState:CPThemeStateEditing];
  570.  
  571. [self _updatePlaceholderState];
  572.  
  573. [self setNeedsLayout];
  574.  
  575. _isEditing = NO;
  576.  
  577. #if PLATFORM(DOM)
  578.  
  579. var string = [self stringValue],
  580. element = [self _inputElement];
  581.  
  582. element.value = string;
  583. element.style.color = [[self currentValueForThemeAttribute:@"text-color"] cssString];
  584. element.style.font = [[self currentValueForThemeAttribute:@"font"] cssString];
  585. element.style.zIndex = 1000;
  586.  
  587. switch ([self alignment])
  588. {
  589. case CPCenterTextAlignment: element.style.textAlign = "center";
  590. break;
  591. case CPRightTextAlignment: element.style.textAlign = "right";
  592. break;
  593. default: element.style.textAlign = "left";
  594. }
  595.  
  596. var contentRect = [self contentRectForBounds:[self bounds]];
  597.  
  598. element.style.top = _CGRectGetMinY(contentRect) + "px";
  599. element.style.left = (_CGRectGetMinX(contentRect) - 1) + "px"; // why -1?
  600. element.style.width = _CGRectGetWidth(contentRect) + "px";
  601. element.style.height = _CGRectGetHeight(contentRect) + "px";
  602.  
  603. _DOMElement.appendChild(element);
  604.  
  605. window.setTimeout(function()
  606. {
  607. element.focus();
  608. CPTextFieldInputOwner = self;
  609. }, 0.0);
  610.  
  611. element.value = [self stringValue];
  612.  
  613. [[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
  614.  
  615. CPTextFieldInputIsActive = YES;
  616.  
  617. if (document.attachEvent)
  618. {
  619. CPTextFieldCachedSelectStartFunction = document.body.onselectstart;
  620. CPTextFieldCachedDragFunction = document.body.ondrag;
  621.  
  622. document.body.ondrag = function () {};
  623. document.body.onselectstart = function () {};
  624. }
  625.  
  626. [self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
  627. #endif
  628.  
  629. return YES;
  630. }
  631.  
  632. /* @ignore */
  633. - (BOOL)resignFirstResponder
  634. {
  635. [self unsetThemeState:CPThemeStateEditing];
  636.  
  637. [self _updatePlaceholderState];
  638.  
  639. [self setNeedsLayout];
  640.  
  641. #if PLATFORM(DOM)
  642.  
  643. var element = [self _inputElement];
  644.  
  645. [self setObjectValue:element.value];
  646.  
  647. CPTextFieldInputResigning = YES;
  648. element.blur();
  649.  
  650. if (!CPTextFieldInputDidBlur)
  651. CPTextFieldBlurFunction();
  652.  
  653. CPTextFieldInputDidBlur = NO;
  654. CPTextFieldInputResigning = NO;
  655.  
  656. if (element.parentNode == _DOMElement)
  657. element.parentNode.removeChild(element);
  658.  
  659. CPTextFieldInputIsActive = NO;
  660.  
  661. if (document.attachEvent)
  662. {
  663. CPTextFieldCachedSelectStartFunction = nil;
  664. CPTextFieldCachedDragFunction = nil;
  665.  
  666. document.body.ondrag = CPTextFieldCachedDragFunction
  667. document.body.onselectstart = CPTextFieldCachedSelectStartFunction
  668. }
  669.  
  670. #endif
  671.  
  672. //post CPControlTextDidEndEditingNotification
  673. if (_isEditing)
  674. {
  675. _isEditing = NO;
  676. [self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:nil]];
  677.  
  678. if ([self sendsActionOnEndEditing])
  679. [self sendAction:[self action] to:[self target]];
  680. }
  681.  
  682. [self textDidBlur:[CPNotification notificationWithName:CPTextFieldDidBlurNotification object:self userInfo:nil]];
  683.  
  684. return YES;
  685. }
  686.  
  687. - (void)textDidBlur:(CPNotification)note
  688. {
  689. //this looks to prevent false propagation of notifications for other objects
  690. if([note object] != self)
  691. return;
  692.  
  693. [[CPNotificationCenter defaultCenter] postNotification:note];
  694. }
  695.  
  696. - (void)textDidFocus:(CPNotification)note
  697. {
  698. //this looks to prevent false propagation of notifications for other objects
  699. if([note object] != self)
  700. return;
  701.  
  702. [[CPNotificationCenter defaultCenter] postNotification:note];
  703. }
  704.  
  705. - (void)mouseDown:(CPEvent)anEvent
  706. {
  707. // Don't track! (ever?)
  708. if ([self isEditable] && [self isEnabled])
  709. return [[self window] makeFirstResponder:self];
  710. else
  711. return [[self nextResponder] mouseDown:anEvent];
  712. }
  713.  
  714. /*!
  715. Returns the string the text field.
  716. */
  717. - (id)objectValue
  718. {
  719. return [super objectValue];
  720. }
  721.  
  722. /*
  723. @ignore
  724. */
  725. - (void)_setStringValue:(id)aValue
  726. {
  727. [self willChangeValueForKey:@"objectValue"];
  728. [super setObjectValue:String(aValue)];
  729. [self _updatePlaceholderState];
  730. [self didChangeValueForKey:@"objectValue"];
  731. }
  732.  
  733. - (void)setObjectValue:(id)aValue
  734. {
  735. [super setObjectValue:aValue];
  736.  
  737. #if PLATFORM(DOM)
  738. if (CPTextFieldInputOwner === self)
  739. [self _inputElement].value = aValue;
  740. #endif
  741.  
  742. [self _updatePlaceholderState];
  743. }
  744.  
  745. - (void)_updatePlaceholderState
  746. {
  747. var string = [self stringValue];
  748.  
  749. if ((!string || string.length === 0) && ![self hasThemeState:CPThemeStateEditing])
  750. [self setThemeState:CPTextFieldStatePlaceholder];
  751. else
  752. [self unsetThemeState:CPTextFieldStatePlaceholder];
  753. }
  754.  
  755. /*!
  756. Sets a placeholder string for the receiver. The placeholder is displayed until editing begins,
  757. and after editing ends, if the text field has an empty string value
  758. */
  759. -(void)setPlaceholderString:(CPString)aStringValue
  760. {
  761. if (_placeholderString === aStringValue)
  762. return;
  763.  
  764. _placeholderString = aStringValue;
  765.  
  766. // Only update things if we need to show the placeholder
  767. if ([self hasThemeState:CPTextFieldStatePlaceholder])
  768. {
  769. [self setNeedsLayout];
  770. [self setNeedsDisplay:YES];
  771. }
  772. }
  773.  
  774. /*!
  775. Returns the receiver's placeholder string
  776. */
  777. - (CPString)placeholderString
  778. {
  779. return _placeholderString;
  780. }
  781.  
  782. /*!
  783. Size to fit has two behavior, depending on if the receiver is an editable text field or not.
  784.  
  785. For non-editable text fields (typically, a label), sizeToFit will change the frame of the
  786. receiver to perfectly fit the current text in stringValue in the current font, and respecting
  787. the current theme values for content-inset, min-size, and max-size.
  788.  
  789. For editable text fields, sizeToFit will ONLY change the HEIGHT of the text field. It will not
  790. change the width of the text field. You can use setFrameSize: with the current height to set the
  791. width, and you can get the size of a string with [CPString sizeWithFont:].
  792.  
  793. The logic behind this decision is that most of the time you do not know what content will be placed
  794. in an editable text field, so you want to just choose a fixed width and leave it at that size.
  795. However, since you don't know how tall it needs to be if you change the font, sizeToFit will still be
  796. useful for making the textfield an appropriate height.
  797. */
  798.  
  799. - (void)sizeToFit
  800. {
  801. var size = [([self stringValue] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]],
  802. contentInset = [self currentValueForThemeAttribute:@"content-inset"],
  803. minSize = [self currentValueForThemeAttribute:@"min-size"],
  804. maxSize = [self currentValueForThemeAttribute:@"max-size"];
  805.  
  806. size.width = MAX(size.width + contentInset.left + contentInset.right, minSize.width);
  807. size.height = MAX(size.height + contentInset.top + contentInset.bottom, minSize.height);
  808.  
  809. if (maxSize.width >= 0.0)
  810. size.width = MIN(size.width, maxSize.width);
  811.  
  812. if (maxSize.height >= 0.0)
  813. size.height = MIN(size.height, maxSize.height);
  814.  
  815. if ([self isEditable])
  816. size.width = CGRectGetWidth([self frame]);
  817.  
  818. [self setFrameSize:size];
  819. }
  820.  
  821. /*!
  822. Select all the text in the CPTextField.
  823. */
  824. - (void)selectText:(id)sender
  825. {
  826. #if PLATFORM(DOM)
  827. var element = [self _inputElement];
  828.  
  829. if (element.parentNode === _DOMElement && ([self isEditable] || [self isSelectable]))
  830. window.setTimeout(function() { element.select(); }, 0);
  831. #endif
  832. }
  833.  
  834. #pragma mark Setting the Delegate
  835.  
  836. - (void)setDelegate:(id)aDelegate
  837. {
  838. var defaultCenter = [CPNotificationCenter defaultCenter];
  839.  
  840. //unsubscribe the existing delegate if it exists
  841. if (_delegate)
  842. {
  843. [defaultCenter removeObserver:_delegate name:CPControlTextDidBeginEditingNotification object:self];
  844. [defaultCenter removeObserver:_delegate name:CPControlTextDidChangeNotification object:self];
  845. [defaultCenter removeObserver:_delegate name:CPControlTextDidEndEditingNotification object:self];
  846. [defaultCenter removeObserver:_delegate name:CPTextFieldDidFocusNotification object:self];
  847. [defaultCenter removeObserver:_delegate name:CPTextFieldDidBlurNotification object:self];
  848. }
  849.  
  850. _delegate = aDelegate;
  851.  
  852. if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)])
  853. [defaultCenter
  854. addObserver:_delegate
  855. selector:@selector(controlTextDidBeginEditing:)
  856. name:CPControlTextDidBeginEditingNotification
  857. object:self];
  858.  
  859. if ([_delegate respondsToSelector:@selector(controlTextDidChange:)])
  860. [defaultCenter
  861. addObserver:_delegate
  862. selector:@selector(controlTextDidChange:)
  863. name:CPControlTextDidChangeNotification
  864. object:self];
  865.  
  866.  
  867. if ([_delegate respondsToSelector:@selector(controlTextDidEndEditing:)])
  868. [defaultCenter
  869. addObserver:_delegate
  870. selector:@selector(controlTextDidEndEditing:)
  871. name:CPControlTextDidEndEditingNotification
  872. object:self];
  873.  
  874. if ([_delegate respondsToSelector:@selector(controlTextDidFocus:)])
  875. [defaultCenter
  876. addObserver:_delegate
  877. selector:@selector(controlTextDidFocus:)
  878. name:CPTextFieldDidFocusNotification
  879. object:self];
  880.  
  881. if ([_delegate respondsToSelector:@selector(controlTextDidBlur:)])
  882. [defaultCenter
  883. addObserver:_delegate
  884. selector:@selector(controlTextDidBlur:)
  885. name:CPTextFieldDidBlurNotification
  886. object:self];
  887. }
  888.  
  889. - (id)delegate
  890. {
  891. return _delegate;
  892. }
  893.  
  894. - (CGRect)contentRectForBounds:(CGRect)bounds
  895. {
  896. var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
  897.  
  898. if (!contentInset)
  899. return bounds;
  900.  
  901. bounds.origin.x += contentInset.left;
  902. bounds.origin.y += contentInset.top;
  903. bounds.size.width -= contentInset.left + contentInset.right;
  904. bounds.size.height -= contentInset.top + contentInset.bottom;
  905.  
  906. return bounds;
  907. }
  908.  
  909. - (CGRect)bezelRectForBounds:(CFRect)bounds
  910. {
  911. var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
  912.  
  913. if (_CGInsetIsEmpty(bezelInset))
  914. return bounds;
  915.  
  916. bounds.origin.x += bezelInset.left;
  917. bounds.origin.y += bezelInset.top;
  918. bounds.size.width -= bezelInset.left + bezelInset.right;
  919. bounds.size.height -= bezelInset.top + bezelInset.bottom;
  920.  
  921. return bounds;
  922. }
  923.  
  924. - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
  925. {
  926. if (aName === "bezel-view")
  927. return [self bezelRectForBounds:[self bounds]];
  928.  
  929. else if (aName === "content-view")
  930. return [self contentRectForBounds:[self bounds]];
  931.  
  932. return [super rectForEphemeralSubviewNamed:aName];
  933. }
  934.  
  935. - (CPView)createEphemeralSubviewNamed:(CPString)aName
  936. {
  937. if (aName === "bezel-view")
  938. {
  939. var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
  940.  
  941. [view setHitTests:NO];
  942.  
  943. return view;
  944. }
  945. else
  946. {
  947. var view = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
  948. //[view setImagePosition:CPNoImage];
  949.  
  950. return view;
  951. }
  952.  
  953. return [super createEphemeralSubviewNamed:aName];
  954. }
  955.  
  956. - (void)layoutSubviews
  957. {
  958. var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
  959. positioned:CPWindowBelow
  960. relativeToEphemeralSubviewNamed:@"content-view"];
  961.  
  962. if (bezelView)
  963. [bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
  964.  
  965. var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
  966. positioned:CPWindowAbove
  967. relativeToEphemeralSubviewNamed:@"bezel-view"];
  968.  
  969. if (contentView)
  970. {
  971. [contentView setHidden:[self hasThemeState:CPThemeStateEditing]];
  972.  
  973. var string = "";
  974.  
  975. if ([self hasThemeState:CPTextFieldStatePlaceholder])
  976. string = [self placeholderString];
  977. else
  978. {
  979. string = [self stringValue];
  980.  
  981. if ([self isSecure])
  982. string = secureStringForString(string);
  983. }
  984.  
  985. [contentView setText:string];
  986.  
  987. [contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
  988. [contentView setFont:[self currentValueForThemeAttribute:@"font"]];
  989. [contentView setAlignment:[self currentValueForThemeAttribute:@"alignment"]];
  990. [contentView setVerticalAlignment:[self currentValueForThemeAttribute:@"vertical-alignment"]];
  991. [contentView setLineBreakMode:[self currentValueForThemeAttribute:@"line-break-mode"]];
  992. [contentView setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
  993. [contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
  994. }
  995. }
  996.  
  997. - (void)setWraps:(BOOL)aFlag {
  998. _wraps = aFlag;
  999. }
  1000.  
  1001. - (void)wraps {
  1002. return _wraps;
  1003. }
  1004.  
  1005. @end
  1006.  
  1007. var secureStringForString = function(aString)
  1008. {
  1009. // This is true for when aString === "" and null/undefined.
  1010. if (!aString)
  1011. return "";
  1012.  
  1013. return Array(aString.length+1).join(CPSecureTextFieldCharacter);
  1014. }
  1015.  
  1016.  
  1017. var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
  1018. CPTextFieldIsSelectableKey = "CPTextFieldIsSelectableKey",
  1019. CPTextFieldIsBorderedKey = "CPTextFieldIsBorderedKey",
  1020. CPTextFieldIsBezeledKey = "CPTextFieldIsBezeledKey",
  1021. CPTextFieldBezelStyleKey = "CPTextFieldBezelStyleKey",
  1022. CPTextFieldDrawsBackgroundKey = "CPTextFieldDrawsBackgroundKey",
  1023. CPTextFieldLineBreakModeKey = "CPTextFieldLineBreakModeKey",
  1024. CPTextFieldBackgroundColorKey = "CPTextFieldBackgroundColorKey",
  1025. CPTextFieldPlaceholderStringKey = "CPTextFieldPlaceholderStringKey";
  1026.  
  1027. @implementation CPTextField (CPCoding)
  1028.  
  1029. /*!
  1030. Initializes the textfield with data from a coder.
  1031. @param aCoder the coder from which to read the textfield data
  1032. @return the initialized textfield
  1033. */
  1034. - (id)initWithCoder:(CPCoder)aCoder
  1035. {
  1036. self = [super initWithCoder:aCoder];
  1037.  
  1038. if (self)
  1039. {
  1040. [self setEditable:[aCoder decodeBoolForKey:CPTextFieldIsEditableKey]];
  1041. [self setSelectable:[aCoder decodeBoolForKey:CPTextFieldIsSelectableKey]];
  1042.  
  1043. [self setDrawsBackground:[aCoder decodeBoolForKey:CPTextFieldDrawsBackgroundKey]];
  1044.  
  1045. [self setTextFieldBackgroundColor:[aCoder decodeObjectForKey:CPTextFieldBackgroundColorKey]];
  1046.  
  1047. [self setPlaceholderString:[aCoder decodeObjectForKey:CPTextFieldPlaceholderStringKey]];
  1048. }
  1049.  
  1050. return self;
  1051. }
  1052.  
  1053. /*!
  1054. Encodes the data of this textfield into the provided coder.
  1055. @param aCoder the coder into which the data will be written
  1056. */
  1057. - (void)encodeWithCoder:(CPCoder)aCoder
  1058. {
  1059. [super encodeWithCoder:aCoder];
  1060.  
  1061. [aCoder encodeBool:_isEditable forKey:CPTextFieldIsEditableKey];
  1062. [aCoder encodeBool:_isSelectable forKey:CPTextFieldIsSelectableKey];
  1063.  
  1064. [aCoder encodeBool:_drawsBackground forKey:CPTextFieldDrawsBackgroundKey];
  1065.  
  1066. [aCoder encodeObject:_textFieldBackgroundColor forKey:CPTextFieldBackgroundColorKey];
  1067.  
  1068. [aCoder encodeObject:_placeholderString forKey:CPTextFieldPlaceholderStringKey];
  1069. }
  1070.  
  1071. @end
Add Comment
Please, Sign In to add comment