1 { $Id: $}
2 { --------------------------------------------
3 cocoatextedits.pas - Cocoa internal classes
4 --------------------------------------------
5
6 This unit contains the private classhierarchy for the Cocoa implemetations
7
8 *****************************************************************************
9 This file is part of the Lazarus Component Library (LCL)
10
11 See the file COPYING.modifiedLGPL.txt, included in this distribution,
12 for details about the license.
13 *****************************************************************************
14 }
15 unit CocoaTextEdits;
16
17 {$mode objfpc}{$H+}
18 {$modeswitch objectivec1}
19 {$modeswitch objectivec2}
20 {$interfaces corba}
21 {$include cocoadefines.inc}
22
23 {.$DEFINE COCOA_DEBUG_SETBOUNDS}
24 {.$DEFINE COCOA_SPIN_DEBUG}
25 {.$DEFINE COCOA_SPINEDIT_INSIDE_CONTAINER}
26
27 interface
28
29 uses
30 // rtl+ftl
31 Types, Classes, SysUtils,
32 CGGeometry,
33 // Libs
34 MacOSAll, CocoaAll, CocoaUtils, CocoaGDIObjects,
35 cocoa_extra, CocoaPrivate,
36 // LCL
37 LCLType;
38
39 const
40 SPINEDIT_DEFAULT_STEPPER_WIDTH = 15;
41 SPINEDIT_EDIT_SPACING_FOR_SELECTION = 4;
42
43 // From Interface Builder MacOSX 10.6
44 // The heights are from layout rectangle.
45 COMBOBOX_REG_HEIGHT = 20;
46 COMBOBOX_SMALL_HEIGHT = 17;
47 COMBOBOX_MINI_HEIGHT = 14;
48
49 COMBOBOX_RO_REG_HEIGHT = 20;
50 COMBOBOX_RO_SMALL_HEIGHT = 17;
51 COMBOBOX_RO_MINI_HEIGHT = 15;
52
53
54 type
55
56 TCocoaFieldEditor = objcclass;
57
58 NSTextField_LCLExt = objcprotocol
59 procedure lclSetMaxLength(amax: integer); message 'lclSetMaxLength:';
60 end;
61
62 { TCocoaTextField }
63
64 TCocoaTextField = objcclass(NSTextField, NSTextField_LCLExt)
65 callback: ICommonCallback;
66 maxLength: Integer;
acceptsFirstRespondernull67 function acceptsFirstResponder: LCLObjCBoolean; override;
lclGetCallbacknull68 function lclGetCallback: ICommonCallback; override;
69 procedure lclClearCallback; override;
70 procedure resetCursorRects; override;
71 // key
72 procedure textDidChange(notification: NSNotification); override;
73 // mouse
74 procedure mouseDown(event: NSEvent); override;
75 procedure mouseUp(event: NSEvent); override;
76 procedure rightMouseDown(event: NSEvent); override;
77 procedure rightMouseUp(event: NSEvent); override;
78 procedure otherMouseDown(event: NSEvent); override;
79 procedure otherMouseUp(event: NSEvent); override;
80 procedure mouseDragged(event: NSEvent); override;
81 procedure mouseMoved(event: NSEvent); override;
82 procedure scrollWheel(event: NSEvent); override;
83
84 procedure lclSetMaxLength(amax: integer);
85 end;
86
87 { TCocoaSecureTextField }
88
89 TCocoaSecureTextField = objcclass(NSSecureTextField, NSTextField_LCLExt)
90 public
91 maxLength: Integer;
92 callback: ICommonCallback;
acceptsFirstRespondernull93 function acceptsFirstResponder: LCLObjCBoolean; override;
94 procedure resetCursorRects; override;
lclGetCallbacknull95 function lclGetCallback: ICommonCallback; override;
96 procedure lclClearCallback; override;
97 // key
98 procedure textDidChange(notification: NSNotification); override;
99 // mouse
100 procedure mouseDown(event: NSEvent); override;
101 procedure mouseUp(event: NSEvent); override;
102 procedure rightMouseDown(event: NSEvent); override;
103 procedure rightMouseUp(event: NSEvent); override;
104 procedure otherMouseDown(event: NSEvent); override;
105 procedure otherMouseUp(event: NSEvent); override;
106 procedure mouseDragged(event: NSEvent); override;
107 procedure mouseMoved(event: NSEvent); override;
108 procedure scrollWheel(event: NSEvent); override;
109
110 procedure lclSetMaxLength(amax: integer);
111 end;
112
113 { TCocoaTextView }
114
115 TCocoaTextView = objcclass(NSTextView, NSTextDelegateProtocol, NSTextViewDelegateProtocol)
116 public
117 callback: ICommonCallback;
118 FEnabled: Boolean;
119 FUndoManager: NSUndoManager;
120
121 supressTextChangeEvent: Integer; // if above zero, then don't send text change event
122 keyCaptured: Boolean;
123 wantReturns: Boolean;
124
125 procedure dealloc; override;
126 function acceptsFirstResponder: LCLObjCBoolean; override;
127 function lclGetCallback: ICommonCallback; override;
128 procedure lclClearCallback; override;
129 procedure resetCursorRects; override;
130
131 procedure changeColor(sender: id); override;
132 // keyboard
133 procedure doCommandBySelector(aSelector: SEL); override;
134 procedure insertNewline(sender: id); override;
135 procedure keyDown(event: NSEvent); override;
136 // mouse
137 procedure mouseDown(event: NSEvent); override;
138 procedure mouseUp(event: NSEvent); override;
139 procedure rightMouseDown(event: NSEvent); override;
140 procedure rightMouseUp(event: NSEvent); override;
141 procedure otherMouseDown(event: NSEvent); override;
142 procedure otherMouseUp(event: NSEvent); override;
143
144 procedure mouseDragged(event: NSEvent); override;
145 procedure mouseEntered(event: NSEvent); override;
146 procedure mouseExited(event: NSEvent); override;
147 procedure mouseMoved(event: NSEvent); override;
148
149 function lclIsEnabled: Boolean; override;
150 procedure lclSetEnabled(AEnabled: Boolean); override;
151
152 // delegate methods
153 procedure textDidChange(notification: NSNotification); message 'textDidChange:';
154 procedure lclExpectedKeys(var wantTabs, wantArrows, wantReturn, wantAll: Boolean); override;
155 function undoManagerForTextView(view: NSTextView): NSUndoManager; message 'undoManagerForTextView:';
156 end;
157
158 { TCococaFieldEditorExt }
159
160 TCococaFieldEditorExt = objccategory(NSTextView)
161 // this override should take care of any Cocoa editors possible
162 // for example NSSecureTextView used with TCocoaSecureField aka NSSecureTextField
163 function lclGetCallback: ICommonCallback; reintroduce;
164 end;
165
166 { TCocoaFieldEditor }
167
168 TCocoaFieldEditor = objcclass(NSTextView)
169 public
170 // This flag is used to hack an infinite loop
171 // when switching "editable" (readonly) mode of NSTextField
172 // see TCocoaWSCustomEdit.SetReadOnly
173 goingReadOnly: Boolean;
174 keyCaptured: Boolean;
175 function lclGetCallback: ICommonCallback; override;
176 function becomeFirstResponder: LCLObjCBoolean; override;
177 // keyboard
178 procedure doCommandBySelector(aSelector: SEL); override;
179 procedure insertNewline(sender: id); override;
180 procedure keyDown(event: NSEvent); override;
181 // mouse
182 procedure mouseDown(event: NSEvent); override;
183 procedure mouseUp(event: NSEvent); override;
184 procedure rightMouseDown(event: NSEvent); override;
185 procedure rightMouseUp(event: NSEvent); override;
186 procedure otherMouseDown(event: NSEvent); override;
187 procedure otherMouseUp(event: NSEvent); override;
188 procedure mouseDragged(event: NSEvent); override;
189 procedure mouseMoved(event: NSEvent); override;
190 procedure scrollWheel(event: NSEvent); override;
191 end;
192
193 const
194 COMBOBOX_RO_MENUITEM_HEIGHT = 18;
195
196 type
197 TCocoaComboBox = objcclass;
198 TCocoaReadOnlyComboBox = objcclass;
199
200 { TCocoaComboBoxList }
201
202 TCocoaComboBoxList = class(TStringList);
203
204 TCocoaEditComboBoxList = class(TCocoaComboBoxList)
205 protected
206 FOwner: TCocoaComboBox;
207 procedure InsertItem(Index: Integer; const S: string; O: TObject); override;
208 procedure Changed; override;
209 public
210 // Pass only 1 owner and nil for the other ones
211 constructor Create(AOwner: TCocoaComboBox);
212 end;
213
214 IComboboxCallBack = interface(ICommonCallBack)
215 procedure ComboBoxWillPopUp;
216 procedure ComboBoxWillDismiss;
217 procedure ComboBoxSelectionDidChange;
218 procedure ComboBoxSelectionIsChanging;
219
220 procedure ComboBoxDrawItem(itemIndex: Integer; ctx: TCocoaContext;
221 const r: TRect; isSelected: Boolean);
222 end;
223
224 { TCocoaComboBox }
225
226 { TCocoaComboBoxItemCell }
227
228 // represents an item in the combobox dropdown
229 // it should be able to call "draw" callback
230
231 TCocoaComboBoxItemCell = objcclass(NSTextFieldCell)
232 procedure drawWithFrame_inView(cellFrame: NSRect; controlView_: NSView); override;
233 end;
234
235 { TCocoaComboBoxCell }
236
237 // represents combobox itself. All functionality is implemented
238 // in NSComboBoxCell. The cell is also acting as a delegate
239 // for NSTextView, that's used in popup drop-down window.
240 // Apple is deprecating "cells" so NSComboBox implementation
241 // will change in future and it must be expected that NSComboBoxCell
242 // would not be used in future.
243
244 TCocoaComboBoxCell = objcclass(NSComboBoxCell)
tableView_objectValueForTableColumn_rownull245 //function tableView_objectValueForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): id; message 'tableView:objectValueForTableColumn:row:';
246 function tableView_dataCellForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): NSCell; message 'tableView:dataCellForTableColumn:row:';
tableView_sizeToFitWidthOfColumnnull247 //function tableView_sizeToFitWidthOfColumn(tableView: NSTableView; column: NSInteger): CGFloat; message 'tableView:sizeToFitWidthOfColumn:';
248 //procedure tableView_willDisplayCell_forTableColumn_row(tableView: NSTableView; cell: id; tableColumn: NSTableColumn; row: NSInteger); message 'tableView:willDisplayCell:forTableColumn:row:';
249 //function tableView_heightOfRow(tableView: NSTableView; row: NSInteger): CGFloat; message 'tableView:heightOfRow:';
250 end;
251
252 TCocoaComboBox = objcclass(NSComboBox, NSComboBoxDataSourceProtocol, NSComboBoxDelegateProtocol)
253 private
254 userSel: boolean;
255 public
256 callback: IComboboxCallBack;
257 list: TCocoaComboBoxList;
258 resultNS: NSString; //use to return values to combo
259 isDown: Boolean;
acceptsFirstRespondernull260 function acceptsFirstResponder: LCLObjCBoolean; override;
261 procedure textDidChange(notification: NSNotification); override;
262 // NSComboBoxDataSourceProtocol
comboBox_objectValueForItemAtIndex_null263 function comboBox_objectValueForItemAtIndex_(combo: TCocoaComboBox; row: NSInteger): id; message 'comboBox:objectValueForItemAtIndex:';
comboBox_indexOfItemWithStringValuenull264 function comboBox_indexOfItemWithStringValue(aComboBox: NSComboBox; string_: NSString): NSUInteger; message 'comboBox:indexOfItemWithStringValue:';
numberOfItemsInComboBoxnull265 function numberOfItemsInComboBox(combo: TCocoaComboBox): NSInteger; message 'numberOfItemsInComboBox:';
266 //
267 procedure dealloc; override;
lclGetCallbacknull268 function lclGetCallback: ICommonCallback; override;
269 procedure lclClearCallback; override;
270 procedure resetCursorRects; override;
271 // NSComboBoxDelegateProtocol
272 procedure comboBoxWillPopUp(notification: NSNotification); message 'comboBoxWillPopUp:';
273 procedure comboBoxWillDismiss(notification: NSNotification); message 'comboBoxWillDismiss:';
274 procedure comboBoxSelectionDidChange(notification: NSNotification); message 'comboBoxSelectionDidChange:';
275 procedure comboBoxSelectionIsChanging(notification: NSNotification); message 'comboBoxSelectionIsChanging:';
276 //
277 procedure setStringValue(avalue: NSString); override;
lclGetFrameToLayoutDeltanull278 function lclGetFrameToLayoutDelta: TRect; override;
279 // mouse
acceptsFirstMousenull280 function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
281 procedure mouseDown(event: NSEvent); override;
282 procedure mouseUp(event: NSEvent); override;
283 procedure rightMouseDown(event: NSEvent); override;
284 procedure rightMouseUp(event: NSEvent); override;
285 procedure rightMouseDragged(event: NSEvent); override;
286 procedure otherMouseDown(event: NSEvent); override;
287 procedure otherMouseUp(event: NSEvent); override;
288 procedure otherMouseDragged(event: NSEvent); override;
289 procedure mouseDragged(event: NSEvent); override;
290 procedure mouseMoved(event: NSEvent); override;
291 procedure scrollWheel(event: NSEvent); override;
292 end;
293
294 { TCocoaReadOnlyView }
295
296 TCocoaReadOnlyView = objcclass (NSView)
297 itemIndex: Integer;
298 combobox: TCocoaReadOnlyComboBox;
299 isMouseOver: Boolean;
300 procedure drawRect(dirtyRect: NSRect); override;
301 procedure mouseUp(event: NSEvent); override;
302 procedure mouseEntered(theEvent: NSEvent); override;
303 procedure mouseExited(theEvent: NSEvent); override;
304 end;
305
306 { TCocoaReadOnlyComboBoxList }
307
308 TCocoaReadOnlyComboBoxList = class(TCocoaComboBoxList)
309 protected
310 FOwner: TCocoaReadOnlyComboBox;
311 procedure InsertItem(Index: Integer; const S: string; O: TObject); override;
312 procedure Put(Index: Integer; const S: string); override;
313 public
314 // Pass only 1 owner and nil for the other ones
315 constructor Create(AOwner: TCocoaReadOnlyComboBox);
316 procedure Delete(Index: Integer); override;
317 procedure Clear; override;
318 end;
319
320 { TCocoaReadOnlyComboBox }
321
322 TCocoaReadOnlyComboBox = objcclass(NSPopUpButton)
323 public
324 //Owner: TCustomComboBox;
325 callback: IComboboxCallBack;
326 list: TCocoaComboBoxList;
327 resultNS: NSString; //use to return values to combo
328 lastSelectedItemIndex: Integer; // -1 means invalid or none selected
329
330 isOwnerDrawn: Boolean;
331 isOwnerMeasure: Boolean;
acceptsFirstRespondernull332 function acceptsFirstResponder: LCLObjCBoolean; override;
333 procedure dealloc; override;
lclGetCallbacknull334 function lclGetCallback: ICommonCallback; override;
335 procedure lclClearCallback; override;
lclGetFrameToLayoutDeltanull336 function lclGetFrameToLayoutDelta: TRect; override;
337 procedure resetCursorRects; override;
338 procedure comboboxAction(sender: id); message 'comboboxAction:';
stringValuenull339 function stringValue: NSString; override;
340 // drawing
341 procedure drawRect(dirtyRect: NSRect); override;
342 // mouse
acceptsFirstMousenull343 function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
344 procedure mouseDown(event: NSEvent); override;
345 procedure mouseUp(event: NSEvent); override;
346 procedure rightMouseDown(event: NSEvent); override;
347 procedure rightMouseUp(event: NSEvent); override;
348 procedure rightMouseDragged(event: NSEvent); override;
349 procedure otherMouseDown(event: NSEvent); override;
350 procedure otherMouseUp(event: NSEvent); override;
351 procedure otherMouseDragged(event: NSEvent); override;
352 procedure mouseDragged(event: NSEvent); override;
353 procedure mouseMoved(event: NSEvent); override;
354 procedure scrollWheel(event: NSEvent); override;
355 end;
356
357 { TCocoaSpinEdit }
358 {$IFDEF COCOA_SPINEDIT_INSIDE_CONTAINER}
359 TCocoaSpinEdit = objcclass(NSControl)
360 public
361 callback: ICommonCallback;
362 Stepper: NSStepper;
363 Edit: NSTextField;
364 Spin: TCustomFloatSpinEdit;
365 procedure dealloc; override;
366 procedure UpdateControl(ASpinEdit: TCustomFloatSpinEdit); message 'UpdateControl:';
367 procedure CreateSubcontrols(ASpinEdit: TCustomFloatSpinEdit; const AParams: TCreateParams); message 'CreateSubControls:AParams:';
368 procedure PositionSubcontrols(const ALeft, ATop, AWidth, AHeight: Integer); message 'PositionSubcontrols:ATop:AWidth:AHeight:';
369 procedure CalculateSubcontrolPos(const ASpinLCLBounds: TRect; out AEditBounds, AStepperBounds: TRect); message 'CalculateSubcontrolPos:AEditBounds:AStepperBounds:';
370 procedure StepperChanged(sender: NSObject); message 'StepperChanged:';
371 // lcl
acceptsFirstRespondernull372 function acceptsFirstResponder: Boolean; override;
lclGetCallbacknull373 function lclGetCallback: ICommonCallback; override;
374 procedure lclClearCallback; override;
375 // NSViewFix
fittingSizenull376 function fittingSize: NSSize; override;
377 end;
378 {$ELSE}
379
380 { TCocoaSpinEditStepper }
381
382 TCocoaSpinEditStepper = objcclass(NSStepper)
383 callback: ICommonCallback;
acceptsFirstMousenull384 function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
385 procedure mouseDown(event: NSEvent); override;
386 procedure mouseUp(event: NSEvent); override;
387 procedure rightMouseDown(event: NSEvent); override;
388 procedure rightMouseUp(event: NSEvent); override;
389 procedure rightMouseDragged(event: NSEvent); override;
390 procedure otherMouseDown(event: NSEvent); override;
391 procedure otherMouseUp(event: NSEvent); override;
392 procedure otherMouseDragged(event: NSEvent); override;
393 procedure mouseDragged(event: NSEvent); override;
394 procedure mouseMoved(event: NSEvent); override;
395 procedure scrollWheel(event: NSEvent); override;
396 end;
397
398 TCocoaSpinEdit = objcclass(TCocoaTextField)
399 Stepper: NSStepper;
400 NumberFormatter: NSNumberFormatter;
401 decimalPlaces: Integer;
402
403 avoidChangeEvent: Integer;
404
405 //Spin: TCustomFloatSpinEdit;
406 procedure dealloc; override;
updateSteppernull407 function updateStepper: boolean; message 'updateStepper';
408 procedure UpdateControl(min, max, inc, avalue: double; ADecimalPlaces: Integer); message 'UpdateControl:::::';
409 procedure lclCreateSubcontrols(const AParams: TCreateParams); message 'lclCreateSubControls:';
410 procedure lclReleaseSubcontrols; message 'lclReleaseSubcontrols';
411 procedure PositionSubcontrols(const ALeft, ATop, AWidth, AHeight: Integer); message 'PositionSubcontrols:ATop:AWidth:AHeight:';
412 procedure StepperChanged(sender: NSObject); message 'StepperChanged:';
413 procedure textDidChange(notification: NSNotification); override;
414 // lcl
acceptsFirstRespondernull415 function acceptsFirstResponder: LCLObjCBoolean; override;
lclGetCallbacknull416 function lclGetCallback: ICommonCallback; override;
417 procedure lclClearCallback; override;
418 procedure resetCursorRects; override;
419 procedure lclSetVisible(AVisible: Boolean); override;
420 procedure lclSetFrame(const r: TRect); override;
421 // NSViewFix
fittingSizenull422 function fittingSize: NSSize; override;
423 // mouse
acceptsFirstMousenull424 function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
425 procedure mouseDown(event: NSEvent); override;
426 procedure mouseUp(event: NSEvent); override;
427 procedure rightMouseDown(event: NSEvent); override;
428 procedure rightMouseUp(event: NSEvent); override;
429 procedure rightMouseDragged(event: NSEvent); override;
430 procedure otherMouseDown(event: NSEvent); override;
431 procedure otherMouseUp(event: NSEvent); override;
432 procedure otherMouseDragged(event: NSEvent); override;
433 procedure mouseDragged(event: NSEvent); override;
434 procedure mouseMoved(event: NSEvent); override;
435 end;
436 {$ENDIF}
437
438 // these constants are missing from CocoaAll for some reason
439 const
440 NSTextAlignmentLeft = 0;
441 NSTextAlignmentRight = 1; // it's 2 for iOS and family
442 NSTextAlignmentCenter = 2; // it's 1 for iOS and family
443 NSTextAlignmentJustified = 3;
444 NSTextAlignmentNatural = 4;
445
GetFieldEditornull446 function GetFieldEditor(afield: NSTextField): TCocoaFieldEditor;
447
448 implementation
449
GetFieldEditornull450 function GetFieldEditor(afield: NSTextField): TCocoaFieldEditor;
451 var
452 lFieldEditor: TCocoaFieldEditor;
453 lText: NSText;
454 window: NSWindow;
455 begin
456 Result := nil;
457 if not Assigned(afield) then Exit;
458 window := afield.window;
459 if window = nil then Exit;
460
461 {$ifdef BOOLFIX}
462 lText := window.fieldEditor_forObject_(Ord(True), afield);
463 {$else}
464 lText := window.fieldEditor_forObject(True, afield);
465 {$endif}
466 if (lText <> nil) and lText.isKindOfClass_(TCocoaFieldEditor) then
467 begin
468 Result := TCocoaFieldEditor(lText);
469 end;
470 end;
471
472 { TCocoaReadOnlyComboBoxList }
473
474 procedure TCocoaReadOnlyComboBoxList.InsertItem(Index: Integer;
475 const S: string; O: TObject);
476 var
477 astr : NSString;
478 mn : NSMenuItem;
479 menuItem : TCocoaReadOnlyView;
480 track: NSTrackingArea;
481 begin
482 inherited InsertItem(Index, S, O);
483
484 // Adding an item with its final name will cause it to be deleted,
485 // so we need to first add all items with unique names, and then
486 // rename all of them, see bug 30847
487 astr := nsstr('@@@add');
488 if Index >= FOwner.numberOfItems then
489 begin
490 FOwner.addItemWithTitle(astr);
491 Index := FOwner.numberOfItems-1;
492 end else
493 FOwner.insertItemWithTitle_atIndex(astr, Index);
494
495 mn := FOwner.itemAtIndex(Index);
496 if not Assigned(mn) then Exit;
497
498 astr := NSStringUtf8(S);
499 mn.setTitle(astr);
500 astr.release;
501
502 if FOwner.isOwnerDrawn then
503 begin
504 menuItem := TCocoaReadOnlyView.alloc.initWithFrame( NSMakeRect(0,0, FOwner.frame.size.width, COMBOBOX_RO_MENUITEM_HEIGHT) );
505 menuItem.itemIndex := Index;
506 menuItem.combobox := FOwner;
507
508 track:=NSTrackingArea(NSTrackingArea.alloc).initWithRect_options_owner_userInfo(
509 menuItem.bounds
510 , NSTrackingMouseEnteredAndExited or NSTrackingActiveAlways
511 , menuItem, nil);
512 menuItem.addTrackingArea(track);
513 track.release;
514 mn.setView(menuItem);
515 end;
516 end;
517
518 procedure TCocoaReadOnlyComboBoxList.Put(Index: Integer; const S: string);
519 var
520 astr: NSString;
521 mn : NSMenuItem;
522 begin
523 inherited Put(Index, S);
524 if ((index >= 0) and (Index <= FOwner.numberOfItems)) then
525 begin
526 mn := FOwner.itemAtIndex(Index);
527 astr := NSStringUtf8(S);
528 mn.setTitle(astr);
529 astr.release;
530 end;
531 end;
532
533 constructor TCocoaReadOnlyComboBoxList.Create(AOwner: TCocoaReadOnlyComboBox);
534 begin
535 inherited Create;
536 FOwner := AOwner;
537 end;
538
539 procedure TCocoaReadOnlyComboBoxList.Delete(Index: Integer);
540 begin
541 inherited Delete(Index);
542 if (Index>=0) and (Index < FOwner.numberOfItems) then
543 FOwner.removeItemAtIndex(Index);
544 end;
545
546 procedure TCocoaReadOnlyComboBoxList.Clear;
547 begin
548 inherited Clear;
549 FOwner.removeAllItems;
550 end;
551
552 { TCococaFieldEditorExt }
553
lclGetCallbacknull554 function TCococaFieldEditorExt.lclGetCallback: ICommonCallback;
555 begin
556 if isFieldEditor and Assigned(delegate) then
557 begin
558 Result := NSObject(delegate).lclGetCallback;
559 end
560 else
561 Result := inherited lclGetCallback;
562 end;
563
564 { TCocoaSpinEditStepper }
565
TCocoaSpinEditStepper.acceptsFirstMousenull566 function TCocoaSpinEditStepper.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
567 begin
568 Result := NSViewCanFocus(Self);
569 end;
570
571 procedure TCocoaSpinEditStepper.mouseDown(event: NSEvent);
572 begin
573 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
574 begin
575 inherited mouseDown(event);
576 if Assigned(Callback) then
577 callback.MouseUpDownEvent(event, true);
578 end;
579 end;
580
581 procedure TCocoaSpinEditStepper.mouseUp(event: NSEvent);
582 begin
583 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
584 inherited mouseUp(event);
585 end;
586
587 procedure TCocoaSpinEditStepper.rightMouseDown(event: NSEvent);
588 begin
589 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
590 inherited rightMouseDown(event);
591 end;
592
593 procedure TCocoaSpinEditStepper.rightMouseUp(event: NSEvent);
594 begin
595 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
596 inherited rightMouseUp(event);
597 end;
598
599 procedure TCocoaSpinEditStepper.rightMouseDragged(event: NSEvent);
600 begin
601 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
602 inherited rightMouseDragged(event);
603 end;
604
605 procedure TCocoaSpinEditStepper.otherMouseDown(event: NSEvent);
606 begin
607 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
608 inherited otherMouseDown(event);
609 end;
610
611 procedure TCocoaSpinEditStepper.otherMouseUp(event: NSEvent);
612 begin
613 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
614 inherited otherMouseUp(event);
615 end;
616
617 procedure TCocoaSpinEditStepper.otherMouseDragged(event: NSEvent);
618 begin
619 if not Assigned(callback) or not callback.MouseMove(event) then
620 inherited otherMouseDragged(event);
621 end;
622
623 procedure TCocoaSpinEditStepper.mouseDragged(event: NSEvent);
624 begin
625 if not Assigned(callback) or not callback.MouseMove(event) then
626 inherited mouseDragged(event);
627 end;
628
629 procedure TCocoaSpinEditStepper.mouseMoved(event: NSEvent);
630 begin
631 if not Assigned(callback) or not callback.MouseMove(event) then
632 inherited mouseMoved(event);
633 end;
634
635 procedure TCocoaSpinEditStepper.scrollWheel(event: NSEvent);
636 begin
637 if not Assigned(callback) or not callback.scrollWheel(event) then
638 inherited scrollWheel(event);
639 end;
640
641 { TCocoaReadOnlyView }
642
643 procedure TCocoaReadOnlyView.drawRect(dirtyRect: NSRect);
644 var
645 ctx : TCocoaContext;
646 begin
647 inherited drawRect(dirtyRect);
648
649 if not Assigned(combobox) then Exit;
650
651 ctx := TCocoaContext.Create(NSGraphicsContext.currentContext);
652 try
653 ctx.InitDraw(Round(dirtyRect.size.width), Round(dirtyRect.size.height));
654 combobox.callback.ComboBoxDrawItem(itemIndex, ctx, NSRectToRect(frame), isMouseOver);
655 finally
656 ctx.Free;
657 end;
658 end;
659
660 procedure TCocoaReadOnlyView.mouseUp(event: NSEvent);
661 begin
662 inherited mouseUp(event);
663 if Assigned(combobox) then
664 begin
665 combobox.selectItemAtIndex(itemIndex);
666 combobox.callback.ComboBoxSelectionDidChange;
667 combobox.menu.performActionForItemAtIndex(itemIndex);
668 combobox.menu.cancelTracking;
669 end;
670 end;
671
672 procedure TCocoaReadOnlyView.mouseEntered(theEvent: NSEvent);
673 begin
674 isMouseOver := true;
675 inherited mouseEntered(theEvent);
676 lclInvalidate;
677 end;
678
679 procedure TCocoaReadOnlyView.mouseExited(theEvent: NSEvent);
680 begin
681 isMouseOver := false;
682 inherited mouseExited(theEvent);
683 lclInvalidate;
684 end;
685
686 { TCocoaComboBoxItemCell }
687
688 procedure TCocoaComboBoxItemCell.drawWithFrame_inView(cellFrame: NSRect; controlView_: NSView);
689 begin
690 inherited drawWithFrame_inView(cellFrame, controlView_);
691 end;
692
tableView_dataCellForTableColumn_rownull693 function TCocoaComboBoxCell.tableView_dataCellForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): NSCell;
694 begin
695 Result := TCocoaComboBoxItemCell.alloc.initTextCell(NSString.string_);
696 end;
697
698 {
699 procedure TCocoaComboBoxCell.tableView_willDisplayCell_forTableColumn_row(
700 tableView: NSTableView; cell: id; tableColumn: NSTableColumn; row: NSInteger);
701 var
702 sz : NSSize;
703 pr : NSView;
704 frm : NSRect;
705 begin
706 writeln('will display ', row);
707 if row = 0 then
708 begin
709 sz := tableView.frame.size;
710 sz.width := 300;
711 tableView.setFrameSize(sz);
712 pr := tableView;
713 while Assigned(pr) do begin
714 writeln(pr.lclClassname);
715 pr := pr.superview;
716 end;
717 writeln('at 10: ', tableView.window.lclClassName);
718 writeln('max size = ', tableView.window.maxSize.width:0:0);
719 writeln('min size = ', tableView.window.minSize.width:0:0);
720 frm := tableView.window.frame;
721 writeln(' size = ', frm.size.width:0:0);
722 frm := NSView(tableView.window.contentView).frame;
723 writeln('clt size = ', frm.size.width:0:0);
724 frm.size.width := 96 * 2; //frm.size.width * 2;
725 tableView.window.setContentSize(frm.size);
726 writeln('clt size = ', frm.size.width:0:0);
727 end;
728 end;
729 }
730
731 {function TCocoaComboBoxCell.tableView_heightOfRow(tableView: NSTableView;
732 row: NSInteger): CGFloat;
733 begin
734 writeln('height of row ', row);
735 Result := 32;
736 end;}
737
738 { TCocoaFieldEditor }
739
GetEditBoxnull740 function GetEditBox(src: TCocoaFieldEditor): NSView;
741 var
742 v : NSObject;
743 begin
744 Result := nil;
745 if not Assigned(src) then Exit;
746 v := NSObject(src.delegate);
747 if Assigned(v) and (v.isKindOfClass(NSView)) then
748 Result := NSView(v);
749 end;
750
TCocoaFieldEditor.lclGetCallbacknull751 function TCocoaFieldEditor.lclGetCallback: ICommonCallback;
752 begin
753 if Assigned(delegate) then Result := NSObject(delegate).lclGetCallback
754 else Result := nil;
755 end;
756
TCocoaFieldEditor.becomeFirstRespondernull757 function TCocoaFieldEditor.becomeFirstResponder: LCLObjCBoolean;
758 begin
759 if goingReadOnly then Result := false
760 else Result:=inherited becomeFirstResponder;
761 end;
762
763 procedure TCocoaFieldEditor.doCommandBySelector(aSelector: SEL);
764 begin
765 inherited doCommandBySelector(aSelector);
766 keyCaptured := False;
767 end;
768
769 procedure TCocoaFieldEditor.insertNewline(sender: id);
770 begin
771 // 10.6 cocoa handles the editors Return key as "insertNewLine" command (that makes sense)
772 // which turns into textDidEndEditing done command (that also makes sense)
773 // however, it ends up in an endless loop of "end-editing" calls.
774 //
775 // todo: find the reason for the endless loop and resolve it properly
776 end;
777
778 procedure TCocoaFieldEditor.keyDown(event: NSEvent);
779 begin
780 // Input methods may capture Enter and Escape to accept/dismiss their popup
781 // windows. There isn't a way to detect the popup is open, so allow the
782 // keys through. If they make it to the default handlers let the LCL process
783 // them further. If they got swallowed prevent further processing.
784 if Assigned(lclGetCallback) and (event.modifierFlags_ = 0) and
785 ((NSEventRawKeyChar(event) = #13) or (NSEventRawKeyChar(event) = #27)) then
786 begin
787 keyCaptured := True;
788 inherited keyDown(event);
789 if keyCaptured then
790 lclGetCallback.KeyEvHandled;
791 end
792 else
793 inherited keyDown(event);
794 end;
795
796 procedure TCocoaFieldEditor.mouseDown(event: NSEvent);
797 var
798 v : NSView;
799 begin
800 v := GetEditBox(Self);
801 if Assigned(v) then
802 begin
803 if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
804 begin
805 inherited mouseDown(event);
806 // NSTextView runs internal mouse-tracking loop in it's mouseDown implemenation.
807 // Thus "inherited mouseDown" only returns after the mouse has been released.
808 // why is TCocoaTextView not affected?
809 if Assigned(v) and Assigned(v.lclGetCallback) then
810 v.lclGetCallback.MouseUpDownEvent(event, true);
811 end;
812 end else
813 inherited mouseDown(event);
814 end;
815
816 procedure TCocoaFieldEditor.mouseUp(event: NSEvent);
817 var
818 v : NSView;
819 begin
820 v := GetEditBox(Self);
821 if Assigned(v) then
822 begin
823 if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
824 inherited mouseUp(event);
825 end else
826 inherited mouseUp(event);
827 end;
828
829 procedure TCocoaFieldEditor.rightMouseDown(event: NSEvent);
830 var
831 v : NSView;
832 begin
833 v := GetEditBox(Self);
834 if Assigned(v) then
835 begin
836 if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
837 inherited rightMouseDown(event);
838 end else
839 inherited rightMouseDown(event);
840 end;
841
842 procedure TCocoaFieldEditor.rightMouseUp(event: NSEvent);
843 var
844 v : NSView;
845 begin
846 v := GetEditBox(Self);
847 if Assigned(v) then
848 begin
849 if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
850 inherited rightMouseUp(event);
851 end else
852 inherited rightMouseUp(event);
853 end;
854
855 procedure TCocoaFieldEditor.otherMouseDown(event: NSEvent);
856 var
857 v : NSView;
858 begin
859 v := GetEditBox(Self);
860 if Assigned(v) then
861 begin
862 if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
863 inherited otherMouseDown(event);
864 end else
865 inherited otherMouseDown(event);
866 end;
867
868 procedure TCocoaFieldEditor.otherMouseUp(event: NSEvent);
869 var
870 v : NSView;
871 begin
872 v := GetEditBox(Self);
873 if Assigned(v) then
874 begin
875 if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
876 inherited otherMouseUp(event);
877 end else
878 inherited otherMouseUp(event);
879 end;
880
881 procedure TCocoaFieldEditor.mouseDragged(event: NSEvent);
882 var
883 v : NSView;
884 begin
885 v := GetEditBox(Self);
886 if Assigned(v) then
887 begin
888 if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseMove(event) then
889 inherited mouseDragged(event);
890 end else
891 inherited mouseDragged(event);
892 end;
893
894 procedure TCocoaFieldEditor.mouseMoved(event: NSEvent);
895 var
896 v : NSView;
897 begin
898 v := GetEditBox(Self);
899 if Assigned(v) then
900 begin
901 if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseMove(event) then
902 inherited mouseMoved(event);
903 end else
904 inherited mouseMoved(event);
905 end;
906
907 procedure TCocoaFieldEditor.scrollWheel(event: NSEvent);
908 var
909 v : NSView;
910 begin
911 v := GetEditBox(Self);
912 if Assigned(v) then
913 begin
914 if Assigned(v.lclGetCallback) and not v.lclGetCallback.scrollWheel(event) then
915 inherited mouseMoved(event);
916 end else
917 inherited scrollWheel(event);
918 end;
919
920 { TCocoaTextField }
921
TCocoaTextField.acceptsFirstRespondernull922 function TCocoaTextField.acceptsFirstResponder: LCLObjCBoolean;
923 begin
924 Result := NSViewCanFocus(Self);
925 end;
926
TCocoaTextField.lclGetCallbacknull927 function TCocoaTextField.lclGetCallback: ICommonCallback;
928 begin
929 Result := callback;
930 end;
931
932 procedure TCocoaTextField.lclClearCallback;
933 begin
934 callback := nil;
935 end;
936
937 procedure TCocoaTextField.resetCursorRects;
938 begin
939 // this will not work well because
940 // cocoa replaced TextField and TextView cursors in
941 // mouseEntered, mouseMoved and CursorUpdate
942 if not callback.resetCursorRects then
943 inherited resetCursorRects;
944 end;
945
946 procedure TCocoaTextField.textDidChange(notification: NSNotification);
947 begin
948 if (maxLength>0) and Assigned(stringValue) and (stringValue.length > maxLength) then
949 setStringValue(stringValue.substringWithRange(NSMakeRange(0,maxLength)));
950 if callback <> nil then
951 callback.SendOnTextChanged;
952 end;
953
954 procedure TCocoaTextField.mouseDown(event: NSEvent);
955 begin
956 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
957 begin
958 inherited mouseDown(event);
959 // the text selection is handled withing mouseDown
960 if Assigned(callback) then
961 callback.MouseUpDownEvent(event, True);
962 end;
963 end;
964
965 procedure TCocoaTextField.mouseUp(event: NSEvent);
966 begin
967 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
968 begin
969 inherited mouseUp(event);
970 end;
971 end;
972
973 procedure TCocoaTextField.rightMouseDown(event: NSEvent);
974 begin
975 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
976 inherited rightMouseDown(event);
977 end;
978
979 procedure TCocoaTextField.rightMouseUp(event: NSEvent);
980 begin
981 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
982 inherited rightMouseUp(event);
983 end;
984
985 procedure TCocoaTextField.otherMouseDown(event: NSEvent);
986 begin
987 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
988 inherited otherMouseDown(event);
989 end;
990
991 procedure TCocoaTextField.otherMouseUp(event: NSEvent);
992 begin
993 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
994 inherited otherMouseUp(event);
995 end;
996
997 procedure TCocoaTextField.mouseDragged(event: NSEvent);
998 begin
999 if Assigned(callback) and not callback.MouseMove(event) then
1000 inherited mouseDragged(event);
1001 end;
1002
1003 procedure TCocoaTextField.mouseMoved(event: NSEvent);
1004 begin
1005 if Assigned(callback) and not callback.MouseMove(event) then
1006 inherited mouseMoved(event);
1007 end;
1008
1009 procedure TCocoaTextField.scrollWheel(event: NSEvent);
1010 begin
1011 if Assigned(callback) and not callback.scrollWheel(event) then
1012 inherited scrollWheel(event);
1013 end;
1014
1015 procedure TCocoaTextField.lclSetMaxLength(amax: integer);
1016 begin
1017 maxLength := amax;
1018 end;
1019
1020 { TCocoaTextView }
1021
1022 procedure TCocoaTextView.changeColor(sender: id);
1023 begin
1024 //preventing text color from being changed
1025 //inherited changeColor(sender);
1026 end;
1027
1028 procedure TCocoaTextView.dealloc;
1029 begin
1030 if Assigned(FUndoManager) then
1031 FUndoManager.release;
1032 inherited dealloc;
1033 end;
1034
acceptsFirstRespondernull1035 function TCocoaTextView.acceptsFirstResponder: LCLObjCBoolean;
1036 begin
1037 Result := NSViewCanFocus(Self);
1038 end;
1039
lclGetCallbacknull1040 function TCocoaTextView.lclGetCallback: ICommonCallback;
1041 begin
1042 Result := callback;
1043 end;
1044
1045 procedure TCocoaTextView.lclClearCallback;
1046 begin
1047 callback := nil;
1048 end;
1049
1050 procedure TCocoaTextView.resetCursorRects;
1051 begin
1052 if not callback.resetCursorRects then
1053 inherited resetCursorRects;
1054 end;
1055
1056 procedure TCocoaTextView.doCommandBySelector(aSelector: SEL);
1057 begin
1058 inherited doCommandBySelector(aSelector);
1059 keyCaptured := False;
1060 end;
1061
1062 procedure TCocoaTextView.insertNewline(sender: id);
1063 begin
1064 if wantReturns then
1065 inherited insertNewline(sender);
1066 end;
1067
1068 procedure TCocoaTextView.keyDown(event: NSEvent);
1069 begin
1070 // See TCocoaFieldEditor.keyDown
1071 if Assigned(lclGetCallback) and (event.modifierFlags_ = 0) and
1072 ((NSEventRawKeyChar(event) = #13) or (NSEventRawKeyChar(event) = #27)) then
1073 begin
1074 keyCaptured := True;
1075 inherited keyDown(event);
1076 if keyCaptured then
1077 lclGetCallback.KeyEvHandled;
1078 end
1079 else
1080 inherited keyDown(event);
1081 end;
1082
1083 procedure TCocoaTextView.mouseDown(event: NSEvent);
1084 begin
1085 if Assigned(callback) then
1086 begin
1087 if not callback.MouseUpDownEvent(event) then
1088 begin
1089 inherited mouseDown(event);
1090
1091 // Cocoa doesn't call mouseUp for NSTextView, so we have to emulate it here :(
1092 // See bug 29000
1093 if Assigned(callback) then
1094 callback.MouseUpDownEvent(event, True);
1095 end;
1096 end else
1097 inherited mouseDown(event);
1098 end;
1099
1100 procedure TCocoaTextView.mouseUp(event: NSEvent);
1101 begin
1102 if callback <> nil then
1103 callback.MouseUpDownEvent(event);
1104 inherited mouseUp(event);
1105 end;
1106
1107 procedure TCocoaTextView.rightMouseDown(event: NSEvent);
1108 begin
1109 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1110 inherited rightMouseDown(event);
1111 end;
1112
1113 procedure TCocoaTextView.rightMouseUp(event: NSEvent);
1114 begin
1115 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1116 inherited rightMouseUp(event);
1117 end;
1118
1119 procedure TCocoaTextView.otherMouseDown(event: NSEvent);
1120 begin
1121 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1122 inherited otherMouseDown(event);
1123 end;
1124
1125 procedure TCocoaTextView.otherMouseUp(event: NSEvent);
1126 begin
1127 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1128 inherited otherMouseUp(event);
1129 end;
1130
1131 procedure TCocoaTextView.mouseDragged(event: NSEvent);
1132 begin
1133 if Assigned(callback) and not callback.MouseMove(event) then
1134 inherited mouseDragged(event);
1135 end;
1136
1137 procedure TCocoaTextView.mouseEntered(event: NSEvent);
1138 begin
1139 inherited mouseEntered(event);
1140 end;
1141
1142 procedure TCocoaTextView.mouseExited(event: NSEvent);
1143 begin
1144 inherited mouseExited(event);
1145 end;
1146
1147 procedure TCocoaTextView.mouseMoved(event: NSEvent);
1148 begin
1149 if Assigned(callback) and not callback.MouseMove(event) then
1150 inherited mouseMoved(event);
1151 end;
1152
lclIsEnablednull1153 function TCocoaTextView.lclIsEnabled: Boolean;
1154 begin
1155 Result := FEnabled;
1156 end;
1157
1158 procedure TCocoaTextView.lclSetEnabled(AEnabled: Boolean);
1159 begin
1160 FEnabled := AEnabled;
1161 end;
1162
1163 procedure TCocoaTextView.textDidChange(notification: NSNotification);
1164 begin
1165 if (callback <> nil) and (supressTextChangeEvent = 0) then
1166 callback.SendOnTextChanged;
1167 end;
1168
1169 procedure TCocoaTextView.lclExpectedKeys(var wantTabs, wantArrows, wantReturn,
1170 wantAll: Boolean);
1171 begin
1172 wantTabs := true;
1173 wantArrows := true;
1174 wantReturn := true;
1175 wantAll := true;
1176 end;
1177
undoManagerForTextViewnull1178 function TCocoaTextView.undoManagerForTextView(view: NSTextView): NSUndoManager;
1179 begin
1180 if not Assigned(FUndoManager) then
1181 FUndoManager := NSUndoManager.alloc.init;
1182 Result := FUndoManager;
1183 end;
1184
1185 { TCocoaSecureTextField }
1186
acceptsFirstRespondernull1187 function TCocoaSecureTextField.acceptsFirstResponder: LCLObjCBoolean;
1188 begin
1189 Result := NSViewCanFocus(Self);
1190 end;
1191
1192 procedure TCocoaSecureTextField.resetCursorRects;
1193 begin
1194 if not callback.resetCursorRects then
1195 inherited resetCursorRects;
1196 end;
1197
lclGetCallbacknull1198 function TCocoaSecureTextField.lclGetCallback: ICommonCallback;
1199 begin
1200 Result := callback;
1201 end;
1202
1203 procedure TCocoaSecureTextField.lclClearCallback;
1204 begin
1205 callback := nil;
1206 end;
1207
1208 procedure TCocoaSecureTextField.textDidChange(notification: NSNotification);
1209 begin
1210 inherited;
1211 if (maxLength>0) and Assigned(stringValue) and (stringValue.length > maxLength) then
1212 setStringValue(stringValue.substringWithRange(NSMakeRange(0,maxLength)));
1213 if callback <> nil then
1214 callback.SendOnTextChanged;
1215 end;
1216
1217 procedure TCocoaSecureTextField.mouseDown(event: NSEvent);
1218 begin
1219 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1220 begin
1221 inherited mouseDown(event);
1222
1223 if Assigned(callback) then
1224 callback.MouseUpDownEvent(event, True);
1225 end;
1226 end;
1227
1228 procedure TCocoaSecureTextField.mouseUp(event: NSEvent);
1229 begin
1230 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1231 inherited mouseUp(event);
1232 end;
1233
1234 procedure TCocoaSecureTextField.rightMouseDown(event: NSEvent);
1235 begin
1236 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1237 inherited rightMouseDown(event);
1238 end;
1239
1240 procedure TCocoaSecureTextField.rightMouseUp(event: NSEvent);
1241 begin
1242 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1243 inherited rightMouseUp(event);
1244 end;
1245
1246 procedure TCocoaSecureTextField.otherMouseDown(event: NSEvent);
1247 begin
1248 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1249 inherited otherMouseDown(event);
1250 end;
1251
1252 procedure TCocoaSecureTextField.otherMouseUp(event: NSEvent);
1253 begin
1254 if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1255 inherited otherMouseUp(event);
1256 end;
1257
1258 procedure TCocoaSecureTextField.mouseDragged(event: NSEvent);
1259 begin
1260 if Assigned(callback) and not callback.MouseMove(event) then
1261 inherited mouseDragged(event);
1262 end;
1263
1264 procedure TCocoaSecureTextField.mouseMoved(event: NSEvent);
1265 begin
1266 if Assigned(callback) and not callback.MouseMove(event) then
1267 inherited mouseMoved(event);
1268 end;
1269
1270 procedure TCocoaSecureTextField.scrollWheel(event: NSEvent);
1271 begin
1272 if Assigned(callback) and not callback.scrollWheel(event) then
1273 inherited scrollWheel(event);
1274 end;
1275
1276 procedure TCocoaSecureTextField.lclSetMaxLength(amax: integer);
1277 begin
1278 MaxLength := amax;
1279 end;
1280
1281 { TCocoaEditComboBoxList }
1282
1283 procedure TCocoaEditComboBoxList.InsertItem(Index: Integer; const S: string;
1284 O: TObject);
1285 begin
1286 inherited InsertItem(Index, S, O);
1287 FOwner.noteNumberOfItemsChanged;
1288 end;
1289
1290 procedure TCocoaEditComboBoxList.Changed;
1291 begin
1292 inherited Changed;
1293 FOwner.reloadData;
1294 end;
1295
1296 constructor TCocoaEditComboBoxList.Create(AOwner: TCocoaComboBox);
1297 begin
1298 inherited Create;
1299 FOwner := AOwner;
1300 end;
1301
1302 { TCocoaComboBox }
1303
1304 procedure TCocoaComboBox.setStringValue(avalue: NSString);
1305 var
1306 ch : Boolean;
1307 s : NSString;
1308 begin
1309 s := stringValue;
1310 ch := (Assigned(s)
1311 and Assigned(avalue)
1312 and (s.compare(avalue) <> NSOrderedSame));
1313
1314 inherited setStringValue(avalue);
1315
1316 if ch and userSel and Assigned(callback) then
1317 callback.SendOnChange;
1318 end;
1319
lclGetFrameToLayoutDeltanull1320 function TCocoaComboBox.lclGetFrameToLayoutDelta: TRect;
1321 begin
1322 // todo: on 10.7 or later there's a special API for that!
1323 // The data is received from 10.6 Interface Builder
1324 case NSCell(Self.Cell).controlSize of
1325 NSSmallControlSize: begin
1326 Result.Left := 0;
1327 Result.Top := 1;
1328 Result.Right := -3;
1329 Result.Bottom := -4;
1330 end;
1331 NSMiniControlSize: begin
1332 Result.Left := 0;
1333 Result.Top := 1;
1334 Result.Right := -2;
1335 Result.Bottom := -4;
1336 end;
1337 else
1338 // NSRegularControlSize
1339 Result.Left := 0;
1340 Result.Top := 2;
1341 Result.Right := -3;
1342 Result.Bottom := -4;
1343 end;
1344 end;
1345
TCocoaComboBox.acceptsFirstRespondernull1346 function TCocoaComboBox.acceptsFirstResponder: LCLObjCBoolean;
1347 begin
1348 Result := NSViewCanFocus(Self);
1349 end;
1350
1351 procedure TCocoaComboBox.textDidChange(notification: NSNotification);
1352 begin
1353 inherited textDidChange(notification);
1354 if Assigned(callback) then
1355 callback.SendOnChange;
1356 end;
1357
TCocoaComboBox.comboBox_objectValueForItemAtIndex_null1358 function TCocoaComboBox.comboBox_objectValueForItemAtIndex_(combo:TCocoaComboBox;
1359 row: NSInteger):id;
1360 var
1361 ns : NSString;
1362 begin
1363 if not Assigned(list) or (row<0) or (row>=list.Count) then
1364 Result:=nil
1365 else
1366 begin
1367 ns := NSStringUtf8(list[row]);
1368 Result := ns;
1369 ns.autorelease;
1370 end;
1371 end;
1372
TCocoaComboBox.comboBox_indexOfItemWithStringValuenull1373 function TCocoaComboBox.comboBox_indexOfItemWithStringValue(
1374 aComboBox: NSComboBox; string_: NSString): NSUInteger;
1375 var
1376 idx : integer;
1377 begin
1378 idx := indexOfSelectedItem;
1379 if (idx>=0) and (idx<list.Count) and (list[idx]=string_.UTF8String) then
1380 // this is used for the case of the same items in the combobox
1381 Result:=idx
1382 else
1383 begin
1384 // todo: consider a faster search?
1385 idx := list.IndexOf(string_.UTF8String);
1386 if idx<0 then
1387 Result := NSNotFound
1388 else
1389 Result := idx;
1390 end;
1391 end;
1392
numberOfItemsInComboBoxnull1393 function TCocoaComboBox.numberOfItemsInComboBox(combo:TCocoaComboBox):NSInteger;
1394 begin
1395 if not Assigned(list) then Result:=0
1396 else Result:=list.Count;
1397 end;
1398
1399 procedure TCocoaComboBox.dealloc;
1400 begin
1401 if Assigned(resultNS) then resultNS.release;
1402 inherited dealloc;
1403 end;
1404
TCocoaComboBox.lclGetCallbacknull1405 function TCocoaComboBox.lclGetCallback: ICommonCallback;
1406 begin
1407 Result := callback;
1408 end;
1409
1410 procedure TCocoaComboBox.lclClearCallback;
1411 begin
1412 callback := nil;
1413 end;
1414
1415 procedure TCocoaComboBox.resetCursorRects;
1416 begin
1417 if not callback.resetCursorRects then
1418 inherited resetCursorRects;
1419 end;
1420
1421 procedure TCocoaComboBox.comboBoxWillPopUp(notification: NSNotification);
1422 begin
1423 callback.ComboBoxWillPopUp;
1424 isDown:=true;
1425 end;
1426
1427 procedure TCocoaComboBox.comboBoxWillDismiss(notification: NSNotification);
1428 begin
1429 callback.ComboBoxWillDismiss;
1430 isDown:=false;
1431 end;
1432
1433 procedure TCocoaComboBox.comboBoxSelectionDidChange(notification: NSNotification);
1434 var
1435 txt : NSString;
1436 begin
1437 txt := comboBox_objectValueForItemAtIndex_(self, indexOfSelectedItem);
1438 if Assigned(txt) then setStringValue( txt );
1439 if userSel then
1440 callback.ComboBoxSelectionDidChange;
1441 userSel := false;
1442 end;
1443
1444 procedure TCocoaComboBox.comboBoxSelectionIsChanging(notification: NSNotification);
1445 begin
1446 userSel := true;
1447 callback.ComboBoxSelectionIsChanging;
1448 end;
1449
TCocoaComboBox.acceptsFirstMousenull1450 function TCocoaComboBox.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
1451 begin
1452 Result:=true;
1453 end;
1454
1455 procedure TCocoaComboBox.mouseDown(event: NSEvent);
1456 begin
1457 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1458 inherited mouseDown(event);
1459 end;
1460
1461 procedure TCocoaComboBox.mouseUp(event: NSEvent);
1462 begin
1463 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1464 inherited mouseUp(event);
1465 end;
1466
1467 procedure TCocoaComboBox.rightMouseDown(event: NSEvent);
1468 begin
1469 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1470 inherited rightMouseDown(event);
1471 end;
1472
1473 procedure TCocoaComboBox.rightMouseUp(event: NSEvent);
1474 begin
1475 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1476 inherited rightMouseUp(event);
1477 end;
1478
1479 procedure TCocoaComboBox.rightMouseDragged(event: NSEvent);
1480 begin
1481 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1482 inherited rightMouseDragged(event);
1483 end;
1484
1485 procedure TCocoaComboBox.otherMouseDown(event: NSEvent);
1486 begin
1487 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1488 inherited otherMouseDown(event);
1489 end;
1490
1491 procedure TCocoaComboBox.otherMouseUp(event: NSEvent);
1492 begin
1493 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1494 inherited otherMouseUp(event);
1495 end;
1496
1497 procedure TCocoaComboBox.otherMouseDragged(event: NSEvent);
1498 begin
1499 if not Assigned(callback) or not callback.MouseMove(event) then
1500 inherited otherMouseDragged(event);
1501 end;
1502
1503 procedure TCocoaComboBox.mouseDragged(event: NSEvent);
1504 begin
1505 if not Assigned(callback) or not callback.MouseMove(event) then
1506 inherited mouseDragged(event);
1507 end;
1508
1509 procedure TCocoaComboBox.mouseMoved(event: NSEvent);
1510 begin
1511 // NSMouseMove event is being sent even after the selection is made.
1512 // In Cocoa world, the event is sent to the comboBox NSTextView edit section.
1513 // (even is the cursor is NOT over the NSTextView itself, but rather the popup window)
1514 //
1515 // The CocoaWS forwards the event to LCL. And LCL recognizes the event as a mouse action
1516 // beyond combobox boundries (it's NSMouseMove and not NSMouseDrag).
1517 // causing the issues with Object Inspector (where the mouse move with a left button down)
1518 // is recognized as a switch to a different row.
1519 //
1520 // WinAPI doesn't send MouseMove events when popup is dropped.
1521 // Enforcing the same approach for Cocoa. If combobox is showing popup
1522 // all mousemoves are suppressed
1523 if isDown then Exit;
1524
1525 if not Assigned(callback) or not callback.MouseMove(event) then
1526 inherited mouseMoved(event);
1527 end;
1528
1529 procedure TCocoaComboBox.scrollWheel(event: NSEvent);
1530 begin
1531 if not Assigned(callback) or not callback.scrollWheel(event) then
1532 inherited scrollWheel(event);
1533 end;
1534
1535 { TCocoaReadOnlyComboBox }
1536
TCocoaReadOnlyComboBox.acceptsFirstRespondernull1537 function TCocoaReadOnlyComboBox.acceptsFirstResponder: LCLObjCBoolean;
1538 begin
1539 Result := NSViewCanFocus(Self);
1540 end;
1541
1542 procedure TCocoaReadOnlyComboBox.dealloc;
1543 begin
1544 if resultNS <> nil then resultNS.release;
1545 inherited dealloc;
1546 end;
1547
lclGetCallbacknull1548 function TCocoaReadOnlyComboBox.lclGetCallback: ICommonCallback;
1549 begin
1550 Result := callback;
1551 end;
1552
1553 procedure TCocoaReadOnlyComboBox.lclClearCallback;
1554 begin
1555 callback := nil;
1556 end;
1557
lclGetFrameToLayoutDeltanull1558 function TCocoaReadOnlyComboBox.lclGetFrameToLayoutDelta: TRect;
1559 begin
1560 // todo: on 10.7 or later there's a special API for that!
1561 // The data is received from 10.6 Interface Builder
1562 case NSCell(Self.Cell).controlSize of
1563 NSSmallControlSize: begin
1564 Result.Left := 3;
1565 Result.Top := 1;
1566 Result.Right := -3;
1567 Result.Bottom := -4;
1568 end;
1569 NSMiniControlSize: begin
1570 Result.Left := 1;
1571 Result.Top := 0;
1572 Result.Right := -2;
1573 Result.Bottom := 0;
1574 end;
1575 else
1576 // NSRegularControlSize
1577 Result.Left := 3;
1578 Result.Top := 2;
1579 Result.Right := -3;
1580 Result.Bottom := -4;
1581 end;
1582 end;
1583
1584 procedure TCocoaReadOnlyComboBox.resetCursorRects;
1585 begin
1586 if not callback.resetCursorRects then
1587 inherited resetCursorRects;
1588 end;
1589
1590 procedure TCocoaReadOnlyComboBox.comboboxAction(sender: id);
1591 begin
1592 //setTitle(NSSTR(PChar(Format('%d=%d', [indexOfSelectedItem, lastSelectedItemIndex])))); // <= for debugging
1593 if Assigned(callback) then
1594 callback.SendOnChange;
1595 if (indexOfSelectedItem <> lastSelectedItemIndex) and (callback <> nil) then
1596 callback.ComboBoxSelectionDidChange;
1597 lastSelectedItemIndex := indexOfSelectedItem;
1598 end;
1599
stringValuenull1600 function TCocoaReadOnlyComboBox.stringValue: NSString;
1601 begin
1602 if Assigned(selectedItem) then
1603 Result:=selectedItem.title
1604 else
1605 Result:=inherited stringValue;
1606 end;
1607
1608 procedure TCocoaReadOnlyComboBox.drawRect(dirtyRect: NSRect);
1609 var
1610 ctx : TCocoaContext;
1611 r : NSRect;
1612 rr : NSRect;
1613 dr : TRect;
1614 begin
1615 inherited drawRect(dirtyRect);
1616
1617 // if ownerDrawn style, then need to call "DrawItem" event
1618 if isOwnerDrawn and Assigned(callback)
1619 and (lastSelectedItemIndex>=0) and (lastSelectedItemIndex<list.Count)
1620 then
1621 begin
1622 ctx := TCocoaContext.Create(NSGraphicsContext.currentContext);
1623 try
1624 // todo: it's possible to query "cell" using titleRectForBounds method
1625 // it actually returns somewhat desired offsets.
1626 // (however, one should be careful and take layout offsets into account!)
1627 // on the other hand, "cells" themselves are being deprecated...
1628 dr := lclFrame;
1629 Types.OffsetRect(dr, -dr.Left, -dr.Top);
1630 SubLayoutFromFrame( lclGetFrameToLayoutDelta, dr);
1631
1632 // magic offsets are based on the macOS 10.13.6 visual style
1633 // but hard-coding is never reliable
1634 inc(dr.Left, 11);
1635 inc(dr.Top, 5);
1636 dec(dr.Right,18);
1637 dec(dr.Bottom, 2);
1638
1639 callback.ComboBoxDrawItem(lastSelectedItemIndex, ctx, dr, false);
1640 finally
1641 ctx.Free;
1642 end;
1643 end;
1644 end;
1645
TCocoaReadOnlyComboBox.acceptsFirstMousenull1646 function TCocoaReadOnlyComboBox.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
1647 begin
1648 Result:=true;
1649 end;
1650
1651 procedure TCocoaReadOnlyComboBox.mouseDown(event: NSEvent);
1652 begin
1653 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1654 begin
1655 // a typical Apple "mouseDown" loop. The popup is shown on mouseDown event
1656 // The event only exists, whenever the popup is closed (for whatever reason)
1657 if Assigned(callback) then callback.ComboBoxWillPopUp;
1658 inherited mouseDown(event);
1659 if Assigned(callback) then callback.ComboBoxWillDismiss;
1660 callback.MouseUpDownEvent(event, true);
1661 end;
1662 end;
1663
1664 procedure TCocoaReadOnlyComboBox.mouseUp(event: NSEvent);
1665 begin
1666 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1667 inherited mouseUp(event);
1668 end;
1669
1670 procedure TCocoaReadOnlyComboBox.rightMouseDown(event: NSEvent);
1671 begin
1672 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1673 inherited rightMouseDown(event);
1674 end;
1675
1676 procedure TCocoaReadOnlyComboBox.rightMouseUp(event: NSEvent);
1677 begin
1678 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1679 inherited rightMouseUp(event);
1680 end;
1681
1682 procedure TCocoaReadOnlyComboBox.rightMouseDragged(event: NSEvent);
1683 begin
1684 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1685 inherited rightMouseDragged(event);
1686 end;
1687
1688 procedure TCocoaReadOnlyComboBox.otherMouseDown(event: NSEvent);
1689 begin
1690 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1691 inherited otherMouseDown(event);
1692 end;
1693
1694 procedure TCocoaReadOnlyComboBox.otherMouseUp(event: NSEvent);
1695 begin
1696 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1697 inherited otherMouseUp(event);
1698 end;
1699
1700 procedure TCocoaReadOnlyComboBox.otherMouseDragged(event: NSEvent);
1701 begin
1702 if not Assigned(callback) or not callback.MouseMove(event) then
1703 inherited otherMouseDragged(event);
1704 end;
1705
1706 procedure TCocoaReadOnlyComboBox.mouseDragged(event: NSEvent);
1707 begin
1708 if not Assigned(callback) or not callback.MouseMove(event) then
1709 inherited mouseDragged(event);
1710 end;
1711
1712 procedure TCocoaReadOnlyComboBox.mouseMoved(event: NSEvent);
1713 begin
1714 if not Assigned(callback) or not callback.MouseMove(event) then
1715 inherited mouseMoved(event);
1716 end;
1717
1718 procedure TCocoaReadOnlyComboBox.scrollWheel(event: NSEvent);
1719 begin
1720 if not Assigned(callback) or not callback.scrollWheel(event) then
1721 inherited scrollWheel(event);
1722 end;
1723
1724 { TCocoaSpinEdit }
1725
1726 {$IFDEF COCOA_SPINEDIT_INSIDE_CONTAINER}
1727
1728 procedure TCocoaSpinEdit.dealloc;
1729 begin
1730 if Stepper <> nil then
1731 Stepper.release;
1732 if Edit <> nil then
1733 Edit.release;
1734 inherited dealloc;
1735 end;
1736
1737 procedure TCocoaSpinEdit.UpdateControl(ASpinEdit: TCustomFloatSpinEdit);
1738 begin
1739 Stepper.setMaxValue(ASpinEdit.MaxValue);
1740 Stepper.setMinValue(ASpinEdit.MinValue);
1741 Stepper.setIncrement(ASpinEdit.Increment);
1742 Stepper.setDoubleValue(ASpinEdit.Value);
1743
1744 // update the UI too
1745 StepperChanged(Self);
1746 end;
1747
1748 procedure TCocoaSpinEdit.CreateSubcontrols(ASpinEdit: TCustomFloatSpinEdit; const AParams: TCreateParams);
1749 var
1750 lParams: TCreateParams;
1751 lEditRect, lStepperRect: TRect;
1752 begin
1753 {$IFDEF COCOA_SPIN_DEBUG}
1754 WriteLn('[TCocoaSpinEdit.CreateSubcontrols]');
1755 {$ENDIF}
1756
1757 Spin := ASpinEdit;
1758 CalculateSubcontrolPos(Types.Bounds(AParams.X, AParams.Y, AParams.Width,
1759 AParams.Height), lEditRect, lStepperRect);
1760
1761 // Now creates the subcontrols
1762 lParams := AParams;
1763 lParams.WndParent := HWND(Self);
1764 lParams.Style := AParams.Style or WS_VISIBLE;
1765
1766 // Stepper
1767 lParams.X := lStepperRect.Left;
1768 lParams.Y := lStepperRect.Top;
1769 lParams.Width := lStepperRect.Right - lStepperRect.Left;
1770 lParams.Height := lStepperRect.Bottom - lStepperRect.Top;
1771 Stepper := NSStepper.alloc.lclInitWithCreateParams(lParams);
1772 Stepper.setValueWraps(False);
1773
1774 // Edit
1775 lParams.X := lEditRect.Left;
1776 lParams.Y := lEditRect.Top;
1777 lParams.Width := lEditRect.Right - lEditRect.Left;
1778 lParams.Height := lEditRect.Bottom - lEditRect.Top;
1779 Edit := NSTextField.alloc.lclInitWithCreateParams(lParams);
1780
1781 // Change event for the stepper
1782 Stepper.setTarget(Self);
1783 Stepper.setAction(objcselector('StepperChanged:'));
1784 end;
1785
1786 procedure TCocoaSpinEdit.PositionSubcontrols(const ALeft, ATop, AWidth, AHeight: Integer);
1787 var
1788 lNSStepperRect, lRect: NSRect;
1789 lStepperRect, lEditRect: TRect;
1790 begin
1791 {$IFDEF COCOA_SPIN_DEBUG}
1792 WriteLn('[TCocoaSpinEdit.PositionSubcontrols] AHeight=', AHeight);
1793 {$ENDIF}
1794
1795 CalculateSubcontrolPos(Types.Bounds(ALeft, ATop, AWidth, AHeight), lEditRect, lStepperRect);
1796
1797 // Stepper
1798 LCLToNSRect(lStepperRect, AHeight, lNSStepperRect);
1799 Stepper.setBounds(lNSStepperRect);
1800
1801 // Edit
1802 LCLToNSRect(lEditRect, AHeight, lRect);
1803 Edit.setBounds(lRect);
1804
1805 {$IFDEF COCOA_SPIN_DEBUG}
1806 WriteLn(':<[TCocoaSpinEdit.PositionSubcontrols] Edit=> X=', lRect.origin.x,
1807 ' Y=', lRect.origin.y, ' W=', lRect.size.width, ' H=', lRect.size.height,
1808 ' Stepper X=', lNSStepperRect.origin.x, ' Y=', lNSStepperRect.origin.y,
1809 ' W=', lNSStepperRect.size.width, ' H=', lNSStepperRect.size.height,
1810 ' frame.size.height=', frame.size.height);
1811 {$ENDIF}
1812 end;
1813
1814 procedure TCocoaSpinEdit.CalculateSubcontrolPos(
1815 const ASpinLCLBounds: TRect; out AEditBounds, AStepperBounds: TRect);
1816 var
1817 lWidth, lHeight: Integer;
1818 begin
1819 lWidth := ASpinLCLBounds.Right - ASpinLCLBounds.Left;
1820 lHeight := ASpinLCLBounds.Bottom - ASpinLCLBounds.Top;
1821
1822 // Stepper
1823 AStepperBounds.Left := lWidth - SPINEDIT_DEFAULT_STEPPER_WIDTH;
1824 AStepperBounds.Top := SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1825 AStepperBounds.Right := lWidth;
1826 AStepperBounds.Bottom := lHeight - SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1827
1828 // Edit
1829 AEditBounds.Left := SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1830 AEditBounds.Top := SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1831 AEditBounds.Right := lWidth - SPINEDIT_DEFAULT_STEPPER_WIDTH;
1832 AEditBounds.Bottom := lHeight - SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1833
1834 {$IFDEF COCOA_SPIN_DEBUG}
1835 WriteLn('[TCocoaSpinEdit.CalculateSubcontrolPos] lWidth=', lWidth, ' lHeight=', lHeight,
1836 ' Stepper.Left=', AStepperBounds.Left, ' Stepper.Top=', AStepperBounds.Top,
1837 ' Stepper.Right=', AStepperBounds.Right, ' Stepper.Bottom=', AStepperBounds.Bottom,
1838 ' Edit.Left=', AEditBounds.Left, ' Edit.Top=', AEditBounds.Top,
1839 ' Edit.Right=', AEditBounds.Right, ' Edit.Bottom=', AEditBounds.Bottom
1840 );
1841 {$ENDIF}
1842 end;
1843
1844 procedure TCocoaSpinEdit.StepperChanged(sender: NSObject);
1845 var
1846 lNSStr: NSString;
1847 lStr: string;
1848 begin
1849 lStr := Format('%.*f', [Spin.DecimalPlaces, Stepper.doubleValue()]);
1850 lNSStr := CocoaUtils.NSStringUtf8(lStr);
1851 Edit.setStringValue(lNSStr);
1852 lNSStr.release;
1853 // This implements OnChange for both user and code changes
1854 if callback <> nil then callback.SendOnTextChanged();
1855 end;
1856
TCocoaSpinEdit.acceptsFirstRespondernull1857 function TCocoaSpinEdit.acceptsFirstResponder: Boolean;
1858 begin
1859 Result := True;
1860 end;
1861
lclGetCallbacknull1862 function TCocoaSpinEdit.lclGetCallback: ICommonCallback;
1863 begin
1864 Result := callback;
1865 end;
1866
1867 procedure TCocoaSpinEdit.lclClearCallback;
1868 begin
1869 callback := nil;
1870 end;
1871
TCocoaSpinEdit.fittingSizenull1872 function TCocoaSpinEdit.fittingSize: NSSize;
1873 begin
1874 Result.width := -1;
1875 Edit.sizeToFit();
1876 Result.height := Edit.bounds.size.height + SPINEDIT_EDIT_SPACING_FOR_SELECTION * 2;
1877 {$IFDEF COCOA_SPIN_DEBUG}
1878 WriteLn('[TCocoaSpinEdit.fittingSize] width=', Result.width,
1879 ' height=', Result.height);
1880 {$ENDIF}
1881 end;
1882
1883 {$ELSE}
1884
1885 procedure TCocoaSpinEdit.dealloc;
1886 begin
1887 lclReleaseSubControls;
1888 inherited dealloc;
1889 end;
1890
updateSteppernull1891 function TCocoaSpinEdit.updateStepper: boolean;
1892 var
1893 lValid: Boolean = False;
1894 lValue: String;
1895 lFloat: Double;
1896 begin
1897 lValue := CocoaUtils.NSStringToString(stringValue());
1898 lValid := SysUtils.TryStrToFloat(lValue, lFloat);
1899 if lValid then
1900 begin
1901 Stepper.setDoubleValue(lFloat);
1902 Result := true;
1903 end else
1904 Result := false;
1905 end;
1906
1907 procedure TCocoaSpinEdit.UpdateControl(min, max, inc, avalue: double; ADecimalPlaces: Integer);
1908 var
1909 notifyChange : Boolean;
1910 v : double;
1911 begin
1912 Stepper.setIncrement(inc);
1913
1914 v := avalue;
1915 if v < min then v := min
1916 else if v > max then v := max;
1917
1918 notifyChange := (v <> Stepper.doubleValue) or (decimalPlaces <> ADecimalPlaces);
1919
1920 // set min/max after checking for notify change
1921 // .doubleValue would be adjusted by setting min/max
1922 decimalPlaces := ADecimalPlaces;
1923 Stepper.setMinValue(min);
1924 Stepper.setMaxValue(max);
1925
1926 if notifychange then
1927 begin
1928 Stepper.setDoubleValue(v);
1929 StepperChanged(Self);
1930 end;
1931 end;
1932
1933 procedure TCocoaSpinEdit.lclCreateSubcontrols(const AParams: TCreateParams);
1934 var
1935 lParams: TCreateParams;
1936 begin
1937 {$IFDEF COCOA_SPIN_DEBUG}
1938 WriteLn('[TCocoaSpinEdit.CreateSubcontrols]');
1939 {$ENDIF}
1940
1941 // Now creates the subcontrols
1942 lParams := AParams;
1943 //lParams.Style := AParams.Style or WS_VISIBLE;
1944
1945 // Stepper
1946 lParams.X := AParams.X + AParams.Width - SPINEDIT_DEFAULT_STEPPER_WIDTH;
1947 lParams.Width := SPINEDIT_DEFAULT_STEPPER_WIDTH;
1948 Stepper := TCocoaSpinEditStepper.alloc.lclInitWithCreateParams(lParams);
1949 TCocoaSpinEditStepper(Stepper).callback := callback;
1950 Stepper.setValueWraps(False);
1951
1952 // Change event for the stepper
1953 Stepper.setTarget(Self);
1954 Stepper.setAction(objcselector('StepperChanged:'));
1955
1956 { The default way to do this in Cocoa is with NSNumberFormatter
1957 But it is a bit annoying, it just disallows losing focus from the control
1958 instead of the Windows like solution to just override with the last value
1959 If we ever want the Cocoa behavior, instead of implementing controlTextDidChange
1960 do this:
1961 var
1962 lNSStr: NSString;
1963 lStr: string;
1964 i: Integer;
1965
1966 NumberFormatter := NSNumberFormatter.alloc.init;
1967 lStr := '##0';
1968 if ASpinEdit.DecimalPlaces > 0 then lStr := lStr + '.';
1969 for i := 0 to ASpinEdit.DecimalPlaces-1 do
1970 lStr := lStr + '0';
1971 lNSStr := CocoaUtils.NSStringUtf8(lStr);
1972 NumberFormatter.setFormat(lNSStr);
1973 lNSStr.release;
1974 NumberFormatter.setNumberStyle(NSNumberFormatterDecimalStyle);
1975 setFormatter(NumberFormatter);}
1976 end;
1977
1978 procedure TCocoaSpinEdit.lclReleaseSubcontrols;
1979 begin
1980 if Assigned(Stepper) then
1981 begin
1982 Stepper.removeFromSuperview;
1983 Stepper.release;
1984 Stepper := nil;
1985 end;
1986 if Assigned(NumberFormatter) then
1987 begin
1988 NumberFormatter.release;
1989 NumberFormatter := nil;
1990 end;
1991 end;
1992
1993 procedure TCocoaSpinEdit.PositionSubcontrols(const ALeft, ATop, AWidth, AHeight: Integer);
1994 begin
1995 lclSetFrame(Types.Bounds(ALeft, ATop, AWidth, AHeight));
1996 end;
1997
1998 procedure TCocoaSpinEdit.StepperChanged(sender: NSObject);
1999 var
2000 lNSStr: NSString;
2001 lStr: string;
2002 begin
2003 // Stepper not might be assigend while creating or destroying handle
2004 if not Assigned(Stepper) then Exit;
2005
2006 lStr := Format('%.*f', [DecimalPlaces, Stepper.doubleValue()]);
2007 lNSStr := CocoaUtils.NSStringUtf8(lStr);
2008 setStringValue(lNSStr);
2009 lNSStr.release;
2010 // This implements OnChange for both user and code changes
2011 if (callback <> nil) and (avoidChangeEvent=0) then
2012 callback.SendOnTextChanged();
2013 end;
2014
2015 procedure TCocoaSpinEdit.textDidChange(notification: NSNotification);
2016 begin
2017 inc(avoidChangeEvent);
2018 try
2019 updateStepper;
2020 StepperChanged(nil); // and refresh self
2021 inherited textDidChange(notification);
2022 finally
2023 dec(avoidChangeEvent);
2024 end;
2025 end;
2026
TCocoaSpinEdit.acceptsFirstRespondernull2027 function TCocoaSpinEdit.acceptsFirstResponder: LCLObjCBoolean;
2028 begin
2029 Result := NSViewCanFocus(Self);
2030 end;
2031
lclGetCallbacknull2032 function TCocoaSpinEdit.lclGetCallback: ICommonCallback;
2033 begin
2034 Result := callback;
2035 end;
2036
2037 procedure TCocoaSpinEdit.lclClearCallback;
2038 begin
2039 callback := nil;
2040 end;
2041
2042 procedure TCocoaSpinEdit.resetCursorRects;
2043 begin
2044 // this will not work well because
2045 // cocoa replaced TextField and TextView cursors in
2046 // mouseEntered, mouseMoved and CursorUpdate
2047 if not callback.resetCursorRects then
2048 inherited resetCursorRects;
2049 end;
2050
2051 procedure TCocoaSpinEdit.lclSetVisible(AVisible: Boolean);
2052 begin
2053 inherited lclSetVisible(AVisible);
2054 {$ifdef BOOLFIX}
2055 Stepper.setHidden_(Ord(not AVisible));
2056 {$else}
2057 Stepper.setHidden(not AVisible);
2058 {$endif}
2059 end;
2060
2061 procedure TCocoaSpinEdit.lclSetFrame(const r: TRect);
2062 var
2063 ns, lStepperNS: NSRect;
2064 svHeight: CGFloat;
2065 lRect, lStepperRect: TRect;
2066 begin
2067 lRect := r;
2068 lStepperRect := r;
2069 lRect.Right := lRect.Right - SPINEDIT_DEFAULT_STEPPER_WIDTH;
2070 lStepperRect.Left := lRect.Right;
2071 svHeight := GetNSViewSuperViewHeight(Self);
2072 if Assigned(superview) then
2073 begin
2074 LCLToNSRect(lRect, svHeight, ns);
2075 LCLToNSRect(lStepperRect, svHeight, lStepperNS);
2076 end
2077 else
2078 begin
2079 ns := RectToNSRect(lRect);
2080 lStepperNS := RectToNSRect(lStepperRect);
2081 end;
2082 {$IFDEF COCOA_DEBUG_SETBOUNDS}
2083 WriteLn(Format('LCLViewExtension.lclSetFrame: %s Bounds=%s height=%d ns_pos=%d %d ns_size=%d %d',
2084 [NSStringToString(Self.ClassName), dbgs(r), Round(svHeight),
2085 Round(ns.origin.x), Round(ns.origin.y), Round(ns.size.width), Round(ns.size.height)]));
2086 {$ENDIF}
2087 setFrame(ns);
2088 Stepper.setFrame(lStepperNS);
2089 end;
2090
TCocoaSpinEdit.fittingSizenull2091 function TCocoaSpinEdit.fittingSize: NSSize;
2092 var
2093 fr : NSRect;
2094 begin
2095 Result.width := -1;
2096 fr:=frame;
2097 sizeToFit();
2098 Result.height := bounds.size.height;
2099 if not NSEqualRects(frame, fr) then setFrame(fr); // prevent changes of frame after sizeToFit();
2100 {$IFDEF COCOA_SPIN_DEBUG}
2101 WriteLn('[TCocoaSpinEdit.fittingSize] width=', Result.width:0:0, ' height=', Result.height:0:0);
2102 {$ENDIF}
2103 end;
2104
TCocoaSpinEdit.acceptsFirstMousenull2105 function TCocoaSpinEdit.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
2106 begin
2107 Result:=true;
2108 end;
2109
2110 procedure TCocoaSpinEdit.mouseDown(event: NSEvent);
2111 begin
2112 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2113 begin
2114 inherited mouseDown(event);
2115 if Assigned(callback) then
2116 callback.MouseUpDownEvent(event, true);
2117 end;
2118 end;
2119
2120 procedure TCocoaSpinEdit.mouseUp(event: NSEvent);
2121 begin
2122 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2123 inherited mouseUp(event);
2124 end;
2125
2126 procedure TCocoaSpinEdit.rightMouseDown(event: NSEvent);
2127 begin
2128 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2129 inherited rightMouseDown(event);
2130 end;
2131
2132 procedure TCocoaSpinEdit.rightMouseUp(event: NSEvent);
2133 begin
2134 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2135 inherited rightMouseUp(event);
2136 end;
2137
2138 procedure TCocoaSpinEdit.rightMouseDragged(event: NSEvent);
2139 begin
2140 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2141 inherited rightMouseDragged(event);
2142 end;
2143
2144 procedure TCocoaSpinEdit.otherMouseDown(event: NSEvent);
2145 begin
2146 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2147 inherited otherMouseDown(event);
2148 end;
2149
2150 procedure TCocoaSpinEdit.otherMouseUp(event: NSEvent);
2151 begin
2152 if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2153 inherited otherMouseUp(event);
2154 end;
2155
2156 procedure TCocoaSpinEdit.otherMouseDragged(event: NSEvent);
2157 begin
2158 if not Assigned(callback) or not callback.MouseMove(event) then
2159 inherited otherMouseDragged(event);
2160 end;
2161
2162 procedure TCocoaSpinEdit.mouseDragged(event: NSEvent);
2163 begin
2164 if not Assigned(callback) or not callback.MouseMove(event) then
2165 inherited mouseDragged(event);
2166 end;
2167
2168 procedure TCocoaSpinEdit.mouseMoved(event: NSEvent);
2169 begin
2170 if not Assigned(callback) or not callback.MouseMove(event) then
2171 inherited mouseMoved(event);
2172 end;
2173
2174 {$ENDIF}
2175
2176 end.
2177
2178