;;; -*- Mode:LISP; Package:OBIE; Readtable:CL; Base:10 -*- ;;; Icons #| Char icons leave inexplicable turds. Maybe just flush them. |# (defobclass icon (window-rect)) ; Instances of this class are types of icons (defobclass char-icon (icon) width height font image-char shape-char) ; An icon with a save-array (defobclass dynamic-icon (icon) save-array) (defobfun (exist char-icon) (&rest stuff) (apply 'shadowed-exist stuff) (unless height (setq height (tv:font-char-height font)) (setq width (if (tv:font-char-width-table font) (aref (tv:font-char-width-table font) shape-char) (tv:font-char-width font))))) (defsubst 32ize (num) (* 32 (1+ (floor (1- num) 32)))) (defobfun (exist dynamic-icon) (&rest stuff &aux bitblt-width) (apply 'shadowed-exist stuff) (setq bitblt-width (32ize width)) (unless save-array (setq save-array (make-array (list height bitblt-width) :element-type 'bit))) (draw)) ;+++ marginal ; An icon with a hot spot, for mouse cursors (defobclass pointer (icon) hot-x hot-y) (defobfun (draw char-icon) () (send window :draw-char font shape-char x y tv:alu-andca) (send window :draw-char font image-char x y tv:alu-ior)) (defobfun (draw dynamic-icon) () (tv:prepare-sheet (window) (bitblt tv:alu-seta width height (send window :screen-array) (+ x (tv:sheet-left-margin-size window)) (+ y (tv:sheet-top-margin-size window)) save-array 0 0)) (shadowed-draw)) (defobfun (erase dynamic-icon) () ; (shadowed-erase) (send window :bitblt tv:alu-seta width height save-array 0 0 x y)) (defobfun (move dynamic-icon) (nx ny) (erase) (shadowed-move nx ny) (draw)) (defobclass dynamic-char-icon (dynamic-icon char-icon)) ;;; Text icons are made up of a text string. Multiple lines are supported (defobclass text-icon (icon) (opaque-p nil) ;T means clear a box around the text (bordered-p t) text font) (defobfun (exist text-icon) (&rest stuff) (apply 'shadowed-exist stuff) (unless width (multiple-value-setq (width height) (send window :compute-motion text 0 nil 0 0 nil 0 nil nil nil font)) (setq height (+ height (tv:font-char-height font))))) (defobfun (draw text-icon) () (if opaque-p (send window :draw-rectangle width height x y tv:alu-andca)) (if bordered-p (draw-border)) (send window :string-out-explicit text (+ x (tv:sheet-left-margin-size window)) (+ y (tv:sheet-top-margin-size window)) nil nil font tv:alu-ior 0 nil (tv:font-char-height font))) (defobclass dynamic-text-icon (dynamic-icon text-icon)) (defobclass array-icon (icon) image-array mask-array) (defobfun (draw array-icon) () (send window :bitblt tv:alu-andca width height mask-array 0 0 x y) (send window :bitblt tv:alu-ior width height image-array 0 0 x y)) (defobfun (exist array-icon) (&rest stuff) (apply 'shadowed-exist stuff) (unless image-array (when (or image-char (memq 'image-char stuff)) ;Allow a font and char to specify the image (multiple-value-setq (image-array height width) (character-array (or font (cadr (memq 'font stuff))) (or image-char (cadr (memq 'image-char stuff))))))) (unless width (setq width (array-dimension image-array 0) height (array-dimension image-array 1))) (unless mask-array (setq mask-array (make-mask image-array))) ) (defobclass dynamic-array-icon (dynamic-icon array-icon)) ;;; Interface to EFH stuff ; An icon with an image object (defobclass image-icon (icon) image mask) (defobfun (exist image-icon) (&rest stuff) (apply 'shadowed-exist stuff) (unless width (setq width (ask image user:width) height (ask image user:height))) (setq mask (make-mask (ask image array)))) (defobfun (draw image-icon) () (send window :bitblt tv:alu-andca width height mask 0 0 x y) (ask-funcall image 'user:draw x y window)) (defobclass dynamic-image-icon (dynamic-icon image-icon)) (defobfun test (&aux ike) (setq ike (oneof dynamic-image-icon 'window (tv:window-under-mouse) 'x 0 'y 0 'image user:*file-image*)) ; (setq ike (oneof dynamic-char-icon 'window (tv:window-under-mouse) 'x 0 'y 0 'font fonts:mouse 'shape-char #\ 'image-char #\0)) ; ; 'shape-char #\4 'image-char #\) ; (setq ike (oneof dynamic-text-icon 'window (tv:window-under-mouse) 'x 0 'y 0 'font fonts:hl12b 'text "Yog shnozz" 'opaque-p t)) (ask ike (drag)) (ask ike (erase))) (defobfun atest (&aux ike image-array) (do-forever (multiple-value-bind (left top right bottom) (tv:mouse-specify-rectangle) (let* ((width (- right left)) (height (- bottom top)) (bitblt-width (32ize width)) (image (make-array (list height bitblt-width) :element-type '(mod 2)))) (send tv:mouse-sheet :bitblt-from-sheet tv:alu-seta width height left top image 0 0) (setq ike (oneof dynamic-array-icon 'window tv:mouse-sheet 'x left 'y top 'width width 'height height 'image-array image 'mask-array (make-array (list height bitblt-width) :element-type '(mod 2) :initial-element 1))) (tv:mouse-warp left top) (ask ike (draw)) (ask ike (drag)))))) (defobfun (drag dynamic-icon) (&aux starting-mouse-buttons) (setq starting-mouse-buttons (tv:mouse-buttons)) (remove-frippery) (tv:with-mouse-grabbed (do-forever (tv:mouse-wait) (if (eql (tv:mouse-buttons) starting-mouse-buttons) (move (- tv:mouse-x (send window :x-offset) (tv:sheet-left-margin-size window)) (- tv:mouse-y (send window :y-offset) (tv:sheet-top-margin-size window))) (return))))) (defobfun obtest (fspec &aux icon) (setq icon (oneof dynamic-text-icon 'window (tv:window-under-mouse) 'x 0 'y 0 'font fonts:cptfont 'opaque-p t 'text (with-output-to-string (stream) (grind-top-level fspec 100 stream)))) (ask icon (drag))) ;;; Region hacking (defobfun lasso-region (&aux last-x last-y (entry-buttons tv:mouse-last-buttons) list) (tv:with-mouse-grabbed (do-forever (tv:mouse-wait) (if (eq tv:mouse-last-buttons entry-buttons) (tv:prepare-sheet (tv:mouse-sheet) (when last-x (tv:%draw-line tv:mouse-x tv:mouse-y last-x last-y tv:alu-ior nil tv:mouse-sheet)) (setq last-x tv:mouse-x last-y tv:mouse-y) (push last-y list) (push last-x list)) (return list last-x last-y))))) ; Given list of points (x0 y0 x1 y1 ...) return 4 values: min-x min-y max-x max-y (defobfun point-list-range (list &aux (min-x most-positive-fixnum) (min-y most-positive-fixnum) (max-x 0) (max-y 0)) (do ((rest list (cddr rest))) ((null rest) (values min-x min-y max-x max-y)) (if (> (car rest) max-x) (setq max-x (car rest))) (if (> (cadr rest) max-y) (setq max-y (cadr rest))) (if (< (car rest) min-x) (setq min-x (car rest))) (if (< (cadr rest) min-y) (setq min-y (cadr rest))))) (defobfun lasso-icon (&aux bitblt-width ike) (multiple-value-bind (list end-x end-y) (lasso-region) (multiple-value-bind (x0 y0 x1 y1) (point-list-range list) (setq width (- x1 x0 -1) height (- y1 y0 -1) bitblt-width (32ize width) image-array (make-array (list height bitblt-width) :element-type '(mod 2) ) mask-array (make-array (list height bitblt-width) :element-type '(mod 2))) ;; Draw the lasso curve in the mask (do ((rest list (cddr rest))) ((null (cddr rest))) (sys:%draw-line (- (car rest) x0) (- (cadr rest) y0) (- (caddr rest) x0) (- (cadddr rest) y0) tv:alu-ior t mask-array)) ;; Generate the mask (aset 0 mask-array (- end-y y0) (- end-x x0)) (fill-region mask-array (- end-y y0) (- end-x x0)) ;; Generate the image (send tv:mouse-sheet :bitblt-from-sheet tv:alu-seta width height x0 y0 image-array 0 0) (bitblt tv:alu-and width height mask-array 0 0 image-array 0 0) (setq ike (oneof dynamic-array-icon 'window tv:mouse-sheet 'x x0 'y y0 'width width 'height height 'image-array image-array 'mask-array mask-array)) (tv:mouse-warp x0 y0) (ask ike (draw)) (ask ike (drag))))) ; This is far too stack-hungry an algorithm (defobfun fill-region (array sx sy) (when (zerop (aref array sx sy)) (aset 1 array sx sy) (fill-region array (1+ sx) sy) (fill-region array (1- sx) sy) (fill-region array sx (1+ sy)) (fill-region array sx (1- sy)) )) ; Fill the raster line, and return the limits (+++ not yet working or used) (defun fill-line (array sx sy) (values (do ((xx sx (1+ xx))) ((not (zerop (aref array xx sy))) xx) (aset 1 array xx sy)) (do ((xx (1- sx) (1- xx))) ((not (zerop (aref array xx sy))) xx) (aset 1 array xx sy)))) ; Given a bit array representing an image, return a mask for it. ; This works by scanning each line for the extreme points. It won't do ; well on non-convex images. (defun make-mask (image &aux mask min-x max-x) (setq mask (make-array (array-dimensions image) :type (array-type image) :initial-element 0)) (dotimes (y (array-dimension image 1)) (setq min-x (do ((xx 0 (1+ xx))) ((or (= xx (1- (array-dimension image 0))) (not (zerop (aref image xx y)))) xx)) max-x (do ((xx (1- (array-dimension image 0)) (1- xx))) ((or (minusp xx) (not (zerop (aref image xx y)))) (1+ xx)))) (do ((xx min-x (1+ xx))) (( xx max-x)) (aset 1 mask xx y))) mask) ; Return an image array for a character (defun character-array (font char &aux fd cd image) (setq fd (fed:font-name-font-descriptor font) cd (aref fd char) image (make-array (list (array-dimension cd 0) (32ize (array-dimension cd 0))) :element-type '(mod 2))) (dotimes (x (array-dimension cd 0)) (dotimes (y (array-dimension cd 1)) (aset (aref cd x y) image x y))) (values-list (cons image (array-dimensions cd)))) ;;; Ported icons (defobclass ported-icon (icon) (ports-visible nil) (ports nil)) ;A list of ports (defobfun (draw ported-icon) () (shadowed-draw) (when ports-visible (draw-ports))) (defobfun (draw-ports ported-icon) () (dolist (port ports) (ask port (draw)))) ; These two functions are what user programs should call. (defobfun (hide-ports ported-icon) () (when ports-visible (draw-ports) (setq ports-visible nil))) (defobfun (expose-ports ported-icon) () (unless ports-visible (draw-ports) (setq ports-visible t))) ; +++ This probably won't work. Need before and after demons (defobfun (move ported-icon) (nx ny) (when ports-visible (draw-ports)) ;erase ports (dolist (p ports) (ask-funcall p 'move-relative (- nx x) (- ny y))) (shadowed-move nx ny) (when ports-visible (draw-ports))) (defobfun (disappear ported-icon) () (mapc-ask ports (disappear)) (shadowed-disappear)) ;;; Debugging tools (defobfun obj-under-mouse (&optional class mumble) (ask (send (tv:window-under-mouse) :oblisp-window) (obj-under-mouse class mumble))) #| Make this work somedau ;;; Mouse blinkers (defobfun make-mouse-blinker (screen) (tv:make-blinker screen 'mouse-icon-blinker :visibility :on)) (tv:mouse-define-blinker-type :icon #'make-mouse-blinker) (defflavor mouse-icon-blinker (ike) (tv:mouse-blinker-mixin tv:blinker) :settable-instance-variables) (defmethod (mouse-icon-blinker :blink) () (Ask-funcall ike (if tv:phase 'erase 'draw))) (defmethod (mouse-icon-blinker :size) () (ask ike (values width height))) |# (defobfun (be-the-mouse-blinker dynamic-icon) () (tv:mouse-set-blinker-definition :icon 8 8 :on :set-ike obj:*object)) ; (tv:mouse-set-blinker-definition :character 8 8 :on :set-character image-char font))