Working bold, italic, heading, paragraph, listing, ...!
parent
a921cceb6f
commit
f9860efb9b
|
@ -66,6 +66,7 @@
|
|||
"variant": "cpp",
|
||||
"bit": "cpp",
|
||||
"qsizepolicy": "cpp",
|
||||
"qgraphicsscene": "cpp"
|
||||
"qgraphicsscene": "cpp",
|
||||
"qstring": "cpp"
|
||||
}
|
||||
}
|
|
@ -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).
|
||||
|
|
114
src/md-parser.cc
114
src/md-parser.cc
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
148
src/md-render.cc
148
src/md-render.cc
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
Scene::Scene(QObject *parent)
|
||||
: QGraphicsScene(parent)
|
||||
{
|
||||
setSceneRect(QRectF(0, 0, 200, 180));
|
||||
setSceneRect(QRectF(0, 0, 300, 300));
|
||||
}
|
Loading…
Reference in New Issue