CLIM: Specify table columns using classes

This commit is contained in:
contrapunctus 2022-04-23 08:19:56 +05:30
parent 5d93dd48bc
commit c5d5ba419e
1 changed files with 88 additions and 53 deletions

View File

@ -2597,56 +2597,89 @@ s-expressions in a text column.")
(:layouts (default (vertically () task-duration int))))
#+END_SRC
**** *table-specification* :custom:variable:
**** task-duration-table
***** column-specifier :class:
#+BEGIN_SRC lisp
(defvar *table-specification*
`((index "#"
,(lambda (index task date) index)
,(lambda (index frame pane)
(format t "~2@A" index)))
(task "Task"
,(lambda (index task date) task)
,(lambda (task frame pane)
(with-output-as-presentation (pane task 'task-name)
(format t "~A" task))))
(duration "Time"
,(lambda (index task date)
(task-duration-one-day task date))
,(lambda (duration frame pane)
(with-output-as-presentation (pane duration 'number)
(format t "~A" duration))))
;; (activity-indicator "Active")
)
"List of table column specifiers.
Each specifier must be a list in the form...
(NAME LABEL DATA-FN PRINT-FN)
...where -
NAME is a symbol unique to this list
LABEL is a string to be displayed as the column header
DATA-FN is a function of three arguments - INDEX, TASK, and DATE.
INDEX is a 1-indexed integer for the row.
TASK is the name of the task in this row, as a string.
DATE is the date, as integer seconds since the UNIX epoch.
PRINT-FN is a function accepting three arguments - DATA, FRAME, and PANE.
DATA is the return value of DATA-FN.
FRAME and PANE are the CLIM frame and pane as passed to the display function.")
(defclass column-specifier ()
((name :initarg :name :type keyword :accessor column-name)
(label :initarg :label :type string :accessor column-label)))
#+END_SRC
**** table-function :function:
***** make-column-specifier :constructor:function:
#+BEGIN_SRC lisp
(defun table-function (table-specification)
(defun make-column-specifier (name label)
(make-instance 'column-specifier :name name :label label))
#+END_SRC
***** cell-data :method:
#+BEGIN_SRC lisp
(defgeneric cell-data (name index task date)
(:documentation "Function to determine the data of cell NAME.
NAME must be a keyword.
INDEX is a 1-indexed integer for the row.
TASK is the name of the task in this row, as a string.
DATE is the date, as integer seconds since the UNIX epoch."))
(defmethod cell-data ((name (eql :index)) (index integer) (task string) (date integer))
index)
(defmethod cell-data ((name (eql :task)) (index integer) (task string) (date integer))
task)
(defmethod cell-data ((name (eql :duration)) (index integer) (task string) (date integer))
(task-duration-one-day task date))
#+END_SRC
***** cell-print :method:
#+BEGIN_SRC lisp
(defgeneric cell-print (name index task date cell-data frame pane)
(:documentation "Function to determine the data of cell NAME.
NAME must be a keyword.
INDEX is a 1-indexed integer for the row.
TASK is the name of the task in this row, as a string.
DATE is the date, as integer seconds since the UNIX epoch.
CELL-DATA is the return value of `cell-data'
FRAME and PANE are the CLIM frame and pane as passed to the display function."))
(defmethod cell-print ((name (eql :index))
(index integer) (task string) (date integer)
cell-data-index
frame pane)
(format t "~2@A" cell-data-index))
(defmethod cell-print ((name (eql :task))
(index integer) (task string) (date integer)
cell-data-task
frame pane)
(with-output-as-presentation (pane cell-data-task 'task-name)
(format t "~A" cell-data-task)))
(defmethod cell-print ((name (eql :duration))
(index integer) (task string) (date integer)
duration
frame pane)
(with-output-as-presentation (pane duration 'number)
(format t "~A" duration)))
#+END_SRC
***** *task-duration-table-spec* :custom:variable:
#+BEGIN_SRC lisp
(defvar *task-duration-table-spec*
(loop for (keyword . string) in '((:index . "#")
(:task . "Task")
(:duration . "Time")
;; (:activity . "Active")
)
collect (make-column-specifier keyword string))
"List of table `column-specifier' instances.")
#+END_SRC
***** task-duration-table-function :function:
#+BEGIN_SRC lisp
(defun task-duration-table-function (table-specification)
(loop with date = (timestamp-to-unix (today))
for task in (task-list)
for index from 0
when (zerop index)
collect (mapcar #'second table-specification)
collect (mapcar #'column-label table-specification)
else
collect (loop for (symbol label data-fn print-fn) in table-specification
collect (funcall data-fn index task date))))
collect (loop for column-spec in table-specification
collect (cell-data (column-name column-spec)
index task date))))
#+END_SRC
**** display
@ -2664,30 +2697,32 @@ PRINT-FN is a function accepting three arguments - DATA, FRAME, and PANE.
***** display-pane :generic:function:
#+BEGIN_SRC lisp
(defgeneric display-pane (frame stream pane-name)
(:documentation "Display Chronometrist application panes."))
(:documentation "Display a Chronometrist application pane."))
#+END_SRC
***** display-pane :method:
#+BEGIN_SRC lisp
(defmethod display-pane (frame pane (pane-name (eql 'task-duration)))
"Display the task-duration pane, using `*task-duration-table-spec*'."
(declare (ignorable frame pane pane-name))
;; (format *debug-io* "*application-frame*: ~a~%" *application-frame*)
(let ((stream *standard-output*)
(task-list (task-list)))
(let ((stream *standard-output*))
(formatting-table (stream)
(loop
with date = (timestamp-to-unix (today))
for task in task-list
for task in (task-list)
for row in (task-duration-table-function *task-duration-table-spec*)
for index from 0 do
(formatting-row (stream)
(loop for (symbol label data-fn print-fn) in *table-specification*
when (zerop index) do
(formatting-cell (stream)
(format t "~A" label))
else do
(formatting-cell (stream)
(let ((data (funcall data-fn index task date)))
(funcall print-fn data frame pane)))))))))
(if (zerop index)
(loop for string in row
do (formatting-cell (stream)
(format t "~A" string)))
(loop for data in row
for col-spec in *task-duration-table-spec*
do (formatting-cell (stream)
(cell-print (column-name col-spec)
index task date data frame pane)))))))))
#+END_SRC
**** refresh :command: