1 ;;;; Ru*** roLeplay Playground virtual tabletop
2 ;;;; Copyright (C) 2022 Zull
4 ;;;; This program is free software: you can redistribute it and/or modify
5 ;;;; it under the terms of the GNU General Public License as published by
6 ;;;; the Free Software Foundation, either version 3 of the License, or
7 ;;;; (at your option) any later version.
9 ;;;; This program is distributed in the hope that it will be useful,
10 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ;;;; GNU General Public License for more details.
14 ;;;; You should have received a copy of the GNU General Public License
15 ;;;; along with this program. If not, see <https://www.gnu.org/licenses/>.
17 (defpackage :rulp.entries
19 (:export *entries-list* push pop entry x y width height rectangle click-event
20 render-entry make-plist))
22 (in-package :rulp.entries)
24 (defparameter *entries-list* nil
25 "List of entries, menues who are generated into the window with options")
27 (defgeneric x (entry))
28 (defgeneric y (entry))
29 (defgeneric width (entry))
30 (defgeneric height (entry))
31 (defgeneric rectangle (entry))
34 ((coordinates :accessor coordinates
37 :documentation "the starting position of the entry generation"
39 (title :accessor title
42 :documentation "title for the entry")
43 (margin :accessor margin
47 :documentation "the margin between the content and the borders of the rectangle")
48 (padding :accessor padding
52 :documentation "the padding between the coordinates and the borders")
53 (action :accessor action
56 :documentation "action when pressed")
57 (contents :accessor contents
61 :documentation "a list of other entries")
62 (text-size :accessor size
66 (content-orientation :accessor orientation
69 :documentation "when :v the contents and title
70 are displaced vertically, when :h the contents are horizontally displayed")))
72 (defmethod x ((entry entry))
73 (car (coordinates entry)))
75 (defmethod y ((entry entry))
76 (cdr (coordinates entry)))
78 ;; (defmethod width ((entry entry))
81 ;; (defmethod height ((entry entry))
84 (defun width-vertical (plist)
86 (labels ((operation (element)
87 (setf i (max i (length (getf element :title)))) ; FIXME: add padding
88 (loop :for x :in (getf element :contents)
95 (defun width-horizontal (plist)
97 (labels ((operation (element)
98 (incf i (length (getf element :title)))
99 (loop :for x :in (getf element :contents)
106 (defun height-vertical (plist)
108 (labels ((operation (element)
110 (loop :for x :in (getf element :contents)
117 (defun height-horizontal (plist) 1)
119 ;; (defmethod width ((entry entry))
120 ;; "give the length of the longest menu entry, useful for creating a box containing them all"
121 ;; (reduce #'max (loop :for content :in (contents entry))))
123 ;; (defmethod height ((entry entry))
124 ;; "give the number of entries, useful for creating a box containing them all"
125 ;; (length (contents entry)))
127 (defmethod rectangle ((entry entry))
128 (flet ((on-size (a) (* a (text-size y))))
129 (sdl2:make-rect (x entry)
131 (on-size (entry-width y))
134 ;; NOTE: this method makes the entry class totally useless. It is possible
135 ;; to replace entries with these plists and use them directly with the macro
136 (defmethod make-plist ((entry entry))
138 ':title (title entry)
139 ':coordinates (coordinates entry)
140 ':padding (padding entry)
141 ':margin (margin entry)
142 ':text-size (size entry)
143 ':action (action entry)
144 ':contents (contents entry)
145 ':orientation (orientation entry))
147 ;; NOTE: The idea of replacing the whole class idea with the plist system makes
148 ;; room for special functions that can create rectangles out of the correct size,
149 ;; can adjust the text and aid the macro in the rendering process.
151 (defun n-letter-content (pair)
152 "given a list of two elements, one a string and the other a generic symbol, it returns the
153 length of the string. This is used in display-entry, where it is used to find
154 the maximum size of a list of options to make the correct dimension for the entry"
157 (defun keep-inside (position size limit)
158 (if (> position (- limit size))
163 (defmacro render-entry (renderer plist)
164 `(let* ((entry-margin (if (numberp (getf ,plist :margin)) (getf ,plist :margin) 0))
165 (entry-padding (if (numberp (getf ,plist :padding)) (getf ,plist :padding) 0))
166 (entry-size (getf ,plist :text-size)) ; NOTE: make error checking instead of redefinition
167 (entry-x (+ entry-margin (car (getf ,plist :coordinates))))
168 (entry-y (+ entry-margin (cdr (getf ,plist :coordinates))))
169 (entry-w (if (eq (getf ,plist :orientation) 'v)
170 (width-vertical ,plist)
171 (width-horizontal ,plist)))
172 (entry-h (if (eq (getf ,plist :orientation) 'v)
173 (height-vertical ,plist)
174 (height-horizontal ,plist))))
175 (setf entry-x (keep-inside entry-x entry-w rulp.graphics:*window-width*))
176 (setf entry-y (keep-inside entry-y entry-h rulp.graphics:*window-height*)) ; BUG: doesn't seems to work
177 ;; (when (> entry-x (- rulp.graphics:*window-width* entry-w))
178 ;; (setf entry-x (- entry-x entry-w)))
179 ;; (when (> entry-y (- rulp.graphics:*window-height* entry-h))
180 ;; (setf entry-y (- entry-y entry-h)))
181 (sdl2:set-render-draw-color ,renderer 255 255 255 255)
182 (sdl2:render-fill-rect ,renderer (rulp.graphics:arrange-rect
185 (+ (* 2 entry-padding) (* entry-w entry-size))
186 (+ (* 2 entry-padding) (* entry-h entry-size)))) ; FIXME: fixed size font
187 ;; NOTE: temporary solution, to be extended with the content-rendering
188 ;; function into a rendering engine for nested schemas
189 (sdl2:set-render-draw-color ,renderer 0 0 0 255)
190 (rulp.graphics:tr-write (getf ,plist :title)
191 (+ entry-x entry-padding)
192 (+ entry-y entry-padding)
196 ;; FIXME: continue with contents
199 (defmacro display-entry (renderer entry)
200 "macro for creating the actual menu given the renderer and the entry"
201 `(let* ((entry-x (+ (car (coordinate ,entry)) (padding ,entry)))
202 (entry-y (+ (cdr (coordinate ,entry)) (padding ,entry)))
203 (maximum-letters (apply #'max (cons (length (title ,entry)) (mapcar #'n-letter-content (contents ,entry)))))
204 (number-of-elements (1+ (length (contents ,entry))))
205 (entry-h (+ (* number-of-elements (text-size ,entry)) (* 2 (margin ,entry))))
206 (entry-w (+ (* maximum-letters (text-size ,entry)) (* (margin ,entry))))
208 (when (> entry-x (- *window-width* entry-w))
209 (setf entry-x (- entry-x entry-w)))
210 (when (> entry-y (- *window-height* entry-h))
211 (setf entry-y (- entry-y entry-h)))
212 (sdl2:set-render-draw-color ,renderer 255 255 255 255)
213 (sdl2:render-fill-rect ,renderer (arrange-rect entry-x entry-y entry-w entry-h))
214 (tr-write (title ,entry)
215 (+ entry-x (margin ,entry))
216 (+ entry-y (margin ,entry))
221 ;; FIXME: to complete with the actual menu
225 (defun select-entry (renderer entry)
226 "given the renderer and an entry, returns the option pressed or return nil if
230 (defgeneric click-event (entry coordinates)
231 (:documentation "this function is activated when the
232 mouse/cursor is pressed with a entry active. From here the method can execute
233 command and destroy itself by returning nil. The destruction has to be handled
234 by whatever call the entry event"))
236 ;; NOTE: test if the mouse-state-p is enought, to get to this method is a long
237 ;; run and it could be that mouse-state-p has changed from when was triggered
238 (defmethod click-event ((entry entry) coordinates)
239 "event for mouse/cursor action. This function applies events on an element of a