253 const char* linkIcon;
258 bool isValid =
false;
259 bool useLinkCallback =
false;
260 ImTextureID user_texture_id = 0;
261 ImVec2 size = ImVec2( 100.0f, 100.0f );
262 ImVec2 uv0 = ImVec2( 0, 0 );
263 ImVec2 uv1 = ImVec2( 1, 1 );
264 ImVec4 tint_col = ImVec4( 1, 1, 1, 1 );
265 ImVec4 border_col = ImVec4( 0, 0, 0, 0 );
266 ImVec4 bg_col = ImVec4( 0, 0, 0, 0 );
269 enum class MarkdownFormatType
280 MarkdownFormatType type = MarkdownFormatType::NORMAL_TEXT;
282 bool itemHovered =
false;
284 const char* text = NULL;
285 int32_t textLength = 0;
293 if( data_.linkData.isImage )
295 ImGui::SetTooltip(
"%.*s", data_.linkData.linkLength, data_.linkData.link );
299 ImGui::SetTooltip(
"%s Open in browser\n%.*s", data_.linkIcon, data_.linkData.linkLength, data_.linkData.link );
304 typedef void MarkdownFormalCallback(
const MarkdownFormatInfo& markdownFormatInfo_,
bool start_ );
306 inline void defaultMarkdownFormatCallback(
const MarkdownFormatInfo& markdownFormatInfo_,
bool start_ );
312 #ifdef IMGUI_HAS_TEXTURES
313 float fontSize = 0.0f;
323 static const int NUMHEADINGS = 3;
325 MarkdownLinkCallback* linkCallback = NULL;
326 MarkdownTooltipCallback* tooltipCallback = NULL;
327 MarkdownImageCallback* imageCallback = NULL;
328 const char* linkIcon =
"";
329 MarkdownHeadingFormat headingFormats[ NUMHEADINGS ] = { { NULL,
true }, { NULL,
true }, { NULL,
true } };
330 void* userData = NULL;
331 MarkdownFormalCallback* formatCallback = defaultMarkdownFormatCallback;
338 inline void Markdown(
const char* markdown_,
size_t markdownLength_,
const MarkdownConfig& mdConfig_ );
346 inline void UnderLine( ImColor col_ );
351 TextRegion() : indentX( 0.0f )
361 void RenderTextWrapped(
const char* text_,
const char* text_end_,
bool bIndentToHere_ =
false )
363 #if IMGUI_VERSION_NUM >= 19197
364 float fontSize = ImGui::GetFontSize();
366 float scale = ImGui::GetIO().FontGlobalScale;
368 float widthLeft = GetContentRegionAvail().x;
369 #if IMGUI_VERSION_NUM >= 19197
370 const char* endLine = ImGui::GetFont()->CalcWordWrapPosition( fontSize, text_, text_end_, widthLeft );
372 const char* endLine = ImGui::GetFont()->CalcWordWrapPositionA( scale, text_, text_end_, widthLeft );
374 ImGui::TextUnformatted( text_, endLine );
377 float indentNeeded = GetContentRegionAvail().x - widthLeft;
380 ImGui::Indent( indentNeeded );
381 indentX += indentNeeded;
384 widthLeft = GetContentRegionAvail().x;
385 while( endLine < text_end_ )
388 if( *text_ ==
' ' ) { ++text_; }
389 #if IMGUI_VERSION_NUM >= 19197
390 endLine = ImGui::GetFont()->CalcWordWrapPosition( fontSize, text_, text_end_, widthLeft );
392 endLine = ImGui::GetFont()->CalcWordWrapPositionA( scale, text_, text_end_, widthLeft );
394 if( text_ == endLine )
398 ImGui::TextUnformatted( text_, endLine );
402 void RenderListTextWrapped(
const char* text_,
const char* text_end_ )
406 RenderTextWrapped( text_, text_end_,
true );
409 bool RenderLinkText(
const char* text_,
const char* text_end_,
const Link& link_,
410 const char* markdown_,
const MarkdownConfig& mdConfig_,
const char** linkHoverStart_ );
412 void RenderLinkTextWrapped(
const char* text_,
const char* text_end_,
const Link& link_,
413 const char* markdown_,
const MarkdownConfig& mdConfig_,
const char** linkHoverStart_,
bool bIndentToHere_ =
false );
419 ImGui::Unindent( indentX );
430 bool isHeading =
false;
431 bool isEmphasis =
false;
432 bool isUnorderedListStart =
false;
433 bool isLeadingSpace =
true;
434 int leadSpaceCount = 0;
435 int headingCount = 0;
436 int emphasisCount = 0;
439 int lastRenderPosition = 0;
454 HAS_SQUARE_BRACKET_OPEN,
456 HAS_SQUARE_BRACKETS_ROUND_BRACKET_OPEN,
458 LinkState state = NO_LINK;
461 bool isImage =
false;
462 int num_brackets_open = 0;
472 EmphasisState state = NONE;
477 inline void UnderLine( ImColor col_ )
479 ImVec2 min = ImGui::GetItemRectMin();
480 ImVec2 max = ImGui::GetItemRectMax();
482 ImGui::GetWindowDrawList()->AddLine( min, max, col_, 1.0f );
485 inline void RenderLine(
const char* markdown_, Line& line_, TextRegion& textRegion_,
const MarkdownConfig& mdConfig_ )
489 if( line_.isUnorderedListStart )
493 for(
int j = indentStart; j < line_.leadSpaceCount / 2; ++j )
500 formatInfo.config = &mdConfig_;
501 int textStart = line_.lastRenderPosition + 1;
502 int textSize = line_.lineEnd - textStart;
503 if( line_.isUnorderedListStart )
505 formatInfo.type = MarkdownFormatType::UNORDERED_LIST;
506 mdConfig_.formatCallback( formatInfo,
true );
507 const char* text = markdown_ + textStart + 1;
508 textRegion_.RenderListTextWrapped( text, text + textSize - 1 );
510 else if( line_.isHeading )
512 formatInfo.level = line_.headingCount;
513 formatInfo.type = MarkdownFormatType::HEADING;
514 const char* text = markdown_ + textStart + 1;
515 formatInfo.text = text;
516 formatInfo.textLength = textSize - 1;
517 mdConfig_.formatCallback( formatInfo,
true );
518 textRegion_.RenderTextWrapped( text, text + textSize - 1 );
520 else if( line_.isEmphasis )
522 formatInfo.level = line_.emphasisCount;
523 formatInfo.type = MarkdownFormatType::EMPHASIS;
524 mdConfig_.formatCallback(formatInfo,
true);
525 const char* text = markdown_ + textStart;
526 textRegion_.RenderTextWrapped(text, text + textSize);
530 formatInfo.type = MarkdownFormatType::NORMAL_TEXT;
531 mdConfig_.formatCallback( formatInfo,
true );
532 const char* text = markdown_ + textStart;
533 textRegion_.RenderTextWrapped( text, text + textSize );
535 mdConfig_.formatCallback( formatInfo,
false );
538 for(
int j = indentStart; j < line_.leadSpaceCount / 2; ++j )
545 inline void Markdown(
const char* markdown_,
size_t markdownLength_,
const MarkdownConfig& mdConfig_ )
547 static const char* linkHoverStart = NULL;
548 ImGuiStyle& style = ImGui::GetStyle();
555 for(
int i=0; i < (int)markdownLength_; ++i )
558 if( c == 0 ) {
break; }
561 if( line.isLeadingSpace )
565 ++line.leadSpaceCount;
570 line.isLeadingSpace =
false;
571 line.lastRenderPosition = i - 1;
572 if(( c ==
'*' ) && ( line.leadSpaceCount >= 2 ))
574 if( ( (
int)markdownLength_ > i + 1 ) && ( markdown_[ i + 1 ] ==
' ' ) )
576 line.isUnorderedListStart =
true;
578 ++line.lastRenderPosition;
585 bool bContinueChecking =
true;
587 while( ++j < (
int)markdownLength_ && bContinueChecking )
596 line.lastRenderPosition = j - 1;
598 line.isHeading =
true;
599 bContinueChecking =
false;
602 line.isHeading =
false;
603 bContinueChecking =
false;
621 if( c ==
'[' && !line.isHeading )
623 link.state = Link::HAS_SQUARE_BRACKET_OPEN;
624 link.text.start = i + 1;
625 if( i > 0 && markdown_[i - 1] ==
'!' )
631 case Link::HAS_SQUARE_BRACKET_OPEN:
634 link.state = Link::HAS_SQUARE_BRACKETS;
638 case Link::HAS_SQUARE_BRACKETS:
641 link.state = Link::HAS_SQUARE_BRACKETS_ROUND_BRACKET_OPEN;
642 link.url.start = i + 1;
643 link.num_brackets_open = 1;
646 case Link::HAS_SQUARE_BRACKETS_ROUND_BRACKET_OPEN:
649 ++link.num_brackets_open;
653 --link.num_brackets_open;
655 if( link.num_brackets_open == 0 )
660 line.lineEnd = link.text.start - ( link.isImage ? 2 : 1 );
661 RenderLine( markdown_, line, textRegion, mdConfig_ );
662 line.leadSpaceCount = 0;
664 line.isUnorderedListStart =
false;
665 ImGui::SameLine( 0.0f, 0.0f );
668 bool drawnImage =
false;
669 bool useLinkCallback =
false;
670 if( mdConfig_.imageCallback )
672 MarkdownImageData imageData = mdConfig_.imageCallback( { markdown_ + link.text.start, link.text.size(), markdown_ + link.url.start, link.url.size(), mdConfig_.userData,
true } );
673 useLinkCallback = imageData.useLinkCallback;
674 if( imageData.isValid )
676#if IMGUI_VERSION_NUM < 19185
677 if( imageData.bg_col.w > 0.0f )
679 ImVec2 p = ImGui::GetCursorScreenPos();
680 ImGui::GetWindowDrawList()->AddRectFilled( p, ImVec2( p.x + imageData.size.x, p.y + imageData.size.y ), ImGui::GetColorU32( imageData.bg_col ));
682 ImGui::Image( imageData.user_texture_id, imageData.size, imageData.uv0, imageData.uv1, imageData.tint_col, imageData.border_col );
684 ImGui::PushStyleColor( ImGuiCol_Border, imageData.border_col );
685 ImGui::ImageWithBg( imageData.user_texture_id, imageData.size, imageData.uv0, imageData.uv1, imageData.bg_col, imageData.tint_col );
686 ImGui::PopStyleColor();
693 ImGui::Text(
"( Image %.*s not loaded )", link.url.size(), markdown_ + link.url.start );
695 if( ImGui::IsItemHovered() )
697 if( ImGui::IsMouseReleased( 0 ) && mdConfig_.linkCallback && useLinkCallback )
699 mdConfig_.linkCallback( { markdown_ + link.text.start, link.text.size(), markdown_ + link.url.start, link.url.size(), mdConfig_.userData,
true } );
701 if( link.text.size() > 0 && mdConfig_.tooltipCallback )
703 mdConfig_.tooltipCallback( { { markdown_ + link.text.start, link.text.size(), markdown_ + link.url.start, link.url.size(), mdConfig_.userData,
true }, mdConfig_.linkIcon } );
709 textRegion.RenderLinkTextWrapped( markdown_ + link.text.start, markdown_ + link.text.start + link.text.size(), link, markdown_, mdConfig_, &linkHoverStart,
false );
711 ImGui::SameLine( 0.0f, 0.0f );
714 line.lastRenderPosition = i;
723 if( link.state == Link::NO_LINK && !line.isHeading )
727 if( ( c ==
'*' || c ==
'_' )
728 && ( i == line.lineStart
729 || markdown_[ prev ] ==
' '
730 || markdown_[ prev ] ==
'\t' )
731 && (
int)markdownLength_ > next
732 && markdown_[ next ] !=
' '
733 && markdown_[ next ] !=
'\n'
734 && markdown_[ next ] !=
'\t' )
736 em.state = Emphasis::LEFT;
739 line.emphasisCount = 1;
747 ++line.emphasisCount;
753 em.state = Emphasis::MIDDLE;
756 case Emphasis::MIDDLE:
759 em.state = Emphasis::RIGHT;
767 #if __cplusplus >= 201703L
770 case Emphasis::RIGHT:
773 if( line.emphasisCount < 3 && ( i - em.text.stop + 1 == line.emphasisCount ) )
776 int lineEnd = em.text.start - line.emphasisCount;
777 if( lineEnd > line.lineStart )
779 line.lineEnd = lineEnd;
780 RenderLine( markdown_, line, textRegion, mdConfig_ );
781 ImGui::SameLine( 0.0f, 0.0f );
782 line.isUnorderedListStart =
false;
783 line.leadSpaceCount = 0;
785 line.isEmphasis =
true;
786 line.lastRenderPosition = em.text.start - 1;
787 line.lineStart = em.text.start;
788 line.lineEnd = em.text.stop;
789 RenderLine( markdown_, line, textRegion, mdConfig_ );
790 ImGui::SameLine( 0.0f, 0.0f );
791 line.isEmphasis =
false;
792 line.lastRenderPosition = i;
799 em.state = Emphasis::NONE;
801 int start = em.text.start - line.emphasisCount;
802 if( start < line.lineStart )
804 line.lineEnd = line.lineStart;
805 line.lineStart = start;
806 line.lastRenderPosition = start - 1;
807 RenderLine(markdown_, line, textRegion, mdConfig_);
808 line.lineStart = line.lineEnd;
809 line.lastRenderPosition = line.lineStart - 1;
820 if( em.state == Emphasis::MIDDLE && line.emphasisCount >=3 &&
821 ( line.lineStart + line.emphasisCount ) == i )
828 RenderLine( markdown_, line, textRegion, mdConfig_ );
835 line.lineStart = i + 1;
836 line.lastRenderPosition = i;
838 textRegion.ResetIndent();
845 if( em.state == Emphasis::LEFT && line.emphasisCount >= 3 )
852 if( markdownLength_ && line.lineStart < (
int)markdownLength_ && markdown_[ line.lineStart ] != 0 )
855 line.lineEnd = (int)markdownLength_;
856 if( 0 == markdown_[ line.lineEnd - 1 ] )
860 RenderLine( markdown_, line, textRegion, mdConfig_ );
865 inline bool TextRegion::RenderLinkText(
const char* text_,
const char* text_end_,
const Link& link_,
866 const char* markdown_,
const MarkdownConfig& mdConfig_,
const char** linkHoverStart_ )
868 MarkdownFormatInfo formatInfo;
869 formatInfo.config = &mdConfig_;
870 formatInfo.type = MarkdownFormatType::LINK;
871 mdConfig_.formatCallback( formatInfo,
true );
872 ImGui::PushTextWrapPos( -1.0f );
873 ImGui::TextUnformatted( text_, text_end_ );
874 ImGui::PopTextWrapPos();
876 bool bThisItemHovered = ImGui::IsItemHovered();
879 *linkHoverStart_ = markdown_ + link_.text.start;
881 bool bHovered = bThisItemHovered || ( *linkHoverStart_ == ( markdown_ + link_.text.start ) );
883 formatInfo.itemHovered = bHovered;
884 mdConfig_.formatCallback( formatInfo,
false );
888 if( ImGui::IsMouseReleased( 0 ) && mdConfig_.linkCallback )
890 mdConfig_.linkCallback( { markdown_ + link_.text.start, link_.text.size(), markdown_ + link_.url.start, link_.url.size(), mdConfig_.userData,
false } );
892 if( mdConfig_.tooltipCallback )
894 mdConfig_.tooltipCallback( { { markdown_ + link_.text.start, link_.text.size(), markdown_ + link_.url.start, link_.url.size(), mdConfig_.userData,
false }, mdConfig_.linkIcon } );
897 return bThisItemHovered;
901 inline bool IsCharInsideWord(
char c_ )
903 return c_ !=
' ' && c_ !=
'.' && c_ !=
',' && c_ !=
';' && c_ !=
'!' && c_ !=
'?' && c_ !=
'\"';
906 inline void TextRegion::RenderLinkTextWrapped(
const char* text_,
const char* text_end_,
const Link& link_,
907 const char* markdown_,
const MarkdownConfig& mdConfig_,
const char** linkHoverStart_,
bool bIndentToHere_ )
909 #if IMGUI_VERSION_NUM >= 19197
910 float fontSize = ImGui::GetFontSize();
912 float scale = ImGui::GetIO().FontGlobalScale;
914 float widthLeft = GetContentRegionAvail().x;
915 const char* endLine = text_;
916 if( widthLeft > 0.0f )
918 #if IMGUI_VERSION_NUM >= 19197
919 endLine = ImGui::GetFont()->CalcWordWrapPosition( fontSize, text_, text_end_, widthLeft );
921 endLine = ImGui::GetFont()->CalcWordWrapPositionA( scale, text_, text_end_, widthLeft );
925 if( endLine > text_ && endLine < text_end_ )
927 if( IsCharInsideWord( *endLine ) )
930 float widthNextLine = widthLeft + GetCursorScreenPos().x - GetWindowPos().x;
931 #if IMGUI_VERSION_NUM >= 19197
932 const char* endNextLine = ImGui::GetFont()->CalcWordWrapPosition( fontSize, text_, text_end_, widthNextLine );
934 const char* endNextLine = ImGui::GetFont()->CalcWordWrapPositionA( scale, text_, text_end_, widthLeft );
936 if( endNextLine == text_end_ || ( endNextLine <= text_end_ && !IsCharInsideWord( *endNextLine ) ) )
943 bool bHovered = RenderLinkText( text_, endLine, link_, markdown_, mdConfig_, linkHoverStart_ );
946 float indentNeeded = GetContentRegionAvail().x - widthLeft;
949 ImGui::Indent( indentNeeded );
950 indentX += indentNeeded;
953 widthLeft = GetContentRegionAvail().x;
954 while( endLine < text_end_ )
957 if( *text_ ==
' ' ) { ++text_; }
958 #if IMGUI_VERSION_NUM >= 19197
959 endLine = ImGui::GetFont()->CalcWordWrapPosition( fontSize, text_, text_end_, widthLeft );
961 endLine = ImGui::GetFont()->CalcWordWrapPositionA( scale, text_, text_end_, widthLeft );
963 if( text_ == endLine )
967 bool bThisLineHovered = RenderLinkText( text_, endLine, link_, markdown_, mdConfig_, linkHoverStart_ );
968 bHovered = bHovered || bThisLineHovered;
970 if( !bHovered && *linkHoverStart_ == markdown_ + link_.text.start )
972 *linkHoverStart_ = NULL;
977 inline void defaultMarkdownFormatCallback(
const MarkdownFormatInfo& markdownFormatInfo_,
bool start_ )
979 switch( markdownFormatInfo_.type )
981 case MarkdownFormatType::NORMAL_TEXT:
983 case MarkdownFormatType::EMPHASIS:
988 if( markdownFormatInfo_.level == 1 )
993 ImGui::PushStyleColor( ImGuiCol_Text, ImGui::GetStyle().Colors[ ImGuiCol_TextDisabled ] );
997 ImGui::PopStyleColor();
1003 fmt = markdownFormatInfo_.config->headingFormats[ MarkdownConfig::NUMHEADINGS - 1 ];
1008 #ifdef IMGUI_HAS_TEXTURES
1009 ImGui::PushFont( fmt.font, 0.0f );
1011 ImGui::PushFont( fmt.font );
1025 case MarkdownFormatType::HEADING:
1028 if( markdownFormatInfo_.level > MarkdownConfig::NUMHEADINGS )
1030 fmt = markdownFormatInfo_.config->headingFormats[ MarkdownConfig::NUMHEADINGS - 1 ];
1034 fmt = markdownFormatInfo_.config->headingFormats[ markdownFormatInfo_.level - 1 ];
1040 #ifdef IMGUI_HAS_TEXTURES
1041 ImGui::PushFont( fmt.font, fmt.fontSize > 0.0f ? fmt.fontSize : fmt.font->LegacySize );
1043 ImGui::PushFont( fmt.font );
1066 case MarkdownFormatType::UNORDERED_LIST:
1068 case MarkdownFormatType::LINK:
1071 ImGui::PushStyleColor( ImGuiCol_Text, ImGui::GetStyle().Colors[ ImGuiCol_ButtonHovered ] );
1075 ImGui::PopStyleColor();
1076 if( markdownFormatInfo_.itemHovered )
1078 ImGui::UnderLine( ImGui::GetStyle().Colors[ ImGuiCol_ButtonHovered ] );
1082 ImGui::UnderLine( ImGui::GetStyle().Colors[ ImGuiCol_Button ] );
Definition imgui_markdown.h:465
Definition imgui_markdown.h:429
Definition imgui_markdown.h:451
Definition imgui_markdown.h:322
Definition imgui_markdown.h:257
Definition imgui_markdown.h:241
Definition imgui_markdown.h:442
Definition imgui_markdown.h:350