Women in Technology

Hear us Roar



Article:
  Building a Scratch Pad with Cocoa
Subject:   small steps on my own to three color padview
Date:   2001-12-05 08:52:23
From:   psheldon
I made a red green and blue color button and connected them to actions that chose currentPath amongst 3 different colored Bezierpaths.


Here is the relevant code I added to PadView.m (obvious variables and headers in PadView.h) :


- (IBAction)SetColorPath:(NSString *)NSstr //wouldn't allow me (void *) for return type, not connected to button
{
BOOL test;
test=[NSstr isEqualToString:@"red"];
if (test) pathCurrent=pathRed;
test=[NSstr isEqualToString:@"green"];
if (test) pathCurrent=pathGreen;
test=[NSstr isEqualToString:@"blue"];
if (test) pathCurrent=pathBlue;
}


- (IBAction)ChooseBlue:(id)sender
{
[self SetColorPath:@"blue"];
}


- (IBAction)ChooseGreen:(id)sender
{
[self SetColorPath:@"green"];
}


- (IBAction)ChooseRed:(id)sender
{
[self SetColorPath:@"red"];
}


I don't know whether my code was that tight for further generalization . That tightness might emerge on attempting to generalize.
I could imagine each mouse down could generate a new path and put in an NSMutableArray that could be inspired by canibalizing sketch project for relevant methods. Once that tested out without bugs,
one might have a new colored path button asking for a color well to attach a color to a new path in the array.
Somehow, as the code got more complex, it should have helper methods to make it more surveyable. I don't know what the warning in response to my putting (void *) output to SetColorPath meant, so my confidence at making things surveyable has gotten bitten.
;-)

Full Threads Oldest First

Showing messages 1 through 6 of 6.

  • Michael Beam photo small steps on my own to three color padview
    2001-12-05 21:49:03  Michael Beam | O'Reilly Author [View]

    I think your inability to make the return type (void *) comes from the fact that IBAction is actually type void. (void *) is the datatype for a generic pointer, which is something where void is nothing. Weird. Anyway, good stuff.

    I enable multiple path support in a version of this little app i wrote a while back by doing exactly what you said with using a mutable array. Basically, it worked by creating a new path and appending it to the mutable array when ever the mouse went down. All mouse drag events appended to the most recently added path in the array. Then in the drawRect method i just enumerated the array, sending a stroke message to each array element. While it would be slow, you could enable color support in this by having each element of this mutable array be an NSDictionary that contains a key-value pair for the path object, and a key-value pair for the color. Thus, in each loop through the enumeration you could set the color to draw in based on the information stored in the dictionary, and then stroke the path. Anyway, have fun!


    Mike
    • NSBezier subclass for ColoredPath
      2001-12-08 23:48:12  psheldon [View]

      Ran off to party and perhaps let go of too much code to the thread. Maybe someone can speed read it and see what I was trying to do. Not that they should finish my assumed homework for me, but could my own structs serve as an addanobject for an NSMutableArray and how do I birth a struct other than on a stack? There isn't a NEW, is there, in objective C?
      I think that a subclass could have an NSColor would inherit a constructor and not have the dot problem because I wouldn't be using a struct. Nor would I try to do something crazy like retain an address on a stack, though I don't particularly know why it is crazy, just suspect it so. I guess I would add an accessor method for the color of the ColoredPath.
      I heard someone at an Apple seminar say that objects were much better than structs. He didn't formally declare why but just seemed to assure from experience. Maybe some more talk than his would make me see the trades of using structs rather than a subclass.
      • NSBezier subclass creation set with no get accessor
        2001-12-09 07:06:48  psheldon [View]

        I would make a method to create a ColorPath with an NSColor value which would assign an internal NSColor value and retain it within the object instantiation initialization which would also perform the inherited function. Then, I would override the stroke function to include the NSColor set for the current environment.
        I am not clear with the "fuzzy typing" whether I could make a subclass of ColorPath be ColorPaths because of my narrow focus on the methods of NSBezierclass. Apple didn't do this in sketch, so I think it would be too much of a stretch for me to try to do.
        • works but with warning (short code eg.)
          2001-12-10 09:24:48  psheldon [View]

          ColorBezier.m:20: warning: instance variable `theColor' accessed in class method

          + (ColorBezier*)initWithColor:(NSColor*)color {
          self = [[super bezierPath] retain];

          if (self) {
          theColor = color;
          }
          return self;
          }
          • got rid of warning with accessor and less self
            2001-12-11 10:15:44  psheldon [View]

            I really don't know the meaning of "self" in newtonscript and objective C and need some years of pain, probably, to teach me the intuition and, beyond, perhaps to formalize into words. I think it is the left argument in the square brackets at first, be it class or instance.
            When I tried to make a new version that used a ColorWell, all my plans went haywire, as I doubted I had understood memory managment deep in my bones. I threw in retains and releases and perhaps too many retains knowing that when I quit, it would clean house, maybe. Still no change of color. I think the click in the color well ran the mousedown method and I got messages about not having a point for a line as soon as I tried to draw. I got rid of those error messages, without understanding them really, by insuring that I last clicked on a button I called "Well" by putting code assigning a new path in the action associated with that button in padView.
            So, having gotten rid of an error message, I went on to see that my color didn't change because my ColorBezier subclass of NSBezierpath didn't change. So, perhaps I don't know how to think in assigning currentPath because of this pointer retain and release stuff not having sunk in.
            Perhaps the button and colorwell actions may illustrate my confusion rather than all my code :

            - (IBAction)ChooseWell:(id)sender
            {
            [pathCurrent release];
            pathCurrent = [ColorBezier initWithColor: colorCurrent];
            [pathCurrent retain];
            [ColorPaths addObject: pathCurrent];
            }


            - (IBAction)colorWellAction:(id)sender
            {
            float *locRed,*locGreen,*locBlue,*locAlpha;
            [colorCurrent release];
            colorCurrent = [sender color];
            [colorCurrent retain];
            //[colorCurrent getRed:locRed green:locGreen blue:locBlue alpha:locAlpha];
            [colorCurrent set];
            //[colorCurrent getRed:locRed green:locGreen blue:locBlue alpha:locAlpha];
            }
    • structs for ColorPath in padview
      2001-12-08 16:02:05  psheldon [View]

      I worry that I am building a struct on a stack and trying to retain it in NSMutableArray.
      With this half baked code I got an error message :
      PadView.m:93: parse error before `.'
      PadView.m:102: parse error before `.'
      PadView.m:103: parse error before `ColorPath'
      PadView.m:104: warning: passing arg 1 of `addObject:' from incompatible pointer type

      Here's full code :
      //
      // PadView.h
      // ScratchPad
      //
      // Created by psheldon on Fri Nov 30 2001.
      // Copyright (c) 2001 __MyCompanyName__. All rights reserved.
      //

      #import <AppKit/AppKit.h>

      typedef struct ColorPath {
      NSColor *color;
      NSBezierPath *path;
      } ColorPath;


      @interface PadView : NSView {

      NSBezierPath *pathRed;
      NSBezierPath *pathGreen;
      NSBezierPath *pathBlue;
      NSBezierPath *pathCurrent;

      NSMutableArray *ColorPaths;

      }
      - (IBAction)SetColorPath:(NSString *)NSstr;
      - (void)ChooseBlue:(id)sender;
      - (void)ChooseGreen:(id)sender;
      - (void)ChooseRed:(id)sender;
      @end



      //
      // PadView.m
      // ScratchPad
      //
      // Created by psheldon on Fri Nov 30 2001.
      // Copyright (c) 2001 __MyCompanyName__. All rights reserved.
      //

      #import "PadView.h"


      @implementation PadView

      - (id)initWithFrame:(NSRect)frame {
      self = [super initWithFrame:frame];
      if (self) {
      // Initialization code here.
      //convenience constructor autoreleases so need retain for PadView

      ColorPaths = [[NSMutableArray alloc] init];

      pathRed = [[NSBezierPath bezierPath] retain];
      pathGreen = [[NSBezierPath bezierPath] retain];
      pathBlue = [[NSBezierPath bezierPath] retain];
      pathCurrent = pathRed;
      }
      return self;
      }

      - (void)drawRect:(NSRect)rect {
      // Drawing code here.
      [[NSColor redColor] set];
      [pathRed stroke];
      [[NSColor greenColor] set];
      [pathGreen stroke];
      [[NSColor blueColor] set];
      [pathBlue stroke];
      }
      - (void)mouseDown:(NSEvent *)theEvent
      {
      //mouse location with window to view coordinate change
      NSPoint loc = [theEvent locationInWindow];
      loc.x -= [self frame].origin.x;
      loc.y -= [self frame].origin.y;

      //convenience constructor autoreleases so need retain for PadView
      //path = [[NSBezierPath bezierPath] retain];
      //put pencil down at point
      [pathCurrent moveToPoint:loc];
      }
      - (void)mouseDragged:(NSEvent *)theEvent
      {
      //mouse location with window to view coordinate change
      NSPoint loc = [theEvent locationInWindow];
      loc.x -= [self frame].origin.x;
      loc.y -= [self frame].origin.y;

      //move pencil to dragged to point
      [pathCurrent lineToPoint:loc];
      //padview needs to redisplay with new path information
      [self setNeedsDisplay:YES];
      }
      - (void)mouseUp:(NSEvent *)theEvent
      {
      //[path release];
      }

      - (void)keyDown:(NSEvent *)theEvent
      {
      NSString *keyChar = [theEvent characters];
      if ( [keyChar isEqualToString:@"c"] )
      {
      [pathRed removeAllPoints];
      [pathGreen removeAllPoints];
      [pathBlue removeAllPoints];
      [self setNeedsDisplay:YES];
      }
      }

      - (BOOL)acceptsFirstResponder
      {
      return YES;
      }

      - (IBAction)SetColorPath:(NSString *)NSstr //wouldn't allow me (void *) for return type, not connected to button
      {
      BOOL test;
      NSBezierPath *NewPath;
      NSColor *NewColor;
      ColorPath *NewColorPath;

      NewPath = [[NSBezierPath bezierPath] retain];
      ColorPath.path=NewPath;

      test=[NSstr isEqualToString:@"red"];
      if (test) NewColor = [[NSColor redColor] retain];
      test=[NSstr isEqualToString:@"green"];
      if (test) NewColor = [[NSColor greenColor] retain];
      test=[NSstr isEqualToString:@"blue"];
      if (test) NewColor = [[NSColor blueColor] retain];

      ColorPath.color=NewColor;
      pathCurrent=ColorPath.path;
      [ColorPaths addObject:NewColorPath];

      }

      - (void)ChooseBlue:(id)sender
      {
      [self SetColorPath:@"blue"];
      }

      - (void)ChooseGreen:(id)sender
      {
      [self SetColorPath:@"green"];
      }

      - (void)ChooseRed:(id)sender
      {
      [self SetColorPath:@"red"];
      }


      @end