]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/guiEditBoxWithScrollbar.cpp
FormSpec: Add list spacing, slot size, and noclip (#10083)
[dragonfireclient.git] / src / gui / guiEditBoxWithScrollbar.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // Modified by Mustapha T.
3 // This file is part of the "Irrlicht Engine".
4 // For conditions of distribution and use, see copyright notice in irrlicht.h
5
6 #include "guiEditBoxWithScrollbar.h"
7
8 #include "IGUISkin.h"
9 #include "IGUIEnvironment.h"
10 #include "IGUIFont.h"
11 #include "IVideoDriver.h"
12 #include "rect.h"
13 #include "porting.h"
14 #include "Keycodes.h"
15
16 /*
17 todo:
18 optional scrollbars [done]
19 ctrl+left/right to select word
20 double click/ctrl click: word select + drag to select whole words, triple click to select line
21 optional? dragging selected text
22 numerical
23 */
24
25 //! constructor
26 GUIEditBoxWithScrollBar::GUIEditBoxWithScrollBar(const wchar_t* text, bool border,
27         IGUIEnvironment* environment, IGUIElement* parent, s32 id,
28         const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar)
29         : GUIEditBox(environment, parent, id, rectangle, border, writable),
30         m_background(true), m_bg_color_used(false)
31 {
32 #ifdef _DEBUG
33         setDebugName("GUIEditBoxWithScrollBar");
34 #endif
35
36
37         Text = text;
38
39         if (Environment)
40                 m_operator = Environment->getOSOperator();
41
42         if (m_operator)
43                 m_operator->grab();
44
45         // this element can be tabbed to
46         setTabStop(true);
47         setTabOrder(-1);
48
49         if (has_vscrollbar) {
50                 createVScrollBar();
51         }
52
53         calculateFrameRect();
54         breakText();
55
56         calculateScrollPos();
57         setWritable(writable);
58 }
59
60 //! Sets whether to draw the background
61 void GUIEditBoxWithScrollBar::setDrawBackground(bool draw)
62 {
63         m_background = draw;
64 }
65
66
67 void GUIEditBoxWithScrollBar::updateAbsolutePosition()
68 {
69         core::rect<s32> old_absolute_rect(AbsoluteRect);
70         IGUIElement::updateAbsolutePosition();
71         if (old_absolute_rect != AbsoluteRect) {
72                 calculateFrameRect();
73                 breakText();
74                 calculateScrollPos();
75         }
76 }
77
78
79 //! draws the element and its children
80 void GUIEditBoxWithScrollBar::draw()
81 {
82         if (!IsVisible)
83                 return;
84
85         const bool focus = Environment->hasFocus(this);
86
87         IGUISkin* skin = Environment->getSkin();
88         if (!skin)
89                 return;
90
91         video::SColor default_bg_color;
92         video::SColor bg_color;
93
94         default_bg_color = m_writable ? skin->getColor(EGDC_WINDOW) : video::SColor(0);
95         bg_color = m_bg_color_used ? m_bg_color : default_bg_color;
96
97         if (!m_border && m_background) {
98                 skin->draw2DRectangle(this, bg_color, AbsoluteRect, &AbsoluteClippingRect);
99         }
100
101         // draw the border
102
103         if (m_border) {
104
105                 if (m_writable) {
106                         skin->draw3DSunkenPane(this, bg_color, false, m_background,
107                                 AbsoluteRect, &AbsoluteClippingRect);
108                 }
109
110                 calculateFrameRect();
111         }
112
113         core::rect<s32> local_clip_rect = m_frame_rect;
114         local_clip_rect.clipAgainst(AbsoluteClippingRect);
115
116         // draw the text
117
118         IGUIFont* font = getActiveFont();
119
120         s32 cursor_line = 0;
121         s32 charcursorpos = 0;
122
123         if (font) {
124                 if (m_last_break_font != font) {
125                         breakText();
126                 }
127
128                 // calculate cursor pos
129
130                 core::stringw *txt_line = &Text;
131                 s32 start_pos = 0;
132
133                 core::stringw s, s2;
134
135                 // get mark position
136                 const bool ml = (!m_passwordbox && (m_word_wrap || m_multiline));
137                 const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
138                 const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
139                 const s32 hline_start = ml ? getLineFromPos(realmbgn) : 0;
140                 const s32 hline_count = ml ? getLineFromPos(realmend) - hline_start + 1 : 1;
141                 const s32 line_count = ml ? m_broken_text.size() : 1;
142
143                 // Save the override color information.
144                 // Then, alter it if the edit box is disabled.
145                 const bool prevOver = m_override_color_enabled;
146                 const video::SColor prevColor = m_override_color;
147
148                 if (Text.size()) {
149                         if (!isEnabled() && !m_override_color_enabled) {
150                                 m_override_color_enabled = true;
151                                 m_override_color = skin->getColor(EGDC_GRAY_TEXT);
152                         }
153
154                         for (s32 i = 0; i < line_count; ++i) {
155                                 setTextRect(i);
156
157                                 // clipping test - don't draw anything outside the visible area
158                                 core::rect<s32> c = local_clip_rect;
159                                 c.clipAgainst(m_current_text_rect);
160                                 if (!c.isValid())
161                                         continue;
162
163                                 // get current line
164                                 if (m_passwordbox) {
165                                         if (m_broken_text.size() != 1) {
166                                                 m_broken_text.clear();
167                                                 m_broken_text.emplace_back();
168                                         }
169                                         if (m_broken_text[0].size() != Text.size()){
170                                                 m_broken_text[0] = Text;
171                                                 for (u32 q = 0; q < Text.size(); ++q)
172                                                 {
173                                                         m_broken_text[0][q] = m_passwordchar;
174                                                 }
175                                         }
176                                         txt_line = &m_broken_text[0];
177                                         start_pos = 0;
178                                 } else {
179                                         txt_line = ml ? &m_broken_text[i] : &Text;
180                                         start_pos = ml ? m_broken_text_positions[i] : 0;
181                                 }
182
183
184                                 // draw normal text
185                                 font->draw(txt_line->c_str(), m_current_text_rect,
186                                         m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT),
187                                         false, true, &local_clip_rect);
188
189                                 // draw mark and marked text
190                                 if (focus && m_mark_begin != m_mark_end && i >= hline_start && i < hline_start + hline_count) {
191
192                                         s32 mbegin = 0, mend = 0;
193                                         s32 lineStartPos = 0, lineEndPos = txt_line->size();
194
195                                         if (i == hline_start) {
196                                                 // highlight start is on this line
197                                                 s = txt_line->subString(0, realmbgn - start_pos);
198                                                 mbegin = font->getDimension(s.c_str()).Width;
199
200                                                 // deal with kerning
201                                                 mbegin += font->getKerningWidth(
202                                                         &((*txt_line)[realmbgn - start_pos]),
203                                                         realmbgn - start_pos > 0 ? &((*txt_line)[realmbgn - start_pos - 1]) : 0);
204
205                                                 lineStartPos = realmbgn - start_pos;
206                                         }
207                                         if (i == hline_start + hline_count - 1) {
208                                                 // highlight end is on this line
209                                                 s2 = txt_line->subString(0, realmend - start_pos);
210                                                 mend = font->getDimension(s2.c_str()).Width;
211                                                 lineEndPos = (s32)s2.size();
212                                         } else {
213                                                 mend = font->getDimension(txt_line->c_str()).Width;
214                                         }
215
216
217                                         m_current_text_rect.UpperLeftCorner.X += mbegin;
218                                         m_current_text_rect.LowerRightCorner.X = m_current_text_rect.UpperLeftCorner.X + mend - mbegin;
219
220
221                                         // draw mark
222                                         skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), m_current_text_rect, &local_clip_rect);
223
224                                         // draw marked text
225                                         s = txt_line->subString(lineStartPos, lineEndPos - lineStartPos);
226
227                                         if (s.size())
228                                                 font->draw(s.c_str(), m_current_text_rect,
229                                                         m_override_color_enabled ? m_override_color : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
230                                                         false, true, &local_clip_rect);
231
232                                 }
233                         }
234
235                         // Return the override color information to its previous settings.
236                         m_override_color_enabled = prevOver;
237                         m_override_color = prevColor;
238                 }
239
240                 // draw cursor
241                 if (IsEnabled && m_writable) {
242                         if (m_word_wrap || m_multiline) {
243                                 cursor_line = getLineFromPos(m_cursor_pos);
244                                 txt_line = &m_broken_text[cursor_line];
245                                 start_pos = m_broken_text_positions[cursor_line];
246                         }
247                         s = txt_line->subString(0, m_cursor_pos - start_pos);
248                         charcursorpos = font->getDimension(s.c_str()).Width +
249                                 font->getKerningWidth(L"_", m_cursor_pos - start_pos > 0 ? &((*txt_line)[m_cursor_pos - start_pos - 1]) : 0);
250
251                         if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) {
252                                 setTextRect(cursor_line);
253                                 m_current_text_rect.UpperLeftCorner.X += charcursorpos;
254
255                                 font->draw(L"_", m_current_text_rect,
256                                         m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT),
257                                         false, true, &local_clip_rect);
258                         }
259                 }
260         }
261
262         // draw children
263         IGUIElement::draw();
264 }
265
266
267 s32 GUIEditBoxWithScrollBar::getCursorPos(s32 x, s32 y)
268 {
269         IGUIFont* font = getActiveFont();
270
271         const u32 line_count = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
272
273         core::stringw *txt_line = 0;
274         s32 start_pos = 0;
275         x += 3;
276
277         for (u32 i = 0; i < line_count; ++i) {
278                 setTextRect(i);
279                 if (i == 0 && y < m_current_text_rect.UpperLeftCorner.Y)
280                         y = m_current_text_rect.UpperLeftCorner.Y;
281                 if (i == line_count - 1 && y > m_current_text_rect.LowerRightCorner.Y)
282                         y = m_current_text_rect.LowerRightCorner.Y;
283
284                 // is it inside this region?
285                 if (y >= m_current_text_rect.UpperLeftCorner.Y && y <= m_current_text_rect.LowerRightCorner.Y) {
286                         // we've found the clicked line
287                         txt_line = (m_word_wrap || m_multiline) ? &m_broken_text[i] : &Text;
288                         start_pos = (m_word_wrap || m_multiline) ? m_broken_text_positions[i] : 0;
289                         break;
290                 }
291         }
292
293         if (x < m_current_text_rect.UpperLeftCorner.X)
294                 x = m_current_text_rect.UpperLeftCorner.X;
295
296         if (!txt_line)
297                 return 0;
298
299         s32 idx = font->getCharacterFromPos(txt_line->c_str(), x - m_current_text_rect.UpperLeftCorner.X);
300
301         // click was on or left of the line
302         if (idx != -1)
303                 return idx + start_pos;
304
305         // click was off the right edge of the line, go to end.
306         return txt_line->size() + start_pos;
307 }
308
309
310 //! Breaks the single text line.
311 void GUIEditBoxWithScrollBar::breakText()
312 {
313         if ((!m_word_wrap && !m_multiline))
314                 return;
315
316         m_broken_text.clear(); // need to reallocate :/
317         m_broken_text_positions.clear();
318
319         IGUIFont* font = getActiveFont();
320         if (!font)
321                 return;
322
323         m_last_break_font = font;
324
325         core::stringw line;
326         core::stringw word;
327         core::stringw whitespace;
328         s32 last_line_start = 0;
329         s32 size = Text.size();
330         s32 length = 0;
331         s32 el_width = RelativeRect.getWidth() - m_scrollbar_width - 10;
332         wchar_t c;
333
334         for (s32 i = 0; i < size; ++i) {
335                 c = Text[i];
336                 bool line_break = false;
337
338                 if (c == L'\r') { // Mac or Windows breaks
339
340                         line_break = true;
341                         c = 0;
342                         if (Text[i + 1] == L'\n') { // Windows breaks
343                                 // TODO: I (Michael) think that we shouldn't change the text given by the user for whatever reason.
344                                 // Instead rework the cursor positioning to be able to handle this (but not in stable release
345                                 // branch as users might already expect this behavior).
346                                 Text.erase(i + 1);
347                                 --size;
348                                 if (m_cursor_pos > i)
349                                         --m_cursor_pos;
350                         }
351                 } else if (c == L'\n') { // Unix breaks
352                         line_break = true;
353                         c = 0;
354                 }
355
356                 // don't break if we're not a multi-line edit box
357                 if (!m_multiline)
358                         line_break = false;
359
360                 if (c == L' ' || c == 0 || i == (size - 1)) {
361                         // here comes the next whitespace, look if
362                         // we can break the last word to the next line
363                         // We also break whitespace, otherwise cursor would vanish beside the right border.
364                         s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
365                         s32 worldlgth = font->getDimension(word.c_str()).Width;
366
367                         if (m_word_wrap && length + worldlgth + whitelgth > el_width && line.size() > 0) {
368                                 // break to next line
369                                 length = worldlgth;
370                                 m_broken_text.push_back(line);
371                                 m_broken_text_positions.push_back(last_line_start);
372                                 last_line_start = i - (s32)word.size();
373                                 line = word;
374                         } else {
375                                 // add word to line
376                                 line += whitespace;
377                                 line += word;
378                                 length += whitelgth + worldlgth;
379                         }
380
381                         word = L"";
382                         whitespace = L"";
383
384
385                         if (c)
386                                 whitespace += c;
387
388                         // compute line break
389                         if (line_break) {
390                                 line += whitespace;
391                                 line += word;
392                                 m_broken_text.push_back(line);
393                                 m_broken_text_positions.push_back(last_line_start);
394                                 last_line_start = i + 1;
395                                 line = L"";
396                                 word = L"";
397                                 whitespace = L"";
398                                 length = 0;
399                         }
400                 } else {
401                         // yippee this is a word..
402                         word += c;
403                 }
404         }
405
406         line += whitespace;
407         line += word;
408         m_broken_text.push_back(line);
409         m_broken_text_positions.push_back(last_line_start);
410 }
411
412 // TODO: that function does interpret VAlign according to line-index (indexed
413 // line is placed on top-center-bottom) but HAlign according to line-width
414 // (pixels) and not by row.
415 // Intuitively I suppose HAlign handling is better as VScrollPos should handle
416 // the line-scrolling.
417 // But please no one change this without also rewriting (and this time
418 // testing!!!) autoscrolling (I noticed this when fixing the old autoscrolling).
419 void GUIEditBoxWithScrollBar::setTextRect(s32 line)
420 {
421         if (line < 0)
422                 return;
423
424         IGUIFont* font = getActiveFont();
425         if (!font)
426                 return;
427
428         core::dimension2du d;
429
430         // get text dimension
431         const u32 line_count = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
432         if (m_word_wrap || m_multiline) {
433                 d = font->getDimension(m_broken_text[line].c_str());
434         } else {
435                 d = font->getDimension(Text.c_str());
436                 d.Height = AbsoluteRect.getHeight();
437         }
438         d.Height += font->getKerningHeight();
439
440         // justification
441         switch (m_halign) {
442         case EGUIA_CENTER:
443                 // align to h centre
444                 m_current_text_rect.UpperLeftCorner.X = (m_frame_rect.getWidth() / 2) - (d.Width / 2);
445                 m_current_text_rect.LowerRightCorner.X = (m_frame_rect.getWidth() / 2) + (d.Width / 2);
446                 break;
447         case EGUIA_LOWERRIGHT:
448                 // align to right edge
449                 m_current_text_rect.UpperLeftCorner.X = m_frame_rect.getWidth() - d.Width;
450                 m_current_text_rect.LowerRightCorner.X = m_frame_rect.getWidth();
451                 break;
452         default:
453                 // align to left edge
454                 m_current_text_rect.UpperLeftCorner.X = 0;
455                 m_current_text_rect.LowerRightCorner.X = d.Width;
456
457         }
458
459         switch (m_valign) {
460         case EGUIA_CENTER:
461                 // align to v centre
462                 m_current_text_rect.UpperLeftCorner.Y =
463                         (m_frame_rect.getHeight() / 2) - (line_count*d.Height) / 2 + d.Height*line;
464                 break;
465         case EGUIA_LOWERRIGHT:
466                 // align to bottom edge
467                 m_current_text_rect.UpperLeftCorner.Y =
468                         m_frame_rect.getHeight() - line_count*d.Height + d.Height*line;
469                 break;
470         default:
471                 // align to top edge
472                 m_current_text_rect.UpperLeftCorner.Y = d.Height*line;
473                 break;
474         }
475
476         m_current_text_rect.UpperLeftCorner.X -= m_hscroll_pos;
477         m_current_text_rect.LowerRightCorner.X -= m_hscroll_pos;
478         m_current_text_rect.UpperLeftCorner.Y -= m_vscroll_pos;
479         m_current_text_rect.LowerRightCorner.Y = m_current_text_rect.UpperLeftCorner.Y + d.Height;
480
481         m_current_text_rect += m_frame_rect.UpperLeftCorner;
482 }
483
484 // calculate autoscroll
485 void GUIEditBoxWithScrollBar::calculateScrollPos()
486 {
487         if (!m_autoscroll)
488                 return;
489
490         IGUISkin* skin = Environment->getSkin();
491         if (!skin)
492                 return;
493         IGUIFont* font = m_override_font ? m_override_font : skin->getFont();
494         if (!font)
495                 return;
496
497         s32 curs_line = getLineFromPos(m_cursor_pos);
498         if (curs_line < 0)
499                 return;
500         setTextRect(curs_line);
501         const bool has_broken_text = m_multiline || m_word_wrap;
502
503         // Check horizonal scrolling
504         // NOTE: Calculations different to vertical scrolling because setTextRect interprets VAlign relative to line but HAlign not relative to row
505         {
506                 // get cursor position
507                 IGUIFont* font = getActiveFont();
508                 if (!font)
509                         return;
510
511                 // get cursor area
512                 irr::u32 cursor_width = font->getDimension(L"_").Width;
513                 core::stringw *txt_line = has_broken_text ? &m_broken_text[curs_line] : &Text;
514                 s32 cpos = has_broken_text ? m_cursor_pos - m_broken_text_positions[curs_line] : m_cursor_pos;  // column
515                 s32 cstart = font->getDimension(txt_line->subString(0, cpos).c_str()).Width;            // pixels from text-start
516                 s32 cend = cstart + cursor_width;
517                 s32 txt_width = font->getDimension(txt_line->c_str()).Width;
518
519                 if (txt_width < m_frame_rect.getWidth()) {
520                         // TODO: Needs a clean left and right gap removal depending on HAlign, similar to vertical scrolling tests for top/bottom.
521                         // This check just fixes the case where it was most noticable (text smaller than clipping area).
522
523                         m_hscroll_pos = 0;
524                         setTextRect(curs_line);
525                 }
526
527                 if (m_current_text_rect.UpperLeftCorner.X + cstart < m_frame_rect.UpperLeftCorner.X) {
528                         // cursor to the left of the clipping area
529                         m_hscroll_pos -= m_frame_rect.UpperLeftCorner.X - (m_current_text_rect.UpperLeftCorner.X + cstart);
530                         setTextRect(curs_line);
531
532                         // TODO: should show more characters to the left when we're scrolling left
533                         //      and the cursor reaches the border.
534                 } else if (m_current_text_rect.UpperLeftCorner.X + cend > m_frame_rect.LowerRightCorner.X)      {
535                         // cursor to the right of the clipping area
536                         m_hscroll_pos += (m_current_text_rect.UpperLeftCorner.X + cend) - m_frame_rect.LowerRightCorner.X;
537                         setTextRect(curs_line);
538                 }
539         }
540
541         // calculate vertical scrolling
542         if (has_broken_text) {
543                 irr::u32 line_height = font->getDimension(L"A").Height + font->getKerningHeight();
544                 // only up to 1 line fits?
545                 if (line_height >= (irr::u32)m_frame_rect.getHeight()) {
546                         m_vscroll_pos = 0;
547                         setTextRect(curs_line);
548                         s32 unscrolledPos = m_current_text_rect.UpperLeftCorner.Y;
549                         s32 pivot = m_frame_rect.UpperLeftCorner.Y;
550                         switch (m_valign) {
551                         case EGUIA_CENTER:
552                                 pivot += m_frame_rect.getHeight() / 2;
553                                 unscrolledPos += line_height / 2;
554                                 break;
555                         case EGUIA_LOWERRIGHT:
556                                 pivot += m_frame_rect.getHeight();
557                                 unscrolledPos += line_height;
558                                 break;
559                         default:
560                                 break;
561                         }
562                         m_vscroll_pos = unscrolledPos - pivot;
563                         setTextRect(curs_line);
564                 } else {
565                         // First 2 checks are necessary when people delete lines
566                         setTextRect(0);
567                         if (m_current_text_rect.UpperLeftCorner.Y > m_frame_rect.UpperLeftCorner.Y && m_valign != EGUIA_LOWERRIGHT) {
568                                 // first line is leaving a gap on top
569                                 m_vscroll_pos = 0;
570                         } else if (m_valign != EGUIA_UPPERLEFT) {
571                                 u32 lastLine = m_broken_text_positions.empty() ? 0 : m_broken_text_positions.size() - 1;
572                                 setTextRect(lastLine);
573                                 if (m_current_text_rect.LowerRightCorner.Y < m_frame_rect.LowerRightCorner.Y)
574                                 {
575                                         // last line is leaving a gap on bottom
576                                         m_vscroll_pos -= m_frame_rect.LowerRightCorner.Y - m_current_text_rect.LowerRightCorner.Y;
577                                 }
578                         }
579
580                         setTextRect(curs_line);
581                         if (m_current_text_rect.UpperLeftCorner.Y < m_frame_rect.UpperLeftCorner.Y) {
582                                 // text above valid area
583                                 m_vscroll_pos -= m_frame_rect.UpperLeftCorner.Y - m_current_text_rect.UpperLeftCorner.Y;
584                                 setTextRect(curs_line);
585                         } else if (m_current_text_rect.LowerRightCorner.Y > m_frame_rect.LowerRightCorner.Y){
586                                 // text below valid area
587                                 m_vscroll_pos += m_current_text_rect.LowerRightCorner.Y - m_frame_rect.LowerRightCorner.Y;
588                                 setTextRect(curs_line);
589                         }
590                 }
591         }
592
593         if (m_vscrollbar) {
594                 m_vscrollbar->setPos(m_vscroll_pos);
595         }
596 }
597
598 void GUIEditBoxWithScrollBar::calculateFrameRect()
599 {
600         m_frame_rect = AbsoluteRect;
601
602
603         IGUISkin *skin = 0;
604         if (Environment)
605                 skin = Environment->getSkin();
606         if (m_border && skin) {
607                 m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X) + 1;
608                 m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y) + 1;
609                 m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X) + 1;
610                 m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y) + 1;
611         }
612
613         updateVScrollBar();
614 }
615
616 //! create a vertical scroll bar
617 void GUIEditBoxWithScrollBar::createVScrollBar()
618 {
619         IGUISkin *skin = 0;
620         if (Environment)
621                 skin = Environment->getSkin();
622
623         m_scrollbar_width = skin ? skin->getSize(gui::EGDS_SCROLLBAR_SIZE) : 16;
624
625         irr::core::rect<s32> scrollbarrect = m_frame_rect;
626         scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width;
627         m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1,
628                         scrollbarrect, false, true);
629
630         m_vscrollbar->setVisible(false);
631         m_vscrollbar->setSmallStep(1);
632         m_vscrollbar->setLargeStep(1);
633 }
634
635
636
637 //! Change the background color
638 void GUIEditBoxWithScrollBar::setBackgroundColor(const video::SColor &bg_color)
639 {
640         m_bg_color = bg_color;
641         m_bg_color_used = true;
642 }
643
644 //! Writes attributes of the element.
645 void GUIEditBoxWithScrollBar::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const
646 {
647         out->addBool("Border", m_border);
648         out->addBool("Background", m_background);
649         // out->addFont("OverrideFont", OverrideFont);
650
651         GUIEditBox::serializeAttributes(out, options);
652 }
653
654
655 //! Reads attributes of the element
656 void GUIEditBoxWithScrollBar::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0)
657 {
658         GUIEditBox::deserializeAttributes(in, options);
659
660         setDrawBorder(in->getAttributeAsBool("Border"));
661         setDrawBackground(in->getAttributeAsBool("Background"));
662 }
663
664 bool GUIEditBoxWithScrollBar::isDrawBackgroundEnabled() const { return false; }
665 bool GUIEditBoxWithScrollBar::isDrawBorderEnabled() const { return false; }
666 void GUIEditBoxWithScrollBar::setCursorChar(const wchar_t cursorChar) { }
667 wchar_t GUIEditBoxWithScrollBar::getCursorChar() const { return '|'; }
668 void GUIEditBoxWithScrollBar::setCursorBlinkTime(irr::u32 timeMs) { }
669 irr::u32 GUIEditBoxWithScrollBar::getCursorBlinkTime() const { return 500; }