From f60e6c047b6b4ac7632395668fc7b1fce298bcc9 Mon Sep 17 00:00:00 2001 From: SqrtMinusOne Date: Fri, 21 Jul 2023 22:32:56 +0300 Subject: [PATCH] basic UI and gradient formatting works --- biome-api.el | 6 ++-- biome-grid.el | 89 ++++++++++++++++++++++++++++++++++++++++---------- biome-query.el | 23 +++++++++++-- biome.el | 22 ++++++++++++- 4 files changed, 115 insertions(+), 25 deletions(-) diff --git a/biome-api.el b/biome-api.el index 292bd71..eaeb197 100644 --- a/biome-api.el +++ b/biome-api.el @@ -68,7 +68,7 @@ QUERY is a form as defined by `biome-query-current'." (cons (car item) (cond - ((listp (cdr item)) (mapconcat #'identity (cdr item) ",")) + ((listp (cdr item)) (mapconcat #'identity (reverse (cdr item)) ",")) (t (cdr item))))))) (alist-get :params query))) @@ -114,7 +114,7 @@ QUERY is a form as defined by `biome-query-current'." "Get data from Open Meteo API. QUERY is a form as defined by `biome-query-current'. CALLBACK is -called with the data as its only argument." +called with QUERY and the data returned by the API as arguments." (let ((url (alist-get (alist-get :name query) biome-api-urls nil nil #'equal))) (request url @@ -123,7 +123,7 @@ called with the data as its only argument." :parser #'json-read :success (cl-function (lambda (&key data &allow-other-keys) - (funcall callback data))) + (funcall callback (copy-tree query) data))) :error (cl-function (lambda (&key error-thrown response &allow-other-keys) (biome-api--show-error error-thrown response)))))) diff --git a/biome-grid.el b/biome-grid.el index 2c7d2ce..d152941 100644 --- a/biome-grid.el +++ b/biome-grid.el @@ -26,29 +26,77 @@ ;;; Code: (require 'cl-lib) -(require 'biome-query) +(require 'ct) +(require 'seq) (require 'tabulated-list) +(require 'biome-query) (defcustom biome-grid-display-units t "Display units in the grid." :type 'boolean :group 'biome) +(defcustom biome-grid-format-units + '(("°C" . (gradient + (-40 . "#ae64a0") + (-30 . "#9488d2") + (-15 . "#90cfd2") + (-5 . "#66adbb") + (5 . "#508c40") + (15 . "#aba10e") + (25 . "#f39506") + (35 . "#bf4112") + (40 . "#8a2b0a")))) + "Format units in the grid." + :group 'biome) + +(defvar biome-grid-mode-map + (let ((keymap (make-sparse-keymap))) + (define-key keymap (kbd "q") #'quit-window) + (when (fboundp 'evil-define-key*) + (evil-define-key* 'normal keymap + "q" #'quit-window)) + keymap) + "Keymap for `biome-api-error-mode'.") + (define-derived-mode biome-grid-mode tabulated-list-mode "Biome Grid" - "Major mode for displaying biome results." - (setq-local truncate-lines t)) + "Major mode for displaying biome results.") -(defun biome-grid--format-entries (entries unit) - (mapcar - (lambda (entry) - (if (stringp entry) - entry - (prin1-to-string entry))) - entries)) +(defun biome-grid--blend-colors (c1 c2 val) + "Blend colors C1 and C2 by VAL. -(defun biome-grid--get-width (entries col-name) - (cl-loop for entry in (cons col-name entries) - maximize (length entry))) +C1 and C2 are hex RGS strings, VAL is a number between 0 and 1." + (let ((color1 (ct-get-rgb c1)) + (color2 (ct-get-rgb c2))) + (apply #'ct-make-rgb + (cl-loop for v1 in color1 + for v2 in color2 + collect (+ (* (- 1 val) v1) (* val v2)))))) + +(defun biome-grid--format-gradient (value def col-width) + (let* ((background-color + (cl-loop for (border1 . color1) in def + for (border2 . color2) in (cdr def) + if (< value border1) return color1 + if (and (>= value border1) (<= value border2)) + return (biome-grid--blend-colors + color1 color2 (/ (- value border1) (- border2 border1))) + finally return color2)) + (foreground-color (if (ct-light-p background-color 65) "#000000" "#ffffff"))) + (propertize + (format (format "%%%ds" col-width) value) + 'face `(:background ,background-color :foreground ,foreground-color)))) + +(defun biome-grid--format-entries (entries unit col-width) + (let ((format-def (alist-get unit biome-grid-format-units nil nil #'equal))) + (mapcar + (lambda (entry) + (cond + ((stringp entry) entry) + ((eq (car-safe format-def) 'gradient) + (biome-grid--format-gradient entry (cdr format-def) col-width)) + (t (prin1-to-string entry)))) + entries))) (defun biome-grid--set-list (query results) (let* ((group (intern (alist-get :group query))) @@ -56,13 +104,16 @@ (var-names (biome-query--get-var-names-cache)) all-entries columns) (cl-loop for (key . values) in (alist-get group results) - for unit = (alist-get key (alist-get group-units results)) + for unit = (replace-regexp-in-string + (regexp-quote "%") "%%" + (alist-get key (alist-get group-units results))) for var-name = (biome-query--get-header (symbol-name key) var-names) for col-name = (if biome-grid-display-units (format "%s (%s)" var-name unit) var-name) - for entries = (biome-grid--format-entries values unit) - for col-width = (biome-grid--get-width entries col-name) + for col-width = (cl-loop for entry in (cons col-name entries) + maximize (+ 0 (length entry))) + for entries = (biome-grid--format-entries values unit col-width) do (push (list col-name col-width nil) columns) do (push entries all-entries)) (setq-local @@ -82,11 +133,13 @@ (defun biome-grid (query results) "Display RESULTS in a grid." + (setq my/test results) (let ((buf (generate-new-buffer "*biome-grid*"))) (with-current-buffer buf (biome-grid--set-list query results) - (biome-grid-mode)) - (display-buffer buf #'display-buffer-same-window))) + (biome-grid-mode) + (toggle-truncate-lines 1)) + (switch-to-buffer buf))) (provide 'biome-grid) ;;; biome-grid.el ends here diff --git a/biome-query.el b/biome-query.el index 72a0859..b7803a9 100644 --- a/biome-query.el +++ b/biome-query.el @@ -54,7 +54,8 @@ value will be inserted." :group 'biome) (defcustom biome-query-coords '(("Helsinki, Finland" 60.16952 24.93545) - ("Berlin, Germany" 52.52437 13.41053)) + ("Berlin, Germany" 52.52437 13.41053) + ("Dubai, UAE" 25.0657 55.17128)) "List of locations with their coordinates. The format is: (name latitude longitude)." @@ -99,6 +100,9 @@ case, the value is a list of variable names available in the group.") (defvar biome-query--var-names-cache nil "Cache for variable names.") +(defvar biome-query--callback nil + "Call this with the selected query.") + ;; TODO delete this (setq biome-query--layout-cache (make-hash-table :test 'equal)) @@ -880,6 +884,13 @@ SUFFIXES is a list of suffix definitions." (:group . ,(caar (biome-query--section-groups biome-query--current-section))) (:params . nil))))) +(defun biome-query--on-return () + "Process the query made by `biome-query'." + (interactive) + (unless biome-query--callback + (user-error "Biome-query--callback is not set")) + (funcall biome-query--callback biome-query-current)) + (transient-define-prefix biome-query--section (section &optional parents) "Render transient for SECTION. @@ -897,6 +908,7 @@ SECTION is a form as defined in `biome-api-parse--page'." (biome-query--section-layout section parents) `(["Actions" :class transient-row + ("RET" "Run" biome-query--on-return) ("q" "Up" transient-quit-one) ("Q" "Quit" transient-quit-all) ,(unless parents @@ -906,7 +918,7 @@ SECTION is a form as defined in `biome-api-parse--page'." (:parents . ,parents)))) (put 'biome-query-section 'transient--layout nil))) -(transient-define-prefix biome-query () +(transient-define-prefix biome-query (callback) ["Open Meteo Data" :setup-children (lambda (_) @@ -925,7 +937,12 @@ SECTION is a form as defined in `biome-api-parse--page'." (biome-query--section ',params)) :transient transient--do-replace))))] ["Actions" - ("q" "Quit" transient-quit-one)]) + ("q" "Quit" transient-quit-one)] + (interactive (list nil)) + (unless callback + (error "Callback is not set. Run M-x `biome' instead")) + (setq biome-query--callback callback) + (transient-setup 'biome-query)) (provide 'biome-query) ;;; biome-query.el ends here diff --git a/biome.el b/biome.el index 6936bd7..cde891e 100644 --- a/biome.el +++ b/biome.el @@ -5,7 +5,7 @@ ;; Author: Korytov Pavel ;; Maintainer: Korytov Pavel ;; Version: 0.1.0 -;; Package-Requires: ((emacs "27.1")) +;; Package-Requires: ((emacs "27.1") (transient "0.3.7") (ct "0.2") (request "0.3.3") (compat "29.1.4.1")) ;; Homepage: https://github.com/SqrtMinusOne/biome ;; This file is NOT part of GNU Emacs. @@ -28,9 +28,29 @@ ;; TODO ;;; Code: +(require 'biome-api) +(require 'biome-query) +(require 'biome-grid) + (defgroup biome nil "Bountiful Interface to Open Meteo for Emacs." :group 'applications) +(defcustom biome-frontend #'biome-grid + "The frontend to use for displaying the data. + +This has to be a function that receives two arguments: query (as +defined by `biome-query-current' and the response of the open-meteo +API." + :type 'function + :group 'biome) + +(defun biome () + "Bountiful Interface to Open Meteo for Emacs." + (interactive) + (biome-query + (lambda (query) + (biome-api-get query biome-frontend)))) + (provide 'biome) ;;; biome.el ends here