diff options
-rw-r--r-- | gnu/installer/newt/page.scm | 250 |
1 files changed, 231 insertions, 19 deletions
diff --git a/gnu/installer/newt/page.scm b/gnu/installer/newt/page.scm index bcede3e333..10849b81eb 100644 --- a/gnu/installer/newt/page.scm +++ b/gnu/installer/newt/page.scm @@ -17,17 +17,22 @@ ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. (define-module (gnu installer newt page) + #:use-module (gnu installer utils) #:use-module (gnu installer newt utils) #:use-module (guix i18n) #:use-module (ice-9 match) #:use-module (ice-9 receive) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) #:use-module (newt) #:export (draw-info-page draw-connecting-page run-input-page run-error-page run-listbox-selection-page - run-scale-page)) + run-scale-page + run-checkbox-tree-page + run-file-textbox-page)) ;;; Commentary: ;;; @@ -66,6 +71,7 @@ this page to TITLE." (define* (run-input-page text title #:key (allow-empty-input? #f) + (default-text #f) (input-field-width 40)) "Run a page to prompt user for an input. The given TEXT will be displayed above the input field. The page title is set to TITLE. Unless @@ -80,6 +86,9 @@ enters an empty input." (ok-button (make-button -1 -1 (G_ "Ok"))) (form (make-form))) + (when default-text + (set-entry-text input-entry default-text)) + (set-grid-field grid 0 0 GRID-ELEMENT-COMPONENT text-box) (set-grid-field grid 0 1 GRID-ELEMENT-COMPONENT input-entry #:pad-top 1) @@ -142,10 +151,18 @@ of the page is set to TITLE." (listbox-default-item #f) (listbox-allow-multiple? #f) (sort-listbox-items? #t) + (allow-delete? #f) + (skip-item-procedure? + (const #f)) button-text (button-callback-procedure (const #t)) + (button2-text #f) + (button2-callback-procedure + (const #t)) (listbox-callback-procedure + identity) + (hotkey-callback-procedure (const #t))) "Run a page asking the user to select an item in a listbox. The page contains, stacked vertically from the top to the bottom, an informative text @@ -168,7 +185,15 @@ be selected (using the <SPACE> key). It that case, a list containing the selected items will be returned. If SORT-LISTBOX-ITEMS? is set to #t, the listbox items are sorted using -'string<=' procedure (after being converted to text)." +'string<=' procedure (after being converted to text). + +If ALLOW-DELETE? is #t, the form will return if the <DELETE> key is pressed, +otherwise nothing will happend. + +Each time the listbox current item changes, call SKIP-ITEM-PROCEDURE? with the +current listbox item as argument. If it returns #t, skip the element and jump +to the next/previous one depending on the previous item, otherwise do +nothing." (define (fill-listbox listbox items) "Append the given ITEMS to LISTBOX, once they have been converted to text @@ -198,6 +223,21 @@ corresponding to each item in the list." (string<= text-a text-b)))))) (map car sorted-items))) + ;; Store the last selected listbox item's key. + (define last-listbox-key (make-parameter #f)) + + (define (previous-key keys key) + (let ((index (list-index (cut eq? key <>) keys))) + (and index + (> index 0) + (list-ref keys (- index 1))))) + + (define (next-key keys key) + (let ((index (list-index (cut eq? key <>) keys))) + (and index + (< index (- (length keys) 1)) + (list-ref keys (+ index 1))))) + (define (set-default-item listbox listbox-keys default-item) "Set the default item of LISTBOX to DEFAULT-ITEM. LISTBOX-KEYS is the association list returned by the FILL-LISTBOX procedure. It is used because @@ -221,18 +261,55 @@ the current listbox item has to be selected by key." info-textbox-width #:flags FLAG-BORDER)) (button (make-button -1 -1 button-text)) + (button2 (and button2-text + (make-button -1 -1 button2-text))) (grid (vertically-stacked-grid GRID-ELEMENT-COMPONENT info-textbox GRID-ELEMENT-COMPONENT listbox - GRID-ELEMENT-COMPONENT button)) + GRID-ELEMENT-SUBGRID + (apply + horizontal-stacked-grid + GRID-ELEMENT-COMPONENT button + `(,@(if button2 + (list GRID-ELEMENT-COMPONENT button2) + '()))))) (sorted-items (if sort-listbox-items? (sort-listbox-items listbox-items) listbox-items)) (keys (fill-listbox listbox sorted-items))) + ;; On every listbox element change, check if we need to skip it. If yes, + ;; depending on the 'last-listbox-key', jump forward or backward. If no, + ;; do nothing. + (add-component-callback + listbox + (lambda (component) + (let* ((current-key (current-listbox-entry listbox)) + (listbox-keys (map car keys)) + (last-key (last-listbox-key)) + (item (assoc-ref keys current-key)) + (prev-key (previous-key listbox-keys current-key)) + (next-key (next-key listbox-keys current-key))) + ;; Update last-listbox-key before a potential call to + ;; set-current-listbox-entry-by-key, because it will immediately + ;; cause this callback to be called for the new entry. + (last-listbox-key current-key) + (when (skip-item-procedure? item) + (when (eq? prev-key last-key) + (if next-key + (set-current-listbox-entry-by-key listbox next-key) + (set-current-listbox-entry-by-key listbox prev-key))) + (when (eq? next-key last-key) + (if prev-key + (set-current-listbox-entry-by-key listbox prev-key) + (set-current-listbox-entry-by-key listbox next-key))))))) + (when listbox-default-item (set-default-item listbox keys listbox-default-item)) + (when allow-delete? + (form-add-hotkey form KEY-DELETE)) + (add-form-to-grid grid form #t) (make-wrapped-grid-window grid title) @@ -241,22 +318,28 @@ the current listbox item has to be selected by key." (dynamic-wind (const #t) (lambda () - (when (eq? exit-reason 'exit-component) - (cond - ((components=? argument button) - (button-callback-procedure)) - ((components=? argument listbox) - (if listbox-allow-multiple? - (let* ((entries (listbox-selection listbox)) - (items (map (lambda (entry) - (assoc-ref keys entry)) - entries))) - (listbox-callback-procedure items) - items) - (let* ((entry (current-listbox-entry listbox)) - (item (assoc-ref keys entry))) - (listbox-callback-procedure item) - item)))))) + (case exit-reason + ((exit-component) + (cond + ((components=? argument button) + (button-callback-procedure)) + ((and button2 + (components=? argument button2)) + (button2-callback-procedure)) + ((components=? argument listbox) + (if listbox-allow-multiple? + (let* ((entries (listbox-selection listbox)) + (items (map (lambda (entry) + (assoc-ref keys entry)) + entries))) + (listbox-callback-procedure items)) + (let* ((entry (current-listbox-entry listbox)) + (item (assoc-ref keys entry))) + (listbox-callback-procedure item)))))) + ((exit-hotkey) + (let* ((entry (current-listbox-entry listbox)) + (item (assoc-ref keys entry))) + (hotkey-callback-procedure argument item))))) (lambda () (destroy-form-and-pop form)))))) @@ -311,3 +394,132 @@ error is raised if the MAX-SCALE-UPDATE limit is reached." (error "Max scale updates reached.")))))) (lambda () (destroy-form-and-pop form))))) + +(define* (run-checkbox-tree-page #:key + info-text + title + items + item->text + (info-textbox-width 50) + (checkbox-tree-height 10) + (ok-button-callback-procedure + (const #t)) + (cancel-button-callback-procedure + (const #t))) + "Run a page allowing the user to select one or multiple items among ITEMS in +a checkbox list. The page contains vertically stacked from the top to the +bottom, an informative text set to INFO-TEXT, the checkbox list and two +buttons, 'Ok' and 'Cancel'. The page title's is set to TITLE. ITEMS are +converted to text using ITEM->TEXT before being displayed in the checkbox +list. + +INFO-TEXTBOX-WIDTH is the width of the textbox where INFO-TEXT will be +displayed. CHECKBOX-TREE-HEIGHT is the height of the checkbox list. + +OK-BUTTON-CALLBACK-PROCEDURE is called when the 'Ok' button is pressed. +CANCEL-BUTTON-CALLBACK-PROCEDURE is called when the 'Cancel' button is +pressed. + +This procedure returns the list of checked items in the checkbox list among +ITEMS when 'Ok' is pressed." + (define (fill-checkbox-tree checkbox-tree items) + (map + (lambda (item) + (let* ((item-text (item->text item)) + (key (add-entry-to-checkboxtree checkbox-tree item-text 0))) + (cons key item))) + items)) + + (let* ((checkbox-tree + (make-checkboxtree -1 -1 + checkbox-tree-height + FLAG-BORDER)) + (info-textbox + (make-reflowed-textbox -1 -1 info-text + info-textbox-width + #:flags FLAG-BORDER)) + (ok-button (make-button -1 -1 (G_ "Ok"))) + (cancel-button (make-button -1 -1 (G_ "Cancel"))) + (grid (vertically-stacked-grid + GRID-ELEMENT-COMPONENT info-textbox + GRID-ELEMENT-COMPONENT checkbox-tree + GRID-ELEMENT-SUBGRID + (horizontal-stacked-grid + GRID-ELEMENT-COMPONENT ok-button + GRID-ELEMENT-COMPONENT cancel-button))) + (keys (fill-checkbox-tree checkbox-tree items)) + (form (make-form))) + + (add-form-to-grid grid form #t) + (make-wrapped-grid-window grid title) + + (receive (exit-reason argument) + (run-form form) + (dynamic-wind + (const #t) + (lambda () + (case exit-reason + ((exit-component) + (cond + ((components=? argument ok-button) + (let* ((entries (current-checkbox-selection checkbox-tree)) + (current-items (map (lambda (entry) + (assoc-ref keys entry)) + entries))) + (ok-button-callback-procedure) + current-items)) + ((components=? argument cancel-button) + (cancel-button-callback-procedure)))))) + (lambda () + (destroy-form-and-pop form)))))) + +(define* (run-file-textbox-page #:key + info-text + title + file + (info-textbox-width 50) + (file-textbox-width 50) + (file-textbox-height 30) + (ok-button-callback-procedure + (const #t)) + (cancel-button-callback-procedure + (const #t))) + (let* ((info-textbox + (make-reflowed-textbox -1 -1 info-text + info-textbox-width + #:flags FLAG-BORDER)) + (file-text (read-all file)) + (file-textbox + (make-textbox -1 -1 + file-textbox-width + file-textbox-height + (logior FLAG-SCROLL FLAG-BORDER))) + (ok-button (make-button -1 -1 (G_ "Ok"))) + (cancel-button (make-button -1 -1 (G_ "Cancel"))) + (grid (vertically-stacked-grid + GRID-ELEMENT-COMPONENT info-textbox + GRID-ELEMENT-COMPONENT file-textbox + GRID-ELEMENT-SUBGRID + (horizontal-stacked-grid + GRID-ELEMENT-COMPONENT ok-button + GRID-ELEMENT-COMPONENT cancel-button))) + (form (make-form))) + + (set-textbox-text file-textbox file-text) + (add-form-to-grid grid form #t) + (make-wrapped-grid-window grid title) + + (receive (exit-reason argument) + (run-form form) + (dynamic-wind + (const #t) + (lambda () + (case exit-reason + ((exit-component) + (cond + ((components=? argument ok-button) + (ok-button-callback-procedure)) + ((components=? argument cancel-button) + (cancel-button-callback-procedure)))))) + (lambda () + (destroy-form-and-pop form)))))) |