PBToolboxAI v1 ← Site

codeeditor — u_pbt_codeeditor #

← Component reference · Guide contents

Code editor with syntax highlighting: ten languages, line numbers, region folding, built-in search, and file drop.

See it live — Demo application, Code editor tile: the preview, the code behind it and this page, side by side.


At a glance #

Userobjectu_pbt_codeeditor
Item class— (component without items)
Used forEntering or displaying code, a SQL query, a configuration file: anywhere a multilineedit is not readable enough
Opt-in optionsib_track_caret, ib_allow_drop, ib_folding

Quick start #

// window open event
uo_editeur.is_syntax = uo_editeur.SYNTAX_SQL
uo_editeur.is_text   = "SELECT c.name, SUM(o.amount) AS total~r~n" &
                       + "FROM customer c~r~n" &
                       + "WHERE o.status = 'paid'"
// Read back what the user has actually typed
string ls_sql

ls_sql = uo_editeur.of_get_text()

is_text gives the same thing: the user's typing lands there as soon as the input settles (ue_changed event).


Supported languages #

is_syntax accepts one of these constants, or SYNTAX_NONE (empty string) for plain text with no highlighting.

LanguageConstantOther accepted spellings
PowerScriptSYNTAX_POWERSCRIPTpb, powerbuilder
SQLSYNTAX_SQLtsql, plsql
JavaScriptSYNTAX_JAVASCRIPTjs, jsx
JSONSYNTAX_JSONjsonc
C familySYNTAX_C · SYNTAX_CPP · SYNTAX_CSHARP · SYNTAX_JAVAc++, cxx, cs
HTML / XMLSYNTAX_HTML · SYNTAX_XMLhtm, xhtml, svg
CSSSYNTAX_CSSscss, less
PythonSYNTAX_PYTHONpy
YAMLSYNTAX_YAMLyml
MarkdownSYNTAX_MARKDOWNmd, mkd

Languages of the same family share their highlighting (SYNTAX_JAVA colors like SYNTAX_C, SYNTAX_XML like SYNTAX_HTML): the constant you pick documents your intent, the result on screen is the same. The value is case-insensitive, and an unknown name falls back to plain text without raising an error.


Properties #

Content and language #

PropertyTypeDefaultPurpose
is_textstring""The code displayed in the editor. When read back, returns the live content, the user's typing included
is_syntaxstring""Highlighting language: SYNTAX_* constants (see the table above). SYNTAX_NONE = plain text
ib_readonlybooleanfalseRead-only editor: the user can consult without being able to modify

Display #

PropertyTypeDefaultPurpose
ib_line_numbersbooleantrueShows or hides the line number gutter, on the left
ib_current_linebooleantrueHighlights the line where the caret sits. No highlight while ib_wrap is on.
ib_foldingbooleanfalseOpt-in: allows regions (#region, blocks between braces) to be folded from the gutter
ib_wrapbooleanfalseWraps long lines instead of scrolling sideways
ii_tab_sizeinteger4Number of columns a tab character takes up
is_font_familystring""Editor font (empty = the theme's monospaced font)
ii_font_sizeinteger0Font size in points (0 = the theme's size)
PropertyTypeDefaultPurpose
ib_search_enabledbooleantrueEnables the built-in search bar (Ctrl+F)
ii_doc_lineinteger-1Scrolls to the given line and selects it (numbering starts at 1). No highlight while ib_wrap is on; the scrolling still works.
ib_track_caretbooleanfalseOpt-in: raises ue_caret_changed every time the caret moves
ib_allow_dropbooleanfalseOpt-in: accepts files dropped from Windows Explorer; the full paths arrive through ue_drop_files
is_theme_stylestringfluentVisual style of the component (THEME_STYLE_* constants)
is_theme_modestringlightLight or dark variant (THEME_MODE_* constants)
il_theme_accentlong-1Accent color of this component (-1 = the theme accent)
is_tooltipstring""Simple tooltip shown when hovering the component
is_super_tooltip_titlestring""Title of the rich tooltip (takes precedence over is_tooltip)
is_super_tooltip_textstring""Text of the rich tooltip (rich markup accepted)
is_super_tooltip_imagestring""Image of the rich tooltip

Methods #

MethodPurpose
of_get_text ( ) → stringReturns the live content — the same value as is_text, for code that prefers a method call
of_find (string as_text)Opens the search bar and highlights every occurrence of the text
of_insert_text (string as_text)Inserts text at the caret, exactly as if the user had typed it: the current selection is replaced and the caret lands after the insertion. This is the method behind an insert snippet button, where is_text would throw the work in progress away. When the editor has the focus, the insertion goes through the browser's own edit path, so Ctrl+Z still undoes it
of_reset ( )Returns every property to its default and empties the editor
of_set_redraw (boolean)Batches a burst of changes into a single render
of_save_as_png (string) · of_save_as_jpg (string)Exports the rendering as an image

Events #

EventRaised when
ue_changed (string as_text)The user has modified the content and their typing has settled; as_text carries the complete new code
ue_caret_changed (long al_line, long al_col)The caret has moved; line and column counted from 1 — requires ib_track_caret = true
ue_find_result (long al_count, long al_index)A search has succeeded: al_count occurrences found, al_index = rank of the one currently highlighted
ue_drop_files (string as_files[])Files have been dropped from Windows: full paths, one entry per file. Requires ib_allow_drop = true
ue_drag_enter ( )A file drag enters the editor (ib_allow_drop)
ue_drag_leave ( )The file drag leaves the editor
ue_ready ( )The component has finished loading; everything sent beforehand has been replayed
ue_runtime_missing ( )The WebView2 runtime is missing: the component stays empty
ue_bg_color (long al_color)The component has computed its theme background color; the userobject has already adopted it (backcolor)

Examples #

A SQL query editor #

uo_requete.of_set_redraw(false)

uo_requete.is_syntax = uo_requete.SYNTAX_SQL
uo_requete.is_text   = "-- Top customers by revenue collected~r~n" &
                       + "SELECT c.name, SUM(o.amount) AS total~r~n" &
                       + "FROM customer c~r~n" &
                       + "  INNER JOIN orders o ON o.cust_id = c.id~r~n" &
                       + "WHERE o.status = 'paid'~r~n" &
                       + "GROUP BY c.name~r~n" &
                       + "ORDER BY total DESC"

uo_requete.of_set_redraw(true)
// clicked event of cb_executer
string ls_sql

ls_sql = uo_requete.of_get_text()     // what the user really typed
of_executer(ls_sql)

A read-only viewer #

Ideal for presenting generated code, a log, or an excerpt the user has to read without modifying it.

uo_apercu.is_syntax = uo_apercu.SYNTAX_POWERSCRIPT

uo_apercu.ib_readonly     = true      // consultation only, no editing caret
uo_apercu.ib_line_numbers = false     // hides the number gutter
uo_apercu.ib_current_line = false     // no current line highlight
uo_apercu.ib_wrap         = true      // wraps rather than scrolls
uo_apercu.ii_tab_size     = 2         // tabs displayed over 2 columns

uo_apercu.is_text = of_generer_code()

Tracking the caret position in a status bar #

uo_editeur.ib_track_caret = true       // explicit opt-in: otherwise no event at all
// ue_caret_changed event of uo_editeur: (long al_line, long al_col)
uo_statut.of_item("pos").is_text = "Line " + String(al_line) + ", col. " + String(al_col)

Without ib_track_caret, the caret reports nothing: this trigger fires very often and stays off until you ask for it.

Searching and jumping to a line #

// Opens the search bar and highlights every occurrence
uo_editeur.of_find(/*text*/ "ll_total")
// ue_find_result event of uo_editeur: (long al_count, long al_index)
if al_count = 0 then
    uo_statut.of_item("main").is_text = "No match"
else
    uo_statut.of_item("main").is_text = String(al_index) + " / " + String(al_count)
end if
// Jump straight to the line a compiler flagged
uo_editeur.ii_doc_line = ll_ligne_erreur     // scrolls to and selects the line

Opening a file dropped from Explorer #

uo_editeur.ib_allow_drop = true
// ue_drop_files event of uo_editeur: (string as_files[])
string ls_contenu, ls_ligne
integer li_fichier

// as_files[1] carries the FULL path of the first dropped file
li_fichier = FileOpen(as_files[1], StreamMode!, Read!)
if li_fichier > 0 then
    FileReadEx(li_fichier, ls_contenu)
    FileClose(li_fichier)

    uo_editeur.is_syntax = of_langage_selon_extension(as_files[1])
    uo_editeur.is_text   = ls_contenu
end if

Reacting to changes #

// ue_changed event of uo_editeur: (string as_text)
ib_modifie = true
uo_statut.of_item("main").is_text = String(Len(as_text)) + " characters"

The event is only raised once the typing has settled: continuous input does not generate one event per keystroke.


Best practices #


← Component reference · Guide contents