diff options
Diffstat (limited to 'src/ui/banner.c')
-rw-r--r-- | src/ui/banner.c | 120 |
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) | |||
32 | struct Impl_BannerItem { | 33 | struct 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 | ||
39 | static void init_BannerItem(iBannerItem *d) { | 40 | static 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 | ||
44 | static void deinit_BannerItem(iBannerItem *d) { | 45 | static 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 | ||
61 | static void updateHeight_Banner_(iBanner *d) { | 62 | static 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 | ||
68 | void init_Banner(iBanner *d) { | 80 | void 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 | ||
100 | static 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 | |||
88 | void setWidth_Banner(iBanner *d, int width) { | 107 | void 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 | ||
123 | size_t numItems_Banner(const iBanner *d) { | ||
124 | return size_Array(&d->items); | ||
125 | } | ||
126 | |||
101 | iBool contains_Banner(const iBanner *d, iInt2 coord) { | 127 | iBool 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 | ||
184 | void 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 | |||
146 | iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { | 231 | iBool 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 | |||
273 | void 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 | } | ||