Boolean Filters Layout
CAPI includes a filtering-layout that adds a text filter to a list-panel. This project shows how to make the filtering-layout work with a multi-column-list-panel, and adds boolean filters that allow you to filter items according to boolean values in specified columns:
The definition
Although the example would be more compact if I'd taken advantage of capi:define-interface, I've done it the long way for clarity. First here's the definition of the window class, with slots for the items, text filter, boolean filters, and list panel:
(defclass items-window (capi:interface) ((things :initarg :things :reader things) (filter :initarg :filter :reader filter) (booleans :initarg :booleans :reader booleans) (list-panel :initarg :list-panel :reader list-panel)))
Make items window
The items window is drawn by the function make-items-window. It assumes each item is a list of values, specified by *columns*:
(defparameter *columns* '("Name" "No" "Even?" "Prime?"))
The columns that contain boolean values are specified by *boolean-columns*:
(defparameter *boolean-columns* '("Even?" "Prime?"))
Here's the function to create the items window:
(defun make-items-window (things) (let* ((list-panel (make-instance 'capi:multi-column-list-panel :items things :external-min-height 140 :columns (map 'list #'(lambda (column) (list :title column :width 100)) *columns*))) (filter (make-instance 'capi:filtering-layout :change-callback 'update-items-window)) (booleans (map 'list #'(lambda (column) (make-instance 'capi:option-pane :title column :items '("" "Yes" "No")
:external-max-width 64 :callback-type :interface
:selection-callback #'update-items-window))
*boolean-columns*)) (booleans-row (make-instance 'capi:row-layout :description booleans)) (layout (make-instance 'capi:column-layout :description (list filter booleans-row list-panel))) (window (make-instance 'items-window :title "Filtering example" :best-width 480 :things things :list-panel list-panel :filter filter :booleans booleans :layout layout))) (capi:display window)))
A pop-up menu with the options blank, Yes, and No is created for each of the columns specified by *boolean-columns*:
Typing into the filter text-input-pane, or changing any of the boolean pop-up menus, calls the update-items-window update function. This recalculates the list panel's collection items to reflect the new values of the filters:
(defun update-items-window (window) (let* ((things (things window)) (options (map 'list #'capi:choice-selection (booleans window))) (positions (map 'list #'(lambda (boolean) (position (capi:titled-object-title boolean) *columns* :test #'equalp)) (booleans window))) (filtered-things (multiple-value-bind (regexp excludep) (capi:filtering-layout-match-object-and-exclude-p (filter window) nil) (remove-if-not #'(lambda (item) (and (if regexp (find-regexp-in-string regexp (first item)) t) (every #'(lambda (option position) (or (zerop option) (eq (not (= 1 option)) (not (nth position item))))) options positions))) things)))) (setf (capi:collection-items (list-panel window)) filtered-things)))
Finally some routines to generate a list of data to display in the items-window:
(defun prime? (n) (let ((test 2)) (loop (when (> (* test test) n) (return t)) (when (zerop (rem n test)) (return nil)) (incf test))))
This generates a list of 1000 entries for the integers 0 to 999:
(setq *things* (let (things) (dotimes (x 1000 (reverse things)) (push (list (format nil "~r" x) x (evenp x) (prime? x)) things))))
Finally we display the items-window with:
(make-items-window *things*)
blog comments powered by Disqus