- ;; *****************************************************
- ;; Chat client Skeleton
- (require 2htdp/image)
- (require 2htdp/universe)
- ;; A (chat) Client is (make-client name lines editor curse-on?)
- ;; where name is a String, lines is a [Listof String],
- ;; and editor is a String. curse-on? is a boolean
- ;; used for making the cursor blink.
- ;; A (chat) Client is our local World.
- ;;
- ;; Interp.
- ;; A chat client needs three things:
- ;; - The (nick)name of the chatter
- ;; - A list of previous lines in the chat
- ;; (prefixed by the nick of the sender)
- ;; - The current line being edited
- ;; - The state of the cursor (true or false) - used for making
- ;; the cursor blink
- (define-struct client (name lines editor curse-on?))
- (define client-1 (make-client "chadwick"
- (list "chadwick: Hola peeps"
- "chadwick: Lame chat party :/")
- "" false))
- (define client-2 (make-client "Joker" '() "Why so seri" true))
- ;; Constants for various specifics of the client GUI
- (define CLIENT-HEIGHT 200)
- (define CLIENT-WIDTH 400)
- (define FONT-SIZE 12)
- (define FONT-COLOR "black")
- (define LINE-SPACING 5)
- (define LINE-INDENT 5)
- (define DIVIDING-COLOR "blue")
- (define NICK-DIVIDER " : ")
- (define CURSOR-IMG-ON (rectangle 4 (+ 4 (* 2 LINE-SPACING)) "solid" "red"))
- (define CURSOR-IMG-OFF (rectangle 3 (+ 4 (* 2 LINE-SPACING)) "solid" "black"))
- (define CHAT-AREA
- (local [(define SEP-Y (- CLIENT-HEIGHT FONT-SIZE (* 2 LINE-SPACING)))]
- (scene+line (empty-scene CLIENT-WIDTH CLIENT-HEIGHT)
- 0 SEP-Y CLIENT-WIDTH SEP-Y DIVIDING-COLOR)))
- ;; W/2 : Image -> Number
- ;; Calculate the image width divided by 2
- (define (W/2 img)
- (if (image? img)
- (/ (image-width img) 2)
- (error "W/2 expect an image, got: " img)))
- ;; H/2 : Image -> Number
- ;; Calculate the image height divided by 2
- (define (H/2 img)
- (if (image? img)
- (/ (image-height img) 2)
- (error "W/2 expect an image, got: " img)))
- ;; chattify : String -> Image
- (define (chattify s)
- (if (string? s)
- (text s FONT-SIZE FONT-COLOR)
- (error "chattify expects a string, got: " s)))
- ;; render-client : Client -> Scene
- ;; Render the client editor and stored chat lines
- (define (render-client c)
- (if (and (client? c) (string? (client-name c)) (string? (client-editor c))
- (list? (client-lines c)) (andmap string? (client-lines c)))
- (local [(define editor (chattify (string-append (client-name c)
- NICK-DIVIDER
- (client-editor c))))]
- (add-chattings (client-lines c) (- CLIENT-HEIGHT (image-height editor)
- (* LINE-SPACING 2))
- (place-image (if (client-curse-on? c) CURSOR-IMG-ON CURSOR-IMG-OFF)
- (+ LINE-INDENT (image-width editor) 4)
- (- CLIENT-HEIGHT (H/2 editor))
- (place-image editor
- (+ LINE-INDENT (W/2 editor))
- (- CLIENT-HEIGHT (H/2 editor))
- CHAT-AREA))))
- (error "render-client expects a client, got: " c)))
- ;; add-chattings : [Listof String] Number Scene -> Scene
- ;; Add the given strings to the chat scene...
- (define (add-chattings los y scn)
- (cond [(not (and (list? los) (andmap string? los)))
- (error "add-chattings: first argument must be a list of strings, was: " los)]
- [(not (number? y))
- (error "add-chattings: second argument must be a number, was: " y)]
- [(not (image? scn))
- (error "add-chattings: third argument must be a scene, was: " scn)]
- [else ; all good
- (cond [(empty? los) scn]
- [else (local [(define txt (chattify (first los)))]
- (place-image txt
- (+ LINE-INDENT (W/2 txt))
- (- y (H/2 txt))
- (add-chattings (rest los)
- (- y (image-height txt)
- LINE-SPACING) scn)))])]))
- ;; strip-last : String -> String
- ;; strips the last character (if any) from a string
- (define (strip-last s)
- (if (string? s)
- (substring s 0 (max (sub1 (string-length s)) 0))
- (error "strip-last expects a string, got: " s)))
- (check-expect (strip-last "") "")
- (check-expect (strip-last "omg") "om")
- ;; tick : Client -> Client
- ;; Blink the cursor...
- (define (tick c)
- (if (and (client? c) (string? (client-name c)) (string? (client-editor c))
- (list? (client-lines c)) (andmap string? (client-lines c)))
- (make-client (client-name c)
- (client-lines c)
- (client-editor c)
- (not (client-curse-on? c)))
- (error "tick expects a client, got: " c)))
- ;; handle-key : Client KeyEvent -> Client
- ;; handles key presses for the chat client
- (define (handle-key c ke)
- (cond [(not (and (client? c) (string? (client-name c)) (string? (client-editor c))
- (list? (client-lines c)) (andmap string? (client-lines c))))
- (error "handle-key: first argument must be a client, was: " c)]
- [(not (key-event? ke))
- (error "handle-key: second argument must be a key-event, was: " c)]
- [else ; all good
- (cond
- ;; ******* MODIFY HERE *********
- ;; ** Local Version... comment out to run Universe...
- #|[(key=? ke "\r")
- (make-client (client-name c)
- (cons (string-append (client-name c)
- NICK-DIVIDER
- (client-editor c))
- (client-lines c))
- "" (client-curse-on? c))]|#
- ;; ******* MODIFY HERE *********
- ;; ** Universe Version...
- [(key=? ke "\r")
- (make-package c
- (string-append (client-name c)
- NICK-DIVIDER
- (client-editor c)))]
- [(key=? ke "\b")
- (make-client (client-name c) (client-lines c)
- (strip-last (client-editor c))
- (client-curse-on? c))]
- [(= (string-length ke) 1)
- (make-client (client-name c) (client-lines c)
- (string-append (client-editor c) ke)
- (client-curse-on? c))]
- [else c])]))
- #;(check-expect (handle-key client-2 "\r")
- (make-client "Joker" (list (string-append "Joker"
- NICK-DIVIDER
- "Why so seri")) "" true))
- (check-expect (handle-key client-2 "\b")
- (make-client "Joker" (list) "Why so ser" true))
- (check-expect (handle-key client-1 "a")
- (make-client "chadwick"
- (list "chadwick: Hola peeps"
- "chadwick: Lame chat party :/")
- "a" false))
- ;; handle-msg : Client Message -> Client
- ;; Handle an incoming message (String) by posting it
- ;; ******* MODIFY HERE *********
- (define (handle-msg c msg)
- (make-client (client-name c)
- (cons msg (client-lines c))
- (client-editor c)
- (client-curse-on? c)))
- (check-expect (handle-msg client-2 "alice: What's up")
- (make-client "Joker" (list "alice: What's up")
- "Why so seri" true))
- (check-expect (handle-msg client-1 "bob: Hacking")
- (make-client "chadwick"
- (list "bob: Hacking"
- "chadwick: Hola peeps"
- "chadwick: Lame chat party :/")
- "" false))
- ;; run : String -> Client
- ;; Runs the chat client, given a nickname
- (define (run nick)
- (if (string? nick)
- (big-bang (make-client nick (list) "" false)
- (on-draw render-client)
- (on-key handle-key)
- (on-tick tick 3/4)
- (register "dubnium.ccs.neu.edu")
- (on-receive handle-msg)
- (name nick))
- (error "run expects a string, got: " nick)))
- ;; ******* MODIFY HERE *********
- (run "br")