imgui_md icon indicating copy to clipboard operation
imgui_md copied to clipboard

Added support for numeric character references and task lists

Open Aarkham opened this issue 1 year ago • 0 comments

Hello

Thanks for sharing your wonderful library. I have added a couple of features buy I won't make a pull request because the custom renderer I'm using make it incompatible with the standard font handling and also I realized too late that I have my text editor configured to change tabs to spaces.

However I'll post here the relevant code fragments in case you find them useful.

Here's how it looks:

image

And now the code.

For numeric character references:

#include <charconv>

// Adapted from RapidJSON (MIT licence)
static std::string UTF8Encoder(unsigned int aCodePoint)
{
  std::string res="";
  if (aCodePoint <= 0x79)
    {
      res.push_back(static_cast<char>(aCodePoint & 0xFF));
    }
  else if (aCodePoint <= 0x7FF)
    {
      res.push_back(static_cast<char>(0xC0 | ((aCodePoint >> 6) & 0xFF)));
      res.push_back(static_cast<char>(0x80 | ((aCodePoint & 0x3F))));
    }
  else if (aCodePoint <= 0xFFFF)
    {
      res.push_back(static_cast<char>(0xE0 | ((aCodePoint >> 12) & 0xFF)));
      res.push_back(static_cast<char>(0x80 | ((aCodePoint >> 6) & 0x3F)));
      res.push_back(static_cast<char>(0x80 | (aCodePoint & 0x3F)));
    }
  else
    {
      //ASSERT(codepoint <= 0x10FFFF);
      res.push_back(static_cast<char>(0xF0 | ((aCodePoint >> 18) & 0xFF)));
      res.push_back(static_cast<char>(0x80 | ((aCodePoint >> 12) & 0x3F)));
      res.push_back(static_cast<char>(0x80 | ((aCodePoint >> 6) & 0x3F)));
      res.push_back(static_cast<char>(0x80 | (aCodePoint & 0x3F)));
    }

  return res;
}


bool imgui_md::render_entity(const char* str, const char* str_end)
{
  const size_t sz = str_end - str;
  if (strncmp(str, "&nbsp;", sz) == 0) {
    ImGui::TextUnformatted(""); ImGui::SameLine();
    return true;
  }

  if(sz<4)
    {
      return false;
    }

  if (strncmp(str, "&#", 2) == 0 )
    {
      int base,offset;
      if(*(str+2)=='x' || *(str+2)=='X')
        {
          base=16;
          offset=3;
        }
      else
        {
          base=10;
          offset=2;
        }

    int code_point;
    auto [ptr, ec] = std::from_chars(str+offset, str_end, code_point,base);
    if (ec == std::errc())
      {
        if(code_point>= 0xD800 && code_point<=0xDBFF)
          {
            // TODO: Handle UTF-16 surrogate pair
            return false;
          }
        std::string utf8_char=UTF8Encoder(code_point);
        ImGui::TextUnformatted(utf8_char.c_str(),utf8_char.c_str()+utf8_char.size()); ImGui::SameLine(0.0f,0.0f);
        return true;
      }
  }
  return false;
}

For task list:

I have added a member variable to personalize indent.

float m_indent_size=40.0f;

And now the BLOCK_LI function becomes:

void imgui_md::BLOCK_LI(const MD_BLOCK_LI_DETAIL* aDetailBlockLI, bool e)
{
  if (e) {
    ImGui::NewLine();

    list_info& nfo = m_list_stack.back();
    if (nfo.is_ol) {
      ImGui::Text("%d%c", nfo.cur_ol++, nfo.delim);
      ImGui::SameLine();
    } else {
      if (nfo.delim == '*') {
        float cx = ImGui::GetCursorPosX();
        cx -= ImGui::GetStyle().FramePadding.x * 2;
        ImGui::SetCursorPosX(cx);
        ImGui::Bullet();
      } else {
        ImGui::Text("%c", nfo.delim);
        ImGui::SameLine();
      }
    }

    ImGui::Indent(m_indent_size);

    if(aDetailBlockLI->is_task)
      {
        bool v=(aDetailBlockLI->task_mark=='x' || aDetailBlockLI->task_mark=='X');
        ImGui::Checkbox("##task",&v);
        ImGui::SameLine(0.0f,m_indent_size*0.25f);
      }

  } else {
    ImGui::Unindent(m_indent_size);
  }
}

Hope you find it useful.

Aarkham avatar Mar 24 '23 16:03 Aarkham