#lang syndicate (require "../../widgets.rkt") (require "cells-canvas.rkt") (require racket/set racket/list racket/format) ;; a simple spreadsheet (will not check for circularities) (require 7GUI/task-7-exp) (require 7GUI/task-7-view) ;; ----------------------------------------------------------------------------- (struct formula (formula dependents) #:transparent) #; {Formula = [formula Exp* || Number || (Setof Ref*)]} (define (spawn-control frame) (spawn (field [*content (make-immutable-hash)] ;; [Hashof Ref* Integer] [*formulas (make-immutable-hash)] ;; [Hashof Ref* Formula] ) (define-syntax-rule (iff selector e default) (let ([v e]) (if v (selector v) default))) (define (get-exp ref*) (iff formula-formula (hash-ref (*formulas) ref* #f) 0)) (define (get-dep ref*) (iff formula-dependents (hash-ref (*formulas) ref* #f) (set))) (define (get-content ref*) (hash-ref (*content) ref* 0)) (local-require 7GUI/should-be-racket) (define (set-content! ref* vc) (define current (get-content ref*)) (*content (hash-set (*content) ref* vc)) (when (and current (not (= current vc))) (when* (get-dep ref*) => propagate-to))) (define (propagate-to dependents) (for ((d (in-set dependents))) (set-content! d (evaluate (get-exp d) (*content))))) (define (set-formula! ref* exp*) (define new (formula exp* (or (get-dep ref*) (set)))) (*formulas (hash-set (*formulas) ref* new)) (register-with-dependents (depends-on exp*) ref*) (set-content! ref* (evaluate exp* (*content)))) (define (register-with-dependents dependents ref*) (for ((d (in-set dependents))) (*formulas (hash-set (*formulas) d (formula (get-exp d) (set-add (get-dep d) ref*)))))) ;; --------------------------------------------------------------------------------------------------- ;; cells and contents (define ((mk-edit title-fmt validator registration source frame) x y) (define cell (list (x->A x) (y->0 y))) (when (and (first cell) (second cell)) (react (define value0 (~a (or (source cell) ""))) ;; maybe need to make use of queue-callback ? (define dialog (spawn-dialog #:parent #f #:style '(close-button) #:label (format title-fmt cell))) (define tf (spawn-text-field #:parent dialog #:label #f #:min-width 200 #:min-height 80 #:init-value value0)) (on (message (text-field-enter tf $contents)) (when* (validator contents) => (lambda (valid) (stop-current-facet (send! (show dialog #f)) (registration cell valid) (send! (update-grid (*content))))))) (on (asserted (dialog@ dialog)) (send! (show dialog #t)))))) (define content-edit (mk-edit "content for cell ~a" valid-content set-content! get-content frame)) (define formula-fmt "a formula for cell ~a") (define formula-edit (mk-edit formula-fmt string->exp* set-formula! (compose exp*->string get-exp) frame)) ;; --------------------------------------------------------------------------------------------------- (on (message (single-click $x $y)) (content-edit x y)) (on (message (double-click $x $y)) (formula-edit x y)) (on-start (send! (update-grid (*content)))) )) ;; --------------------------------------------------------------------------------------------------- (define frame (spawn-frame #:label "Cells" #:width (/ WIDTH 2) #:height (/ HEIGHT 3))) (define canvas (spawn-cells-canvas frame WIDTH HEIGHT)) (spawn-control frame) (spawn (on (asserted (frame@ frame)) (send! (show frame #t)) (stop-current-facet)))