markdownview — u_pbt_markdownview #
← Component reference · Guide contents
markdownview shows a Markdown document, read-only, at the colours of the theme: headings, lists, tables, task lists, coloured code, pictures. You set the text in
is_markdown, or the path of a file inis_source; a link never navigates, it raisesue_link_clickedand the application decides. The raw HTML of a document is never run.
▶ See it live — Demo application, Markdown viewer tile, with the code and this page side by side.
At a glance #
| Userobject | u_pbt_markdownview |
| Input | A Markdown text (is_markdown), or a file of the workstation or an https:// URL (is_source) |
| Syntax | CommonMark, plus GitHub's tables, task lists, strikethrough, bare links and e-mail addresses, alerts (> [!NOTE]) and emoji (:+1:) |
| Feedback | ue_link_clicked for a link, with its kind (the application decides), ue_loaded / ue_load_failed for a document, ue_search_result for the search, ue_task_toggled for a ticked box |
| Safety | Raw HTML is shown as text; a javascript:, vbscript: or data: link is not clickable; an http:// picture is refused, and so is a picture on a network share; a document from the web shows no file of the workstation |
Quick start #
// Show a Markdown file : its pictures and relative links are read against its folder
uo_md.is_source = "C:\Acme\Help\guide.md"
// ... or any text the application has
uo_md.is_markdown = "# Release notes~n~n- [x] Orders~n- [ ] Invoices"
// ue_link_clicked : the viewer never navigates, the application decides where to open as_url
Properties #
| Property | Type | Default | Description |
|---|---|---|---|
is_markdown | string | "" | The Markdown text to show. Setting it replaces the document (is_source then reads back empty); empty clears the view. A new text of the SAME document — the editing recipe — keeps the reading position. The raw HTML it holds is displayed as text, never run (see ib_allow_html). Reads back exactly as it was set |
is_source | string | "" | A Markdown file to show: a path of the workstation (.md, .markdown, .txt…), a file:/// address or an https:// URL — read by the native HTTPS client of the library (the proxy and the Windows identity of the workstation, no CORS wall). Its relative pictures and links are read against its folder. http:// is refused (REASON_INSECURE), as is any file that is not text (REASON_UNSUPPORTED); over 16 MB, file or URL, REASON_TOO_LARGE. The file is read in the background: read the table of contents or move in it once ue_loaded has come (while it is read, of_toc returns 0 and of_scroll_to waits for the file). Setting it replaces the document (is_markdown then reads back empty) |
is_base_path | string | "" | The folder (or the https:// address) the relative pictures and links of a TEXT set by is_markdown are read against — a help text kept in a database beside its pictures. Empty: the current folder of the application. A file shown by is_source always uses its own folder. Read back live |
ib_allow_html | boolean | false | true draws a SHORT closed list of inert HTML tags of the document — b, i, u, s, kbd, sub, sup, mark, small, br, img (src, alt, width, height: the rules of a Markdown picture), a (href: the rules of a Markdown link) and details / summary. Any other tag, a script, a style, an event handler stay text. false: all the HTML is shown as written |
ib_network_pictures | boolean | false | false: a picture of the document that lies on the NETWORK (a share of a server) is not loaded — opening a share hands the Windows identity of the user to that server — unless it lies on the share the document itself was read from. true: for an application that shows its OWN documents from a share. A document read from the web never shows a file of the workstation |
ib_remote_pictures | boolean | true | true loads the https:// pictures of a document; false loads none — nothing leaves the workstation just because a document is displayed (a tracking pixel stays a placeholder) |
ib_tasks_editable | boolean | false | true lets the user tick the boxes of a task list (a click, or Space on a box): ue_task_toggled gives the LINE of the source and the new state; the TEXT is the application's to rewrite. false: the boxes are read-only |
ib_track_heading | boolean | false | true raises ue_heading_visible each time another heading reaches the top of the view while the reader scrolls — a table of contents that follows the reading |
ii_zoom | integer | 100 | The reading size, 50 to 300 % (100: the size of the theme). Ctrl+wheel in the document changes it by 10; read back live |
is_position | string | "" | The reading position, a text such as 12:35 (the first block in view and how far it starts above the top). Read it before closing a long document, set it back to reopen it where the reader left it. Read back live; empty when nothing is shown. A file being read takes it once it is on screen |
ib_search_enabled | boolean | true | Ctrl+F in the document opens a search box — the same search as of_search: typing highlights, Enter or F3 next, Shift+Enter or Shift+F3 previous, Escape closes. false leaves the shortcut to an application that searches from its own box |
ib_find_match_case | boolean | false | Search option: finds only the text with the same case. The Aa toggle of the search box is the same switch. Read back live; of_reset puts it back to false |
ib_find_whole_word | boolean | false | Search option: finds the text only as a whole word (pen is not found inside pencil). The ab toggle of the search box is the same switch. Read back live; of_reset puts it back to false |
is_theme_style | string | "" | Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change |
is_theme_mode | string | "" | Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change |
il_theme_accent | long | -1 | Accent colour of this component (-1 = the application accent, or the theme's) |
is_tooltip | string | "" | Simple tooltip shown when hovering the component |
is_super_tooltip_title | string | "" | Title of the rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the rich tooltip |
Methods #
| Method | Description |
|---|---|
of_clear ( ) → long | Empties the view: is_markdown and is_source read back empty; ib_search_enabled and the search options keep their values (of_reset is the one that puts every property back). An open search says so: ue_search_result(0, 0). Returns 0, -2 when the component is not created |
of_clear_search ( ) → long | Drops the search: no more highlight nor current occurrence (of_match_count returns 0), the search box closes. Returns 0, -2 when the component is not created |
of_copy ( ) → long | Copies to the clipboard the text the user selected — what Ctrl+C and Ctrl+Insert do in the document. Returns 0, -5 when nothing is selected, -2 when the component is not created |
of_match_count ( ) → long | Returns how many occurrences the current search found, read LIVE — the 12 of a 3 / 12 status line; 50,000 at most are counted. 0 without a search |
of_match_index ( ) → long | Returns the 1-based position of the current occurrence, read LIVE — the 3 of a 3 / 12 status line. 0 without an occurrence |
of_refresh ( ) → long | Reads is_source again (from the disk or the network): the way to show a file rewritten at the same path; the reading position is kept; ue_loaded or ue_load_failed follows. Returns 0, -5 when no document is shown, -2 when the component is not created |
of_scroll_to ( string as_anchor ) → long | Brings the heading as_anchor to the top of the view: its anchor as of_toc gives it, with or without #, or its text; a heading inside a closed details opens it. While a file set by is_source is being read, the anchor waits for it and the call returns 0. Returns 0, -5 when no heading matches, -2 when the component is not created |
of_search ( string as_query ) → long | Highlights every occurrence (whatever the case by default — see ib_find_match_case —, as a whole word with ib_find_whole_word) and jumps to the first; an occurrence may cross a bold, a link or a line break of the source, never a hard line break. ue_search_result says how many (50,000 at most). An empty query is of_clear_search. While a file is being read, the search runs on it when it arrives. The search box opens with the query in it; with ib_search_enabled false, the document only highlights. Returns 0, -2 when the component is not created |
of_search_next ( ) → long | Moves to the NEXT occurrence (back to the first after the last); ue_search_result gives the new position. Returns 0, -2 when the component is not created |
of_search_prev ( ) → long | Moves to the PREVIOUS occurrence (back to the last before the first); ue_search_result gives the new position. Returns 0, -2 when the component is not created |
of_selected_text ( ) → string | Returns the text the user selected in the document, read LIVE — empty when nothing is selected |
of_toc ( ref string as_titles[], ref long al_levels[], ref string as_anchors[] ) → long | The table of contents, read LIVE: one entry per heading, in order — its text, its level (1 to 6) and its anchor (what of_scroll_to and a #anchor link take, GitHub style: “Release checklist” gives release-checklist). The arrays are emptied first. Returns the number of headings, 0 for a document without any — and while a file set by is_source is being read: ask for it in ue_loaded |
Events #
| Event | When |
|---|---|
ue_heading_visible (string as_anchor) | With ib_track_heading: another heading reached the top of the view. as_anchor is its anchor as of_toc gives it, empty above the first heading |
ue_link_clicked (string as_url, string as_kind) | The user followed a link (a click, or Enter on a focused link). The component does NOT navigate: open as_url where you want. as_kind says what it is — LINK_WEB, LINK_MAIL, LINK_DOCUMENT (a Markdown or text file this component shows), LINK_FILE (any other file: never run it without asking), LINK_OTHER. A relative link of a file shown by is_source arrives resolved against its folder; a #heading link scrolls by itself and is not raised; a javascript: or data: link is not even clickable |
ue_loaded (string as_source) | The document is on screen: as_source is is_source as you wrote it, or an empty string for a text set by is_markdown |
ue_load_failed (string as_source, string as_reason) | The document could not be shown: as_reason is one of the REASON_* constants — REASON_NOT_FOUND (no such file), REASON_TOO_LARGE (over 16 MB), REASON_INSECURE (http://), REASON_UNSUPPORTED (not a text file, or another scheme), REASON_FAILED (a read or network error, or a text that cannot be read). as_source is empty for a text set by is_markdown; the previous document stays on screen. ue_loaded is not raised |
ue_search_result (long al_count, long al_index) | A search ran or moved: al_count occurrences in all (50,000 at most), the current one is al_index (1-based, 0 when none) — the makings of “3 / 12” |
ue_task_toggled (long al_line, boolean ab_checked) | With ib_tasks_editable: the user ticked (ab_checked true) or unticked a box of a task list. al_line is the line of the Markdown source (1-based). The component does not rewrite the text: the application changes [ ] / [x] on that line and sets the text back (see the example) |
Example #
An online help that opens its links #
A document often comes from elsewhere: sort by the kind of the link, never by its text. A web page or a mail opens in its program, another document shows in the same component, and a file — an .exe, a .bat — is never run without asking.
// ue_link_clicked of uo_md : the viewer never navigates, the application decides by the kind
choose case as_kind
case u_pbt_markdownview.LINK_WEB, u_pbt_markdownview.LINK_MAIL
// A web page, a mail : their program
ShellExecuteW(0, "open", as_url, "", "", 1)
case u_pbt_markdownview.LINK_DOCUMENT
// Another page of the help : in this viewer
uo_md.is_source = as_url
case else
// A file of the disk or of the network : never run without asking
MessageBox("Link", "This link leads to a file : " + as_url)
end choose
Editing Markdown: a codeeditor and a markdownview #
The viewer keeps its reading position when the text changes: you edit the end of a long document and the preview does not jump to the top.
// Local variables
string ls_notes
// The notes of the customer, in Markdown
ls_notes = "# Notes~n~n- [ ] Call back on Monday"
// The editor colours Markdown ; the viewer shows the page
uo_editor.is_syntax = u_pbt_codeeditor.SYNTAX_MARKDOWN
uo_editor.is_text = ls_notes
uo_md.is_markdown = ls_notes
// ue_changed of uo_editor : the whole recipe
uo_md.is_markdown = uo_editor.is_text
A table of contents in a list #
For a file set by is_source, fill the list in ue_loaded: before it, the file is still being read.
// Local variables
string ls_titles[], ls_anchors[]
long ll_levels[], ll_i
// ue_loaded of uo_md : one line per heading, indented by its level
lb_toc.Reset()
for ll_i = 1 to uo_md.of_toc(/*titles*/ ls_titles, /*levels*/ ll_levels, /*anchors*/ ls_anchors)
lb_toc.AddItem(Fill(" ", (ll_levels[ll_i] - 1) * 3) + ls_titles[ll_i])
next
// Local variables
string ls_titles[], ls_anchors[]
long ll_levels[]
// SelectionChanged of lb_toc : bring that heading to the top
uo_md.of_toc(/*titles*/ ls_titles, /*levels*/ ll_levels, /*anchors*/ ls_anchors)
uo_md.of_scroll_to(/*anchor*/ ls_anchors[index])
A check-list the user ticks #
// Local variables
string ls_text
long ll_at, ll_n
// The user may tick the boxes of the text
uo_md.ib_tasks_editable = true
// ue_task_toggled of uo_md : the start of line al_line of the text
ls_text = uo_md.is_markdown
ll_at = 1
for ll_n = 2 to al_line
ll_at = Pos(ls_text, "~n", ll_at) + 1
next
// Its box, the first "[" of the line, becomes [x] or [ ]
ll_at = Pos(ls_text, "[", ll_at)
if ab_checked then
ls_text = Replace(ls_text, ll_at, 3, "[x]")
else
ls_text = Replace(ls_text, ll_at, 3, "[ ]")
end if
// The text set back : the view keeps its place
uo_md.is_markdown = ls_text
The component never rewrites the text itself: the application keeps control of what it saves.
Reopening a document where the reader left it #
// Before closing the window : remember the place in the guide
SetProfileString("acme.ini", "help", "position", uo_md.is_position)
// When the guide is shown again : the place is taken once the file is on screen
uo_md.is_source = "C:\Acme\Help\guide.md"
uo_md.is_position = ProfileString("acme.ini", "help", "position", "")
Good practice #
- It is a reader, not an editor: to write Markdown, put a
codeeditorinSYNTAX_MARKDOWNbeside it, linked by one line in its ue_changed (see the example); the viewer keeps its place when the text changes. - Links are yours: nothing opens by itself;
ue_link_clickedgives the address, already resolved against the folder of the file, and its kind (as_kind) — sort by it, never by the text of the address, and never run aLINK_FILEwithout asking. - A document runs nothing: raw HTML is displayed as written (or, with
ib_allow_html, only a short list of inert tags), ajavascript:link stays text, anhttp://picture is refused, and so is a picture on a network share (ib_network_pictures) — a document from elsewhere is shown safely. - Pictures: a relative path is read against the folder of the file (or
is_base_pathfor a text); for a picture from the Web, preferhttps://, or download it first (restclient.of_download) and give its path.ib_remote_picturesfalse loads none. - A file is read in the background: after
is_source, wait forue_loadedto read the table of contents; an anchor and a search asked meanwhile wait for the file. - A large document stays smooth: the first blocks appear at once, the rest in the following moments; the search (50,000 occurrences at most),
of_scroll_toand printing wait until it is whole. - Right to left: the document follows the direction of the application (lists, quotes and tables turn round), a code block stays left to right, and a column the author aligned (
:--,--:) keeps its side.
Inherited from the common base #
These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.
| Members | Role | Detailed in |
|---|---|---|
of_reset | Put the component back to zero | 3.6 Resetting a component: of_reset() |
of_register_shortcut · of_clear_shortcuts | The component's keyboard chords | 3.5 Keyboard shortcuts |
of_is_created · of_is_ready · of_get_last_error | Whether it was born, whether it is ready, what failed | 3.7 Diagnostics |
of_save_as_png · of_save_as_jpg | Export the rendering as an image | 3.8 Exporting the rendering as an image |
of_set_redraw | Group changes into a single repaint | 3.10 Best practices |
of_preload_icons | Icons shown with no delay | Instant display: of_icon |
of_set_translation | Translate one of the component's labels | 5.2 Adapting a label: of_set_translation |
of_focus_webview | Give the component the focus | 6.4 Keyboard and focus |
of_print · of_print_to_pdf | Print, or write a PDF | 6.9 Printing |
of_set_property · of_get_property · of_component_name | Driving a property by its name | 3.1 The property engine |
is_accessible_name · is_accessible_description | The name and description a screen reader says | 3.4 Optional properties and events |