Markup display for JUCE

Hi everyone,

I’ve released BarelyML, a JUCE-based markup display on GitHub, which is useful for displaying formatted text, tables, images, and links inside JUCE apps (for example for in-app documentation, hints, etc.). My goal was flexibility and good performance on mobile devices (scrolling tables, large areas for links, imports simple Markdown, DokuWiki and AsciiDoc content, etc.). Less than 1500 lines, under MIT license, and only dependent on JUCE.

The git repository contains also an interactive demo (PIP => just drag BarelyMLDemo.h onto a Projucer window and it will create the project for you), as a quick way to test the rendering of documents in various formats. The demo also serves as example code for the integration of BarelyML in JUCE applications.

Best regards,
Fritz

[Edited for clarity and to reflect the current state of the project]

21 Likes

Here’s a little test:

The markup document looks like this:

# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5

Here's some *bold text, _some bold italic text*, and some italic text_.

Now some <c:red>_*bold italic*_ red text</c>, and some <c#52F>arbitrarily colored *bold* text</c>.

-    An unordered list item
  -  And a subitem
  2.   and a numbered subitem
  3. one more item

And some non-Latin text:
<c:lightblue>שלום</c>, מה שלומך? (Hebrew)
<c:green>سلام</c>، حالت چطوره؟ (Persian)
*สวัสดีค่ะ* เป็นไงบ้าง (Thai).

^ Table Heading ^ Column 2     ^ Column 3 with a really long header ^
| Row 2         |              | |
|               | Row 3        | |
^ Also a header | Not a header | |

INFO: This is an info paragraph (blue tab).

HINT: This is a hint paragraph (green tab).

IMPORTANT: This is an important paragraph (red tab).

CAUTION: This is a caution paragraph (yellow tab).

WARNING: This is a warning paragraph (orange tab).

[[https://juce.com|JUCE Website]]

[[http://mnsp.ch|{{sunrise.jpeg?200}}]]

Thanks a ton for creating and sharing this!

2 Likes

You’re welcome. I’m happy if it is useful to other people, too!

Just as a side note: this is the very first release and there are almost certainly still bugs and missing features. So if you find any of those, please let me know (either here or via GitHub).

Best regards,
Fritz

[Update: there is an example project now, see BarelyMLDemo.h on GitHub]
As there’s no example project on GitHub (yet), here’s some instructions on how to use BarelyML:

  1. include BarelyML.h and declare a display Component:
    BarelyMLDisplay bmlDisplay;
  2. (optional, only needed if you want to display images):
    Declare one of your own classes to be a BarelyMLDisplay::FileSource, for example like this:
    class MainComponent : public juce::Component, BarelyMLDisplay::FileSource
    and override the method getImageForFilename:
    Image getImageForFilename(String filename) override;
    This method simply produces Image objects when given a filename. You could do anything in there, like programmatically painting your images. But quite likely you’d want to implement it something like this:
Image MainComponent::getImageForFilename(String filename) {
  Image image;
  int numBytes;
  const char* data = BinaryData::getNamedResource(filename.replaceCharacter('.', '_').toRawUTF8(),numBytes);
  if (data != nullptr) {
    image = ImageFileFormat::loadFrom(data, numBytes);
  }  else {
    File file = File(getSharedResourceFolder().getFullPathName() + "/" + filename);
    image = ImageFileFormat::loadFrom(file);
  }
  return image;
}

File MainComponent::getSharedResourceFolder()
{
  File bundle = File::getSpecialLocation (File::invokedExecutableFile).getParentDirectory();

  // macOS uses Contents/MacOS structrue, iOS bundle structure is flat
 #if JUCE_MAC
  bundle = bundle.getParentDirectory().getParentDirectory();
 #endif

  // appex is in a PlugIns folder inside the parent bundle
  if (SystemStats::isRunningInAppExtensionSandbox())
      bundle = bundle.getParentDirectory().getParentDirectory();

 #if JUCE_MAC
  bundle = bundle.getChildFile ("Contents/Resources");
 #endif

  return bundle;
}
  1. Finally, you need to initialize your BarelyMLDisplay object with the right parameters and the Markup String:
  addAndMakeVisible(&bmlDisplay);
  bmlDisplay.setFileSource(this);
  bmlDisplay.setMarkupString(CharPointer_UTF8(R"(# Heading 1

Here's some <c:red>_*bold italic*_ red text</c>,
and some <c#52F>arbitrarily colored *bold* text</c>.)"),
  Font("Helvetica Neue", 20.0f, Font::FontStyleFlags::plain));

Of course you’ll also need to properly set the bounds of the component.

Alternatively, if you have a simple Markdown String, you can directly read that using the setMarkdownString method:

bmlDisplay.setMarkdownString(CharPointer_UTF8(R"(# Header

### Here's an image

![This text will be ignored](sunrise.jpeg)

### And here's a table

| Table header    | Another one |
| --------------- | ----------- |
| row 2           |             |
|                 | row 3.      |)"),
Font("Helvetica Neue", 20.0f, Font::FontStyleFlags::plain));

1 Like

Very cool contribution - thanks so much for making this and for making it MIT license for others to use. I’ll definitely be integrating this into some of my JUCE apps shortly.

1 Like

FYI, I’ve recently updated BarelyML to version 0.2 on GitHub:

Here’s what’s new:

1 Like

I still needed some improvements, so I’ve released a second update today:

I’d also highly recommend to try out the interactive demo (available as a PIP together with the source code). It’s just the easiest way to try out what you can do with BarelyML.

3 Likes

Thank you for this, very nice to be able to use markdown in juce apps.

1 Like

You’re welcome. Anyone interested in support for Org-mode?