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/2 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 4) last :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 2 [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)/2 (first bf ls)/2
seth 90 label last :caption_
button_offset :corner (first ls)/2 -(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)/2 (last :size)/4
seth 90 label last :caption_
button_offset :corner (first :size)/2 (last :size)*3/4
seth 90 label first :caption_
][
button_offset corner (first size)/2 (last size)/2
seth 90 label :caption_
]
end
; -----------------------------
to action_loop
forever [action_once]
end
to action_once
if keyp [button_readkey]
if MouseButtons != 0 [button_mouseclick]
dispatchMessages
end
to button_readkey [:char_ rc] [:button_ 0]
foreach :buttons_ [
localmake "key item 3 ?
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