summaryrefslogtreecommitdiff
path: root/src/ui/banner.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/banner.c')
-rw-r--r--src/ui/banner.c120
1 files changed, 90 insertions, 30 deletions
diff --git a/src/ui/banner.c b/src/ui/banner.c
index d1ed470c..d95c853b 100644
--- a/src/ui/banner.c
+++ b/src/ui/banner.c
@@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
24 24
25#include "command.h" 25#include "command.h"
26#include "documentwidget.h" 26#include "documentwidget.h"
27#include "lang.h"
27#include "paint.h" 28#include "paint.h"
28#include "util.h" 29#include "util.h"
29 30
@@ -32,17 +33,17 @@ iDeclareType(BannerItem)
32struct Impl_BannerItem { 33struct Impl_BannerItem {
33 enum iBannerType type; 34 enum iBannerType type;
34 enum iGmStatusCode code; 35 enum iGmStatusCode code;
35 iString message; 36 iString text; /* Entire message in presentation form. */
36 int height; 37 int height;
37}; 38};
38 39
39static void init_BannerItem(iBannerItem *d) { 40static void init_BannerItem(iBannerItem *d) {
40 init_String(&d->message); 41 init_String(&d->text);
41 d->height = 0; 42 d->height = 0;
42} 43}
43 44
44static void deinit_BannerItem(iBannerItem *d) { 45static void deinit_BannerItem(iBannerItem *d) {
45 deinit_String(&d->message); 46 deinit_String(&d->text);
46} 47}
47 48
48/*----------------------------------------------------------------------------------------------*/ 49/*----------------------------------------------------------------------------------------------*/
@@ -60,9 +61,20 @@ iDefineTypeConstruction(Banner)
60 61
61static void updateHeight_Banner_(iBanner *d) { 62static void updateHeight_Banner_(iBanner *d) {
62 d->rect.size.y = 0; 63 d->rect.size.y = 0;
63 if (!isEmpty_String(&d->site)) { //} || !isEmpty_String(&d->icon)) { 64 if (!isEmpty_String(&d->site)) {
64 d->rect.size.y += lineHeight_Text(banner_FontId) * 2; 65 d->rect.size.y += lineHeight_Text(banner_FontId) * 2;
65 } 66 }
67 const size_t numItems = size_Array(&d->items);
68 if (numItems) {
69 const int outerPad = 2 * gap_UI;
70 const int innerPad = gap_UI;
71 iConstForEach(Array, i, &d->items) {
72 const iBannerItem *item = i.value;
73 d->rect.size.y += item->height;
74 }
75 d->rect.size.y += (numItems - 1) * innerPad;
76 d->rect.size.y += outerPad;
77 }
66} 78}
67 79
68void init_Banner(iBanner *d) { 80void init_Banner(iBanner *d) {
@@ -85,8 +97,18 @@ void setOwner_Banner(iBanner *d, iDocumentWidget *owner) {
85 d->doc = owner; 97 d->doc = owner;
86} 98}
87 99
100static void updateItemHeight_Banner_(const iBanner *d, iBannerItem *item) {
101 item->height = measureWrapRange_Text(uiContent_FontId,
102 width_Rect(d->rect) - 6 * gap_UI,
103 range_String(&item->text))
104 .bounds.size.y + 4 * gap_UI;
105}
106
88void setWidth_Banner(iBanner *d, int width) { 107void setWidth_Banner(iBanner *d, int width) {
89 d->rect.size.x = width; 108 d->rect.size.x = width;
109 iForEach(Array, i, &d->items) {
110 updateItemHeight_Banner_(d, i.value);
111 }
90 updateHeight_Banner_(d); 112 updateHeight_Banner_(d);
91} 113}
92 114
@@ -98,6 +120,10 @@ int height_Banner(const iBanner *d) {
98 return d->rect.size.y; 120 return d->rect.size.y;
99} 121}
100 122
123size_t numItems_Banner(const iBanner *d) {
124 return size_Array(&d->items);
125}
126
101iBool contains_Banner(const iBanner *d, iInt2 coord) { 127iBool contains_Banner(const iBanner *d, iInt2 coord) {
102 return contains_Rect(d->rect, coord); 128 return contains_Rect(d->rect, coord);
103} 129}
@@ -127,7 +153,19 @@ void add_Banner(iBanner *d, enum iBannerType type, enum iGmStatusCode code, cons
127 init_BannerItem(&item); 153 init_BannerItem(&item);
128 item.type = type; 154 item.type = type;
129 item.code = code; 155 item.code = code;
130 set_String(&item.message, message); 156 const iGmError *error = get_GmError(code);
157 if (error->icon) {
158 appendCStr_String(&item.text, escape_Color(tmBannerIcon_ColorId));
159 appendChar_String(&item.text, error->icon);
160 appendCStr_String(&item.text, restore_ColorEscape);
161 }
162 appendFormat_String(&item.text, " \x1b[1m%s%s\x1b[0m \u2014 %s%s",
163 escape_Color(tmBannerItemTitle_ColorId),
164 !isEmpty_String(message) ? cstr_String(message) : error->title,
165 escape_Color(tmBannerItemText_ColorId),
166 error->info);
167 translate_Lang(&item.text);
168 updateItemHeight_Banner_(d, &item);
131 pushBack_Array(&d->items, &item); 169 pushBack_Array(&d->items, &item);
132 updateHeight_Banner_(d); 170 updateHeight_Banner_(d);
133} 171}
@@ -143,6 +181,53 @@ void remove_Banner(iBanner *d, enum iGmStatusCode code) {
143 updateHeight_Banner_(d); 181 updateHeight_Banner_(d);
144} 182}
145 183
184void draw_Banner(const iBanner *d) {
185 if (isEmpty_Banner(d)) {
186 return;
187 }
188 iRect bounds = d->rect;
189 iInt2 pos = addY_I2(topLeft_Rect(bounds), lineHeight_Text(banner_FontId) / 2);
190 iPaint p;
191 init_Paint(&p);
192// drawRect_Paint(&p, bounds, red_ColorId);
193 /* Draw the icon. */
194 if (!isEmpty_String(&d->icon)) {
195 const int font = banner_FontId;
196 const iRect iconRect = visualBounds_Text(font, range_String(&d->icon));
197 drawRange_Text(font,
198 addY_I2(pos, -mid_Rect(iconRect).y + lineHeight_Text(font) / 2),
199 tmBannerIcon_ColorId,
200 range_String(&d->icon));
201 pos.x += right_Rect(iconRect) + 3 * gap_Text;
202 }
203 /* Draw the site name. */
204 if (!isEmpty_String(&d->site)) {
205 drawRange_Text(banner_FontId, pos, tmBannerTitle_ColorId, range_String(&d->site));
206 pos.y += lineHeight_Text(banner_FontId) * 3 / 2;
207 }
208 else {
209 pos.y = top_Rect(bounds);
210 }
211 const int innerPad = gap_UI;
212 pos.x = left_Rect(bounds);
213 iConstForEach(Array, i, &d->items) {
214 const iBannerItem *item = i.value;
215 const iRect itemRect = { pos, init_I2(d->rect.size.x, item->height) };
216 fillRect_Paint(&p, itemRect, tmBannerItemBackground_ColorId);
217 drawRect_Paint(&p, itemRect, tmBannerItemFrame_ColorId);
218 setBaseAttributes_Text(uiContent_FontId, tmBannerItemText_ColorId);
219 iWrapText wt = {
220 .text = range_String(&item->text),
221 .maxWidth = width_Rect(itemRect) - 6 * gap_UI,
222 .mode = word_WrapTextMode
223 };
224 draw_WrapText(&wt, uiContent_FontId, add_I2(pos, init_I2(3 * gap_UI, 2 * gap_UI)),
225 tmBannerItemText_ColorId);
226 pos.y += innerPad;
227 }
228 setBaseAttributes_Text(-1, -1);
229}
230
146iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { 231iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) {
147 iWidget *w = as_Widget(d->doc); 232 iWidget *w = as_Widget(d->doc);
148 switch (ev->type) { 233 switch (ev->type) {
@@ -269,28 +354,3 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2
269 deinit_String(&str); 354 deinit_String(&str);
270} 355}
271#endif 356#endif
272
273void draw_Banner(const iBanner *d) {
274 if (isEmpty_Banner(d)) {
275 return;
276 }
277 iRect bounds = d->rect;
278 iInt2 pos = addY_I2(topLeft_Rect(bounds), lineHeight_Text(banner_FontId) / 2);
279 iPaint p;
280 init_Paint(&p);
281// drawRect_Paint(&p, bounds, red_ColorId);
282 /* Draw the icon. */
283 if (!isEmpty_String(&d->icon)) {
284 const int font = banner_FontId;
285 const iRect iconRect = visualBounds_Text(font, range_String(&d->icon));
286 drawRange_Text(font,
287 addY_I2(pos, -mid_Rect(iconRect).y + lineHeight_Text(font) / 2),
288 tmBannerIcon_ColorId,
289 range_String(&d->icon));
290 pos.x += right_Rect(iconRect) + 3 * gap_Text;
291 }
292 /* Draw the site name. */
293 if (!isEmpty_String(&d->site)) {
294 drawRange_Text(banner_FontId, pos, tmBannerTitle_ColorId, range_String(&d->site));
295 }
296}