gui icon indicating copy to clipboard operation
gui copied to clipboard

pasteboard% generates artifacts when snips are dragged behind each other

Open bmitc opened this issue 3 years ago • 4 comments

I am using Racket 8.0 on Windows 10. Run the following code.

#lang racket/gui

(struct dc-state (smoothing pen brush font))

(define (capture-dc-state dc)
  (dc-state (send dc get-smoothing)
            (send dc get-pen)
            (send dc get-brush)
            (send dc get-font)))

(define (restore-dc-state dc dc-state)
  (send dc set-smoothing (dc-state-smoothing dc-state))
  (send dc set-pen (dc-state-pen dc-state))
  (send dc set-brush (dc-state-brush dc-state))
  (send dc set-font (dc-state-font dc-state)))

(define node-snip-class
  (make-object
      (class snip-class%
        (super-new)
        (send this set-classname "node-snip"))))

(send (get-the-snip-class-list) add node-snip-class)

(define (set-extent-others others)
  (for/list ([other others])
    (when other (set-box! other 0.0))))

(define node%
  (class snip%
    (init-field size color)
    (super-new)
    (send this set-snipclass node-snip-class)

    (define/override (get-extent dc x y width height . others)
      (when width (set-box! width size))
      (when height (set-box! height size))
      (set-extent-others others))

    (define/override (draw dc x y . other)
      (let ([dc-state (capture-dc-state dc)])
        (send dc set-smoothing 'smoothed)
        (send dc set-pen "Black" 3 'solid)
        (send dc set-brush color 'solid)
        (send dc draw-rounded-rectangle x y size size 7)
        (restore-dc-state dc dc-state)))
    ))

(define diagram (new pasteboard%))

(define frame (new frame%
                   [label "Diagram"]
                   [width 500]
                   [height 500]))

(define canvas (new editor-canvas%
                    [parent frame]
                    [style '(no-hscroll no-vscroll)]
                    [horizontal-inset 0]
                    [vertical-inset 0]
                    [editor diagram]))

(send diagram insert (new node% [size 50] [color "Pale Green"]) 100 100)

(send diagram insert (new node% [size 50] [color "Pale Turquoise"]) 200 200)

(send frame show #t)

Note that the dc state is captured and restored in the draw override. Drag the green node over the blue one and note that no artifacts occur.

image

Now drag the blue node, which is behind the green node, and note that artifacts appear, potentially from the smoothing property not being correctly handled by the pasteboard%. Although, I'm not sure where exactly the artifacts are coming from at the moment. Notice that the artifacts only occur while the blue node is being dragged behind the green node and that they stop after the blue node "clears" the green node.

image

bmitc avatar Mar 06 '21 06:03 bmitc

This is not a bug. The artifacts are caused by the fact that the snips draw method is drawing outside the snip's bounding box. It is drawing outside the bounding box because you set the pen width to 3 and the line is drawn along the edge of the snip, so half of the line width is outside the snip.

Have you considered using racket-users for Racket related questions?

alex-hhh avatar Mar 06 '21 07:03 alex-hhh

Thank you for catching that. However, I disagree that there isn't a bug here or at least that there isn't something else going on beyond your explanation. Please see the updated code here. I also think that the get-extent method documentation should be updated to provide some guidance to not draw outside the bounding box returned by their implementation of get-extent.

#lang racket/gui

(struct dc-state (smoothing pen brush font))

(define (capture-dc-state dc)
  (dc-state (send dc get-smoothing)
            (send dc get-pen)
            (send dc get-brush)
            (send dc get-font)))

(define (restore-dc-state dc dc-state)
  (send dc set-smoothing (dc-state-smoothing dc-state))
  (send dc set-pen (dc-state-pen dc-state))
  (send dc set-brush (dc-state-brush dc-state))
  (send dc set-font (dc-state-font dc-state)))

(define node-snip-class
  (make-object
      (class snip-class%
        (super-new)
        (send this set-classname "node-snip"))))

(send (get-the-snip-class-list) add node-snip-class)

(define (set-extent-others others)
  (for/list ([other others])
    (when other (set-box! other 0.0))))

(define node%
  (class snip%
    (init-field size color)
    (super-new)
    (send this set-snipclass node-snip-class)

    (define/override (get-extent dc x y width height . others)
      (when width (set-box! width size))
      (when height (set-box! height size))
      (set-extent-others others))

    (define/override (draw dc x y . other)
      (let ([dc-state (capture-dc-state dc)])
        (send dc set-smoothing 'smoothed)
        (send dc set-pen "Black" 1 'solid)
        (send dc set-brush color 'solid)
        (send dc draw-rounded-rectangle (+ x 5) (+ y 5) (- size 10) (- size 10) 7)
        (restore-dc-state dc dc-state)))
    ))

(define diagram (new pasteboard%))

(define frame (new frame%
                   [label "Diagram"]
                   [width 500]
                   [height 500]))

(define canvas (new editor-canvas%
                    [parent frame]
                    [style '(no-hscroll no-vscroll)]
                    [horizontal-inset 0]
                    [vertical-inset 0]
                    [editor diagram]))

(send diagram insert (new node% [size 50] [color "Pale Green"]) 100 100)

(send diagram insert (new node% [size 50] [color "Pale Turquoise"]) 200 200)

(send frame show #t)

Note that I am drawing well within the bounding box determined by get-extent. If one comments out the set-smoothing method call inside the draw method, then these artifacts do not occur. However, this is confusing since I am (supposedly) properly capturing and restoring the smoothing state of the drawing context.

image

bmitc avatar Mar 06 '21 15:03 bmitc

I have learned that this bug does not occur on Mac OS X but still occurs in Windows 10 for Racket 8.1. I'm not sure about Linux.

bmitc avatar May 17 '21 17:05 bmitc

I thought it would help seeing a video of this behavior. This is on Racket 8.10 on Windows 11 with the most recent code above copy and pasted.

https://github.com/racket/gui/assets/65685447/cebcd59f-7445-4ba4-adff-1ac5f330c4ea

bmitc avatar Aug 17 '23 22:08 bmitc