From 610280225ccf5d5acf308ca28de95abec5da3ff4 Mon Sep 17 00:00:00 2001 From: Neil Toronto Date: Sat, 3 Mar 2012 12:33:49 -0700 Subject: [PATCH] Added unstable/gui/snip (provides snip-canvas%) and docs Changed plot-frame to use snip-canvas%, so resizing the frame resizes the plot; added Escape handler (closes the frame) --- collects/plot/common/gui.rkt | 54 +++++------ collects/plot/scribblings/plot2d.scrbl | 2 +- collects/unstable/gui/snip.rkt | 81 +++++++++++++++++ collects/unstable/scribblings/gui.scrbl | 1 + collects/unstable/scribblings/gui/snip.scrbl | 95 ++++++++++++++++++++ 5 files changed, 200 insertions(+), 33 deletions(-) create mode 100644 collects/unstable/gui/snip.rkt create mode 100644 collects/unstable/scribblings/gui/snip.scrbl diff --git a/collects/plot/common/gui.rkt b/collects/plot/common/gui.rkt index 08c1d97e89..cb7085ab91 100644 --- a/collects/plot/common/gui.rkt +++ b/collects/plot/common/gui.rkt @@ -1,44 +1,34 @@ #lang racket/base -;; Extra GUI classes. +;; GUI helpers -(require racket/gui/base racket/class) +(require racket/gui/base racket/class unstable/gui/snip) (provide (all-defined-out)) -(define read-only-text% - (class text% +(define snip-frame% + (class frame% (super-new) - (send this hide-caret #t) - - (define writable? #t) - (define/public (set-writable w?) (set! writable? w?)) - - (define/augment (can-change-style? start len) writable?) - (define/augment (can-delete? start len) writable?) - (define/augment (can-insert? start len) writable?) - (define/augment (can-load-file? filename format) writable?) - (define/augment (can-save-file? filename format) writable?) - (define/override (can-do-edit-operation? op [recursive? #t]) - (case op - [(copy select-all) #t] - [else writable?])) + (define/override (on-traverse-char event) + (define key-code (send event get-key-code)) + (case key-code + [(escape) (send this show #f)] + [else (super on-traverse-char event)])) )) (define (make-snip-frame snip width height label) - (define frame (new frame% [width width] [height height] [label label])) - (define text (new read-only-text%)) - (define canvas (new editor-canvas% [parent frame] [editor text] - [horizontal-inset 0] [vertical-inset 0] [horiz-margin 0] [vert-margin 0] - [enabled #t] [style '(no-hscroll no-vscroll no-border)])) - (send text insert snip) - (send text set-writable #f) + (define (make-snip w h) + (send snip resize w h) + snip) + + (define frame + (new snip-frame% [label label] [width (+ 10 width)] [height (+ 10 height)])) + + (new snip-canvas% + [parent frame] + [make-snip make-snip] + [horiz-margin 5] [vert-margin 5] + [horizontal-inset 5] [vertical-inset 5]) + frame) - -(define (snip->canvas snip) - (let/ec return - (define admin (send snip get-admin)) - (when (not admin) (return #f)) - (define editor (send admin get-editor)) - (send editor get-active-canvas))) diff --git a/collects/plot/scribblings/plot2d.scrbl b/collects/plot/scribblings/plot2d.scrbl index b214d260f4..e72ec8095e 100644 --- a/collects/plot/scribblings/plot2d.scrbl +++ b/collects/plot/scribblings/plot2d.scrbl @@ -84,7 +84,7 @@ Use @(racket plot-bitmap) to create a @(racket bitmap%). Use @(racket plot-frame) to create a @(racket frame%) regardless of the value of @(racket plot-new-window?). The frame is initially hidden. -Use @(racket plot-snip) to create an @(racket image-snip%) regardless of the value of @(racket plot-new-window?). +Use @(racket plot-snip) to create an interactive @(racket image-snip%) regardless of the value of @(racket plot-new-window?). } @doc-apply[plot/dc]{ diff --git a/collects/unstable/gui/snip.rkt b/collects/unstable/gui/snip.rkt new file mode 100644 index 0000000000..2ae69240f4 --- /dev/null +++ b/collects/unstable/gui/snip.rkt @@ -0,0 +1,81 @@ +#lang racket/base + +(require racket/gui/base racket/class) + +(provide snip-canvas%) + +(define snip-canvas% + (class editor-canvas% + (init parent + make-snip + [style null] + [label #f] + [horizontal-inset 5] + [vertical-inset 5] + [enabled #t] + [vert-margin 0] + [horiz-margin 0] + [min-width 0] + [min-height 0] + [stretchable-width #t] + [stretchable-height #t]) + + (define snip #f) + (define text (new read-only-text%)) + (send text set-writable #f) + + (super-new [parent parent] + [editor text] + [horizontal-inset horizontal-inset] + [vertical-inset vertical-inset] + [label label] + [enabled enabled] + [style (list* 'no-hscroll 'no-vscroll style)] + [vert-margin vert-margin] + [horiz-margin horiz-margin] + [min-width min-width] + [min-height min-height] + [stretchable-width stretchable-width] + [stretchable-height stretchable-height]) + + (define/public (get-snip) snip) + + (define/override (on-size w h) + (update-snip w h) + (super on-size w h)) + + (define (update-snip w h) + (define snip-w (max 0 (- w (* 2 horizontal-inset)))) + (define snip-h (max 0 (- h (* 2 vertical-inset)))) + (cond [snip (send snip resize snip-w snip-h)] + [else (set-snip (make-snip snip-w snip-h))])) + + (define (set-snip s) + (unless (is-a? s snip%) + (raise-type-error 'set-snip "snip%" s)) + (set! snip s) + (send text set-writable #t) + (send text erase) + (send text insert snip) + (send text set-writable #f)) + )) + +(define read-only-text% + (class text% + (super-new) + + (send this hide-caret #t) + + (define writable? #t) + (define/public (set-writable w?) (set! writable? w?)) + + (define/augment (can-change-style? start len) writable?) + (define/augment (can-delete? start len) writable?) + (define/augment (can-insert? start len) writable?) + (define/augment (can-load-file? filename format) writable?) + (define/augment (can-save-file? filename format) writable?) + (define/override (can-do-edit-operation? op [recursive? #t]) + (case op + [(copy select-all) #t] + [else writable?])) + )) diff --git a/collects/unstable/scribblings/gui.scrbl b/collects/unstable/scribblings/gui.scrbl index 6a521243cc..2aea0dcf93 100644 --- a/collects/unstable/scribblings/gui.scrbl +++ b/collects/unstable/scribblings/gui.scrbl @@ -12,3 +12,4 @@ @include-section["gui/pict.scrbl"] @include-section["gui/slideshow.scrbl"] @include-section["gui/pslide.scrbl"] +@include-section["gui/snip.scrbl"] diff --git a/collects/unstable/scribblings/gui/snip.scrbl b/collects/unstable/scribblings/gui/snip.scrbl new file mode 100644 index 0000000000..0a1e31eaf3 --- /dev/null +++ b/collects/unstable/scribblings/gui/snip.scrbl @@ -0,0 +1,95 @@ +#lang scribble/manual + +@(require "../utils.rkt" + (for-label racket/base + racket/class + racket/gui/base + unstable/gui/snip + racket/contract + plot)) + +@title[#:tag "snip"]{Snip Utilities} +@unstable[@author+email["Neil Toronto" "neil.toronto@gmail.com"]] + +@defmodule[unstable/gui/snip] + +@defclass[snip-canvas% editor-canvas% ()]{ +A canvas that contains a single snip. + +Snips cannot be placed directly on dialogs, frames and panels. +To use an interactive snip (such as one returned by @racket[plot-snip]) in a GUI, +it must be inserted into an editor, which itself must be placed on a special canvas, which can be placed in a GUI container. +To provide a seamless user experience, the editor should be enabled but not writable, +not be able to receive focus, not have scrollbars, and other small details. + +The @racket[snip-canvas%] class handles these details, making it easy to use interactive snips as normal GUI elements. + +@defconstructor[([parent (or/c (is-a?/c frame%) (is-a?/c dialog%) + (is-a?/c panel%) (is-a?/c pane%))] + [make-snip ((integer-in 0 10000) (integer-in 0 10000) . -> . snip%)] + [style (listof (one-of/c 'no-border 'control-border 'combo + 'resize-corner 'no-focus 'deleted + 'transparent)) null] + [label (or/c label-string? false/c) #f] + [horizontal-inset (integer-in 0 1000) 5] + [vertical-inset (integer-in 0 1000) 5] + [enabled any/c #t] + [vert-margin (integer-in 0 1000) 0] + [horiz-margin (integer-in 0 1000) 0] + [min-width (integer-in 0 10000) 0] + [min-height (integer-in 0 10000) 0] + [stretchable-width any/c #t] + [stretchable-height any/c #t])]{ +Unlike instances of @racket[editor-canvas%], each instance of this class creates and manages its own editor. +The editor contains one object: a @racket[snip%] instance created by @racket[make-snip]. + +The @racket[make-snip] function receives the requested width and height of the snip, which are calculated from the size of the snip canvas. +It is called the first time the snip canvas is resized, which most likely coincides with the first time the snip canvas is shown. +The snip is thus created @italic{lazily}: only when needed, at the size needed. +See @method[snip-canvas% on-size] for more details and an example. + +The @racket[style] list is prepended with @racket['no-hscroll] and @racket['no-vscroll] before being passed to the @racket[editor-canvas%] constructor. +The other constructor arguments are passed untouched. +} + +@defmethod[(get-snip) (or/c (is-a?/c snip%) #f)]{ +Returns the wrapped snip, or @racket[#f] if @racket[make-snip] has not been called yet. +} + +@defmethod[#:mode override + (on-size [width (integer-in 0 10000)] + [height (integer-in 0 10000)]) + void?]{ +This is called when the snip canvas is resized. + +On the first call, @method[snip-canvas% on-size] calls @racket[make-snip] with width and height arguments +respectively @racket[(max 0 (- width (* 2 horizontal-inset)))] and @racket[(max 0 (- height (* 2 vertical-inset)))]. +It then inserts the resulting snip into its editor. + +On subsequent calls, @method[snip-canvas% on-size] calls the snip's @method[snip% resize] method, calculating the width and height arguments the same way. + +When a @racket[snip-canvas%] instance is intended to wrap an existing @racket[snip%] instance, @racket[make-snip] should simply resize it and return it. + +Example: @racket[plot-frame] and @racket[plot3d-frame] create snips and call a function similar to the following to place them in a frame: +@racketblock[ +(define (make-snip-frame snip w h label) + (define (make-snip width height) + (send snip resize width height) + snip) + + (define frame + (new frame% + [label label] + [width (+ 5 5 5 5 w)] + [height (+ 5 5 5 5 h)])) + + (new snip-canvas% + [parent frame] + [make-snip make-snip] + [horiz-margin 5] [vert-margin 5] + [horizontal-inset 5] [vertical-inset 5]) + + frame)] +} + +} @; defclass snip-canvas%