2015-12-08 12:06:26 -06:00
|
|
|
// 8 december 2015
|
|
|
|
#import "uipriv_darwin.h"
|
|
|
|
|
2016-05-12 20:24:12 -05:00
|
|
|
// NSTextView has no intrinsic content size by default, which wreaks havoc on a pure-Auto Layout system
|
|
|
|
// we'll have to take over to get it to work
|
|
|
|
// see also http://stackoverflow.com/questions/24210153/nstextview-not-properly-resizing-with-auto-layout and http://stackoverflow.com/questions/11237622/using-autolayout-with-expanding-nstextviews
|
2016-05-28 22:08:56 -05:00
|
|
|
@interface intrinsicSizeTextView : NSTextView {
|
|
|
|
uiMultilineEntry *libui_e;
|
|
|
|
}
|
|
|
|
- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e;
|
2016-05-12 20:24:12 -05:00
|
|
|
@end
|
|
|
|
|
2016-05-28 22:08:56 -05:00
|
|
|
struct uiMultilineEntry {
|
|
|
|
uiDarwinControl c;
|
|
|
|
NSScrollView *sv;
|
|
|
|
intrinsicSizeTextView *tv;
|
|
|
|
struct scrollViewData *d;
|
|
|
|
void (*onChanged)(uiMultilineEntry *, void *);
|
|
|
|
void *onChangedData;
|
|
|
|
BOOL changing;
|
|
|
|
};
|
|
|
|
|
2016-05-12 20:24:12 -05:00
|
|
|
@implementation intrinsicSizeTextView
|
|
|
|
|
2016-05-28 22:08:56 -05:00
|
|
|
- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e
|
|
|
|
{
|
|
|
|
self = [super initWithFrame:r];
|
|
|
|
if (self)
|
|
|
|
self->libui_e = e;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2016-05-12 20:24:12 -05:00
|
|
|
- (NSSize)intrinsicContentSize
|
|
|
|
{
|
|
|
|
NSTextContainer *textContainer;
|
|
|
|
NSLayoutManager *layoutManager;
|
|
|
|
NSRect rect;
|
|
|
|
|
|
|
|
textContainer = [self textContainer];
|
|
|
|
layoutManager = [self layoutManager];
|
|
|
|
[layoutManager ensureLayoutForTextContainer:textContainer];
|
|
|
|
rect = [layoutManager usedRectForTextContainer:textContainer];
|
|
|
|
return rect.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)didChangeText
|
|
|
|
{
|
|
|
|
[super didChangeText];
|
|
|
|
[self invalidateIntrinsicContentSize];
|
2016-05-28 22:08:56 -05:00
|
|
|
if (!self->libui_e->changing)
|
|
|
|
(*(self->libui_e->onChanged))(self->libui_e, self->libui_e->onChangedData);
|
2016-05-12 20:24:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2016-05-27 22:56:44 -05:00
|
|
|
uiDarwinControlAllDefaultsExceptDestroy(uiMultilineEntry, sv)
|
|
|
|
|
|
|
|
static void uiMultilineEntryDestroy(uiControl *c)
|
|
|
|
{
|
|
|
|
uiMultilineEntry *e = uiMultilineEntry(c);
|
|
|
|
|
|
|
|
scrollViewFreeData(e->sv, e->d);
|
|
|
|
[e->tv release];
|
|
|
|
[e->sv release];
|
|
|
|
uiFreeControl(uiControl(e));
|
|
|
|
}
|
2015-12-08 12:06:26 -06:00
|
|
|
|
|
|
|
static void defaultOnChanged(uiMultilineEntry *e, void *data)
|
|
|
|
{
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
char *uiMultilineEntryText(uiMultilineEntry *e)
|
|
|
|
{
|
|
|
|
return uiDarwinNSStringToText([e->tv string]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text)
|
|
|
|
{
|
2016-05-28 22:08:56 -05:00
|
|
|
[[e->tv textStorage] replaceCharactersInRange:NSMakeRange(0, [[e->tv string] length])
|
|
|
|
withString:toNSString(text)];
|
|
|
|
// must be called explicitly according to the documentation of shouldChangeTextInRange:replacementString:
|
|
|
|
e->changing = YES;
|
|
|
|
[e->tv didChangeText];
|
|
|
|
e->changing = NO;
|
2015-12-08 12:06:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO scroll to end?
|
|
|
|
void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text)
|
|
|
|
{
|
2016-05-28 22:08:56 -05:00
|
|
|
[[e->tv textStorage] replaceCharactersInRange:NSMakeRange([[e->tv string] length], 0)
|
|
|
|
withString:toNSString(text)];
|
|
|
|
e->changing = YES;
|
|
|
|
[e->tv didChangeText];
|
|
|
|
e->changing = NO;
|
2015-12-08 12:06:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data)
|
|
|
|
{
|
|
|
|
e->onChanged = f;
|
|
|
|
e->onChangedData = data;
|
|
|
|
}
|
|
|
|
|
|
|
|
int uiMultilineEntryReadOnly(uiMultilineEntry *e)
|
|
|
|
{
|
|
|
|
return [e->tv isEditable] == NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly)
|
|
|
|
{
|
|
|
|
BOOL editable;
|
|
|
|
|
|
|
|
editable = YES;
|
|
|
|
if (readonly)
|
|
|
|
editable = NO;
|
|
|
|
[e->tv setEditable:editable];
|
|
|
|
}
|
|
|
|
|
2016-05-22 13:20:54 -05:00
|
|
|
static uiMultilineEntry *finishMultilineEntry(BOOL hscroll)
|
2015-12-08 12:06:26 -06:00
|
|
|
{
|
|
|
|
uiMultilineEntry *e;
|
2015-12-09 08:05:28 -06:00
|
|
|
NSFont *font;
|
2016-05-27 22:56:44 -05:00
|
|
|
struct scrollViewCreateParams p;
|
2015-12-08 12:06:26 -06:00
|
|
|
|
2016-04-25 10:54:09 -05:00
|
|
|
uiDarwinNewControl(uiMultilineEntry, e);
|
2015-12-08 12:06:26 -06:00
|
|
|
|
2016-05-28 22:08:56 -05:00
|
|
|
e->tv = [[intrinsicSizeTextView alloc] initWithFrame:NSZeroRect e:e];
|
2016-05-28 18:29:25 -05:00
|
|
|
|
|
|
|
// verified against Interface Builder for a sufficiently customized text view
|
|
|
|
|
|
|
|
// NSText properties:
|
|
|
|
// this is what Interface Builder sets the background color to
|
|
|
|
[e->tv setBackgroundColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]];
|
|
|
|
[e->tv setDrawsBackground:YES];
|
2015-12-08 12:06:26 -06:00
|
|
|
[e->tv setEditable:YES];
|
|
|
|
[e->tv setSelectable:YES];
|
2016-05-28 18:29:25 -05:00
|
|
|
[e->tv setFieldEditor:NO];
|
2015-12-08 12:06:26 -06:00
|
|
|
[e->tv setRichText:NO];
|
|
|
|
[e->tv setImportsGraphics:NO];
|
2016-05-28 18:29:25 -05:00
|
|
|
[e->tv setUsesFontPanel:NO];
|
|
|
|
[e->tv setRulerVisible:NO];
|
|
|
|
// we'll handle font last
|
2016-05-28 20:50:24 -05:00
|
|
|
// while setAlignment: has been around since 10.0, the named constant "NSTextAlignmentNatural" seems to have only been introduced in 10.11
|
|
|
|
#define ourNSTextAlignmentNatural 4
|
|
|
|
[e->tv setAlignment:ourNSTextAlignmentNatural];
|
2016-05-28 18:29:25 -05:00
|
|
|
// textColor is set to nil, just keep the dfault
|
2015-12-08 12:06:26 -06:00
|
|
|
[e->tv setBaseWritingDirection:NSWritingDirectionNatural];
|
2016-05-28 18:29:25 -05:00
|
|
|
[e->tv setHorizontallyResizable:NO];
|
|
|
|
[e->tv setVerticallyResizable:YES];
|
|
|
|
|
|
|
|
// NSTextView properties:
|
|
|
|
[e->tv setAllowsDocumentBackgroundColorChange:NO];
|
|
|
|
[e->tv setAllowsUndo:YES];
|
|
|
|
// default paragraph style is nil; keep default
|
2015-12-08 12:06:26 -06:00
|
|
|
[e->tv setAllowsImageEditing:NO];
|
|
|
|
[e->tv setAutomaticQuoteSubstitutionEnabled:NO];
|
|
|
|
[e->tv setAutomaticLinkDetectionEnabled:NO];
|
2016-05-28 18:29:25 -05:00
|
|
|
[e->tv setDisplaysLinkToolTips:YES];
|
2015-12-08 12:06:26 -06:00
|
|
|
[e->tv setUsesRuler:NO];
|
|
|
|
[e->tv setUsesInspectorBar:NO];
|
|
|
|
[e->tv setSelectionGranularity:NSSelectByCharacter];
|
2016-05-28 18:29:25 -05:00
|
|
|
// there is a dedicated named insertion point color but oh well
|
|
|
|
[e->tv setInsertionPointColor:[NSColor controlTextColor]];
|
|
|
|
// typing attributes is nil; keep default (we change it below for fonts though)
|
|
|
|
[e->tv setSmartInsertDeleteEnabled:NO];
|
2015-12-08 12:06:26 -06:00
|
|
|
[e->tv setContinuousSpellCheckingEnabled:NO];
|
|
|
|
[e->tv setGrammarCheckingEnabled:NO];
|
2016-05-28 18:29:25 -05:00
|
|
|
[e->tv setUsesFindPanel:YES];
|
2015-12-08 12:06:26 -06:00
|
|
|
[e->tv setEnabledTextCheckingTypes:0];
|
|
|
|
[e->tv setAutomaticDashSubstitutionEnabled:NO];
|
2016-05-28 18:29:25 -05:00
|
|
|
[e->tv setAutomaticDataDetectionEnabled:NO];
|
2015-12-08 12:06:26 -06:00
|
|
|
[e->tv setAutomaticSpellingCorrectionEnabled:NO];
|
|
|
|
[e->tv setAutomaticTextReplacementEnabled:NO];
|
2016-05-28 18:29:25 -05:00
|
|
|
[e->tv setUsesFindBar:NO];
|
|
|
|
[e->tv setIncrementalSearchingEnabled:NO];
|
|
|
|
|
|
|
|
// NSTextContainer properties:
|
|
|
|
[[e->tv textContainer] setWidthTracksTextView:YES];
|
|
|
|
[[e->tv textContainer] setHeightTracksTextView:NO];
|
|
|
|
|
|
|
|
// NSLayoutManager properties:
|
|
|
|
[[e->tv layoutManager] setAllowsNonContiguousLayout:YES];
|
|
|
|
|
2015-12-08 12:06:26 -06:00
|
|
|
// now just to be safe; this will do some of the above but whatever
|
|
|
|
disableAutocorrect(e->tv);
|
2016-05-28 18:29:25 -05:00
|
|
|
|
2016-05-22 13:37:02 -05:00
|
|
|
if (hscroll) {
|
2016-05-28 18:29:25 -05:00
|
|
|
// see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html
|
2016-05-22 13:37:02 -05:00
|
|
|
[e->tv setHorizontallyResizable:YES];
|
|
|
|
[[e->tv textContainer] setWidthTracksTextView:NO];
|
|
|
|
[[e->tv textContainer] setContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
|
|
|
|
}
|
2016-05-28 18:29:25 -05:00
|
|
|
|
2015-12-09 08:05:28 -06:00
|
|
|
// don't use uiDarwinSetControlFont() directly; we have to do a little extra work to set the font
|
|
|
|
font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
|
|
|
|
[e->tv setTypingAttributes:[NSDictionary
|
|
|
|
dictionaryWithObject:font
|
|
|
|
forKey:NSFontAttributeName]];
|
|
|
|
// e->tv font from Interface Builder is nil, but setFont:nil throws an exception
|
|
|
|
// let's just set it to the standard control font anyway, just to be safe
|
|
|
|
[e->tv setFont:font];
|
|
|
|
|
2016-05-27 22:56:44 -05:00
|
|
|
memset(&p, 0, sizeof (struct scrollViewCreateParams));
|
|
|
|
p.DocumentView = e->tv;
|
2016-05-27 23:35:56 -05:00
|
|
|
// this is what Interface Builder sets it to
|
|
|
|
p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0];
|
2016-05-27 22:56:44 -05:00
|
|
|
p.DrawsBackground = YES;
|
|
|
|
p.Bordered = YES;
|
|
|
|
p.HScroll = hscroll;
|
|
|
|
p.VScroll = YES;
|
|
|
|
e->sv = mkScrollView(&p, &(e->d));
|
2015-12-09 08:05:28 -06:00
|
|
|
|
2015-12-08 12:06:26 -06:00
|
|
|
uiMultilineEntryOnChanged(e, defaultOnChanged, NULL);
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
2015-12-09 08:05:28 -06:00
|
|
|
|
2016-05-22 13:20:54 -05:00
|
|
|
uiMultilineEntry *uiNewMultilineEntry(void)
|
|
|
|
{
|
|
|
|
return finishMultilineEntry(NO);
|
|
|
|
}
|
|
|
|
|
|
|
|
uiMultilineEntry *uiNewNonWrappingMultilineEntry(void)
|
|
|
|
{
|
|
|
|
return finishMultilineEntry(YES);
|
|
|
|
}
|