summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-07-22 13:46:10 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-07-22 13:46:10 +0300
commite16f37f75399ef4dd2f267efbc720768489f96a1 (patch)
tree5bd78084384d8cf556b0416789c621249cfa094a
parentf2f64e2fbca93e81a7367efac1be635f7f00919c (diff)
GmDocument: Normalize spaces; apply line margins
-rw-r--r--src/gmdocument.c110
1 files changed, 96 insertions, 14 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 0a47c924..feff44e6 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -2,6 +2,7 @@
2#include "ui/color.h" 2#include "ui/color.h"
3#include "ui/text.h" 3#include "ui/text.h"
4#include "ui/metrics.h" 4#include "ui/metrics.h"
5
5#include <the_Foundation/array.h> 6#include <the_Foundation/array.h>
6 7
7struct Impl_GmDocument { 8struct Impl_GmDocument {
@@ -55,6 +56,17 @@ static void trimLine_Rangecc_(iRangecc *line, enum iGmLineType type) {
55 trim_Rangecc(line); 56 trim_Rangecc(line);
56} 57}
57 58
59static int lastVisibleRunBottom_GmDocument_(const iGmDocument *d) {
60 iReverseConstForEach(Array, i, &d->layout) {
61 const iGmRun *run = i.value;
62 if (isEmpty_Range(&run->text)) {
63 continue;
64 }
65 return bottom_Rect(run->bounds);
66 }
67 return 0;
68}
69
58static void doLayout_GmDocument_(iGmDocument *d) { 70static void doLayout_GmDocument_(iGmDocument *d) {
59 if (d->size.x <= 0 || isEmpty_String(&d->source)) { 71 if (d->size.x <= 0 || isEmpty_String(&d->source)) {
60 return; 72 return;
@@ -66,7 +78,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
66 iRangecc line = iNullRange; 78 iRangecc line = iNullRange;
67 static const int fonts[max_GmLineType] = { 79 static const int fonts[max_GmLineType] = {
68 paragraph_FontId, 80 paragraph_FontId,
69 paragraph_FontId, 81 paragraph_FontId, /* bullet */
70 preformatted_FontId, 82 preformatted_FontId,
71 quote_FontId, 83 quote_FontId,
72 header1_FontId, 84 header1_FontId,
@@ -76,15 +88,24 @@ static void doLayout_GmDocument_(iGmDocument *d) {
76 static const int indents[max_GmLineType] = { 88 static const int indents[max_GmLineType] = {
77 4, 10, 4, 10, 0, 0, 0 89 4, 10, 4, 10, 0, 0, 0
78 }; 90 };
91 static const float topMargin[max_GmLineType] = {
92 0.0f, 0.5f, 1.0f, 0.5f, 2.0f, 1.5f, 1.0f
93 };
94 static const float bottomMargin[max_GmLineType] = {
95 0.0f, 0.5f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f
96 };
79 static const char *bullet = "\u2022"; 97 static const char *bullet = "\u2022";
80 iRangecc preAltText = iNullRange; 98 iRangecc preAltText = iNullRange;
99 enum iGmLineType prevType = text_GmLineType;
81 while (nextSplit_Rangecc(&content, "\n", &line)) { 100 while (nextSplit_Rangecc(&content, "\n", &line)) {
82 int indent = 0;
83 iGmRun run; 101 iGmRun run;
84 run.color = white_ColorId; 102 run.color = white_ColorId;
85 run.linkId = 0; 103 run.linkId = 0;
104 enum iGmLineType type;
105 int indent = 0;
86 if (!isPreformat) { 106 if (!isPreformat) {
87 enum iGmLineType type = lineType_Rangecc_(&line); 107 type = lineType_Rangecc_(&line);
108 indent = indents[type];
88 if (type == preformatted_GmLineType) { 109 if (type == preformatted_GmLineType) {
89 isPreformat = iTrue; 110 isPreformat = iTrue;
90 trimLine_Rangecc_(&line, type); 111 trimLine_Rangecc_(&line, type);
@@ -94,30 +115,47 @@ static void doLayout_GmDocument_(iGmDocument *d) {
94 } 115 }
95 trimLine_Rangecc_(&line, type); 116 trimLine_Rangecc_(&line, type);
96 run.font = fonts[type]; 117 run.font = fonts[type];
97 indent = indents[type];
98 if (type == bullet_GmLineType) {
99 run.bounds.pos = addX_I2(pos, indent * gap_UI);
100 run.bounds.size = advance_Text(run.font, bullet);
101 run.bounds.pos.x -= 4 * gap_UI - run.bounds.size.x / 2;
102 run.text = (iRangecc){ bullet, bullet + strlen(bullet) };
103 pushBack_Array(&d->layout, &run);
104 }
105 } 118 }
106 else { 119 else {
120 type = preformatted_GmLineType;
107 if (startsWithSc_Rangecc(&line, "```", &iCaseSensitive)) { 121 if (startsWithSc_Rangecc(&line, "```", &iCaseSensitive)) {
108 isPreformat = iFalse; 122 isPreformat = iFalse;
109 preAltText = iNullRange; 123 preAltText = iNullRange;
110 continue; 124 continue;
111 } 125 }
112 run.font = preformatted_FontId; 126 run.font = preformatted_FontId;
113 indent = indents[preformatted_GmLineType]; 127 indent = indents[type];
128 }
129 /* Check the margin. */
130 if (isEmpty_Range(&line)) {
131 pos.y += lineHeight_Text(run.font);
132 prevType = text_GmLineType;
133 continue;
134 }
135 if (!isPreformat || (prevType != preformatted_GmLineType)) {
136 int required =
137 iMax(topMargin[type], bottomMargin[prevType]) * lineHeight_Text(paragraph_FontId);
138 if (isEmpty_Array(&d->layout)) {
139 required = 0; /* top of document */
140 }
141 int delta = pos.y - lastVisibleRunBottom_GmDocument_(d);
142 if (delta < required) {
143 pos.y += required - delta;
144 }
145 }
146 if (type == bullet_GmLineType) {
147 run.bounds.pos = addX_I2(pos, indent * gap_UI);
148 run.bounds.size = advance_Text(run.font, bullet);
149 run.bounds.pos.x -= 4 * gap_UI - run.bounds.size.x / 2;
150 run.text = (iRangecc){ bullet, bullet + strlen(bullet) };
151 pushBack_Array(&d->layout, &run);
114 } 152 }
115 run.text = line; 153 run.text = line;
116 run.bounds.pos = pos; 154 run.bounds.pos = addX_I2(pos, indent * gap_UI);
117 run.bounds.size = advanceRange_Text(run.font, line); 155 run.bounds.size = advanceRange_Text(run.font, line);
118 adjustEdges_Rect(&run.bounds, 0, 0, 0, indent * gap_UI);
119 pushBack_Array(&d->layout, &run); 156 pushBack_Array(&d->layout, &run);
120 pos.y += run.bounds.size.y; 157 pos.y += run.bounds.size.y;
158 prevType = type;
121 } 159 }
122 d->size.y = pos.y; 160 d->size.y = pos.y;
123} 161}
@@ -138,8 +176,52 @@ void setWidth_GmDocument(iGmDocument *d, int width) {
138 doLayout_GmDocument_(d); /* TODO: just flag need-layout and do it later */ 176 doLayout_GmDocument_(d); /* TODO: just flag need-layout and do it later */
139} 177}
140 178
179iLocalDef iBool isNormalizableSpace_(char ch) {
180 return ch == ' ' || ch == '\t';
181}
182
183static void normalize_GmDocument(iGmDocument *d) {
184 iString *normalized = new_String();
185 iRangecc src = range_String(&d->source);
186 iRangecc line = iNullRange;
187 iBool isPreformat = iFalse;
188 while (nextSplit_Rangecc(&src, "\n", &line)) {
189 if (isPreformat) {
190 appendRange_String(normalized, line);
191 appendCStr_String(normalized, "\n");
192 if (lineType_Rangecc_(&line) == preformatted_GmLineType) {
193 isPreformat = iFalse;
194 }
195 continue;
196 }
197 if (lineType_Rangecc_(&line) == preformatted_GmLineType) {
198 isPreformat = iTrue;
199 appendRange_String(normalized, line);
200 appendCStr_String(normalized, "\n");
201 continue;
202 }
203 iBool isPrevSpace = iFalse;
204 for (const char *ch = line.start; ch != line.end; ch++) {
205 if (isNormalizableSpace_(*ch)) {
206 if (isPrevSpace) {
207 continue;
208 }
209 isPrevSpace = iTrue;
210 }
211 else {
212 isPrevSpace = iFalse;
213 }
214 appendCStrN_String(normalized, ch, 1);
215 }
216 appendCStr_String(normalized, "\n");
217 }
218 set_String(&d->source, collect_String(normalized));
219 printf("normalized:\n%s\n", cstr_String(&d->source));
220}
221
141void setSource_GmDocument(iGmDocument *d, const iString *source, int width) { 222void setSource_GmDocument(iGmDocument *d, const iString *source, int width) {
142 set_String(&d->source, source); 223 set_String(&d->source, source);
224 normalize_GmDocument(d);
143 setWidth_GmDocument(d, width); 225 setWidth_GmDocument(d, width);
144 /* TODO: just flag need-layout and do it later */ 226 /* TODO: just flag need-layout and do it later */
145} 227}