PBToolboxAI v4 ← Site

codeeditor — u_pbt_codeeditor #

← Référence des composants · Sommaire du guide

Éditeur de code à coloration syntaxique : dix langages, numéros de ligne, repli de régions, recherche et remplacement, marqueurs de diagnostic dans la gouttière et dépôt de fichiers.

▶ Le voir en vrai — Application de démonstration, tuile Code editor : l'aperçu, le code qui le produit et cette page, côte à côte.


En bref #

Userobjectu_pbt_codeeditor
Classe d'items— (composant sans items)
Sert àSaisir ou afficher du code, une requête SQL, un fichier de configuration : partout où un multilineedit manque de lisibilité
Options opt-inib_track_caret, ib_allow_drop, ib_folding

Démarrage rapide #

// event open de la fenetre
uo_editor.is_syntax = uo_editor.SYNTAX_SQL
uo_editor.is_text   = "SELECT c.name, SUM(o.amount) AS total~r~n" &
                       + "FROM customer c~r~n" &
                       + "WHERE o.status = 'paid'"
// Local variables
string ls_sql

// Relire ce que l'utilisateur a reellement saisi
ls_sql = uo_editor.of_get_text()

is_text donne la même chose : la frappe de l'utilisateur y est reportée dès que la saisie se pose (event ue_changed).


Langages reconnus #

is_syntax accepte l'une de ces constantes, ou SYNTAX_NONE (chaîne vide) pour du texte brut sans coloration.

LangageConstanteAutres écritures acceptées
PowerScriptSYNTAX_POWERSCRIPTpb, powerbuilder
SQLSYNTAX_SQLtsql, plsql
JavaScriptSYNTAX_JAVASCRIPTjs, jsx
JSONSYNTAX_JSONjsonc
Famille CSYNTAX_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

Les langages d'une même famille partagent la coloration (SYNTAX_JAVA colore comme SYNTAX_C, SYNTAX_XML comme SYNTAX_HTML) : la constante que vous choisissez documente votre intention, le résultat à l'écran est le même. La valeur est insensible à la casse et un nom inconnu revient au texte brut, sans erreur.


Propriétés #

Contenu et langage #

PropriétéTypeDéfautRôle
is_textstring""Le code affiché dans l'éditeur. En lecture, renvoie le contenu vivant, frappe de l'utilisateur comprise, avec les fins de ligne reçues : un texte en CRLF se relit en CRLF, un texte en CR seul en CR, replié ou non ; un texte qui les mélange se relit en CRLF. Un nouveau texte s'affiche depuis sa première ligne, efface les marqueurs d'of_add_marker et n'est pas modifié (ib_modified)
is_syntaxstring""Langage de coloration : constantes SYNTAX_* (voir le tableau ci-dessus). SYNTAX_NONE = texte brut. Se relit tel qu'écrit : SYNTAX_CSHARP reste SYNTAX_CSHARP, même si C# partage la grammaire du C
ib_readonlybooleanfalseÉditeur en lecture seule : l'utilisateur consulte sans pouvoir modifier
ib_modifiedbooleanfalsetrue dès que le texte diffère de celui déclaré enregistré : un nouveau is_text n'est pas modifié, la frappe, of_insert_text ou un remplacement le rendent modifié, annuler jusqu'au texte enregistré le remet à false. Remettez-le à false après l'enregistrement ; true le force

Affichage #

PropriétéTypeDéfautRôle
ib_line_numbersbooleantrueAffiche ou masque la gouttière de numéros de ligne, à gauche ; masqués, une gouttière étroite garde les marqueurs de repli et les diagnostics d'of_add_marker
ib_current_linebooleantrueSurligne la ligne où se trouve le curseur ; en mode ib_wrap, la bande couvre toutes les rangées de la ligne
ib_foldingbooleanfalseOpt-in : autorise le repli des régions #region … #endregion (//#region en PowerScript, JavaScript ou C) depuis la gouttière : #region s'ouvre replié, #regionopen déplié ; les marqueurs restent même avec ib_line_numbers = false. C'est un mode de lecture : tant qu'il est actif l'éditeur ne se laisse pas taper, parce que la zone de saisie porte alors le texte replié et non la source; l'activer sur des régions fermées oublie aussi l'historique de Ctrl+Z. Un marqueur s'atteint au clavier (Tab) ; Entrée ou Espace le plie ou le déplie
ib_wrapbooleanfalseRenvoie les lignes longues à la ligne au lieu de défiler latéralement
ii_tab_sizeinteger4Nombre de colonnes occupées par une tabulation, de 1 à 12 (toute autre valeur revient à 4) ; Entrée après une accolade ouvrante indente de cette largeur
is_font_familystring""Police de l'éditeur (vide = police à chasse fixe du thème)
ii_font_sizeinteger0Taille de la police en pixels (0 = taille du thème)
PropriétéTypeDéfautRôle
ib_search_enabledbooleantrueActive la barre de recherche intégrée (Ctrl+F, Ctrl+H pour remplacer — voir « Clavier ») ; à false, of_find renvoie -4
ib_find_match_casebooleanfalseOption de recherche : ne trouve que le texte de même casse. Le bouton Aa de la barre est le même interrupteur ; vaut pour of_find, of_replace et of_replace_all
ib_find_whole_wordbooleanfalseOption de recherche : ne trouve le texte que comme mot entier (le _ fait partie du mot : ls_a n'est pas trouvé dans ls_ab). Bouton ab de la barre
ib_find_regexbooleanfalseOption de recherche : le texte est une expression régulière (syntaxe JavaScript) ; le remplacement d'of_replace peut alors employer $1, $& et $<name>. Une expression invalide ne trouve rien, encadre le champ en rouge et fait renvoyer -5 à of_replace. Bouton .* de la barre
il_doc_linelong0Amène la ligne indiquée au milieu de la vue et la marque d'une bande d'accent (numérotation à partir de 1, comme la gouttière) — la ligne qu'une documentation ou un résultat de recherche désigne. Le curseur et la sélection de l'utilisateur ne bougent pas (il_caret_line déplace le curseur). Une ligne cachée dans une région repliée ouvre cette région ; la bande se peint aussi en mode ib_wrap. 0 efface la bande ; une ligne au-delà du texte ne marque rien et se relit telle qu'écrite ; tout nouveau is_text efface la bande
il_caret_linelong1La ligne du curseur, à partir de 1, lue en direct (replié : celle de la gouttière). L'écrire pose le curseur au début de cette ligne et l'amène à l'écran — le « aller à la ligne » d'une erreur de compilation ; une ligne cachée dans une région repliée ouvre cette région, une ligne au-delà du texte s'arrête à la dernière. Avec ib_track_caret, ue_caret_changed suit, comme pour un clic — rien quand le curseur ne bouge pas
il_caret_columnlong1La colonne du curseur, à partir de 1, lue en direct : le rang du caractère dans sa ligne (une tabulation compte pour un). L'écrire déplace le curseur le long de sa ligne ; au-delà de la fin de ligne, il s'arrête à cette fin. Avec ib_track_caret, ue_caret_changed suit, comme pour un clic
ib_track_caretbooleanfalseOpt-in : déclenche ue_caret_changed à chaque déplacement du curseur — geste de l'utilisateur ou ordre de votre code ; un nouveau document (is_text, un chargement) ne dit rien du curseur
ib_allow_dropbooleanfalseOpt-in : accepte le dépôt de fichiers depuis l'Explorateur Windows ; les chemins complets arrivent par ue_drop_files
is_theme_stylestring""Style visuel du composant (constantes THEME_STYLE_*) ; vide = celui de l'application, suivi à chaque changement
is_theme_modestring""Variante claire ou sombre (constantes THEME_MODE_*) ; vide = celle de l'application, suivie à chaque changement
il_theme_accentlong-1Couleur d'accent de ce composant (-1 = l'accent de l'application, ou celui du thème)
is_tooltipstring""Info-bulle simple affichée au survol du composant
is_super_tooltip_titlestring""Titre de l'info-bulle enrichie (prend le pas sur is_tooltip)
is_super_tooltip_textstring""Texte de l'info-bulle enrichie (balisage riche accepté)
is_super_tooltip_imagestring""Image de l'info-bulle enrichie

Méthodes #

MéthodeRôle
of_get_text ( ) → stringRenvoie le contenu vivant — la même valeur que is_text, pour le code qui préfère un appel de méthode
of_find (string as_text)Ouvre la barre de recherche, y inscrit le texte et met en évidence toutes ses occurrences, selon les options ib_find_* (replié, dans les lignes visibles seulement). La première occurrence à partir du curseur est amenée à l'écran ; le curseur lui-même ne bouge qu'à la fermeture de la barre par l'utilisateur. Un texte vide vide le champ et efface les surlignages. Renvoie 0 une fois appliqué, -4 quand ib_search_enabled vaut false, -2 si le composant n'est pas créé
of_insert_text (string as_text)Insère du texte au curseur, exactement comme si l'utilisateur l'avait tapé : la sélection en cours est remplacée, le curseur se pose après l'insertion et celle-ci est amenée à l'écran. C'est la méthode d'un bouton insérer un extrait, là où is_text jetterait le travail en cours. Pendant une recherche, l'insertion se fait au curseur de l'utilisateur, jamais sur l'occurrence. Quand le focus est dans l'éditeur ou dans sa barre de recherche, l'insertion passe par le chemin d'édition du navigateur : Ctrl+Z l'annule encore. Lève ue_changed, et avec ib_track_caret ue_caret_changed suit, comme pour une frappe. Rien n'est inséré tant que l'éditeur est en lecture seule (ib_readonly) ou replié (ib_folding). Renvoie 0 une fois appliqué, -4 en lecture seule ou replié, -5 sur un texte vide, -2 si le composant n'est pas créé
of_replace (string as_find, string as_replace)Remplace la première occurrence de as_find à partir du curseur (en repartant du début) et l'amène à l'écran ; les options ib_find_* s'appliquent, et avec ib_find_regex le remplacement peut employer $1, $& et $<name>. Un Ctrl+Z l'annule. Avec ib_track_caret, ue_caret_changed suit quand le curseur bouge. Renvoie le nombre d'occurrences remplacées (1, ou 0 s'il n'y en a aucune), -4 en lecture seule ou replié, -5 sur une recherche vide ou une expression invalide, -2 si le composant n'est pas créé
of_replace_all (string as_find, string as_replace)Remplace toutes les occurrences de as_find, en un seul pas d'annulation. Mêmes options et mêmes codes qu'of_replace ; renvoie le nombre d'occurrences remplacées
of_selected_text ( ) → stringRenvoie le texte que l'utilisateur a sélectionné, lu en direct ("" sans sélection), avec les fins de ligne de is_text
of_select_range (long al_from_line, long al_from_col, long al_to_line, long al_to_col)Sélectionne de (ligne, colonne) à (ligne, colonne), le tout à partir de 1, et amène la sélection à l'écran ; le curseur se place au bout donné en dernier, et une colonne au-delà de la fin de sa ligne s'arrête à cette fin. Avec ib_track_caret, ue_caret_changed suit, comme pour un glisser. Renvoie 0 une fois appliqué, -5 sur une ligne hors du document ou une colonne inférieure à 1, -4 quand l'éditeur est replié, -2 si le composant n'est pas créé
of_line_count ( )Renvoie le nombre de lignes du document, replié ou non, lu en direct — la borne d'une boîte « aller à la ligne »
of_add_marker (long al_line, string as_kind, string as_tooltip)Marque une ligne de la gouttière d'un diagnostic — MARKER_ERROR, MARKER_WARNING ou MARKER_INFO — avec une info-bulle (balisage accepté) : ce que dit un compilateur, là où il le dit. Plusieurs marqueurs peuvent partager une ligne : le plus grave donne l'icône, l'info-bulle les liste tous. Ils appartiennent au document : un nouveau is_text les efface. Renvoie 0 une fois appliqué, -5 sur une ligne hors du document ou un genre inconnu, -2 si le composant n'est pas créé
of_remove_marker (long al_line)Retire tous les marqueurs d'une ligne. Renvoie 0 une fois retirés, -5 si la ligne n'en porte aucun, -2 si le composant n'est pas créé
of_clear_markers ( )Retire tous les marqueurs de la gouttière. Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé
of_marker_count ( )Renvoie le nombre de marqueurs de la gouttière, lu en direct (deux sur une ligne comptent pour deux)
of_reset ( )Remet toutes les propriétés à leur défaut et vide l'éditeur. Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé
of_set_redraw (boolean)Regroupe une rafale de modifications en un seul rendu. Renvoie 0 une fois appliqué, -2 si le composant n'est pas créé
of_save_as_png (string) · of_save_as_jpg (string)Exporte le rendu en image. Renvoie 0 une fois l'image écrite, -4 si l'écriture échoue, -2 si le composant n'est pas créé

Événements #

ÉvénementDéclenché quand
ue_changed ( )L'utilisateur a modifié le contenu et sa frappe s'est stabilisée — levé aussi après of_insert_text ou un remplacement. L'événement ne porte rien : le remplir imposait de relire tout le document à chaque frappe posée, pour une application qui le plus souvent veut seulement savoir que ça a changé. Celle qui veut le code le demande — is_text ou of_get_text() ; ib_modified dit s'il diffère du texte enregistré
ue_caret_changed (long al_line, long al_col)Le curseur a bougé (touche, clic, glisser, frappe, ou votre code) ; ligne et colonne comptées à partir de 1, la ligne étant celle de la gouttière quand l'éditeur est replié, la colonne le rang du caractère dans sa ligne (une tabulation compte pour un). Votre code le lève comme le geste (il_caret_line, il_caret_column, of_select_range, of_insert_text, of_replace) ; rien quand le curseur ne bouge pas, et un nouveau document (is_text, un chargement) ne dit rien du curseur — nécessite ib_track_caret = true
ue_find_result (long al_count, long al_index)Une recherche a abouti, ou passe à une autre occurrence : al_count occurrences trouvées, al_index = rang de celle qui est mise en avant (à partir de 1). Pendant la frappe, il n'est levé que si le compte change ; fermer la barre ne le lève pas ; une recherche vide non plus
ue_drop_files (string as_files[])Des fichiers ont été déposés depuis Windows : chemins complets, une entrée par fichier. Nécessite ib_allow_drop = true
ue_drag_enter ( )Un glissement de fichiers entre sur l'éditeur (ib_allow_drop)
ue_drag_leave ( )Le glissement de fichiers quitte l'éditeur
ue_ready ( )Le composant a fini de charger ; tout ce qui a été envoyé avant a été rejoué
ue_runtime_missing ( )Le runtime WebView2 est absent : le composant reste vide
ue_bg_color (long al_color)Le composant a calculé sa couleur de fond de thème ; l'userobject l'a déjà adoptée (backcolor)

Clavier #

ToucheEffet
TabIndente jusqu'au taquet suivant (ii_tab_size) ; plusieurs lignes sélectionnées : indente tout le bloc
Maj+TabDésindente la ligne ou le bloc
EntréeNouvelle ligne à la même indentation, un niveau de plus après {, ( ou [
Ctrl+FOuvre la barre de recherche, remplie avec la sélection (ib_search_enabled)
Ctrl+HOuvre la barre avec sa ligne de remplacement (éditeur modifiable seulement)
F3 · Maj+F3Occurrence suivante · précédente, depuis le code ou la barre
Entrée · Maj+Entrée (barre)Occurrence suivante · précédente ; dans le champ de remplacement, Entrée remplace l'occurrence courante et Ctrl+Entrée les remplace toutes
ÉchapFerme la barre et pose le curseur sur l'occurrence courante
Tab, puis Entrée ou Espace (gouttière)Atteint un marqueur de repli, le plie ou le déplie (ib_folding)

La colonne que rapportent ue_caret_changed et il_caret_column est le rang du caractère dans sa ligne : une tabulation compte pour un, quelle que soit sa largeur à l'écran.


Exemples #

Un éditeur de requête SQL #

// Geler l'affichage pendant la mise en place
uo_query.of_set_redraw(/*on*/ false)

// Coloration SQL, puis la requete a afficher
uo_query.is_syntax = uo_query.SYNTAX_SQL
uo_query.is_text   = "-- Meilleurs clients par chiffre d'affaires encaisse~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"

// Un seul dessin, tout d'un coup
uo_query.of_set_redraw(/*on*/ true)
// event clicked de cb_execute
string ls_sql

// Executer la requete telle que saisie
ls_sql = uo_query.of_get_text()     // la saisie reelle de l'utilisateur
of_execute(ls_sql)

Une visionneuse en lecture seule #

Idéale pour présenter du code généré, un journal ou un extrait que l'utilisateur doit lire sans le modifier.

// Coloration PowerScript
uo_preview.is_syntax = uo_preview.SYNTAX_POWERSCRIPT

// Une visionneuse en lecture seule : ni curseur, ni gouttiere, ni surlignage
uo_preview.ib_readonly     = true      // consultation seule, pas de curseur de saisie
uo_preview.ib_line_numbers = false     // masque la gouttiere de numeros
uo_preview.ib_current_line = false     // pas de surlignage de la ligne courante
uo_preview.ib_wrap         = true      // renvoie a la ligne plutot que defiler
uo_preview.ii_tab_size     = 2         // tabulations affichees sur 2 colonnes

// Le code a afficher
uo_preview.is_text = of_generate_code()

Suivre la position du curseur dans une barre d'état #

// Signaler chaque deplacement du curseur (ue_caret_changed)
uo_editor.ib_track_caret = true       // abonnement explicite : sinon aucun evenement
// event ue_caret_changed de uo_editor : (long al_line, long al_col)
uo_status.of_panel(/*key*/ "pos").is_text = "Ligne " + String(al_line) + ", col. " + String(al_col)

Sans ib_track_caret, le curseur ne remonte rien : ce déclencheur est de fréquence élevée et reste coupé tant que vous ne le demandez pas.

Rechercher et aller à une ligne #

// Ouvre la barre de recherche et met en evidence toutes les occurrences
uo_editor.of_find(/*text*/ "ll_total")
// event ue_find_result de uo_editor : (long al_count, long al_index)
if al_count = 0 then
    uo_status.of_panel(/*key*/ "main").is_text = "Aucune occurrence"
else
    uo_status.of_panel(/*key*/ "main").is_text = String(al_index) + " / " + String(al_count)
end if
// Aller a la ligne qu'un compilateur a signalee et la marquer dans la gouttiere
uo_editor.of_add_marker(/*line*/ ll_error_line, /*kind*/ uo_editor.MARKER_ERROR, /*tooltip*/ ls_error_text)
uo_editor.il_caret_line = ll_error_line     // le curseur y va, la ligne vient a l'ecran
uo_editor.of_focus_webview()                // l'utilisateur corrige aussitot

Ouvrir un fichier déposé depuis l'Explorateur #

// Accept files dropped from the Explorer
uo_editor.ib_allow_drop = true
// event ue_drop_files de uo_editor : (string as_files[])
string ls_content
integer li_file

// as_files[1] porte le chemin COMPLET du premier fichier depose
li_file = FileOpen(as_files[1], StreamMode!, Read!)
if li_file > 0 then
    FileReadEx(li_file, ls_content)
    FileClose(li_file)

    // Colorer selon l'extension du fichier, puis afficher son contenu
    uo_editor.is_syntax = of_syntax_for_extension(as_files[1])
    uo_editor.is_text   = ls_content
end if

Réagir aux modifications #

// event ue_changed de uo_editor : ( )
cb_save.enabled = uo_editor.ib_modified     // annuler jusqu'au texte enregistre le remet a false
// event clicked de cb_save
if of_save_script(uo_editor.is_text) = 1 then
    uo_editor.ib_modified = false     // le texte enregistre devient la reference
    cb_save.enabled = false
end if

L'événement n'est levé qu'une fois la frappe stabilisée : une saisie continue ne génère pas un événement par touche.

Renommer une variable dans tout le script #

// Local variables
long ll_count

// Mots entiers seulement : ll_total2 est une autre variable
uo_editor.ib_find_whole_word = true
ll_count = uo_editor.of_replace_all(/*find*/ "ll_total", /*replace*/ "ldc_amount")   // un seul Ctrl+Z annule tout

Bonnes pratiques #

Hérité du socle commun #

Ces membres existent sur tous les composants visuels — ils ne sont pas propres à celui-ci. Ils sont détaillés une seule fois, dans les chapitres transverses ; cette table dit seulement où les lire.

MembresRôleDétaillé dans
of_resetRemettre le composant à zéro3.6 Remettre un composant à zéro : of_reset()
of_register_shortcut · of_clear_shortcutsRaccourcis clavier du composant3.5 Les raccourcis clavier
of_is_created · of_is_ready · of_get_last_errorS'il est né, s'il est prêt, ce qui a échoué3.7 Diagnostic
of_save_as_png · of_save_as_jpgExporter le rendu en image3.8 Exporter le rendu en image
of_set_redrawGrouper les modifications en un seul repaint3.10 Bonnes pratiques
of_preload_iconsIcônes affichées sans délaiAffichage instantané : of_icon
of_set_translationTraduire un libellé du composant5.2 Adapter un libellé : of_set_translation
of_focus_webviewDonner le focus au composant6.4 Clavier et focus
of_print · of_print_to_pdfImprimer, ou écrire un PDF6.9 Imprimer
of_set_property · of_get_property · of_component_namePiloter une propriété par son nom3.1 Le moteur de propriétés

Deux aides ne sont pas héritées : of_icon et of_escape_markup vivent sur n_pbt_utils. Déclarez-en une — n_pbt_utils lnv_utils, rien à créer — et appelez-les dessus.


← Référence des composants · Sommaire du guide