PBToolboxAI v3 ← Site

commandpalette — n_pbt_commandpalette #

← Component reference · Guide contents

Command palette: the user presses a chord, types three letters, and reaches any action in your application — without hunting through the menus.

▶ See it live — Demo application, Command palette tile: the preview, the code behind it and this page, side by side.


At a glance #

Objectn_pbt_commandpalette — non-visual: nothing to drop on the window
Used forMaking every action of the application reachable from the keyboard, in three letters
ReturnNon-blocking: of_open() returns at once; the choice comes back as an event

The palette is a detached window of its own: it floats above your application, takes the focus while the user types, and hands it back on closing.


Quick start #

// Once, at startup : your application's actions
inv_palette.ipo_owner = this
inv_palette.of_add_command(/*key*/ "new",  /*label*/ "N", /*group*/ "F")
inv_palette.of_add_command(/*key*/ "open", /*label*/ "O", /*group*/ "F")
inv_palette.of_add_command(/*key*/ "save", /*label*/ "S", /*group*/ "F")
inv_palette.of_register_shortcut()
// event ue_command_selected : (string as_key)
choose case as_key
	case "new";  of_nouveau()
	case "open"; of_ouvrir()
	case "save"; of_enregistrer()
end choose

The wiring: the host window #

The palette is a non-visual object, but you have no receiver and no message to wire: set ipo_owner to your window, and the palette drains its own events and raises them on that object.

Two lines, once, when the window opens:

inv_palette.ipo_owner = this        // the window the palette belongs to
inv_palette.of_register_shortcut()  // the palette has to answer its key

That is all there is to wire. The user's choice, the opening and the closing then come back to you as plain events on ipo_owner — no receiver, no message to map, no timer.


The chord: the DLL is the one that hears it #

The chord that opens the palette is not listened to by the page: it is registered with the DLL, which alone sees the keys pressed while the focus is on another control. That is the whole difference between a palette people find and a palette that only answers once you have clicked on it.

The DLL hears the chord, but it opens nothing by itself: it tells you through ue_shortcut, and you decide. A palette opening over a modal dialog would help nobody.

// event ue_shortcut : the key has fired
if not ib_dialog_open then inv_palette.of_open()

is_shortcut picks the chord; of_register_shortcut() hands it over. Call it once when the window opens — otherwise the palette only answers its key after having been opened once already. of_open hands it over again on the way, so a chord changed later needs nothing more.

// The chord everyone already knows, from the code editors
inv_palette.is_shortcut = inv_palette.SHORTCUT_DEFAULT

// Or yours
inv_palette.is_shortcut = "ctrl+shift+p"

// Or none: the palette then opens through of_open() only
inv_palette.is_shortcut = inv_palette.SHORTCUT_NONE

Two components asking for the same chord: the one holding the focus wins, otherwise the first registered. The keyboard chapter has the detail.


Where the palette appears #

is_position says where the window lands. It is always brought back inside the screen: a palette anchored under a field at the bottom of the window does not disappear behind the taskbar.

ConstantWhere
POSITION_WINDOW_CENTERCentred on ipo_owner — the default, and what the eye expects
POSITION_SCREEN_CENTERCentred on the screen, whatever the window
POSITION_ABSOLUTEAt il_x / il_y, in screen pixels

PowerBuilder works in PBU: convert before filling il_x / il_y.

Its height follows the number of commands shown, and shrinks as you filter — without its top corner moving, or the search box would slide away under your fingers. It is capped at half the screen: past that the list scrolls inside it and the search box stays at the top.

A click elsewhere in the application closes the palette, and that click still reaches its target — like leaving a menu. Nothing to do for it.


Properties #

PropertyTypeDefaultRole
ipo_ownerpowerobject—The window the palette belongs to: it owns the popup window and anchors it, and the palette's events are raised on it. Set it before of_open — it is the only wiring to do
ipo_receiverpowerobject—Optional, legacy: a separate visual object on which to deliver the events, instead of ipo_owner. Leave it empty — the palette now delivers its events on its own via ipo_owner
is_shortcutstring"ctrl+k"Chord that opens the palette, from anywhere in the window. Constants SHORTCUT_DEFAULT (ctrl+k) and SHORTCUT_NONE (none). Takes effect on of_register_shortcut
is_positionstringwindow-centerWhere the window lands (POSITION_* constants)
il_x · il_ylong0Position in screen pixels, read by POSITION_ABSOLUTE only
is_placeholderstring""Grey text shown in the search box while nothing has been typed
is_recentstring""Usage memory: the ids most recently launched, newest first, comma separated. The palette floats them to the top, and recency breaks ties when filtering — it never overrules the match. Read it back after use and persist it; set it at startup. A palette that starts blank every morning learns nothing
il_max_recentlong8How many the « Recently used » block holds. 8 by default. 0 switches it off: an application whose users would rather see their groups untouched can say so. An entry in the block stays in its group and carries that group's name — a shortcut does not move what it is a shortcut to

Methods #

MethodRole
of_add_command (string as_key, string as_label, string as_group)Declares an action: its identifier, its label, and the group it appears under. Returns 0 once applied, -5 on an invalid argument (empty key, wrong address), -2 when the component is not created
of_add_command (string as_key, string as_label, string as_group, string as_hint, string as_shortcut, string as_keywords)Same, with the hint on the right, the chord to display, which runs its command while the palette is open — that is how one learns it ; outside, your application keeps its own accelerators — and keywords the search reads without showing them. Returns 0 once applied, -5 on an invalid argument (empty key, wrong address), -2 when the component is not created
of_remove_command (string as_key)Removes one action; the others stay. Returns 0 once applied, -5 on an invalid argument (empty key, wrong address), -2 when the component is not created
of_command (string as_key) → n_pbt_commandpalette_commandThe handle to a command, to rename it, change its chord, grey it or hide it through its properties. Greying rather than removing: removing what the user cannot do right now also removes any chance of discovering that it exists. The state travels with the commands: a change made while the palette is open shows at the next opening
of_key ( ) → stringOn the handle of_command returns: the key of the command it designates — what of_command took to get it, and what to keep when the handle is passed around
of_clear_commands ( )Empties the palette. Returns 0 once applied, -2 when the component is not created
of_count ( ) → integerHow many commands the palette holds
of_keys_at ( integer ai_index ) → stringThe id of the command at rank ai_index (1 based), or "" past either end. With of_count, this is what lets you walk a palette you did not fill yourself — a shared module adds its own
of_has ( string as_key ) → booleanDoes a command exist under this id? Asking beats guessing: declaring a second one under an id already taken is how a list grows a twin
of_open ( )Opens the palette: a window of its own, owned by ipo_owner, placed by is_position. It takes the focus, and gives it back on closing. Returns 0 once open, -1 when the WebView2 runtime is missing, -4 when its window could not be created
of_is_open ( )TRUE while the palette is on screen. This is what lets the chord toggle: pressed a second time a palette closes — calling of_open again would destroy the window and rebuild it identically, which reads as a flicker, not as a close. The DLL still decides nothing: it informs
of_close ( )Closes it. Losing the focus closes it too, like a menu. Returns 0
of_register_shortcut ( )Hands the chord of is_shortcut to the DLL. Call it once when the window opens. One chord per window: calling it again after changing is_shortcut replaces the previous one, which stops answering at once — there is never anything to remove first. Returns 0 when set, 1 when it replaced one, 2 when an empty is_shortcut removed it
of_process_events ( )Drains the queued events and raises them on ipo_owner. The component calls it itself as long as the palette lives: you normally do not have to
of_reset ( )Empties the commands and returns the properties to their defaults. ipo_owner and ipo_receiver are left alone: they are the wiring, not the content. Returns 0 once applied, -2 when the component is not created

Command properties — n_pbt_commandpalette_command #

Obtained through of_command(key). The palette rebuilds its window from its list at each of_open: a property changed while it is open shows at the next opening.

PropertyTypeDefaultRole
is_labelstring—The text of the row
is_shortcutstring""The chord shown at the right of the row, and honoured while the palette is open (Ctrl+Shift+S)
ib_enabledbooleantrueCommand greyed: visible, searchable, and inert — neither click, Enter nor its chord
ib_visiblebooleantrueCommand hidden: out of the list and of the chords, without being removed; it comes back as it was

Events #

EventRaised when
ue_command_selected (string as_key)The user picked an action. The palette has already closed: doing what it announces is up to you
ue_shortcut ( )The chord was pressed. The DLL relays, PB decides. A palette toggles on its own key: if of_is_open() then of_close() else of_open(). You may also refuse
ue_opened ( )The palette is on screen — through of_open
ue_closed ( )It has just closed, whether or not something was picked

The palette does nothing by itself. It reports the identifier picked, and closes. Your application is what acts — the same action, triggered from a menu or from the palette, therefore goes through the same code.


From the keyboard #

KeyEffect
The chord of is_shortcutTells your code through ue_shortcut; that is what opens it
TypingFilters as you type: the letters need not follow each other, nwf finds "New file"
Up / down arrowsMove the selection through the list
EnterPicks the selected action (ue_command_selected)
The chord shown on a rowRuns that command, without having to select it first
EscapeCloses without picking anything

Examples #

Feeding the palette from your menu #

// Keywords are not displayed, but the search reads them :
// typing "pdf" finds the export even if the label never says it
inv_palette.of_add_command(/*key*/ "export", /*label*/ "E", /*group*/ "F", /*hint*/ "H", /*shortcut*/ "Ctrl+E", /*keywords*/ "pdf csv xlsx")

Choosing another chord #

// Ctrl+K already taken by your application ? Pick another one.
// of_register_shortcut hands it over, and the old one goes on its own.
inv_palette.is_shortcut = "ctrl+shift+p"
inv_palette.of_register_shortcut()

Anchoring it under a field #

// Anchored under an input field : PB counts in PBU, the DLL in pixels
// The palette is brought back inside the screen if it overflowed
inv_palette.is_position = inv_palette.POSITION_ABSOLUTE
inv_palette.il_x = UnitsToPixels(sle_1.x, XUnitsToPixels!)
inv_palette.il_y = UnitsToPixels(sle_1.y + sle_1.height, YUnitsToPixels!)
inv_palette.is_placeholder = "P"
inv_palette.of_open()
inv_palette.of_remove_command(/*key*/ "print")
inv_palette.of_clear_commands()
inv_palette.of_close()

Best practices #


← Component reference · Guide contents