Table of Contents
Popups, Menus and Dialogs
A popup is a window of its own for menus, lists to choose from and
tooltips: its controls draw into it while it is shown, laid out in its
size. show-popup shows it against a rectangle of the viewport it is in,
in that viewport's coordinates, such as a button's from
control-viewport-rectangle, which goes through the button's canvas,
the viewport's zoom and pan or its canvas layer's, to where the button
is drawn: below it by default,
moved to the other side or along the screen's edge where it would not
fit, as large as its controls need unless given :width and :height.
A popup-panel draws a panel behind them. The examples here add to the
settings form of Widgets, each part a node of the form, found by
its path, and each function connected to a signal given the form. Tell
the settings were saved:
(sc:call-in-tree *tree*
(lambda ()
(let ((save (sc:find-node (form *tree*) "box/save"))
(saved (make-instance 'ui:popup-panel :name "saved" :grab nil)))
(sc:add-child save saved)
(sc:add-child saved (make-instance 'ui:label :text "Saved"))
(ui:show-popup saved (ui:control-viewport-rectangle save)))))
hide-popup hides it again, and it emits popup-shown and
popup-hidden. A popup that grabs, as :grab t by default makes a
menu's, takes the keyboard while it is shown, and hides on Escape, when
the user clicks outside it, and when the window system dismisses it, as
on a click outside the program; a press outside it then does nothing
else. Its controls take its window's input, with a focus of their own.
A popup is a window of the window system's, or it is embedded, drawn
inside the window it is shown in, over everything else, as Godot's
embedded subwindows are: where the viewport it is shown on has
viewport-embed-subwindows, as a project's game does by
default, so that in fullscreen it keeps its menus in its frame; and
always in a tree whose engine draws offscreen, that takes no window, or
that has no engine, as in tests. Embedded, it is placed by the same
rules within the window
rather than the screen, takes the window's input first, the pointer's
over it and the keys while it grabs, and popup-embedded-p is true.
A control's control-tooltip-text, given as :tooltip-text, shows in a
tooltip once the pointer has rested on it for (tooltip-delay) seconds,
half a second as in Godot unless set, beside the pointer; it goes when
the pointer leaves the control and on a press. It shows under the
control, as Godot's does, so it draws with the themes that control finds:
tooltip-panel's :panel and tooltip-label's :font-color. A control
with parts gives each its own text with a control-tooltip method, which
gets where the pointer is.
A popup-menu is a popup of items to choose from, Godot's PopupMenu. Its
items are menu-item objects, which make-menu-item and
make-menu-separator make: a text; an icon, an image drawn before the
text, tinted with its menu-item-icon-modulate; an identifier, its
index unless it gives one; whether it is disabled, checkable, :check
or :radio, and checked; whether it is a separator; the submenu it opens, the name of a
popup menu among the menu's children; a shortcut, an action or a binding
that chooses it from the keyboard, whose text it shows; and a tooltip.
Items are value objects, saved inside the scene with their control, and
each belongs to one control: changing one, as with (setf menu-item-checked), draws its menu again. The pointer or the arrow keys
highlight an item, and a click, Return or Space chooses it, which hides
the menu and emits id-pressed and index-pressed; so does its
shortcut, while the menu shows and, for a menu bar's or a menu button's
menu, while it does not. A submenu opens as the pointer rests on
its item, or by Right, and Left closes it. A menu-bar opens the popup
menus among its children from a row of their titles, its items'
shortcuts choosing them only while the keyboard focus is in its
menu-bar-shortcut-context, if it has one, as the menus of Godot's
script editor choose theirs while the focus is in it; a menu-button the
one among its children, its items' shortcuts choosing them only while
the focus is in its button-shortcut-context, if it has one, as Godot's
controls take shortcuts; and an option-button shows one choice of a
list it opens, its items option-item objects, as make-option-item
and make-option-separator make them. Add presets and a quality to the
form:
(defun choose-preset (form volume)
(setf (ui:range-value (sc:find-node form "box/volume-row/volume")) volume))
(defun choose-quality (form index)
(declare (ignore form))
(format t "~&Quality ~D.~%" index))
(sc:call-in-tree *tree*
(lambda ()
(let* ((form (form *tree*))
(presets (make-instance 'ui:menu-button :name "presets" :text "Presets"))
(menu (make-instance
'ui:popup-menu
:name "menu"
:items (list (ui:make-menu-item "Quiet" :id 20)
(ui:make-menu-item "Loud" :id 100)
(ui:make-menu-separator)
(ui:make-menu-item "Default" :id 70
:shortcut '(:symbol :d :control)))))
(quality (make-instance 'ui:option-button
:name "quality"
:items (mapcar #'ui:make-option-item
'("Low" "Medium" "High"))
:selected 1)))
(sc:connect menu 'ui:id-pressed form 'choose-preset)
(sc:connect quality 'ui:item-selected form 'choose-quality)
(sc:add-child presets menu)
(add (sc:find-node form "box") presets quality))))
Control+D chooses Default, the menu open or not. Choosing a checkable
item does not check it, as in Godot: check it with
(setf (ui:menu-item-checked (ui:popup-menu-item menu index)) t).
add-menu-item, add-menu-separator and clear-menu change a menu's
items, and popup-menu-item gives one. To give a control an item another
holds, give it a copy-item of it.
A dialog tells the user something, as Godot's AcceptDialog, and a
confirmation-dialog asks them to confirm, with a Cancel button beside
OK. show-dialog shows one over the middle of the window it is in, as
large as its message, its controls and its buttons need, its OK button
taking the keyboard focus. OK confirms it, emitting confirmed; its
Cancel button, Escape and closing its window cancel it, emitting
canceled; either hides it. add-dialog-button adds a button that
emits custom-action with an action, and the controls added to a dialog
go above its buttons. Ask before resetting the volume:
(defun ask-to-reset (form)
(ui:show-dialog (sc:find-node form "box/reset/confirm")))
(defun reset-volume (form)
(setf (ui:range-value (sc:find-node form "box/volume-row/volume")) 70))
(sc:call-in-tree *tree*
(lambda ()
(let ((form (form *tree*))
(reset (make-instance 'ui:button :name "reset" :text "Reset"))
(confirm (make-instance 'ui:confirmation-dialog
:name "confirm" :title "Reset"
:text "Reset the volume?")))
(sc:connect reset 'ui:pressed form 'ask-to-reset)
(sc:connect confirm 'ui:confirmed form 'reset-volume)
(add (sc:find-node form "box") reset)
(sc:add-child reset confirm))))
A dialog is modal unless made with :modal nil: the window it is shown
on takes no input until it hides. Like a popup it is a window of the
window system's, a dialog of the window it is shown on, which
dialog-window gives; or embedded where popups are, drawn inside that
window over everything else with a border and a title bar of its own,
by which it is dragged within the window and closed, as Godot's embedded
windows, and dialog-embedded-p is true. An embedded dialog that is not
modal gets the window's keys while it has the focus, which a press on it
gives and a press elsewhere in the window takes back.
A file-dialog, Godot's FileDialog, chooses files and folders: it shows
a folder in a line edit, the folder's folders and files in a list, and
under them the file's name and filters of what the list shows. Its
file-dialog-file-mode says what it chooses, :open-file,
:open-files, :open-dir, :open-any, or by default :save-file,
and it emits file-selected, files-selected or dir-selected with
absolute pathnames. Its theme's icons :folder and :file show
before its folders and files, and :parent-folder and :reload on its
Up and Refresh buttons, as Godot's: the default theme gives Pyssel's
own, SVG files drawn at the window's scale in the dialog's font colour;
where a theme's cannot be drawn, the buttons show words and folders a
slash after their names. Its file-dialog-filters are file-filter
objects, which make-file-filter makes of a description and patterns.
Save the settings to a file:
(defun ask-where-to-save (form)
(ui:show-dialog (sc:find-node form "box/save-as/file")))
(defun save-settings-to (form path)
(with-open-file (*standard-output* path :direction :output :if-exists :supersede)
(save-settings form)))
(sc:call-in-tree *tree*
(lambda ()
(let ((form (form *tree*))
(save-as (make-instance 'ui:button :name "save-as" :text "Save as..."))
(file (make-instance 'ui:file-dialog
:name "file"
:filters (list (ui:make-file-filter
"Settings" "*.settings")))))
(sc:connect save-as 'ui:pressed form 'ask-where-to-save)
(sc:connect file 'ui:file-selected form 'save-settings-to)
(add (sc:find-node form "box") save-as)
(sc:add-child save-as file))))
A name typed without the filter's extension gets it, .settings, and
saving over a file asks first.
Getting started
The core
Drawing
Scenes
User interface
Games and the editor
- Playing a Game
- Projects
- The Editor
- The File System Dock
- The Script Screen
- The REPL
- The Debugger
- Lisp Text
- Code Services
- Project Manager
- Undo and Redo
- Exporting
The project