How can you get the shared NSColorPanel to an show alpha / opacity slider? - cocoa

When you add an NSColorWell control, and click it, it displays the shared instance of NSColorPanel.
Unfortunately, by default it does not show the alpha / opacity slider.
This is also true when it is invoked from the default MainMenu > Format > Font > Show Colors

Simply call the following line at any time in your app.
[[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
You can call it once in applicationDidFinishLaunching: or you can easily tie it to a switch like an NSButton checkbox with a simple IBAction method like this:
- (IBAction)showAlphaSliderInColorPanel:(id)sender {
if ([sender state] == NSOnState) {
[[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
} else {
[[NSColorPanel sharedColorPanel] setShowsAlpha:NO];
}
}
Just connect that to the Sent Actions selector item in the Connections Inspector with for a button configured to have an on / off state.
The change will occur live as you click.
A great example of how awesome Cocoa is when you want it to be.

Related

NSTextView not changing font size or colour from NSFontPanel

I created a basic NSTextView, I selected the following options in Interface Builder:
Editable
Selectable
Field Editor
Rich Text
Undo
Graphics
Non-contiguous Layout
Font Panel
Ruler
Inspector Bar
I set the NSViewController to be the delegate of the NSTextView and the only other custom thing I've done for this NSTextView is to enable inserting tabs and new lines (by accepting First responder):
func textView(_ textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
if commandSelector == #selector(insertNewline(_:)) {
textView.insertNewlineIgnoringFieldEditor(self)
return true
} else if commandSelector == #selector(insertTab(_:)) {
textView.insertTabIgnoringFieldEditor(self)
return true
} //else if commandSelector == #selector(changeColor(_:)) {
//textView.setTextColor(NSFontPanel.colo, range: <#T##NSRange#>)
//}
return false
}
When I try to use the commands from the Font Panel + Inspector Bar, All the commands work fine except changing Font size or colour, is there anything that could be wrong? Or do I need to do extra binding/delegates, etc for this to work?
It is strange because if I change the Font itself (of a selected text) or the weight, it works fine (no coding was needed).
Update
I've found the root of the problem causing this. I'm displaying the TextView in a ViewController that is displayed using a Modal segue. If I change from Modal to Show, the size and colour work fine. There's also no need for the extra commands for insert new line and tab.
Is there any reason why this is the case? Is there any customisation that should be done to the segue to avoid this? And, why is the view controller presentation affecting the behaviour of the font panel?
NSFontPanel has a 'worksWhenModal' property which sounds as if it might be set to 'false'.
A Boolean that indicates whether the receiver allows fonts to be changed in modal windows and panels.
Documentation:

How to change the action of NSColorWell?

I can use NSColorWell as button to change the color of selected text. Since NSColorWell is object of an NSControl it has target and action. I guess, the action is implementing the code to change the color of the selected text in NSTextView. Where can I find this code for NSColorWell action? I would like to change it in away that I can use NSColorWell to change the background of the selected text, and ultimately to have in ToolBar two NSColorWell buttons: one to change text's foreground color and second one for text's background color.
NSColorWell is just a rectangular control to change a color.
You can either create an IBAction and connect it to the action of the color well in the Connections Inspector (⌥⌘6) of Interface Builder
#IBAction func changeColor(_ sender : NSColorWell)
{
let color = sender.color
// do something with the color
}
Or bind the value in Bindings Inspector (⌥⌘7) of Interface Builder to a dynamic property, this example will set the color well to a default value of green.
dynamic var color : NSColor = .green {
didSet {
// do something with the color
}
}

Mouse Event in Outline View

I have a window which contains a split view. One of the "splits" contains an outline view. I have a window controller (which is the file owner for the window's XIB). The window controller is the delegate and data source of the outline view.
When I call the -(void)mouseDown:(NSEvent *)e method in the window controller only the toolbar responds to the method - the outline view does not.
How do I get the mouse events, e.g. the mouseDown, of the outline view?
To get the mouse event of the outline view:
Subclass the outline view.
In Interface Builder (IB) > Library panel > Classes tab select NSOutlineView
Right-click NSOutlineView and select "New Subclass..."
Complete the following pop-up windows selecting "Generate Source Files" and add the fils to your project
Select the NSOutlineView
In Inspector Panel > Identity tab > Class Identity > Class select your new class
Implement your mouse event method
In Xcode > your new subclass of your outline view > the implementation (.m) file type your method e.g.
(void)mouseDown:(NSEvent *)theEvent {
/* CODE YOU WANT EXECUTED WHEN MOUSE IS CLICKED */
NSLog(#"Mouse down occurred");
// call this to get the usual behaviour of your outline
// view in addition to your custom code
[super mouseDown:theEvent];
}
It may be useful to know that one can get mouse events by using [NSEvent modifierFlags]. This is will work not just for the outline view but for views throughout the app. For example, in the window controller (referred to in the question) I could include code like:
if ([NSEvent modifierFlags] == NSAlternateKeyMask) { // if the option key is being pressed
/*SOME CODE*/
}

How to disable NSBox from code

I put several controls (button,textfield,...) in a NSBox. is it possible to disable NSBox that user can't access controls (means can't click on button or write in textfield)?
how about nsview ?
An NSBox is basically just a view with a border, there's no way to "disable" it. If you want to disable all the controls in a box, you could loop through all its subviews and disable them , or another way I've done this is to put an overlay view over the whole box and override mouseDown in that overlay (to capture any mouseDown events so they aren't queued in the event loop). You can also give the overlay a semi-transparent white color so the box has a disabled appearance.
Or, if you have a custom NSBox, you can override NSView's -hitTest: (conditionally)
- (NSView *)hitTest:(NSPoint)aPoint {
if (!enabled) return nil;
else return [super hitTest:aPoint];
}
To stop the window from sending events to all your subviews.
To provide visual feedback, conditionally drawing some sort of overlay in the custom NSBox's -drawRect method would work.
Yes, you just need to look at the subviews of the NSBox, which is typically just one NSView, and then your actual controls will be under the subviews of that.
Here's a quick C-style function I wrote to enable/disable most common UI controls, including NSBox...
void SetObjEnabled(NSObject * Obj, bool Enabled)
{
//Universal way to enable/disable a UI object, including NSBox contents
NSControl * C = (NSControl *)Obj;
if([C respondsToSelector:#selector(setEnabled:)])
[C setEnabled:Enabled];
if([C.className compare:#"NSTextField"] == NSOrderedSame)
{
NSTextField * Ct = (NSTextField*)C;
if(!Enabled)
[Ct setTextColor:[NSColor disabledControlTextColor]];
else //Enabled
[Ct setTextColor:[NSColor controlTextColor]];
}
else if([C.className compare:#"NSBox"] == NSOrderedSame)
{
NSBox * Cb = (NSBox*)C;
//There is typically just one subview at this level
for(NSView * Sub in Cb.subviews)
{
//Here is where we'll get the actual objects within the NSBox
for(NSView * SubSub in Sub.subviews)
SetObjEnabled(SubSub, Enabled);
}
}
}

How to put a disclosure triangle's title to the right of the triangle?

In the HIG's example of how to use disclosure triangles, it shows a label directly to the right of the triangle.
However, when I throw one of these onto my view in Interface Builder, the text is centered on top of the triangle. I've searched the NSButton API docs, and poked at everything I can find in IB, but nothing I try will put the text to the right of the triangle. What am I missing?
What I generally do is use 2 buttons: one disclosure button and another button for the label:
While you can use a text field for the label, I prefer using a button and setting the button to call performClick: on the disclosure triangle. This makes for a much larger target area to be able to click on than a tiny triangle. (Users with trackpads will thank you).
To set up the button, change it so it looks like this:
Then set its action:
I'm not sure if there's an actual way to get the button to show both properly (without subclassing I mean), since I've generally just used separate items to give the effect. (I just checked and there is indeed a Carbon disclosure control that has both the triangle and the label built-in).
The Carbon control has the right idea where clicking on the label will automatically trigger the control. In some places (notably the re-written Cocoa Finder), you can see that you don't get that behavior for free (unless you use a button like I've shown). I still have an open bug on that one (rdar://6828042): BugID 6828042: 10.6 (10A335) Finder: Inspector's disclsr. triangle's text label not toggleable". ;-)
Have you tried just using a triangle and using a separate label?
The disclosure triangle widget is drawn by the button's bezel, centered in the available space. To create a disclosure triangle button which also has a title, you just need to subclass NSButtonCell and make sure the bezel is restricted to the left side of the button and that the title avoids the bezel. Then add your button in IB, expand it and set your title, and set the class of the cell. Unfortunately, IB won't know how to display your subclass and will put the triangle in the middle of the button. Just make sure it's big enough.
In Objective-C:
#interface TitledDisclosureTriangleButtonCell : NSButtonCell
#end
#define TRIANGLE_PADDING 15.f
#implementation TitledDisclosureTriangleButtonCell
- (NSRect)titleRectForBounds:(NSRect)theRect
{
NSRect titleRect = [super titleRectForBounds:theRect];
titleRect.origin.x = TRIANGLE_PADDING;
titleRect.size.width = NSWidth(titleRect) - TRIANGLE_PADDING;
return titleRect;
}
- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView
{
NSRect bezelFrame = frame;
bezelFrame.size.width = TRIANGLE_PADDING;
[super drawBezelWithFrame:bezelFrame inView:controlView];
}
#end
And in Swift:
let TRIANGLE_PADDING: CGFloat = 15
class TitledDisclosureTriangleButtonCell: NSButtonCell
{
override func titleRectForBounds(theRect: NSRect) -> NSRect {
var titleRect = super.titleRectForBounds(theRect)
titleRect.origin.x = TRIANGLE_PADDING
titleRect.size.width = titleRect.size.width - TRIANGLE_PADDING
return titleRect
}
override func drawBezelWithFrame(frame: NSRect, inView controlView: NSView) {
var bezelFrame = frame
bezelFrame.size.width = TRIANGLE_PADDING
super.drawBezelWithFrame(bezelFrame, inView: controlView)
}
}

Resources