clearing NSView outside of drawRect:

  • How do you clear an NSView when drawing outside of drawRect:?

    I added and positioned a custom view on top another view
    (addSubView:positioned:relativeTo:) that draws a complex image so that
    I can draw selections in my custom view without having to worry about
    redrawing my original image or using a cached image.  Inside my
    mouseDragged: implementation, I lock my custom view's focus, attempt
    to erase the view using fillRect with clearColor (which doesn't work),
    draw my selection, then unlock the focus.  When that didn't work (and
    I tried returning yes and no for isOpaque), I went ahead and just did
    a setNeedsDisplay:YES on my custom view and just drew the selection in
    drawRect:.  That works but there's a noticeable lag while the mouse is
    being dragged.

    In Carbon, I was able to do the above by using an overlay and
    CGContextClearRect().  The only thing I can think of to try next is to
    use similar logic in Cocoa and to place the custom view in a
    borderless window and use NSRectFillUsingOperation(rect,
    NSCompositeClear).  I was hoping to avoid having to do that and deal
    with keeping the two view's positions and sizes in sync.

    -Chinh Nguyen
      <cnguyen...>
  • On Thu, Sep 11, 2008 at 5:09 PM, Chinh Nguyen <cnguyen...> wrote:
    > How do you clear an NSView when drawing outside of drawRect:?

    Don't. Drawing outside of drawRect: is, essentially, wrong. There are
    extremely rare cases where it makes sense, but in virtually all cases,
    if you suddenly feel the urge to draw outside of drawRect:, you should
    take this as a sign that you are making some kind of error.

    > I added and positioned a custom view on top another view
    > (addSubView:positioned:relativeTo:) that draws a complex image so that I can
    > draw selections in my custom view without having to worry about redrawing my
    > original image or using a cached image.  Inside my mouseDragged:
    > implementation, I lock my custom view's focus, attempt to erase the view
    > using fillRect with clearColor (which doesn't work), draw my selection, then
    > unlock the focus.  When that didn't work (and I tried returning yes and no
    > for isOpaque), I went ahead and just did a setNeedsDisplay:YES on my custom
    > view and just drew the selection in drawRect:.  That works but there's a
    > noticeable lag while the mouse is being dragged.

    It's possible to do high-performance animation by using
    setNeedsDisplay:. If you're getting noticeable lag then the problem is
    not the use of drawRect:, the problem is that something that you're
    drawing (or some other piece of code) is taking too long. Find out
    what it is and stop it from taking so long, and you'll be good to go.

    Mike
  • If I read this correctly, you're hoping that you can erase some of the
    drawing done in your overlay view, just revealing the original drawing
    in your complex background view.

    That won't work (with some caveats about layer-backed mode).  All
    views draw into one flat buffer, called the window backing store.  If
    your selection view draws solid black in a rect, that destroys the
    color information that the background view had drawn there.

    You can get the effect you want in layer backed mode (c.f. -[NSView
    setWantsLayer:]).  Then each view has its own buffer, so your
    selection drawing doesn't wipe out the background drawing, it only
    covers it.  You don't need to do anything funky with drawing outside
    of drawRect:, though.  Just call setNeedsDisplay: on the parts of your
    selection view that need to be redrawn.  This will not cause the
    background view to redraw.  It's similar to using an overlay window,
    just easier.

    -Ken

    On Thu, Sep 11, 2008 at 2:09 PM, Chinh Nguyen <cnguyen...> wrote:
    > How do you clear an NSView when drawing outside of drawRect:?
    >
    > I added and positioned a custom view on top another view
    > (addSubView:positioned:relativeTo:) that draws a complex image so that I can
    > draw selections in my custom view without having to worry about redrawing my
    > original image or using a cached image.  Inside my mouseDragged:
    > implementation, I lock my custom view's focus, attempt to erase the view
    > using fillRect with clearColor (which doesn't work), draw my selection, then
    > unlock the focus.  When that didn't work (and I tried returning yes and no
    > for isOpaque), I went ahead and just did a setNeedsDisplay:YES on my custom
    > view and just drew the selection in drawRect:.  That works but there's a
    > noticeable lag while the mouse is being dragged.
    >
    > In Carbon, I was able to do the above by using an overlay and
    > CGContextClearRect().  The only thing I can think of to try next is to use
    > similar logic in Cocoa and to place the custom view in a borderless window
    > and use NSRectFillUsingOperation(rect, NSCompositeClear).  I was hoping to
    > avoid having to do that and deal with keeping the two view's positions and
    > sizes in sync.
    >
    > -Chinh Nguyen
    > <cnguyen...>
    >
  • On Sep 11, 2008, at 10:16 PM, Ken Ferry wrote:

    > If I read this correctly, you're hoping that you can erase some of the
    > drawing done in your overlay view, just revealing the original drawing
    > in your complex background view.

    > You can get the effect you want in layer backed mode (c.f. -[NSView
    > setWantsLayer:]).  Then each view has its own buffer, so your
    > selection drawing doesn't wipe out the background drawing, it only
    > covers it.  You don't need to do anything funky with drawing outside
    > of drawRect:, though.  Just call setNeedsDisplay: on the parts of your
    > selection view that need to be redrawn.  This will not cause the
    > background view to redraw.  It's similar to using an overlay window,
    > just easier.

    Thanks for your suggestions.  Unfortunately, I can't use [NSView
    setWantsLayer:] because I'm trying to support Tiger.  I had assumed
    that views already had their own backing stores which is why I went
    down this route.  Your explanation help clear that up and point me in
    the right direction.

    As for using setNeedsDisplay instead of drawing immediately during
    mouseDragged events.  My selection is several pixels behind my mouse
    as I'm dragging it when I use setNeedsDisplay: (when I tried
    setWantsLayer:, it was only a few pixels behind).  When I draw
    immediately, the selection stays right under the mouse as I drag it.
    It's not my background view's drawRect: because I turned it off to
    confirm whether it was the source of the lag and it's not my selection
    view's drawRect: because my test case is simply drawing a line between
    a fixed point and the current location of the mouse.  I have not tried
    a mouse-tracking loop yet.

    -Chinh Nguyen
    <cnguyen...>
  • On 13 Sep 2008, at 1:29 am, Chinh Nguyen wrote:

    > My selection is several pixels behind my mouse as I'm dragging it
    > when I use setNeedsDisplay:

    This suggests you're handling the drag loop incorrectly. There's no
    reason this should be the case when using setNeedDisplay: - if you
    organise your code right, it will work fine, and no extra tricks like
    using your own tracking loop are needed.

    Maybe you could show your code?

    Normally, for dragging a selection rect I use the following basic
    approach:

    1. On mouse down, make a note of the initial point in an ivar. This is
    the "anchor".
    2. On mouse dragged, form a rect from two corner points, being the
    current mouse and the previously recorded anchor. Save this in an
    ivar. Invalidate that rect with setNeedsDisplayInRect:.
    3. On drawRect:, use the rect calculated in (2) to draw the selection
    highlight as you wish. If you are using the rect to select objects,
    you can also pass it to other methods that find which objects it
    touches or encloses.

    The above should keep up with the mouse with no noticeable lag. If
    your "detect selected objects" method is very slow it may stutter
    somewhat, but that's a different area for optimisation - the
    fundamental approach to dragging the box doesn't need to change.

    hth,

    Graham
  • On 13 Sep 2008, at 10:21 am, Graham Cox wrote:

    > If you are using the rect to select objects, you can also pass it
    > to other methods that find which objects it touches or encloses.

    To be clear - of course you'd do this in step (2), not step (3), then
    *draw* the feedback of being in a selected state in step (3).

    cheers, Graham
previous month september 2008 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          
Go to today