gpt4 book ai didi

cocoa - 可选的NSTextField和NSColorPanel –如何打破它们不希望的相互作用?

转载 作者:行者123 更新时间:2023-12-03 17:01:25 26 4
gpt4 key购买 nike

在看似微不足道的设置中,我遇到了一个无法消除的可选NSTextField和NSColorPanel之间的不良相互作用,这使我发疯。

设置如下:在一个窗口中,我有一个可选的多行标签(实际上是一个NSTextField)和一个NSColorWell。

Color Well允许用户在GUI中为几何对象着色;它与文本无关。当然,点击好颜色会激活它,即调出共享的NSColorPanel并将颜色很好地连接到它。

文本字段完全独立于GUI中的彩色对象,并向用户显示数据。它是只读的,即不可编辑。由于数据是按列组织的,因此我使用选项卡进行文本格式设置,并使用NSTextField的setAttributedStringValue:方法显示数据。

乍一看,一切都可以在如此简单的设置中按预期进行。

但是有一个难题:我希望用户能够复制文本字段中的数据以在其他地方进行处理。因此,NSTextField必须是可选的。并将其设置为selectable是问题开始的地方:

当用户单击可选文本字段以选择文本时,窗口的字段编辑器将接管工作,结果,属性文本的所有选项卡设置都将丢失,文本将被混合。防止这种情况的通常方法是将NSTextField的allowsEditingTextAttributes属性设置为YES。如果执行此操作,则用户选择文本时将保留选项卡格式。但是现在NSColorPanel(如果可见)也无意间也切换为文本颜色(始终为黑色),并且如果颜色处于活动状态(连接到NSColorPanel),它将保持活动状态,从而将所有几何GUI对象的颜色更改为黑色。哎哟!

我找不到将NSTextField的selectableallowsEditingTextAttributes属性设置为YES的方法,但是仍然阻止它与NSColorPanel通信。

显而易见的替代方法是,即使将allowsEditingTextAttributes设置为NO,也可以保留选定文本的选项卡格式(根据需要将颜色面板与文本字段断开连接)。但是我也没有成功,尽管我真的不明白为什么:

我的想法是将所需的选项卡设置为文本字段的字段编辑器的defaultParagraphStyle。因此,我设置了一个定制的字段编辑器:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSArray *myTabs = @[
[[NSTextTab alloc] initWithType:NSRightTabStopType location:100],
[[NSTextTab alloc] initWithType:NSRightTabStopType location:200],
[[NSTextTab alloc] initWithType:NSRightTabStopType location:300]
];
NSMutableParagraphStyle *myParagraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
[myParagraphStyle setTabStops:myTabs];

myFieldEditor = [NSTextView new]; // myFieldEditor is an instance variable
[myFieldEditor setDefaultParagraphStyle:myParagraphStyle];

[window setDelegate:self];
[window fieldEditor:YES forObject:myTextField];
}


并在 windowWillReturnFieldEditor:toObject:委托方法中的文本字段中激活它:

- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
{
if (client == myTextField) return myFieldEditor;
return nil;
}


我甚至通过子类化我的文本字段的NSTextFieldCell并记录传播的字段编辑器来确保确实使用了自定义字段编辑器:

@implementation myTextFieldCell

- (NSText *)setUpFieldEditorAttributes:(NSText *)textObj
{
NSTextView *newTextObj = (NSTextView*)[super setUpFieldEditorAttributes:textObj];
NSLog(@"STYLE: %@", [newTextObj defaultParagraphStyle]);
return newTextObj;
}

@end


现在,当我在文本字段中选择文本时,将获得以下日志输出:

2017-11-02 11:51:07.432 Demo[94807:303] STYLE: Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (
100R,
200R,
300R
), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningFactor 0.05, HeaderLevel 0


这正是预期的结果。

但是,一旦我选择了文本,选项卡格式就会在文本字段中消失。我不知道为什么这行不通。

所以我被卡住了。如果将NSTextField的 allowsEditingTextAttributes属性设置为 YES,则在选择文本时会保留选项卡格式,但是GUI中我的彩色对象无意间变成了黑色。如果将 allowsEditingTextAttributes属性设置为 NO,则彩色面板的行为应达到预期,但是一旦选择文本,选项卡格式就会丢失。

这是一个非常不幸的案例,可可试图变得太聪明,从而使完全琐碎的设置成为一个巨大的问题。

有任何想法吗?

最佳答案

好的,因此我得到了@Willeke(感谢!)在他对我的问题的评论中的建议:使用NSTextView而不是NSTextField来实现我的多行标签。

我将首先总结一下为什么似乎无法使用NSTextField来完成我想做的事情,然后再使用NSTextView解决方案。

为什么NSTextField不起作用

如前所述,我解决方案的想法是为NSTextField自定义字段编辑器,设置所需的制表位,这样就不必将NSTextField的allowsEditingTextAttributes属性设置为YES(这会无意中将文本字段耦合到颜色面板)。我希望,这样可以在我在文本字段中选择文本并激活字段编辑器时保留属性字符串的段落样式的制表位。

广泛的测试表明,此操作不可行有以下几个原因:


正如@Willeke指出的那样,将NSTextView的usesFontPanel属性设置为NO也会断开文本视图与颜色面板的连接(根据需要)。但是,这不适用于作为NSTextField字段编辑器的NSTextView,因为在这种情况下,此设置始终被NSTextField的allowsEditingTextAttributes属性覆盖:如果allowsEditingTextAttributesYES,则字体和彩色面板是不考虑usesFontPanel的值而耦合的;如果值为NO,则字体和彩色面板是不考虑usesFontPanel值的耦合的。
无论如何,使用自定义字段编辑器的制表位而不是使用我的属性字符串的段落样式的制表位(需要allowsEditingTextAttributesYES)的想法无论如何都不会起作用,因为制表位会停止设置显然,无论allowsEditingTextAttributes属性的值如何,NSTextField都会始终完全忽略字段编辑器。 NSTextField始终使用均匀间隔的默认制表位。


从激烈的谷歌搜索来看,另一个变体-将allowsEditingTextAttributes设置为YES但以某种方式修改NSColorPanel使其仍然不连接到NSTextField-如果不使用NSColorPanel的私有方法,则无法实现。

如何使用NSTextView实施解决方案

尽管实例化嵌入在剪辑视图和滚动视图中的完整NSTextView只是为了获得文本字段的功能似乎有些矫kill过正,但最终,它是最简单(甚至唯一可能)的解决方案。

要使滚动视图消失,您必须基本上取消选中IB中NSScrollView的“属性”检查器中的所有内容,尤其是“显示垂直滚动条”。将“绘制背景”和边框类型设置为所需的外观;如果您想模仿多行标签(就像我一样),请取消选中“绘制背景”,然后选择不可见的边框类型。在嵌入式NSTextView的“属性”检查器中,还要取消选中“可选”以外的所有属性,尤其是“使用字体面板”。

确保NSTextView的大小足以容纳完整的内容字符串,以避免意外的滚动效果并固定文本位置。如果您的内容字符串以行制动器结尾,则您需要有足够的空间在其下方留空行。如果您没有取消选中“绘制背景”,并且看起来不理想,请不要绘制NSScrollView或NSTextView的背景,为NSScrollView选择不可见的边框,然后在其下放置所需大小和外观的NSBox 。

您现在可以使用以下方式设置属性内容字符串:

[[myTextView textStorage] setAttributedString:myAttributedString];


请注意,尽管由于您正在修改NSTextStorage而不是NSTextView本身,所以将NSTextView的 editable属性设置为 NO,但这确实起作用。

但是很遗憾,我们还没有完成。

当您使用NSTextField像通常在Label中那样显示只读数据时,通常您不希望文本字段成为键视图循环的一部分(通过按Tab键在控件中盘旋) 。为此,您可以简单地将NSTextField的 refusesFirstResponder属性设置为 YES。但是NSTextView不继承自NSControl,因此没有此属性。因此,最后,我们必须对NSTextView进行子类化,以添加 refusesFirstResponder属性。

该实现将覆盖 becomeFirstResponder,如下所示:

- (BOOL)becomeFirstResponder
{
if (!_refusesFirstResponder) return [super becomeFirstResponder];

NSEvent *event = [NSApp currentEvent];
if ([event type] == NSLeftMouseDown || [event type] == NSRightMouseDown) return [super becomeFirstResponder];

NSView *validKeyView = ([event modifierFlags] & NSShiftKeyMask)? [[[self previousValidKeyView] previousValidKeyView] previousValidKeyView] : [self nextValidKeyView];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{[[self window] makeFirstResponder:validKeyView];}];
return NO;
}


如果 refusesFirstResponderNO,我们只返回super的实现。

如果它是 YES,我们将检查NSTextView是否由于鼠标单击而成为第一响应者。如果是这样,我们还可以简单地返回super的实现,从而允许使用鼠标选择文本。

除此之外,我们将第一个响应者请求转发到下一个或上一个键视图(取决于是否按下Shift键)并返回 NO,拒绝成为第一响应者。确定上一个键视图有些棘手,因为最接近的上一个键视图是我们不想要或不需要的嵌入式NSClipView,但是必须使用,因为Interface Builder不提供“纯” NSTextView。然后是嵌入的NSScrollView,然后才是我们真正想要的上一个键视图。

另外,由于我们处于确定第一响应者的过程之中,因此我们不能简单地调用 makeFirstResponder:,而必须将其推迟到运行循环的下一个迭代中。

现在,我们已经实现了 refusesFirstResponder,我们仍然必须模仿NSTextField的行为,以在失去第一响应者状态时取消所有文本选择。我们可以在NSText委托方法中做到这一点。假设我们不需要其他委托功能,则可以使子类成为自己的委托,并添加以下委托方法:

- (void)textDidEndEditing:(NSNotification*)notification
{
[[notification object] setSelectedRange:NSMakeRange(UINT64_MAX, 0)];
}


最后,无论如何,如果必须子类化,我们不妨添加一个 setAttributedString:便捷方法。

因此,最终结果是:

标头:

#import <Cocoa/Cocoa.h>

IB_DESIGNABLE

@interface MyTextFieldLikeTextView : NSTextView <NSTextViewDelegate>

@property IBInspectable BOOL refusesFirstResponder;

- (void)setAttributedString:(NSAttributedString*)attributedString;

@end


实现方式:

#import "MyTextFieldLikeTextView.h"

@implementation MyTextFieldLikeTextView

- (void)awakeFromNib
{
[self setDelegate:self];
}

- (BOOL)becomeFirstResponder
{
if (!_refusesFirstResponder) return [super becomeFirstResponder];

NSEvent *event = [NSApp currentEvent];
if ([event type] == NSLeftMouseDown || [event type] == NSRightMouseDown) return [super becomeFirstResponder];

NSView *validKeyView = ([event modifierFlags] & NSShiftKeyMask)? [[[self previousValidKeyView] previousValidKeyView] previousValidKeyView] : [self nextValidKeyView];

[[NSOperationQueue mainQueue] addOperationWithBlock:^{[[self window] makeFirstResponder:validKeyView];}];
return NO;
}

- (void)textDidEndEditing:(NSNotification*)notification
{
[[notification object] setSelectedRange:NSMakeRange(UINT64_MAX, 0)];
}

- (void)setAttributedString:(NSAttributedString*)attributedString
{
[[self textStorage] setAttributedString:attributedString];
}

@end


仍然需要付出很多努力,因为可可试图超越我们,并坚持将NSColorPanel连接到每个允许属性文本的NSTextField……

关于cocoa - 可选的NSTextField和NSColorPanel –如何打破它们不希望的相互作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47075982/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com