Sign in Join
Wiki copytemporary

Cocoa Internals/Text Controls: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Edit
22
33NSTextField. The field itself is readonly. Whenever it starts to edit, it's actually TCocoaFormEditor.
44
55### MaxLength
66
77The maximum length is not supported natively in Cocoa. There are a few ways to do that:
88
99- textDidChange - (currently used) once the text changed - adjust it to the max length, before sending a notification
1010- NSFormatter - there's an example of [stackoverflow](https://stackoverflow.com/questions/827014/how-to-limit-nstextfield-text-length-and-keep-it-always-upper-case) on how to do that. But it doesn't work in Pascal
1111
1212## ComboBox
1313
1414Non-readonly ComboBox is implemented via NSComboBox type. Readonly combobox is implemented via NSPopupButton
1515
1616### NSComboBox features
1717
1818- It doesn't like to be 29px or taller in height. The height is fixed to 26 pixels
1919- It doesn't like custom fonts (other than default) to be assigned to itself (see [\#33626](https://bugs.freepascal.org/view.php?id=33626))
2020
21-<figure>
22-<img src="cocoa_combobox_issue.png" title="cocoa_combobox_issue.png" />
23-<figcaption>cocoa_combobox_issue.png</figcaption>
24-</figure>
21+![](/wiki-images/1/1a/Cocoa_combobox_issue.png)
2522
2623### Programmatic Drop
2724
2825NSAppKit doesn't provide any means to trigger the comboBox dropdown via an API call. Thus any approach would be hacking solution anyway.
2926
3027There are a few options, that can be found online.
3128
32291\) Using NSAccessibility APIs [stackoverflow](https://stackoverflow.com/questions/4499262/how-to-programmatically-open-an-nscomboboxs-list)
3330
3431The solution suggests using a deprecated APIs. The solution itself is pretty old, and that's why deprecated methods are used.
3532
3633
3734It should not be applied for macOS 10.11 and later.
3835
3936```objc
4037- (void) setExpanded: (BOOL)expanded
4138{
4239 id ax = NSAccessibilityUnignoredDescendant(self);
4340 [ax accessibilitySetValue: [NSNumber numberWithBool: expanded]
4441 forAttribute: NSAccessibilityExpandedAttribute];
4542}
4643```
4744
48452\) The same link as the method above, but it's suggesting to call **popUp:** selector. The selector isn't documented (but can be seen if studying backtrace) \<source lang="objc\>
4946
5047`[(NSComboBoxCell*)self.acomboBox.cell performSelector:@selector(popUp:)];`
51-
52-</source>
5348
5449This is a hack and undesired approach, as it might cause problems in future.
5550
56513\) Instead of using **popUp:**, call for **moveDown:** which is a documented API However, it should not be called while processing the event. Instead it should be scheduled to be called after the current event is processed. As the key pressed stucks in the event queue
5752
5853```delphi
5954procedure TCocoaComboBox.lclPopup;
6055var
6156 c : NSCell;
6257begin
6358 if isDown then Exit;
6459 c:=cell;
6560 if not Assigned(c) then Exit;
6661 // cannot implement it immediately, as it blocks the current event processing
6762 c.performSelector_withObject_afterDelay(
6863 ObjCSelector('moveDown:'), nil, 0
6964 );
7065end;
7166```
7267
7368## TMemo (NSTextView)
7469
7570TMemo widgetset is implemented over NSTextView and NSScrollView
7671
7772By default NSTextView is designed to be constantly word-wrapped. Disabling word-wrapping could be quite complicated from a start due to odd-default values chosen by Apple, as well as complex (yet flexible) Text Layout system.
7873
7974- NSTextView is a "cocoa" control, however it's not drawing the text by it's own it's also using:
8075- NSTextContainer. Both NSTextContainer and NSTextView settings influence on how the text is rendered in the end.
8176
8277The example shows, of creating NSTextView that automatically resizes itself horizontally. However NSTextView doesn't provide its own scrollbars, thus no scrollbars would be seen.
8378
8479```delphi
8580procedure TForm1.FormShow(Sender: TObject);
8681var
8782 txt : NSTextView;
8883begin
8984 txt := NSTextView.alloc.initWithFrame(NSMakeRect(10,ClientHeight-10-50,50,50));
9085
9186 txt.setFont(NSFont.systemFontOfSize(NSFont.systemFontSizeForControlSize(NSRegularControlSize)));
9287
9388 // making the maximum size - maximum!
9489 // 10000000 is a "constant" could be found in Apple documentation
9590 txt.setMaxSize( NSMakeSize(10000000, 10000000));
9691 // preventing textContainer from following the width of NSTextView
9792 txt.textContainer.setWidthTracksTextView(false);
9893 // making TextContainer large enough.
9994 txt.textContainer.setContainerSize ( NSMakeSize( 10000000, 1024));
10095 // making NSTextView to resize automatically to the text boundries (max width)
10196 txt.setHorizontallyResizable(true);
10297
10398 NSView(Self.Handle).addSubView(txt);
10499end;
105100```
106101
107102The next step is actually to embed NSTextView into ScrollView (as a documentView).
108103
109104### Inserting into Scroll View
110105
111106The process is straight-forward - allocate scroll view, use NSTextView as it's document view
112107
113108```delphi
114109procedure TForm1.FormShow(Sender: TObject);
115110var
116111 txt : NSTextView;
117112 sc : NSScrollView;
118113begin
119114 txt := NSTextView.alloc.initWithFrame(NSMakeRect(10,ClientHeight-10-50,50,50));
120115
121116 txt.setMaxSize( NSMakeSize(10000000, 10000000));
122117 txt.textContainer.setWidthTracksTextView(false);
123118 txt.textContainer.setContainerSize ( NSMakeSize( 10000000, 1024));
124119 txt.setHorizontallyResizable(true);
125120
126121 // allocating scroll view and placing NSTextView inside
127122 sc := NSScrollView.alloc.initWithFrame(NSMakeRect(10,ClientHeight-10-150,150,150));
128123 sc.setHasVerticalScroller(true);
129124 sc.setHasHorizontalScroller(true);
130125 sc.setAutohidesScrollers(true);
131126 sc.setDocumentView(txt);
132127
133128 NSView(Self.Handle).addSubView(sc);
134129end;
135130```
136131
137132Now text-view and scrollbars would act as expected.
138133
139134### Line padding oddity
140135
141136By default, there's 5 pixels padding between border of NSTextView and left-side of the text.
142137
143138It's possible to decrease it, however, setting it to zero is causing a very weird and unpleasant glitch.
144139
145140```delphi
146141var
147142 txt : NSTextView;
148143 sc : NSScrollView;
149144begin
150145 txt := NSTextView.alloc.initWithFrame(NSMakeRect(10,ClientHeight-10-50,50,50));
151146
152147 txt.setMaxSize( NSMakeSize(10000000, 10000000));
153148 txt.textContainer.setWidthTracksTextView(false);
154149 txt.textContainer.setContainerSize ( NSMakeSize( 10000000, 1024));
155150 txt.setHorizontallyResizable(true);
156151
157152 // setting this value to 0 is causing the problem.
158153 txt.textContainer.setLineFragmentPadding(0);
159154
160155 sc := NSScrollView.alloc.initWithFrame(NSMakeRect(10,ClientHeight-10-150,150,150));
161156 sc.setHasVerticalScroller(true);
162157 sc.setHasHorizontalScroller(true);
163158 sc.setAutohidesScrollers(true);
164159 sc.setDocumentView(txt);
165160
166161 NSView(Self.Handle).addSubView(sc);
167162end;
168163```
169164
170165Now. Everytime a new line break is added **to the end of the text** the view scrolls to the top (while having cursor at the bottom).
171166
172167Either by pressing Enter on keyboard having cursor at the end of the text. Or by inserting a line break at the end of the text programmatically.
173168
174169The only way to avoid the problem is to have Fragment padding set to a value greater than zero.
175170
176171### Forcing Lines Breaks
177172
178173NSTextView is a rich-text controls, thus it can (visually) handle all variety of Line breaks
179174
180175- CRLF (#13#10) - Windows
181176- LF (#10) - Unix
182177- CR (#13) - Classic Mac
183178
184179For consistency and Carbon compatibility, unix style (#10) is enforced.
185180
186181## TSpinEdit
187182
188183Spin edit control is implemented using 2 controls NSEdit and NSStepper (up/down arrow).
189184
190185- Handle returned is NSEdit class. Even though the exact bounds of NSEdit ARE NOT identical to the SpinEdit boundaries. There's an additional offset to fit NSStepper
191186- It's possible to implement **EditorEnabled** property, simply by only setting NSEdit to readonly mode and allowing NSStepper to change values
192187
193188## Text Input
194189
195190### Dead Keys
196191
197192By default Cocoa doesn't support dead keys entries for non NSTextView controls. The sample of they dead-keys can be seen, by installing (qwerty) Spanish keyboard layout (where accents are used). An accent is placed by pressing "'" followed by vowel character (a, e, i, o).
198193
199194If following code is used, you should see that two key presses followed by a single unicode character generated.
200195
201196Also, if "'" is followed by non-vowel character, exactly 2 separate characters would be generated
202197
203198```delphi
204199procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
205200 );
206201begin
207202 case Key of
208203 VK_SPACE, VK_BACK, VK_DELETE:
209204 Caption := '';
210205 else
211206 writeln('down: ', Integer(Key));
212207 end;
213208end;
214209
215210procedure TForm1.FormUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
216211begin
217212 if (UTF8Key <> ' ') and (UTF8Key <> #8) then begin
218213 Caption:=Caption+UTF8Key;
219214 writeln(UTF8Key);
220215 end;
221216end;
222217```
223218
224219## See Also
225220
226221- [[Cocoa Internals]]
227222- [Tracking the Size of a Text View](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextStorageLayer/Tasks/TrackingSize.html) - Apple's official documentation explains how text size is tracked between NSViewText and NSViewContainer
228223- [Putting an NSTextView Object in an NSScrollView](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html) - Apple's official documentation on how to place NSTextView into a scrollview.
229224- [How to Disable Word-Wrap of NSTextview](https://stackoverflow.com/questions/3174140/how-to-disable-word-wrap-of-nstextview) - stackoverflow discussion of the problem.
230225
231226---
232227*From the Free Pascal wiki, page "Cocoa Internals/Text Controls", as it stood on 2021-10-13 after a change by Skalogryz, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
233228Category: Cocoa
234229Category: macOS

Back to history