Smalltick DrawingEditor

George Peter Staplin: July 5, 2004: My Smalltick widgets are progressing well. I've managed to create a canvas-like widget that can be used to draw images. Today I implemented most of a decode.png command to go with a gui.create.image (for creating XImages) and displayed my first PNG using it. I just need to complete the < 24-bit display handling. I will then focus on storing pixmaps with encode.png (through gui.get.image (to get an XImage with ->data)).

I've wanted to clone A symmetric doodler for quite some time using Smalltick, and I may soon achieve that goal. I also have plans to write decode.jpeg and encode.jpeg.

Without further ado we can begin the example code:

Here's the usercode for the DrawingEditor widget/class:

 proc create.gui mw {
  [set topc [Container [new.object] $mw]] \
   -row 0 -column 0 -sticky we -map

  [set botc [Container [new.object] $mw]] \
   -row 1 -column 0 -sticky news -map

  [set de [DrawingEditor [new.object] $botc]] \
   -width 400 -height 450 -row 0 -column 0 -sticky news -map

  set i 0
  foreach col [list red green blue orange purple] {
   [Button [new.object] $topc] \
    -text $col -command [list [list $de -set.pen.color $col]] \
    -bg $col -row 0 -column $i -map
   incr i

 proc main {} {
  set mw [Widget [new.object]]
  $mw set {window-path .} \
   -column.weight [list 0 100] \
   -row.weight [list 0 100]

  create.gui $mw

And here we have the DrawingEditor widget/class implementation:

 proc DrawingEditor {d p} {
  Widget $d

  $d create.window $p

  $d set {drawing-editor-pixmap {}}
  $d set {drawing-editor-pen-color {}}
  $d set {drawing-editor-pen-color-string {}}
  $d set [list drawing-editor-pen-gc [gui.create.gc [$d get wid]]]
  $d set {drawing-editor-pen-start-x 0}
  $d set {drawing-editor-pen-start-y 0}
  $d set {drawing-editor-busy 0}
  $d set {drawing-editor-pixmap-width 2000}
  $d set {drawing-editor-pixmap-height 2000}

  $d append.destroy.command \
    [list {gui.destroy.gc [$self get drawing-editor-pen-gc]}] \
   append.destroy.command \
    [list {gui.destroy.pixmap [$self get drawing-editor-pixmap]}] \
   append.destroy.command \
    [list {gui.destroy.gc [$self get drawing-editor-pen-gc]}] \
   append.destroy.command \
    [list {gui.destroy.color [$self get drawing-editor-pen-color]}]

  $d : -set.pen.color color-string {
   $self ?set [list drawing-editor-pen-color-string [set color-string]]
   if {"" != [set gcol [$self get drawing-editor-pen-color]]} {
    gui.destroy.color $gcol
   $self ?set \
    [list drawing-editor-pen-color [set col [gui.create.color [set color-string]]]]
   gui.set.gc.color [$self get drawing-editor-pen-gc] $col

  $d : create.drawing.editor.pixmap {} {
   if {"" != [set p [$self get drawing-editor-pixmap]]} {
    gui.destroy.pixmap $p
   $self ?set \
    [list drawing-editor-pixmap [gui.create.pixmap [$self get window-path] \
     [$self get drawing-editor-pixmap-width] \
     [$self get drawing-editor-pixmap-height]]]

   set gc [gui.create.gc [$self get wid]]
   gui.set.gc.color $gc [set col [gui.create.color white]]
   gui.draw.rectangle \
    [$self get drawing-editor-pixmap] \
    $gc \
    0 0 \
    [$self get drawing-editor-pixmap-width] \
    [$self get drawing-editor-pixmap-height]

   gui.destroy.color $col
   gui.destroy.gc $gc

  $d : draw.drawing.editor {} {
   $self ?set {drawing-editor-busy 0}

   gui.copy.area \
    [$self get drawing-editor-pen-gc] \
    [$self get drawing-editor-pixmap] \
    [$self get wid] \
    0 0 \
    [winfo width [$self get window-path]] \
    [winfo height [$self get window-path]] \
    0 0 

  $d : draw.drawing.editor.when.idle {} {
   if {[$self get drawing-editor-busy]} return

   $self ?set {drawing-editor-busy 1}
   after idle [list $self draw.drawing.editor]

  $d : draw.on.pixmap {x y} {
   gui.draw.line \
    [$self get drawing-editor-pixmap] \
    [$self get drawing-editor-pen-gc] \
    [$self get drawing-editor-pen-start-x] \
    [$self get drawing-editor-pen-start-y] \
    $x $y
   $self set.initial.start.coordinates [list $x $y]
   $self draw.drawing.editor.when.idle 

  $d : drawing.editor.configure.event {w h} {
   $self draw.drawing.editor.when.idle

  $d : dump.structure {} {
   puts PIXMAP:[$self get drawing-editor-pixmap]

   puts PEN_COLOR:[$self get drawing-editor-pen-color]

   puts COLOR_STRING:[$self get drawing-editor-pen-color-string]

   puts PEN_GC:[$self  get drawing-editor-pen-gc]

   puts START_X:[$self get drawing-editor-pen-start-x]

   puts START_Y:[$self get drawing-editor-pen-start-y]

  $d : set.initial.start.coordinates {x y} {
   $self ?set [list drawing-editor-pen-start-x $x]
   $self ?set [list drawing-editor-pen-start-y $y]

  $d create.drawing.editor.pixmap 

  $d -set.pen.color red

  bind DrawingEditor$d <ButtonPress-1> \
   [list $d set.initial.start.coordinates {%x %y}]

  bind DrawingEditor$d <B1-Motion> \
   [list $d draw.on.pixmap {%x %y}]

  bind DrawingEditor$d <Expose> [list $d draw.drawing.editor.when.idle]

  bind DrawingEditor$d <Configure> \
   [list $d drawing.editor.configure.event {%w %h}]

  set w [$d get window-path]

  #bindtags $w [list $w DrawingEditor$d]
  bindtags $w [linsert [bindtags $w] end DrawingEditor$d]

  $d draw.drawing.editor.when.idle

  return $d}

This is what it can look like with the usercode:

Category Widget