
GL scaling in a canvas depends on a combination of the `gl-config%` setting and the platform, so `get-gl-client-size` takes into account both.
318 lines
11 KiB
Racket
318 lines
11 KiB
Racket
#lang scribble/doc
|
|
@(require "common.rkt")
|
|
|
|
@definterface/title[canvas<%> (subwindow<%>)]{
|
|
|
|
A canvas is a subwindow onto which graphics and text can be drawn. Canvases also
|
|
receive mouse and keyboard events.
|
|
|
|
The @racket[canvas<%>] interface is implemented by two classes:
|
|
@itemize[
|
|
|
|
@item{@racket[canvas%] --- a canvas for arbitrary drawing and
|
|
event handling; and}
|
|
|
|
@item{@racket[editor-canvas%] --- a canvas for displaying
|
|
@racket[editor<%>] objects.}
|
|
|
|
]
|
|
|
|
To draw onto a canvas, get its device context via @method[canvas<%>
|
|
get-dc]. There are two basic approaches to updating a canvas:
|
|
|
|
@itemlist[
|
|
|
|
@item{Drawing normally occurs during the canvas's @method[canvas<%>
|
|
on-paint] callback. The @racket[canvas%] class supports a
|
|
@racket[paint-callback] initialization argument to be called
|
|
from the default @method[canvas<%> on-paint] method.
|
|
|
|
A canvas's @method[canvas<%> on-paint] method is called
|
|
automatically as an event when the windowing system determines
|
|
that the canvas must be updated, such as when the canvas is
|
|
first shown or when it is resized. Use the @method[window<%>
|
|
refresh] method to explicitly trigger an @method[canvas<%>
|
|
on-paint] call from the windowing system. (Multiple refresh
|
|
requests before @method[canvas<%> on-paint] can be called are
|
|
coaleced into a single @method[canvas<%> on-paint] call.)
|
|
|
|
Before the windowing system calls @method[canvas<%> on-paint],
|
|
it may erase the canvas's background (see @method[dc<%>
|
|
erase]), depending on the style of the canvas (e.g., as
|
|
determined by the @racket[style] initialization argument for
|
|
@racket[canvas%]). Even when the canvas's style suppresses
|
|
explicit clearing of the canvas, a canvas may be erased by the
|
|
windowing system due to window-moving and -resizing
|
|
operations. For a transparent canvas, ``erased'' means that the
|
|
canvas's parent window shows through.}
|
|
|
|
@item{Drawing can also occur at any time outside an @method[canvas<%>
|
|
on-paint] call form the windowing system, including from
|
|
threads other than the @tech{handler thread} of the canvas's
|
|
eventspace. Drawing outside an @method[canvas<%> on-paint]
|
|
callback from the system is transient in the sense that
|
|
windowing activity can erase the canvas, but the drawing is
|
|
persistent as long as no windowing refresh is needed.
|
|
|
|
Calling an @method[canvas<%> on-paint] method directly is the
|
|
same as drawing outside an @method[canvas<%> on-paint] callback
|
|
from the windowing system. For a @racket[canvas%], use
|
|
@method[canvas% refresh-now] to force an immediate update of
|
|
the canvas's content that is otherwise analogous to queueing an
|
|
update with @method[window<%> refresh].}
|
|
|
|
]
|
|
|
|
Drawing to a canvas's drawing context actually renders into an
|
|
offscreen buffer. The buffer is automatically flushed to the screen
|
|
asynchronously, explicitly via the @method[canvas<%> flush] method, or
|
|
explicitly via @racket[flush-display]---unless flushing has been
|
|
disabled for the canvas. The @method[canvas<%> suspend-flush] method
|
|
suspends flushing for a canvas until a matching @method[canvas<%>
|
|
resume-flush] calls; calls to @method[canvas<%> suspend-flush] and
|
|
@method[canvas<%> resume-flush] can be nested, in which case flushing
|
|
is suspended until the outermost @method[canvas<%> suspend-flush] is
|
|
balanced by a @method[canvas<%> resume-flush]. An @method[canvas<%>
|
|
on-paint] call from the windowing system is implicitly wrapped with
|
|
@method[canvas<%> suspend-flush] and @method[canvas<%> resume-flush]
|
|
calls, as is a call to a paint procedure by @method[canvas% refresh-now].
|
|
|
|
In the case of a transparent canvas, line and text smoothing can
|
|
depend on the window that serves as the canvas's background. For
|
|
example, smoothing may color pixels differently depending on whether
|
|
the target context is white or gray. Background-sensitive smoothing
|
|
is supported only if a relatively small number of drawing commands are
|
|
recorded in the canvas's offscreen buffer, however.
|
|
|
|
|
|
@defmethod*[([(accept-tab-focus)
|
|
boolean?]
|
|
[(accept-tab-focus [on? any/c])
|
|
void?])]{
|
|
|
|
@index['("keyboard focus" "navigation")]{Gets} or sets whether
|
|
tab-focus is enabled for the canvas (assuming that the canvas is
|
|
not created with the @racket['no-focus] style for @racket[canvas%]). When tab-focus is
|
|
enabled, the canvas can receive the keyboard focus when the user
|
|
navigates among a frame or dialog's controls with the Tab and
|
|
arrow keys. By default, tab-focus is disabled.
|
|
|
|
When tab-focus is enabled for a @racket[canvas%] object, Tab, arrow,
|
|
Enter, and Escape keyboard events are consumed by a frame's default
|
|
@method[top-level-window<%> on-traverse-char] method. (In addition, a
|
|
dialog's default method consumes Escape key events.) Otherwise,
|
|
@method[top-level-window<%> on-traverse-char] allows the keyboard
|
|
events to be propagated to the canvas.
|
|
|
|
For an @racket[editor-canvas%] object, handling of Tab, arrow, Enter,
|
|
and Escape keyboard events is determined by the
|
|
@method[editor-canvas% allow-tab-exit] method.
|
|
|
|
|
|
}
|
|
|
|
|
|
@defmethod[(flush) void?]{
|
|
|
|
Like @racket[flush-display], but constrained if possible to the canvas.}
|
|
|
|
|
|
@defmethod[(get-canvas-background)
|
|
(or/c (is-a?/c color%) #f)]{
|
|
Returns the color currently used to ``erase'' the canvas content before
|
|
@method[canvas<%> on-paint] is called. See also
|
|
@method[canvas<%> set-canvas-background].
|
|
|
|
The result is @racket[#f] if the canvas was created with the
|
|
@indexed-racket['transparent] style, otherwise it is always a
|
|
@racket[color%] object.
|
|
|
|
}
|
|
|
|
|
|
@defmethod[(get-dc)
|
|
(is-a?/c dc<%>)]{
|
|
Gets the canvas's device context. See @racket[dc<%>] for more information about
|
|
drawing.
|
|
|
|
}
|
|
|
|
|
|
@defmethod[(get-scaled-client-size) (values dimension-integer? dimension-integer?)]{
|
|
|
|
Returns the canvas's drawing-area dimensions in unscaled pixels---that
|
|
is, without scaling (see @secref["display-resolution"]) that is
|
|
implicitly applied to the canvas size and content.
|
|
|
|
For example, when a canvas on Mac OS X resides on a Retina display, it
|
|
has a backing scale of @racket[2], and so the results from
|
|
@method[canvas<%> get-scaled-client-size] will be twice as large as results from
|
|
@method[window<%> get-client-size]. If the same canvas's frame is dragged to a
|
|
non-Retina screen, its backing scale can change to @racket[1], in
|
|
which case @method[canvas<%> get-scaled-client-size] and
|
|
@method[window<%> get-client-size] will produce the same value. Whether
|
|
a canvas's backing scale can change depends on the platform.
|
|
|
|
The size reported by @method[canvas<%> get-scaled-client-size] may match
|
|
a viewport size for OpenGL drawing in @racket[canvas%] instance with
|
|
the @racket['gl] style. On Mac OS X, however, the viewport will match
|
|
the scaled size unless the canvas is created with a
|
|
@racket[gl-config%] specification that is adjusted to high-resolution
|
|
mode via @method[gl-config% set-hires-mode]. See also
|
|
@xmethod[canvas% get-gl-client-size].
|
|
|
|
@history[#:added "1.13"]}
|
|
|
|
|
|
@defmethod*[([(min-client-height)
|
|
dimension-integer?]
|
|
[(min-client-height [h dimension-integer?])
|
|
void?])]{
|
|
|
|
Gets or sets the canvas's minimum height for geometry management,
|
|
based on the client size rather than the full size. The client height
|
|
is obtained or changed via
|
|
@xmethod[area<%> min-height], adding or subtracting border and scrollbar sizes as appropriate.
|
|
|
|
The minimum height is ignored when it is smaller than the canvas's
|
|
@tech{graphical minimum height}. See @|geomdiscuss| for
|
|
more information.
|
|
}
|
|
|
|
|
|
@defmethod*[([(min-client-width)
|
|
dimension-integer?]
|
|
[(min-client-width [w dimension-integer?])
|
|
void?])]{
|
|
|
|
Gets or sets the canvas's minimum width for geometry management, based
|
|
on the canvas's client size rather than its full size. The client
|
|
width is obtained or changed via
|
|
@xmethod[area<%> min-width], adding or subtracting border and scrollbar sizes as appropriate.
|
|
|
|
The minimum width is ignored when it is smaller than the canvas's
|
|
@tech{graphical minimum width}. See @|geomdiscuss| for
|
|
more information.
|
|
|
|
}
|
|
|
|
|
|
@defmethod[(on-char [ch (is-a?/c key-event%)])
|
|
void?]{
|
|
@methspec{
|
|
|
|
Called when the canvas receives a keyboard event. See also
|
|
@|mousekeydiscuss|.
|
|
|
|
}
|
|
@methimpl{
|
|
|
|
Does nothing.
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
@defmethod[(on-event [event (is-a?/c mouse-event%)])
|
|
void?]{
|
|
@methspec{
|
|
|
|
Called when the canvas receives a mouse event. See also
|
|
@|mousekeydiscuss|, noting in particular that certain mouse events
|
|
can get dropped.
|
|
|
|
}
|
|
@methimpl{
|
|
|
|
Does nothing.
|
|
|
|
|
|
|
|
}}
|
|
|
|
@defmethod[(on-paint)
|
|
void?]{
|
|
@methspec{
|
|
|
|
Called when the canvas is exposed or resized so that the image in the
|
|
canvas can be repainted.
|
|
|
|
When
|
|
@method[canvas<%> on-paint] is called in response to a system expose event and only a portion of
|
|
the canvas is newly exposed, any drawing operations performed by
|
|
@method[canvas<%> on-paint] are clipped to the newly-exposed region; however, the clipping region
|
|
as reported by
|
|
@method[dc<%> get-clipping-region] does not change.
|
|
|
|
}
|
|
@methimpl{
|
|
|
|
Does nothing.
|
|
|
|
|
|
|
|
}}
|
|
|
|
@defmethod[(on-tab-in)
|
|
void?]{
|
|
@methspec{
|
|
|
|
Called when the keyboard focus enters the canvas via keyboard
|
|
navigation events. The
|
|
@method[window<%> on-focus] method is also called, as usual for a focus change. When the keyboard
|
|
focus leaves a canvas due to a navigation event, only
|
|
@method[window<%> on-focus] is called.
|
|
|
|
See also
|
|
@method[canvas<%> accept-tab-focus] and
|
|
@xmethod[top-level-window<%> on-traverse-char] .
|
|
|
|
}
|
|
@methimpl{
|
|
|
|
Does nothing.
|
|
|
|
|
|
|
|
}}
|
|
|
|
|
|
@defmethod[(resume-flush) void?]{
|
|
|
|
See @racket[canvas<%>] for information on canvas flushing.}
|
|
|
|
|
|
|
|
@defmethod[(set-canvas-background [color (is-a?/c color%)])
|
|
void?]{
|
|
|
|
Sets the color used to ``erase'' the canvas content before
|
|
@method[canvas<%> on-paint] is called. (This color is typically associated with the canvas at a
|
|
low level, so that it is used even when a complete refresh of the
|
|
canvas is delayed by other activity.)
|
|
|
|
If the canvas was created with the @indexed-racket['transparent] style,
|
|
@|MismatchExn|.
|
|
|
|
}
|
|
|
|
@defmethod[(set-resize-corner [on? any/c])
|
|
void?]{
|
|
|
|
On Mac OS X, enables or disables space for a resize tab at the
|
|
canvas's lower-right corner when only one scrollbar is visible. This
|
|
method has no effect on Windows or Unix, and it has no effect when
|
|
both or no scrollbars are visible. The resize corner is disabled by
|
|
default, but it can be enabled when a canvas is created with the
|
|
@racket['resize-corner] style.
|
|
|
|
}
|
|
|
|
|
|
@defmethod[(suspend-flush) void?]{
|
|
|
|
See @racket[canvas<%>] for information on canvas flushing.
|
|
|
|
Beware that suspending flushing for a canvas can discourage refreshes
|
|
for other windows in the same frame on some platforms.}} |