Working bold, italic, heading, paragraph, listing, ...!

master
Melroy van den Berg 2020-11-19 02:44:30 +01:00
parent a921cceb6f
commit f9860efb9b
8 changed files with 151 additions and 155 deletions

View File

@ -66,6 +66,7 @@
"variant": "cpp",
"bit": "cpp",
"qsizepolicy": "cpp",
"qgraphicsscene": "cpp"
"qgraphicsscene": "cpp",
"qstring": "cpp"
}
}

View File

@ -35,7 +35,7 @@ MainWindow::MainWindow()
scene = new Scene(this);
view = new QGraphicsView(scene);
//view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
view->setAlignment(Qt::Alignment::enum_type::AlignLeft);
view->setAlignment(Qt::Alignment::enum_type::AlignTop|Qt::Alignment::enum_type::AlignLeft);
layout->addWidget(view);
// We will not use TextEdit, it does only support HTML (and markdown, but we don't want to use the built-in parser).

View File

@ -58,14 +58,6 @@ std::string const Parser::renderHTML(cmark_node *node)
return output;
}
std::string const Parser::renderMyLayout(cmark_node *node)
{
char *tmp = renderLayout(node, options, 0, NULL);
std::string output = std::string(tmp);
free(tmp);
return output;
}
/**
* This is a function that will make enabling extensions easier
*/
@ -74,109 +66,3 @@ void Parser::addMarkdownExtension(cmark_parser *parser, const char *extName) {
if ( ext )
cmark_parser_attach_syntax_extension(parser, ext);
}
char *Parser::renderLayout(cmark_node *root, int options, int width, cmark_llist *extensions)
{
return cmark_render(cmark_node_mem(root), root, options, width, outc, renderNode);
}
int Parser::renderNode(cmark_renderer *renderer, cmark_node *node,
cmark_event_type ev_type, int options)
{
bool entering = (ev_type == CMARK_EVENT_ENTER);
switch (node->type) {
case CMARK_NODE_DOCUMENT:
printf("Document\n");
break;
case CMARK_NODE_BLOCK_QUOTE:
break;
case CMARK_NODE_LIST:
printf("List\n");
break;
case CMARK_NODE_ITEM:
printf("Item\n");
break;
case CMARK_NODE_HEADING:
printf("Heading\n");
break;
case CMARK_NODE_CODE_BLOCK:
break;
case CMARK_NODE_HTML_BLOCK:
break;
case CMARK_NODE_CUSTOM_BLOCK:
break;
case CMARK_NODE_THEMATIC_BREAK:
break;
case CMARK_NODE_PARAGRAPH:
printf("Paragraph\n");
break;
case CMARK_NODE_TEXT:
printf("Text\n");
// False = no wrap, we didn't specify a width
OUT(cmark_node_get_literal(node), false, NORMAL);
break;
case CMARK_NODE_LINEBREAK:
break;
case CMARK_NODE_SOFTBREAK:
break;
case CMARK_NODE_CODE:
break;
case CMARK_NODE_HTML_INLINE:
break;
case CMARK_NODE_CUSTOM_INLINE:
break;
case CMARK_NODE_STRONG:
printf("Bold\n");
if (entering) {
LIT("[b]");
} else {
LIT("[/b]");
}
break;
case CMARK_NODE_EMPH:
printf("Italic\n");
if (entering) {
LIT("_");
} else {
LIT("_");
}
break;
case CMARK_NODE_LINK:
break;
case CMARK_NODE_IMAGE:
break;
case CMARK_NODE_FOOTNOTE_REFERENCE:
break;
case CMARK_NODE_FOOTNOTE_DEFINITION:
break;
default:
assert(false);
break;
}
return 1;
}

View File

@ -14,14 +14,10 @@ public:
Parser();
cmark_node * parseFile(const std::string &filePath);
std::string const renderHTML(cmark_node *node);
std::string const renderMyLayout(cmark_node *node);
private:
int options;
void addMarkdownExtension(cmark_parser *parser, const char *extName);
char * renderLayout(cmark_node *root, int options, int width, cmark_llist *extensions);
static int renderNode(cmark_renderer *renderer, cmark_node *node,
cmark_event_type ev_type, int options);
};
#endif

View File

@ -15,10 +15,17 @@ Renderer::Renderer(Scene* scene) :
sceneMarginY(3.0),
bold(false),
italic(false),
headingLevel(0),
listLevel(0),
currentX(0.0),
currentY(0.0),
fontFamilty("Open Sans"),
wordSpacing(4.0), // spacing may depend on the font
heighestHigh(0.0),
paragraphOffsetHeight(5.0) {
paragraphHeightOffset(5.0),
headingHeightOffset(10.0),
listXOffset(15.0),
bulletWithTemp(0.0) {
}
/**
@ -44,7 +51,6 @@ void Renderer::renderNode(cmark_node *node, cmark_event_type ev_type)
switch (node->type) {
case CMARK_NODE_DOCUMENT:
printf("Document\n");
if (entering) {
currentX = sceneMarginX;
currentY = sceneMarginY;
@ -55,15 +61,56 @@ void Renderer::renderNode(cmark_node *node, cmark_event_type ev_type)
break;
case CMARK_NODE_LIST:
printf("List\n");
if (entering) {
listLevel++;
} else {
listLevel--;
if (listLevel < 0)
listLevel = 0;
}
if (listLevel == 0) {
// Reset X to be safe
currentX = sceneMarginX;
} else if (listLevel > 0) {
if (entering) {
currentX += listXOffset;
} else {
currentX -= listXOffset;
}
}
break;
case CMARK_NODE_ITEM:
printf("Item\n");
// Line break for list items
currentY += heighestHigh;
// Reset heighest high (Y-axis)
heighestHigh = 0;
// Add bullet before text items
if (entering) {
const QRectF rec = drawBullet();
bulletWithTemp = rec.width() + 2.0; // + offset
currentX += bulletWithTemp;
} else {
currentX -= bulletWithTemp;
}
break;
case CMARK_NODE_HEADING:
printf("Heading\n");
if (entering) {
headingLevel = node->as.heading.level;
} else {
headingLevel = 0; // reset
}
// Move to left again
currentX = sceneMarginX;
// New heading
currentY += heighestHigh + headingHeightOffset;
// Reset heighest high (Y-axis)
heighestHigh = 0;
break;
case CMARK_NODE_CODE_BLOCK:
@ -79,31 +126,46 @@ void Renderer::renderNode(cmark_node *node, cmark_event_type ev_type)
break;
case CMARK_NODE_PARAGRAPH:
printf("Paragraph\n");
// Move to left again
currentX = sceneMarginX;
// New paragraph
currentY += heighestHigh + paragraphOffsetHeight;
// Reset heighest high (Y-axis)
heighestHigh = 0;
printf("p\n");
// Skip paragraph if listing is enabled
if (listLevel == 0) {
// Move to left again
currentX = sceneMarginX;
// New paragraph
currentY += heighestHigh + paragraphHeightOffset;
// Reset heighest high (Y-axis)
heighestHigh = 0;
}
break;
case CMARK_NODE_TEXT: {
printf("Text\n");
const QRectF rec = drawText(cmark_node_get_literal(node), bold, italic);
currentX += rec.width();
printf("text\n");
const QRectF rec = drawText(cmark_node_get_literal(node));
// Skip paragraph if listing is enabled
if (listLevel == 0) {
currentX += rec.width();
}
if (rec.height() > heighestHigh)
heighestHigh = rec.height();
}
break;
case CMARK_NODE_LINEBREAK:
printf("Line break\n");
// Move to left again
currentX = sceneMarginX;
// Line break (no soft break)
currentY += heighestHigh;
// Reset heighest high (Y-axis)
heighestHigh = 0;
break;
case CMARK_NODE_SOFTBREAK:
printf("Soft-Line break\n");
// ignore
// Only insert a space between the words
currentX += wordSpacing;
break;
case CMARK_NODE_CODE:
@ -116,12 +178,10 @@ void Renderer::renderNode(cmark_node *node, cmark_event_type ev_type)
break;
case CMARK_NODE_STRONG:
printf("Bold\n");
bold = entering;
break;
case CMARK_NODE_EMPH:
printf("Italic\n");
italic = entering;
break;
@ -142,27 +202,55 @@ void Renderer::renderNode(cmark_node *node, cmark_event_type ev_type)
}
}
QRectF const Renderer::drawText(const std::string& text, bool bold, bool italic)
QRectF const Renderer::drawText(const QString& text)
{
// We can still extend the QGraphicsSimpleTextItem class (or QAbstractGraphicsShapeItem) and override paint method.
// Or just use QPainter with a paint device (like QWidgets), to have maximal control.
QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(QString::fromStdString(text));
QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(text);
QFont font;
if (bold)
font.setBold(true);
if (italic)
font.setItalic(true);
font.setFamily("Open Sans"); // Arial
if (headingLevel > 0) {
font.setBold(true);
switch(headingLevel) {
case 1:
font.setPixelSize(24);
break;
case 2:
font.setPixelSize(20);
break;
case 3:
font.setPixelSize(16);
break;
case 4:
font.setPixelSize(14);
break;
case 5:
font.setPixelSize(12);
break;
default:
break;
}
}
font.setFamily(fontFamilty);
textItem->setFont(font);
textItem->setPos(currentX, currentY);
scene->addItem(textItem);
if (false) {
// For debugging only
QGraphicsRectItem* box = new QGraphicsRectItem(textItem->boundingRect());
box->setPos(currentX, currentY);
scene->addItem(box);
}
return textItem->boundingRect();
}
QRectF const Renderer::drawBullet()
{
QGraphicsSimpleTextItem *bullet = new QGraphicsSimpleTextItem("\u2022");
QFont font;
font.setFamily(fontFamilty);
bullet->setFont(font);
bullet->setPos(currentX, currentY);
scene->addItem(bullet);
return bullet->boundingRect();
}

View File

@ -1,10 +1,10 @@
#ifndef MD_RENDER_H
#define MD_RENDER_H
#include <string>
#include <cmark-gfm.h>
#include <render.h>
#include <QtGlobal>
#include <QString>
class Scene;
class QRectF;
@ -24,12 +24,21 @@ private:
qreal sceneMarginY;
bool bold;
bool italic;
int headingLevel;
int listLevel;
qreal currentX;
qreal currentY;
QString fontFamilty;
qreal wordSpacing;
qreal heighestHigh;
qreal paragraphOffsetHeight;
qreal paragraphHeightOffset;
qreal headingHeightOffset;
qreal listXOffset;
qreal bulletWithTemp;
void renderNode(cmark_node *node, cmark_event_type ev_type);
QRectF const drawText(const std::string& text, bool bold = false, bool italic = false);
QRectF const drawText(const QString& text);
QRectF const drawBullet();
};
#endif

View File

@ -3,5 +3,5 @@
Scene::Scene(QObject *parent)
: QGraphicsScene(parent)
{
setSceneRect(QRectF(0, 0, 200, 180));
setSceneRect(QRectF(0, 0, 300, 300));
}

18
test.md
View File

@ -1,3 +1,19 @@
Hello *World*, What a **happy** day!
blabla.
test
New paragraph/line.
# Heading 1
## Heading 2
***New*** paragraph.
New line.
* list 1
* list 2
* list 3
* list 3.1
* list 3.2
* list 4
Normal text.