# (Auto) scrollbars in \`canvas%\`

**URL:** <https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914>\
**Category:** General\
**Tags:** question, gui\
**Created:** [May 13, 2024, 3:06pm UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914 "2024-05-13T15:06:30Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [May 13, 2024, 3:06pm UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914/1 "2024-05-13T15:06:30Z")

</div>

I'm back with [another scrollbar question](https://racket.discourse.group/t/putting-a-large-pict-in-a-text-scroll-bar-problems/2700) 🙂

This time you can demo with

1. `raco pkg install https://github.com/benknoble/frosthaven-manager#pict-text-display`
2. `racket -l frosthaven-manager/gui/pict-text-display`

The central issue is that `canvas%` doesn't support the `'auto-vscroll` style (like `editor-canvas%` and `horizontal-panel%`, for example). So when resizing an element so that the pict displayed in the canvas extends out of sight (even with `'vscroll`), there's no scroll bar to recover the full image.

I've tried a few invocations of `(send this init-auto-scrollbars …)` (largely via the `#:mixin` argument for GUI Easy's `pict-canvas`), but nothing worked in the way I was hoping: I could never make a useable scrollbar "thumb" appear after resizing the example window from (2) so that the canvas was smaller than the pict.

I did go peeking around mrcanvas and the wxme/gtk/cocoa canvas files, but I got confused. It seems the latter all support `'auto-vscroll` but the former doesn't? It was never clear to me which actual canvas definition was relevant, and then the code for `editor-canvas%` was convoluted enough that I had a difficult time understanding how it implemented `'auto-vscroll`.

If anyone knows a way to make scrollbars in `canvas%` work (especially via a mixin), please let me know.

P.S. I've read [Racket/gui: how to use canvas%'s init-auto-scrollbar method](https://racket.discourse.group/t/racket-gui-how-to-use-canvas-s-init-auto-scrollbar-method/1263), thanks Discourse! Unfortunately, it doesn't seem to help clarify my particular question.

---

<div class="post-metadata">

**Author:** ![jjsimpso](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jjsimpso/32/602_2.png) [@jjsimpso](https://racket.discourse.group/u/jjsimpso)\
**Post date:** [May 14, 2024, 3:15am UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914/2 "2024-05-14T03:15:40Z")

</div>

I know this doesn't really answer your question, but I ended up using manual scrollbars(I posed the question in the thread you referenced).

---

<div class="post-metadata">

**Author:** ![alexh](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/alexh/32/315_2.png) [@alexh](https://racket.discourse.group/u/alexh)\
**Post date:** [May 14, 2024, 8:49am UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914/3 "2024-05-14T08:49:13Z")

</div>

It looks like the `'auto-vscroll` and `'auto-hscroll` styles are not supported for `canvas%` objects, but they are easy to implement. Below is an example on how to do it. My implementation assumes that `init-auto-scrollbars` is only called once, so the virtual canvas size never changes, but you can override that method as well to trigger the showing of scrollbars, as it is done for `on-size`.

hope this helps,  
Alex.

```scheme
#lang racket/gui

;; a canvas% object supporting 'auto-hscroll and 'auto-vscroll styles
(define auto-scroll-canvas%
  (class canvas%
    (init [style null])

    (define auto-hscroll? (member 'auto-hscroll style))
    (define auto-vscroll? (member 'auto-vscroll style))
    (define hscroll? (member 'hscroll style))
    (define vscroll? (member 'vscroll style))

    ;; Replace auto-scroll options in style with plain scroll options and pass
    ;; it on to the super class...
    (let* ([c (remf* (lambda (v) (member v '(auto-hscroll auto-vscroll hscroll vscroll))) style)]
           [h (if (or hscroll? auto-hscroll?) (cons 'hscroll c) c)]
           [v (if (or vscroll? auto-vscroll?) (cons 'vscroll h) h)])
      (super-new [style v]))

    ;; Enable/Disable scroll bars if the virtual canvas size is larger than
    ;; the client area
    (define/override (on-size window-width window-height)
      (define-values (w h) (send this get-client-size))
      (define-values (vw vh) (send this get-virtual-size))
      (send this show-scrollbars
            (or hscroll? (and auto-hscroll? (> vw w)))
            (or vscroll? (and auto-vscroll? (> vh h)))))

    ))

(define the-frame (new frame% [label "Canvas Test"] [width 200] [height 200]))
(define the-canvas (new auto-scroll-canvas% [parent the-frame] [style '(auto-vscroll auto-hscroll)]))
(send the-canvas init-auto-scrollbars 400 400 0 0)

(send the-frame show #t)

```

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [June 2, 2024, 9:47pm UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914/4 "2024-06-02T21:47:56Z")

</div>

Thanks, I'll take a look.

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [June 16, 2024, 3:12pm UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914/5 "2024-06-16T15:12:49Z")

</div>

I'm just now getting around to trying to integrate this with GUI Easy. I noticed that in your demo scrolling with the mouse doesn't scroll the canvas (click-dragging the scrollbars works).

Is this another instance of [Panels/Frames do not respond to scrolling · Issue #68 · racket/gui · GitHub](https://github.com/racket/gui/issues/68)?

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [June 16, 2024, 4:03pm UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914/6 "2024-06-16T16:03:50Z")

</div>

> Is this another instance of [Panels/Frames do not respond to scrolling · Issue #68 · racket/gui · GitHub](https://github.com/racket/gui/issues/68)?

If so, it might be because the canvas's [`on-char` forwarding to `wx`'s `do-on-char`](https://github.com/racket/gui/blob/3a800c8f36f082300009500cf8aac204aa6afd19/gui-lib/mred/private/mrcanvas.rkt#L39) doesn't handle wheel-up and wheel-down events, but I'm not sure.

I couldn't add those via a mixin on canvases because I couldn't update the automatic scroll bar positions in a reasonable way—I expected to be able to retrieve auto-scroll-bar positions to calculate new values (or at least to be able to track positions myself using `on-scroll`, but the passed scroll event doesn't seem to give me what I need for that).

---

<div class="post-metadata">

**Author:** ![alexh](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/alexh/32/315_2.png) [@alexh](https://racket.discourse.group/u/alexh)\
**Post date:** [June 17, 2024, 1:07pm UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914/7 "2024-06-17T13:07:20Z")

</div>

To use the mouse wheel scroll, you need to override the `on-char` event to handle the `'wheel-up` and `'wheel-down` events and call the `scroll` method with the appropriate parameters. Below is an example on how this could be done, although, depending on what application requirements you have, you might need to update the code or use some other strategy.

I also added the `'wheel-left` and `'wheel-right` handlers, for horizonal scrolling - the trackpad of my laptop does generate these key codes, but I'm not sure if other trackpads do.

Alex.

```scheme
#lang racket/gui

;; a canvas% object supporting 'auto-hscroll and 'auto-vscroll styles
(define auto-scroll-canvas%
  (class canvas%
    (init [style null])

    (define auto-hscroll? (member 'auto-hscroll style))
    (define auto-vscroll? (member 'auto-vscroll style))
    (define hscroll? (member 'hscroll style))
    (define vscroll? (member 'vscroll style))

    ;; Replace auto-scroll options in style with plain scroll options and pass
    ;; it on to the super class...
    (let* ([c (remf* (lambda (v) (member v '(auto-hscroll auto-vscroll hscroll vscroll))) style)]
           [h (if (or hscroll? auto-hscroll?) (cons 'hscroll c) c)]
           [v (if (or vscroll? auto-vscroll?) (cons 'vscroll h) h)])
      (super-new [style v]))

    ;; Enable/Disable scroll bars if the virtual canvas size is larger than
    ;; the client area
    (define/override (on-size window-width window-height)
      (define-values (w h) (send this get-client-size))
      (define-values (vw vh) (send this get-virtual-size))
      (send this show-scrollbars
            (or hscroll? (and auto-hscroll? (> vw w)))
            (or vscroll? (and auto-vscroll? (> vh h)))))

    (define (do-vscroll percent)
      (define-values (vw vh) (send this get-virtual-size))
      (define-values (cw ch) (send this get-client-size))
      (define-values (x y) (send this get-view-start))
      (define v-value
        (let ([v (/ y (- vh ch))])
          (min 1.0 (max 0.0 (+ v percent)))))
      (send this scroll #f v-value))

    (define (do-hscroll percent)
      (define-values (vw vh) (send this get-virtual-size))
      (define-values (cw ch) (send this get-client-size))
      (define-values (x y) (send this get-view-start))
      (define h-value
        (let ([v (/ x (- vw cw))])
          (min 1.0 (max 0.0 (+ v percent)))))
      (send this scroll h-value #f))
    
    (define/override (on-char event)
      (case (send event get-key-code)
        ((wheel-up) (do-vscroll -1/100))
        ((wheel-down) (do-vscroll 1/100))
        ((wheel-left) (do-hscroll -1/100))
        ((wheel-right) (do-hscroll 1/100))
        (else (super on-char event))))

    ))

(define the-frame (new frame% [label "Canvas Test"] [width 200] [height 200]))
(define the-canvas (new auto-scroll-canvas% [parent the-frame] [style '(auto-vscroll auto-hscroll)]))
(send the-canvas init-auto-scrollbars 1000 1000 0 0)

(send the-frame show #t)

```

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [June 17, 2024, 5:01pm UTC](https://racket.discourse.group/t/auto-scrollbars-in-canvas/2914/8 "2024-06-17T17:01:20Z")

</div>

Aha! The h-value/v-value calculations were what was missing; otherwise this is close to what I had. I'll give this a try soon, thanks.
