I am making an app that is a standalone menu item and the basis for the code is sample code I found on a website. The sample code uses a number as the menu icon, but I want to change it to an image.
I want it to be like other apps where it shows icon.png when not clicked and icon-active.png when clicked.
The current code is this:
- (void)drawRect:(NSRect)rect {
// Draw background if appropriate.
if (clicked) {
[[NSColor selectedMenuItemColor] set];
NSRectFill(rect);
}
// Draw some text, just to show how it's done.
NSString *text = #"3"; // whatever you want
NSColor *textColor = [NSColor controlTextColor];
if (clicked) {
textColor = [NSColor selectedMenuItemTextColor];
}
NSFont *msgFont = [NSFont menuBarFontOfSize:15.0];
NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
[paraStyle setParagraphStyle:[NSParagraphStyle defaultParagraphStyle]];
[paraStyle setAlignment:NSCenterTextAlignment];
[paraStyle setLineBreakMode:NSLineBreakByTruncatingTail];
NSMutableDictionary *msgAttrs = [NSMutableDictionary dictionaryWithObjectsAndKeys:
msgFont, NSFontAttributeName,
textColor, NSForegroundColorAttributeName,
paraStyle, NSParagraphStyleAttributeName,
nil];
[paraStyle release];
NSSize msgSize = [text sizeWithAttributes:msgAttrs];
NSRect msgRect = NSMakeRect(0, 0, msgSize.width, msgSize.height);
msgRect.origin.x = ([self frame].size.width - msgSize.width) / 2.0;
msgRect.origin.y = ([self frame].size.height - msgSize.height) / 2.0;
[text drawInRect:msgRect withAttributes:msgAttrs];
}
Also, I found a post describing a method on how to do this, but it did not work for me. The url to that is this: http://mattgemmell.com/2008/03/04/using-maattachedwindow-with-an-nsstatusitem/comment-page-1#comment-46501.
Thanks!
Use an NSImage and draw it where desired. For example:
NSString *name = clicked? #"icon-active" : #"icon";
NSImage *image = [NSImage imageNamed:name];
NSPoint p = [self bounds].origin;
[image drawAtPoint:p fromRect:NSZeroRect
operation:NSCompositeSourceOver fraction:1.0];
If this is for a status item and you just want an icon with no programmatic drawing, drop the view and set the status item's image and alternateImage. The former is what the status item uses normally; the status item switches to the alternate image (if it has one) when the user opens its menu.
Related
EDITED:
I am trying to show an iTunes-style like information bar. This was subject was covered in detail earlier, for example at iTunes or Xcode style information box at top of window
I only slightly modified the code from the above referenced link, so make it compile under a recent XCode.
My code is below:
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
static NSShadow *kDropShadow = nil;
static NSShadow *kInnerShadow = nil;
static NSGradient *kBackgroundGradient = nil;
static NSColor *kBorderColor = nil;
if (kDropShadow == nil) {
kDropShadow = [[NSShadow alloc] init];
[kDropShadow setShadowColor:[NSColor colorWithCalibratedWhite:.863 alpha:.75]];
[kDropShadow setShadowOffset:NSMakeSize(0.0, -1.0)];
[kDropShadow setShadowBlurRadius:1.0];
kInnerShadow = [[NSShadow alloc] init];
[kInnerShadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.52]];
[kInnerShadow setShadowOffset:NSMakeSize(0.0, -1.0)];
[kInnerShadow setShadowBlurRadius:4.0];
kBorderColor = [[NSColor colorWithCalibratedWhite:0.569 alpha:1.0] retain];
// iTunes style
// kBackgroundGradient = [[NSGradient alloc] initWithColorsAndLocations:[NSColor colorWithCalibratedRed:0.929 green:0.945 blue:0.882 alpha:1.0],0.0,[NSColor colorWithCalibratedRed:0.902 green:0.922 blue:0.835 alpha:1.0],0.5,[NSColor colorWithCalibratedRed:0.871 green:0.894 blue:0.78 alpha:1.0],0.5,[NSColor colorWithCalibratedRed:0.949 green:0.961 blue:0.878 alpha:1.0],1.0, nil];
// Xcode style
kBackgroundGradient = [[NSGradient alloc] initWithColorsAndLocations:[NSColor colorWithCalibratedRed:0.957 green:0.976 blue:1.0 alpha:1.0],0.0,[NSColor colorWithCalibratedRed:0.871 green:0.894 blue:0.918 alpha:1.0],0.5,[NSColor colorWithCalibratedRed:0.831 green:0.851 blue:0.867 alpha:1.0],0.5,[NSColor colorWithCalibratedRed:0.82 green:0.847 blue:0.89 alpha:1.0],1.0, nil];
}
NSRect bounds = [self bounds];
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:bounds xRadius:3.5 yRadius:3.5];
[NSGraphicsContext saveGraphicsState];
[kDropShadow set];
[path fill];
[NSGraphicsContext restoreGraphicsState];
[kBackgroundGradient drawInBezierPath:path angle:-90.0];
[kBorderColor setStroke];
[path stroke];
}
It is not working, however. I don't think drawRect() method ever gets called. What am I missing? Please advise.
Thank you
I had an extra line at the end:
[path fill];
Removing it does the trick.
I would like to display an NSImageView in my main View:
img=[[NSImageView alloc]init];
[img setImage:[[NSImage alloc]initWithContentsOfFile:filename]];
width=img.image.size.width/2;
height=img.image.size.height/2;
[img setFrame:NSMakeRect(0,0,width ,height)];
mainview = [((AppDelegate *)[NSApp delegate]).window contentView];
[mainview addSubview:img];
The image is properly displayed, however instead of being in the top-left corner of my main window, it's completely going to the right side of the screen.
What is wrong above?
Try like this:-
- (void)windowDidLoad
{
NSString *imageName = [[NSBundle mainBundle] pathForResource:#"yourImage ofType:#"tiff"];
NSImage *photoImage = [[NSImage alloc] initWithContentsOfFile:imageName];
[yourimgView setImage:photoImage];
CGFloat width=yourimgView.bounds.size.width;
CGFloat height=yourimgView.bounds.size.height;
CGFloat xboundspoint=yourimgView.bounds.origin.x;
CGFloat yboundspoint=yourimgView.bounds.origin.y;
[yourimgView setFrame:NSMakeRect(xboundspoint, yboundspoint+150.0, width, height)];
[yourview addSubview:yourimgView];
[[[self window]contentView]addSubview:yourview];
[super windowDidLoad];
}
I must be doing something wrong here:
My Cocoa app has a scrollview around a custom view which in turn has a textview. I only expect to see one "This is a " string but there the extra one up in the corner.
I have reduced the code to something very minimal and still do not understand what my error is, so here I am fishing for a clue.
The view controller for the custom view follows, but for simplicity here is a link to the project.
#import "TTTSimpleCtrlView.h"
#interface TTTSimpleCtrlView ()
#property (strong,nonatomic) NSTextView *tv1;
#property (strong,nonatomic) NSTextStorage *ts;
#end
#implementation TTTSimpleCtrlView
- (void) awakeFromNib {
NSFont *font = [NSFont fontWithName:#"Courier New Bold" size:20.0f];
NSMutableParagraphStyle *styleModel = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[styleModel setLineHeightMultiple:1.0];
// [styleModel setLineSpacing:fontRect.size.height * 2];
NSDictionary *textAttrs = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName,
[NSColor blackColor] ,NSForegroundColorAttributeName,
[NSColor whiteColor], NSBackgroundColorAttributeName,
styleModel, NSParagraphStyleAttributeName,
nil];
NSString *pilcrowStr = #"This is a test.";
NSAttributedString *s = [[NSAttributedString alloc] initWithString:pilcrowStr attributes:textAttrs];
NSRect rect = [s boundingRectWithSize:NSMakeSize(INFINITY,INFINITY)options:0];
NSLayoutManager *lm = [[NSLayoutManager alloc] init];
NSTextContainer *tc = [NSTextContainer new];
[tc setContainerSize:s.size];
[lm addTextContainer:tc];
_ts = [[NSTextStorage alloc] init];
[_ts setAttributedString:s];
[_ts addLayoutManager:lm];
[lm replaceTextStorage:_ts];
rect.origin.x = 10;
rect.origin.y = rect.size.height;
NSTextView *v = [[NSTextView alloc] initWithFrame:rect textContainer:tc];
[v setDrawsBackground:YES];
[self addSubview:v];
}
- (BOOL) isFlipped {
return YES;
}
- (void)drawRect:(NSRect)rect
{
NSLog(#"drawRect & %lu subviews",self.subviews.count);
for (NSTextView *v in self.subviews) {
if(CGRectIntersectsRect(v.frame, rect) || CGRectContainsRect(rect, v.frame)) {
[v drawRect:rect];
NSLog(#"frame = %#",NSStringFromRect(v.frame));
}
}
[super drawRect:rect];
}
You are calling:
[super drawRect:rect];
and you are drawing the text yourself in your draw function.
In effect you are drawing the text and cocoa is drawing the text for you as well.
So don't call super.
I have NSStatusItem with a custom NSView which shows images.
Whether the menu is opened or not it show different images just like that:
isMenuVisible = NO;
- (void)awakeFromNib {
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem retain];
dragStatusView = [[DragStatusView alloc] init];
[dragStatusView retain];
dragStatusView.statusItem = statusItem;
[dragStatusView setMenu:statusMenu];
[dragStatusView setToolTip:NSLocalizedString(#"Menubar Countdown",
#"Status Item Tooltip")];
[statusItem setView:dragStatusView];
[dragStatusView setTitle:#"11"];
}
- (void)drawImage:(NSImage *)aImage centeredInRect:(NSRect)aRect{
NSRect imageRect = NSMakeRect((CGFloat)round(aRect.size.width*0.5f-aImage.size.width*0.5f),
(CGFloat)round(aRect.size.height*0.5f-aImage.size.height*0.5f),
aImage.size.width,
aImage.size.height);
[aImage drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
}
- (void)drawRect:(NSRect)rect {
// Draw status bar background, highlighted if menu is showing
[statusItem drawStatusBarBackgroundInRect:[self bounds]
withHighlight:isMenuVisible];
if(isMenuVisible) {
[self drawImage:image2 centeredInRect:rect];
}else {
[self drawImage:image1 centeredInRect:rect];
}}
(Of course this is not everything, but i hope all the relevant code to understand my problem)
Now i want to show a NSProgressIndicator in this NSView (in this NSStatusItem) if an upload is proceeding which means
1. Set the NSProgressIndicator when upload starts
2.Received something ? ==> Hide NSProgressIndicator show the image again.
How would i solve this ?
Please help me. Thanks
Here is the solution for View based status item.
NSStatusBar *bar = [NSStatusBar systemStatusBar];
self.theItem = [bar statusItemWithLength:NSVariableStatusItemLength];
self.statusView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 40, 20)];
NSProgressIndicator* progress = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(0, 0, 40, 20)];
[progress setIndeterminate:NO];
[self.statusView addSubview:progress];
self.theItem.view = self.statusView;
You can also draw a progress bar your self in Menu based status item
on progress change you can simply change the image of your status item.
- (void) updateIconWithProgress:(float)aProgress
{
NSImage* image = [NSImage imageNamed:#"small_icon_16_16.png"];
NSImage* img = [image copy];
[img lockFocus];
NSRect rect = NSMakeRect(2, 4, 12.0*aProgress, 8.0);
[[NSColor blueColor] set];
[NSBezierPath fillRect:rect];
[img unlockFocus];
[self.theItem setImage:img];
}
p.s. The code was written for ARC (Automatic Reference Counting) so there is no retain or release.
I have a window with a button for printing receipts. What I need to do is to create a simple NSTextView, add an NSString to it (at least something like "Hello World") and print it without displaying NSTextView on the window.
Here is what I currently have:
NSTextView *textView = [[NSTextView alloc] init];
NSString *text = #"testing";
[textView setEditable:true];
NSRange range = NSMakeRange( 0, [[textView string] length]);
[textView setSelectedRange:range];
[[[textView textStorage] mutableString] appendString:text];
NSPrintOperation *printOperation;
printOperation = [NSPrintOperation printOperationWithView:textView];
[printOperation runOperation];
when I run it, I see printing dialog, but preview is empty.
When I change printOperationWithView:textView]; from textView to one of the existing views on my window, it prints ok.
The main thing is.. I don't want to display the view after I click Print button. Ideally I would like to print the text, not the view.
Have you tried using -initWithFrame with a nonzero rect?
NSTextView *textview = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, 468, 648);
(468 x 648 would make it fit an A4 sheet with 1-inch margins.)
Here is an implementation which works in an NSDocument subclass. Modify it to suit your needs.
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary<NSString *,id> *)printSettings error:(NSError * _Nullable __autoreleasing *)outError
{
NSPrintInfo * printInfo = [self printInfo];
printInfo.topMargin = 15.0;
printInfo.leftMargin = 10.0;
printInfo.rightMargin = 10.0;
printInfo.bottomMargin = 15.0;
printInfo.verticallyCentered = NO;
NSRect bounds = printInfo.imageablePageBounds;
NSTextView * printView = [[NSTextView alloc] initWithFrame:bounds];
printView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[printView.textStorage appendAttributedString:self.attributedStringRepresentation];
return [NSPrintOperation printOperationWithView:printView printInfo:printInfo];
}
Have you tried just:
[textView print];
This is very good. Just one more enhancement. In order for the print out not to be centered, and to fill up the page correctly, pass printInfo in as a parameter.
NSPrintOperation *printOperation;
NSPrintInfo *pInfo = [[NSPrintInfo alloc] init];
[pInfo setBottomMargin:50];
[pInfo setTopMargin:50];
[pInfo setVerticallyCentered:false];
printOperation = [NSPrintOperation printOperationWithView:textView printInfo:pInfo];
[printOperation runOperation];