aUCBLogo Demos and Tests / buttons


;;; Primitive GUI for Logo games.

;;; Displays buttons, then accepts mouseclicks or keystrokes
;;; to control actions.

;;; To clear the screen and all the remembered buttons:
;;;   init.buttons

;;; To install a button:
;;;   SETBUTTON [150 130] [40 25] [make "mysecret "true throw "newgame] ~
;;;             :mysecret 0 [Logo guess] []
;;;
;;; Inputs are:
;;;   1. Position of lower left corner of button
;;;   2. Size [x y] of button
;;;   3. Action to take if button pressed
;;;   4. TRUE if box should be drawn thick, FALSE if thin
;;;   5. Color to fill box (0 if no fill)
;;;   6. Text caption inside button (a word or a two-word list for
;;;        a two-line caption) or empty list for no caption
;;;   7. Equivalent keystroke (empty list if no equivalent keystroke)
;;;        (DEL means char 8 or 127; RET means char 10 or 13)
;;;        (Keystroke inside list, e.g., [X], means don't draw it.)

;;; REBUTTON (same inputs as SETBUTTON) looks for existing matching button
;;; and, if found, just redraws border (possibly changing thickness).

;;; To display a descriptive caption (e.g., for a group of buttons)
;;; without making a button:
;;;   CAPTION [150 130] [40 25] [Number of boxes:]
;;;   CENTER.CAPTION [150 130] [40 25] [Number of boxes:]
;;;
;;; Inputs are position, size, caption.
;;; CENTER.CAPTION centers the text within the box; CAPTION puts it
;;; at the left edge of the box.

;;; To loop reading keystrokes or mouseclicks and taking actions as set:
;;;   ACTION.LOOP
;;; Within an action, :CHAR is the character typed (or zero if the action
;;; was triggered by a mouse click), :BUTTON is the mouse button pressed
;;; (or zero if the action was triggered by a keystroke), and :MOUSEPOS is
;;; the mouse position (or unspecified for a keystroke).  Actions triggered
;;; by a mouse click happen when the mouse button is released.

; -----------------------------

;;; IMPORTANT:  Here is how we know the size of a text character as
;;; drawn by the LABEL command.  Change these numbers if necessary:

make "labelcharsize ifelse equalp LogoVersion.3 "Windows [[8 13]] [[6 11]]

; -----------------------------

to buttons
end

to init_buttons
   
cs ht
   
setLabelAlign 0 0
   
make "buttons_ []
end

; -----------------------------

to setbutton :corner :size :action :thickp :fillcolor :caption_ :key
   
pu setpos :corner
   
seth 0
   
pd setpensize ifelse :thickp [[3 3]] [[1 1]]
   
setpc 7
   
apply "button_rectangle :size
   
setpensize [1 1]
   
if not equalp :fillcolor 0 
   
[   setfc :fillcolor
      
fillRect pensize/size-pensize
      setpc 
7
   
]
   
center_caption :corner :size :caption_
   
if (and (not listp :key) (not emptyp :key) (not equalp :key :caption_)) 
   
[   caption (list (sum first :corner first :size 4last :corner+size/2) ~
            
:size 
            
:key
   
]
   
if and (listp :key) (not emptyp :key) [make "key first :key]
   
push "buttons_ (list :corner :size :key :action)
end

to rebutton :corner :size :action :thickp :fillcolor :caption_ :key
   
localmake "thekey :key
   
if and listp :key not emptyp :key [make "thekey first :key]
   
localmake "test_ (list :corner :size :thekey :action)
   
localmake "button_ find [equalp ? :test_:buttons_
   
if emptyp :button_ ~
      
[setbutton :corner :size :action :thickp :fillcolor :caption_ :key  stop]
   
penup setpos :corner
   
seth 0
   
setpc 7
   
penerase setpensize [3 3]
   
apply "button_rectangle :size
   
penpaint setpensize ifelse :thickp [[3 3]] [[1 1]]
   
apply "button_rectangle :size
   
setpensize [1 1]
end

to button_offset :corner :dx :dy
   
penup setxy (first :corner)+:dx (last :corner)+:dy
end

to button_rectangle :x :y
   
repeat [fd :y rt 90 fd :x rt 90]
end

; -----------------------------

to caption :corner :size :caption_
   
if emptyp :caption_ [stop]
   
local [ls]
   
ls=LabelSize caption_
   
setpc 7
   
ifelse listp :caption_ 
   
[   button_offset :corner (first ls)/(first bf ls)/2
      
seth 90 label last :caption_
      button_offset :corner 
(first ls)/-(first bf ls)/2
      
seth 90 label first :caption_
   
][
      
button_offset :corner (first ls)/2 0
      
seth 90 label :caption_
   
]
end

to center_caption :corner :size :caption_
   
if emptyp :caption_ [stop]
   
setpc 7
   
ifelse listp :caption_ 
   
[   button_offset :corner (first :size)/(last :size)/4
      
seth 90 label last :caption_
      button_offset :corner 
(first :size)/(last :size)*3/4
      
seth 90 label first :caption_
   
][
      
button_offset corner (first size)/(last size)/2
      
seth 90 label :caption_
   
]
end

; -----------------------------

to action_loop
   
forever [action_once]
end

to action_once
   
if keyp [button_readkey]
   
if MouseButtons != [button_mouseclick]
   
dispatchMessages
end

to button_readkey [:char_ rc] [:button_ 0]
   
foreach :buttons_ [
     
localmake "key item ?
     
ifelse equalp :key "DEL [
      
if memberp (ascii :char_) [8 127] [run last stop]
     ] [
      
ifelse equalp :key "RET [
        
if memberp (ascii :char_) [10 13] [run last stop]
      ] [
        
if equalp :char_ :key [run last stop]
      ]
     ]
   ]
end

to button_mouseclick [:mousepos_ mousepos] [:button_ mouseButtons] [:char_ 0]
   
while [mouseButtons != 0] []   ; wait for release of button_
   
foreach :buttons_ 
   
[   if apply "button_inrange [run last stop]
   ]
end

to button_inrange :corner :size :key :action
   
(foreach bl :mousepos_ :corner :size 
   
[   if ?1 ?2 [output false]
      
if ?1 > (?2 ?3) [output false]
   ])
   
output true
end