|
| 1 | +--- |
| 2 | +title: Dashboard |
| 3 | +--- |
| 4 | + |
| 5 | +Lem ships with a customizable dashboard, `lem-dashboard`, out of the box: |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +Besides a more friendly greeting than a `*tmp*` buffer, it ships with useful shortcuts like starting a lisp scratch or seeing recent files/projects at a glance. |
| 10 | + |
| 11 | +The dashboard is launched when you open lem without passing any file arguments. If you wish to open/reopen it, you can do so with `M-x open-dashboard`. |
| 12 | + |
| 13 | +## Basic Customization |
| 14 | + |
| 15 | +### Disable |
| 16 | + |
| 17 | +To disable, simply set `*dashboard-enable*` to `nil` in your `init.lisp`: |
| 18 | + |
| 19 | +```lisp |
| 20 | +(setf lem-dashboard:*dashboard-enable* nil) |
| 21 | +``` |
| 22 | + |
| 23 | +### Customizing default dashboard |
| 24 | + |
| 25 | +If you want to make changes to the dashboard without defining a custom layout, there is a convenience function `set-default-dashboard` that you can use to make minor modifications: |
| 26 | + |
| 27 | +```lisp |
| 28 | +(lem-dashboard:set-default-dashboard :project-count 1 |
| 29 | + :file-count 6 |
| 30 | + :hide-links t |
| 31 | + :splash '("hello" "hello again")) |
| 32 | +``` |
| 33 | + |
| 34 | +Which would look like: |
| 35 | + |
| 36 | + |
| 37 | + |
| 38 | +Here are the currently available keywords: |
| 39 | + |
| 40 | +- `hide-links`: Whether to hide the doc/GitHub links. |
| 41 | +- `project-count`: How many projects to list. |
| 42 | +- `file-count`: How many recent files to list. |
| 43 | +- `splash`: List of splash messages, will be randomly chosen. |
| 44 | +- `footer-messages`: List of footer messages, will be randomly chosen. |
| 45 | + |
| 46 | +## Advanced Customization |
| 47 | + |
| 48 | +`lem-dashboard` is designed to be customizable. If you wish to make more changes than `set-default-dashboard` allows, you can make your own dashboard layout. |
| 49 | + |
| 50 | +A dashboard is simply a list of `dashboard-item` instances. Here's a list of available items: |
| 51 | + |
| 52 | +- `dashboard-splash`: Randomly displays one of `SPLASH-TEXTS`. |
| 53 | +- `dashboard-url`: Creates link/button with `DISPLAY-TEXT` that opens `URL` externally. |
| 54 | +- `dashboard-working-dir`: Prints current working directory. |
| 55 | +- `dashboard-footer-message`: Randomly displays one of the passed-in `MESSAGES`. |
| 56 | +- `dashboard-command`: Creates a link/button with `DISPLAY-TEXT` that executes `ACTION-COMMAND` when clicked/activated. |
| 57 | +- `dashboard-recent-projects`: Displays a list of recent projects, limited to the last `PROJECT-COUNT`. |
| 58 | +- `dashboard-recent-files`: Displays a list of recent files, limited to the last `FILE-COUNT`. |
| 59 | + |
| 60 | +All you need to do is call `set-dashboard` and pass it a list of `dashboard-item` instances: |
| 61 | + |
| 62 | +```lisp |
| 63 | +(in-package :lem-dashboard) |
| 64 | +
|
| 65 | +(set-dashboard (list (make-instance 'dashboard-splash |
| 66 | + :item-attribute 'document-metadata-attribute |
| 67 | + :splash-texts '("Welcome!" "Second splash message!") |
| 68 | + :top-margin 4 |
| 69 | + :bottom-margin 2) |
| 70 | + (make-instance 'dashboard-working-dir) |
| 71 | + (make-instance 'dashboard-recent-files |
| 72 | + :file-count 5 |
| 73 | + :bottom-margin 1) |
| 74 | + (make-instance 'dashboard-command |
| 75 | + :display-text " New Lisp Scratch Buffer (l)" |
| 76 | + :action-command 'lem-lisp-mode/internal:lisp-scratch |
| 77 | + :item-attribute 'document-header2-attribute |
| 78 | + :bottom-margin 2))) |
| 79 | +``` |
| 80 | + |
| 81 | +The base class, `dashboard-item`, has the following slots: |
| 82 | + |
| 83 | +- `item-attribute`: The attribute used when drawing the item |
| 84 | +- `top-margin`: The amount of vertical space (lines) to apply before the item. |
| 85 | +- `bottom-margin`: The amount of vertical space (lines) to apply after the item. |
| 86 | +- `action`: Function to execute when `RET` is pressed over this item. Most of the existing `dashboard-item` implementations have good default behavior here. |
| 87 | + |
| 88 | +### Creating your own `dashboard-item` |
| 89 | + |
| 90 | +Creating a custom `dashboard-item` is straightforward; just derive the class and implement the `draw-dashboard-item` method. |
| 91 | + |
| 92 | +You are responsible for the actual insertion of text inside of `draw-dashboard-item`. There is a convenience function, `create-centered-string`, which you can use when inserting text. |
| 93 | + |
| 94 | +As an example, let's make a `dashboard-item` that fetches a random fact from an HTTP API and prints it: |
| 95 | + |
| 96 | +```lisp |
| 97 | +(defclass dashboard-random-fact (dashboard-item) |
| 98 | + ((fact :initform nil :accessor fact) |
| 99 | + (fetching :initform nil :accessor fetching)) |
| 100 | + (:documentation "Creates a dashboard item that displays a random fact.")) |
| 101 | +
|
| 102 | +(defmethod draw-dashboard-item ((item dashboard-random-fact) point) |
| 103 | + ;; Display fact if exists, or 'loading fact..' while fetching |
| 104 | + (insert-string point |
| 105 | + (create-centered-string (or (fact item) "Loading fact...")) |
| 106 | + :attribute (item-attribute item)) |
| 107 | + |
| 108 | + ;; If we don't currently have a fact and are not fetching, start fetching fact |
| 109 | + (unless (or (fact item) (fetching item)) |
| 110 | + (setf (fetching item) t) |
| 111 | + ;; Start a background thread so we don't block |
| 112 | + (bt2:make-thread |
| 113 | + (lambda () |
| 114 | + (handler-case |
| 115 | + (let* ((response (dexador:get "https://uselessfacts.jsph.pl/api/v2/facts/random")) |
| 116 | + (json-data (json:decode-json-from-string response)) |
| 117 | + (fact-text (cdr (assoc :text json-data)))) |
| 118 | + ;; Once we receive the fact, we'll set it and redraw the dashboard |
| 119 | + (send-event (lambda () |
| 120 | + (setf (fact item) fact-text) |
| 121 | + (redraw-dashboard)))) |
| 122 | + (error (e) |
| 123 | + (send-event (lambda () (message (format nil "Error fetching fact: ~A" e)))))) |
| 124 | + :name "random-fact-fetcher")))) |
| 125 | +``` |
| 126 | + |
| 127 | +Multithreading is overkill here, but it lets us fetch the fact without delaying the startup of the dashboard. Adding `dashboard-random-fact` to the last above, we get: |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | +### Current limitations |
| 132 | + |
| 133 | +- Only vertical stacking is currently supported. If you want to stack multiple elements horizontally, you'll have to make a dashboard item that does so. |
| 134 | +- No image support. |
0 commit comments