aboutsummaryrefslogtreecommitdiffhomepage
path: root/gui/menus.lisp
blob: f758d513badb1d3e7c96621062f838ab9e2deab7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
;;;; menus.lisp

(in-package :wheelwork)

(defclass/std menu (unit interactive)
  ((options :with :std nil
                  :doc "A list of interactive units")
   (focus :with :std nil
          :doc "The option that is focused in this menu, if any.")
   (region :std (error "Menus require an explicit region"))))

(defmethod initialize-instance :after ((menu menu) &key)
  (with-slots (base-width base-height region) menu 
    (setf base-width (width region)
          base-height (height region)
          (x menu) (region-left region)
          (y menu) (region-bottom region))))

(defmethod (setf closer-mop:slot-value-using-class) :before
    (new-value class (menu menu) slot)
  (case (closer-mop:slot-definition-name slot)
    (focus
     (when (slot-boundp menu 'focus)
       (unless (eq new-value (slot-value menu 'focus))
         (fire-blur-event-on (slot-value menu 'focus)))))))

(defmethod (setf closer-mop:slot-value-using-class ) :after
    (new-value class (menu menu) slot)
  (case (closer-mop:slot-definition-name slot)
    (focus
     (fire-focus-event-on new-value))))

(macrolet
    ((def-menu-accessors (&rest accessor-names)
       (let ((defs
               (loop for name in accessor-names
                     collect
                     `(defmethod ,name ((menu menu))
                        (,name (unit-region menu)))

                     collect
                     `(defmethod (setf ,name) (newval (menu menu))
                        (let ((diff (- newval (,name menu)))) 
                          (setf (,name (unit-region menu)) newval)
                          (dolist (o (menu-options menu))
                            (incf (,name o) diff)))))))
         `(progn ,@defs))))
  (def-menu-accessors x y width height))


(defmethod add-unit :after ((menu menu))
  (dolist (o (menu-options menu))
    (add-unit o)))

(defmethod drop-unit :before ((menu menu))
  (dolist (o (menu-options menu))
    (drop-unit o)))

(defgeneric add-menu-option (menu option))
(defmethod add-menu-option ((menu menu) option)
  (setf (unit-region option) (unit-region menu))
  (setf (menu-options menu)
        (nconc (menu-options menu) (list option)))
  (when (unit-in-scene-p menu)
    (add-unit option)))

(defun remove-menu-option (menu option)
  (when (member option (menu-options menu))
    (setf (unit-region option) *application*)
    (setf (menu-options menu)
          (delete option (menu-options menu)))
    (drop-unit option)))

(defmethod render ((menu menu))
  (dolist (o (menu-options menu))
    (render o)))


(defclass/std vscroller (menu) 
  ((scroll-speed :std 1)
   (vert-scroll :std 0
                :doc "Vertical distance options have been displaced.")))

(defmethod (setf vert-scroll) :after (val (vs vscroller))
  (loop
    for o in  (menu-options vs)
    for y = (+ (y vs) (height vs) val) then (- y (height o))
    do (setf (y o) y)))

(defhandler vscroller-scroll
    (on-mousewheel (vs horiz vert)
      (let ((oh (vscroller-options-height vs))
            (h (height vs)))
        (setf (vert-scroll vs)
              (clamp 0
                     (+ (vert-scroll vs) (* vert (scroll-speed vs)))
                     (- oh h))))))

(defun vscroller-options-height (vs)
  (loop for o in (menu-options vs) summing (height o)))

(defmethod add-menu-option :before ((vs vscroller) option)
  (setf (x option) (x vs)
        (y option) (- (+ (y vs) (height vs) (vert-scroll vs))
                      (vscroller-options-height vs))))

(defmethod initialize-instance :after ((vscroller vscroller) &key)
  (add-handler vscroller #'vscroller-scroll))