diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-22 13:46:10 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-22 13:46:10 +0300 |
commit | e16f37f75399ef4dd2f267efbc720768489f96a1 (patch) | |
tree | 5bd78084384d8cf556b0416789c621249cfa094a | |
parent | f2f64e2fbca93e81a7367efac1be635f7f00919c (diff) |
GmDocument: Normalize spaces; apply line margins
-rw-r--r-- | src/gmdocument.c | 110 |
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 | ||
7 | struct Impl_GmDocument { | 8 | struct 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 | ||
59 | static 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 | |||
58 | static void doLayout_GmDocument_(iGmDocument *d) { | 70 | static 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 | ||
179 | iLocalDef iBool isNormalizableSpace_(char ch) { | ||
180 | return ch == ' ' || ch == '\t'; | ||
181 | } | ||
182 | |||
183 | static 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 | |||
141 | void setSource_GmDocument(iGmDocument *d, const iString *source, int width) { | 222 | void 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 | } |