OSDN Git Service

correcting input and rulp main loop
[rulp/rulp.git] / graphics / menu.lisp
index efe4fdf..3519ae8 100644 (file)
 ;;;; You should have received a copy of the GNU General Public License
 ;;;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-(in-package :graphics)
+(defpackage :rulp.entries
+  (:use :cl)
+  (:export *entries-list* push pop entry x y width height rectangle click-event
+           render-entry make-plist))
 
-(defgeneric entry-width (y))
-(defgeneric entry-height (y))
-(defgeneric entry-rectangle (y))
+(in-package :rulp.entries)
+
+(defparameter *entries-list* nil
+  "List of entries, menues who are generated into the window with options")
+
+(defgeneric x (entry))
+(defgeneric y (entry))
+(defgeneric width (entry))
+(defgeneric height (entry))
+(defgeneric rectangle (entry))
 
 (defclass entry ()
-    ((coordinate :accessor coordinate
-                 :initarg :coordinate
-                 :initform '(0 . 0)
-                 :documentation "the starting position of the entry generation"
-                 :type list)
+    ((coordinates :accessor coordinates
+                  :initarg :coordinates
+                  :initform '(0 . 0)
+                  :documentation "the starting position of the entry generation"
+                  :type list)
      (title :accessor title
             :initarg :title
-            :initform ""
+            :initform "custom"
             :documentation "title for the entry")
-     (text-size :accessor text-size
-                :initarg :text-size
-                :initform 30
-                :type number
-                :documentation "the font size in the entry")
+     (margin :accessor margin
+             :initarg :margin
+             :initform 5
+             :type number
+             :documentation "the margin between the content and the borders of the rectangle")
+     (padding :accessor padding
+              :initarg :padding
+              :initform 0
+              :type number
+              :documentation "the padding between the coordinates and the borders")
+     (action :accessor action
+             :initarg :action
+             :initform nil
+             :documentation "action when pressed")
      (contents :accessor contents
-               :initarg :contents
-               :initform nil
-               :type list
-               :documentation "alist containing entry name and related action")))
+                :initarg :contents
+                :initform nil
+                :type list
+                :documentation "a list of other entries")
+     (text-size :accessor size
+                :initarg :size
+                :initform 20
+                :type number)
+     (content-orientation :accessor orientation
+                          :initarg :orientation
+                          :initform :v
+                          :documentation "when :v the contents and title
+are displaced vertically, when :h the contents are horizontally displayed")))
+
+(defmethod x ((entry entry))
+  (car (coordinates entry)))
+
+(defmethod y ((entry entry))
+  (cdr (coordinates entry)))
+
+;; (defmethod width ((entry entry))
+;;   200)
+
+;; (defmethod height ((entry entry))
+;;   200)
+
+(defun width-vertical (plist)
+  (let* ((i 0))
+    (labels ((operation (element)
+               (setf i (max i (length (getf element :title)))) ; FIXME: add padding
+               (loop :for x :in (getf element :contents)
+                     :do (operation x))
+               ))
+      (operation plist))
+    i
+    ))
+
+(defun width-horizontal (plist)
+  (let* ((i 0))
+    (labels ((operation (element)
+               (incf i (length (getf element :title)))
+               (loop :for x :in (getf element :contents)
+                     :do (operation x))
+               ))
+      (operation plist))
+    i
+    ))
+
+(defun height-vertical (plist)
+  (let* ((i 0))
+    (labels ((operation (element)
+               (1+ i)
+               (loop :for x :in (getf element :contents)
+                     :do (operation x))
+               ))
+      (operation plist))
+    i
+    ))
 
-(defmethod entry-width ((y entry))
-  "give the length of the longest menu entry, useful for creating a box containing them all"
-  (reduce #'max (loop :for content :in (contents y)
-                      :collect (length (car content)))))
+(defun height-horizontal (plist) 1)
 
-(defmethod entry-height ((y entry))
-  "give the number of entries, useful for creating a box containing them all"
-  (length (contents y)))
+;; (defmethod width ((entry entry))
+;;   "give the length of the longest menu entry, useful for creating a box containing them all"
+;;   (reduce #'max (loop :for content :in (contents entry))))
 
-(defmethod entry-rectangle ((y entry))
+;; (defmethod height ((entry entry))
+;;   "give the number of entries, useful for creating a box containing them all"
+;;   (length (contents entry)))
+
+(defmethod rectangle ((entry entry))
   (flet ((on-size (a) (* a (text-size y))))
-    (sdl2:make-rect (car (coordinate y))
-                    (cdr (coordinate y))
+    (sdl2:make-rect (x entry)
+                    (y entry)
                     (on-size (entry-width y))
-                    (on-size (entry-height y)))))
+                    )))
+
+;; NOTE: this method makes the entry class totally useless. It is possible
+;; to replace entries with these plists and use them directly with the macro
+(defmethod make-plist ((entry entry))
+  (list ':entry 'entry
+        ':title (title entry)
+        ':coordinates (coordinates entry)
+        ':padding (padding entry)
+        ':margin (margin entry)
+        ':text-size (size entry)
+        ':action (action entry)
+        ':contents (contents entry)
+        ':orientation (orientation entry))
+  )
+;; NOTE: The idea of replacing the whole class idea with the plist system makes
+;; room for special functions that can create rectangles out of the correct size,
+;; can adjust the text and aid the macro in the rendering process.
+
+(defun n-letter-content (pair)
+  "given a list of two elements, one a string and the other a generic symbol, it returns the
+length of the string. This is used in display-entry, where it is used to find
+the maximum size of a list of options to make the correct dimension for the entry"
+  (length (car pair)))
+
+(defun keep-inside (position size limit)
+  (if (> position (- limit size))
+      (- position size)
+      position
+    ))
+
+(defmacro render-entry (renderer plist)
+  `(let* ((entry-margin (if (numberp (getf ,plist :margin)) (getf ,plist :margin) 0))
+          (entry-padding (if (numberp (getf ,plist :padding)) (getf ,plist :padding) 0))
+          (entry-size (getf ,plist :text-size)) ; NOTE: make error checking instead of redefinition
+          (entry-x (+ entry-margin (car (getf ,plist :coordinates))))
+          (entry-y (+ entry-margin (cdr (getf ,plist :coordinates))))
+          (entry-w (if (eq (getf ,plist :orientation) 'v)
+                       (width-vertical ,plist)
+                       (width-horizontal ,plist)))
+          (entry-h (if (eq (getf ,plist :orientation) 'v)
+                       (height-vertical ,plist)
+                       (height-horizontal ,plist))))
+     (setf entry-x (keep-inside entry-x entry-w rulp.graphics:*window-width*))
+     (setf entry-y (keep-inside entry-y entry-h rulp.graphics:*window-height*)) ; BUG: doesn't seems to work
+     ;; (when (> entry-x (- rulp.graphics:*window-width* entry-w))
+     ;;   (setf entry-x (- entry-x entry-w)))
+     ;; (when (> entry-y (- rulp.graphics:*window-height* entry-h))
+     ;;   (setf entry-y (- entry-y entry-h)))
+     (sdl2:set-render-draw-color ,renderer 255 255 255 255)
+     (sdl2:render-fill-rect ,renderer (rulp.graphics:arrange-rect
+                                       entry-x
+                                       entry-y
+                                       (+ (* 2 entry-padding) (* entry-w entry-size))
+                                       (+ (* 2 entry-padding) (* entry-h entry-size)))) ; FIXME: fixed size font
+     ;; NOTE: temporary solution, to be extended with the content-rendering
+     ;; function into a rendering engine for nested schemas
+     (sdl2:set-render-draw-color ,renderer 0 0 0 255)
+     (rulp.graphics:tr-write (getf ,plist :title)
+                             (+ entry-x entry-padding)
+                             (+ entry-y entry-padding)
+                             entry-size
+                             entry-size
+                             ,renderer)
+     ;; FIXME: continue with contents
+     ))
+
+(defmacro display-entry (renderer entry)
+  "macro for creating the actual menu given the renderer and the entry"
+  `(let* ((entry-x (+ (car (coordinate ,entry)) (padding ,entry)))
+          (entry-y (+ (cdr (coordinate ,entry)) (padding ,entry)))
+          (maximum-letters (apply #'max (cons (length (title ,entry)) (mapcar #'n-letter-content (contents ,entry)))))
+          (number-of-elements (1+ (length (contents ,entry))))
+          (entry-h (+ (* number-of-elements (text-size ,entry)) (* 2 (margin ,entry))))
+          (entry-w (+ (* maximum-letters (text-size ,entry)) (* (margin ,entry))))
+          )
+     (when (> entry-x (- *window-width* entry-w))
+       (setf entry-x (- entry-x entry-w)))
+     (when (> entry-y (- *window-height* entry-h))
+       (setf entry-y (- entry-y entry-h)))
+     (sdl2:set-render-draw-color ,renderer 255 255 255 255)
+     (sdl2:render-fill-rect ,renderer (arrange-rect entry-x entry-y entry-w entry-h))
+     (tr-write (title ,entry)
+               (+ entry-x (margin ,entry))
+               (+ entry-y (margin ,entry))
+               (text-size ,entry)
+               (text-size ,entry)
+               ,renderer
+               )
+     ;; FIXME: to complete with the actual menu
+     )
+  )
+
+(defun select-entry (renderer entry)
+  "given the renderer and an entry, returns the option pressed or return nil if
+nothing was pressed"
+  nil)
+
+(defgeneric click-event (entry coordinates)
+  (:documentation "this function is activated when the
+mouse/cursor is pressed with a entry active. From here the method can execute
+command and destroy itself by returning nil. The destruction has to be handled
+by whatever call the entry event"))
+
+;; NOTE: test if the mouse-state-p is enought, to get to this method is a long
+;; run and it could be that mouse-state-p has changed from when was triggered
+(defmethod click-event ((entry entry) coordinates)
+  "event for mouse/cursor action. This function applies events on an element of a
+list"
+  'destroy
+  )