00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "xrb_lineedit.hpp"
00012
00013 #include "xrb_input_events.hpp"
00014 #include "xrb_render.hpp"
00015 #include "xrb_screen.hpp"
00016 #include "xrb_utf8.hpp"
00017
00018 namespace Xrb
00019 {
00020
00021 LineEdit::LineEdit (
00022 Uint32 const character_limit,
00023 ContainerWidget *const parent,
00024 std::string const &name)
00025 :
00026 TextWidget("", parent, name),
00027 m_sender_text_updated(this),
00028 m_sender_text_updated_v(this),
00029 m_sender_text_set_by_enter_key(this),
00030 m_sender_text_set_by_enter_key_v(this)
00031 {
00032 m_accepts_focus = true;
00033
00034 m_character_limit = character_limit;
00035 m_text.reserve(m_character_limit);
00036 m_text_width = 0;
00037 m_text_offset = ScreenCoordVector2::ms_zero;
00038 m_alignment = LEFT;
00039 m_cursor_position = 0;
00040 m_does_cursor_overwrite = false;
00041 m_is_cursor_visible = false;
00042 m_cursor_blink_period = 0.5f;
00043 m_next_cursor_blink_time = 0.0f;
00044 m_character_filter = CharacterFilter(CharacterFilter::DENY, "");
00045 m_is_read_only = false;
00046
00047 ASSERT1(m_text.empty());
00048 ASSERT1(m_last_text_update.empty());
00049
00050 SetIsHeightFixedToTextHeight(true);
00051
00052 LineEdit::UpdateRenderBackground();
00053 }
00054
00055 void LineEdit::SetText (std::string const &text)
00056 {
00057 ASSERT1(RenderFont().IsValid());
00058
00059
00060 if (m_text != text)
00061 {
00062 AssignFilteredString(text);
00063 if (m_cursor_position > m_text.length())
00064 SetCursorPosition(m_text.length());
00065 SignalTextUpdated();
00066 UpdateMinAndMaxSizesFromText();
00067 }
00068 }
00069
00070 void LineEdit::SetAlignment (Alignment const alignment)
00071 {
00072 ASSERT1(alignment == LEFT || alignment == CENTER || alignment == RIGHT);
00073 m_alignment = alignment;
00074 }
00075
00076 void LineEdit::Draw (RenderContext const &render_context) const
00077 {
00078 ASSERT1(RenderFont().IsValid());
00079
00080
00081 Widget::Draw(render_context);
00082
00083 ScreenCoordRect contents_rect(ContentsRect());
00084 ScreenCoordVector2 initial_pen_position(InitialPenPositionX(), contents_rect.Top());
00085
00086
00087 {
00088 if (contents_rect.IsValid())
00089 {
00090
00091 RenderContext string_render_context(render_context);
00092
00093 string_render_context.ApplyClipRect(contents_rect);
00094
00095 string_render_context.ApplyColorMask(RenderTextColor());
00096
00097 string_render_context.SetupGLClipRect();
00098
00099 RenderFont()->DrawString(
00100 string_render_context,
00101 initial_pen_position,
00102 m_text.c_str());
00103 }
00104 }
00105
00106
00107 if (m_is_cursor_visible && !IsReadOnly())
00108 {
00109 initial_pen_position[Dim::Y] = contents_rect.Bottom();
00110
00111 ScreenCoordRect cursor_rect(
00112 ScreenCoordVector2(
00113 CursorWidth(m_cursor_position),
00114 RenderFont()->PixelHeight()));
00115
00116 ScreenCoordVector2 cursor_screen_position(initial_pen_position);
00117 cursor_screen_position += ScreenCoordVector2(CursorOffset(m_cursor_position), 0);
00118
00119 cursor_rect += cursor_screen_position;
00120
00121
00122 Render::DrawScreenRect(
00123 render_context,
00124 RenderTextColor(),
00125 cursor_rect);
00126 }
00127 }
00128
00129 void LineEdit::SetRenderFont (Resource<Font> const &render_font)
00130 {
00131 TextWidget::SetRenderFont(render_font);
00132 UpdateTextWidth();
00133 }
00134
00135 void LineEdit::UpdateRenderBackground ()
00136 {
00137 SetRenderBackground(
00138 WidgetSkinWidgetBackground(WidgetSkin::LINE_EDIT_BACKGROUND));
00139 }
00140
00141 void LineEdit::HandleChangedWidgetSkinWidgetBackground (
00142 WidgetSkin::WidgetBackgroundType const widget_background_type)
00143 {
00144 if (widget_background_type == WidgetSkin::LINE_EDIT_BACKGROUND)
00145 UpdateRenderBackground();
00146 }
00147
00148 void LineEdit::HandleFrame ()
00149 {
00150
00151 if (IsFocused() && FrameTime() >= m_next_cursor_blink_time)
00152 {
00153 m_is_cursor_visible = !m_is_cursor_visible;
00154 m_next_cursor_blink_time = FrameTime() + 0.5f * m_cursor_blink_period;
00155 }
00156 }
00157
00158 bool LineEdit::ProcessKeyEvent (EventKey const *const e)
00159 {
00160
00161 if (IsReadOnly())
00162 return false;
00163
00164 if (e->IsKeyDownEvent() || e->IsKeyRepeatEvent())
00165 {
00166 switch (e->KeyCode())
00167 {
00168 case Key::LEFT:
00169 MoveCursorLeft();
00170 MakeCursorVisible();
00171 break;
00172
00173 case Key::RIGHT:
00174 MoveCursorRight();
00175 MakeCursorVisible();
00176 break;
00177
00178 case Key::HOME:
00179 SetCursorPosition(0);
00180 MakeCursorVisible();
00181 break;
00182
00183 case Key::END:
00184 SetCursorPosition(m_text.length());
00185 MakeCursorVisible();
00186 break;
00187
00188 case Key::INSERT:
00189 m_does_cursor_overwrite = !m_does_cursor_overwrite;
00190 break;
00191
00192 case Key::BACKSPACE:
00193 if (m_cursor_position > 0)
00194 {
00195 bool shift_text_worked = ShiftText(m_cursor_position, -1);
00196 if (shift_text_worked)
00197 MoveCursorLeft();
00198 UpdateTextWidth();
00199 }
00200 MakeCursorVisible();
00201 break;
00202
00203 case Key::DELETE:
00204 if (m_cursor_position < m_text.length())
00205 {
00206 ShiftText(m_cursor_position + 1, -1);
00207 UpdateTextWidth();
00208 }
00209 MakeCursorVisible();
00210 break;
00211
00212 case Key::RETURN:
00213 case Key::KP_ENTER:
00214 SignalTextUpdated();
00215 m_sender_text_set_by_enter_key.Signal(m_text);
00216 m_sender_text_set_by_enter_key_v.Signal(m_text);
00217 break;
00218
00219 default:
00220
00221
00222 char c =
00223 GetCharacterFilter().
00224 FilteredCharacter(
00225 e->ModifiedAscii());
00226 if (c != '\0')
00227 {
00228 TypeCharacter(c);
00229 MakeCursorVisible();
00230 UpdateTextWidth();
00231 }
00232 break;
00233 }
00234 }
00235
00236 return true;
00237 }
00238
00239 bool LineEdit::ProcessMouseButtonEvent (EventMouseButton const *const e)
00240 {
00241 return e->IsMouseButtonDownEvent() && !IsReadOnly();
00242 }
00243
00244 void LineEdit::HandleFocus ()
00245 {
00246 MakeCursorVisible();
00247 }
00248
00249 void LineEdit::HandleUnfocus ()
00250 {
00251 m_is_cursor_visible = false;
00252 SignalTextUpdated();
00253 }
00254
00255 void LineEdit::SignalTextUpdated ()
00256 {
00257 if (m_last_text_update != m_text)
00258 {
00259 m_last_text_update = m_text;
00260 m_sender_text_updated.Signal(m_last_text_update);
00261 m_sender_text_updated_v.Signal(m_last_text_update);
00262 }
00263 }
00264
00265 void LineEdit::UpdateMinAndMaxSizesFromText ()
00266 {
00267 m_is_min_width_fixed_to_text_width = false;
00268 m_is_max_width_fixed_to_text_width = false;
00269 m_is_min_height_fixed_to_text_height = true;
00270 m_is_max_height_fixed_to_text_height = true;
00271 TextWidget::UpdateMinAndMaxSizesFromText();
00272 }
00273
00274 ScreenCoord LineEdit::InitialPenPositionX () const
00275 {
00276 ScreenCoordRect contents_rect(ContentsRect());
00277 ScreenCoord initial_pen_position_x;
00278 switch (m_alignment)
00279 {
00280 case LEFT:
00281 initial_pen_position_x = 0;
00282 break;
00283 case CENTER:
00284 initial_pen_position_x =
00285 m_text_width < contents_rect.Width() ?
00286 (contents_rect.Right()-contents_rect.Left()-m_text_width)/2 :
00287 0;
00288 break;
00289 case RIGHT:
00290 initial_pen_position_x =
00291 m_text_width < contents_rect.Width() ?
00292 contents_rect.Right()-contents_rect.Left()-m_text_width :
00293 0;
00294 break;
00295 default:
00296 ASSERT1(false && "Invalid Alignment");
00297 initial_pen_position_x = 0;
00298 break;
00299 }
00300 initial_pen_position_x += contents_rect.Left();
00301 initial_pen_position_x += m_text_offset[Dim::X];
00302 return initial_pen_position_x;
00303 }
00304
00305 ScreenCoord LineEdit::CursorOffset (Uint32 cursor_position) const
00306 {
00307 ASSERT1(RenderFont().IsValid());
00308
00309 FontCoordVector2 pen_position_26_6(FontCoordVector2::ms_zero);
00310
00311 if (cursor_position < 0)
00312 cursor_position = 0;
00313 else if (cursor_position > m_text.length())
00314 cursor_position = m_text.length();
00315
00316 char const *current_glyph = m_text.c_str();
00317 char const *next_glyph;
00318 for (Uint32 i = 0; i < cursor_position && current_glyph != '\0'; ++i)
00319 {
00320 next_glyph = UTF8::NextCharacter(current_glyph);
00321 RenderFont()->MoveThroughGlyph(
00322 &pen_position_26_6,
00323 ScreenCoordVector2::ms_zero,
00324 current_glyph,
00325 next_glyph);
00326 current_glyph = next_glyph;
00327 }
00328
00329 return FontToScreenCoord(pen_position_26_6[Dim::X]);
00330 }
00331
00332 ScreenCoord LineEdit::CursorWidth (Uint32 cursor_position) const
00333 {
00334 if (cursor_position < 0)
00335 cursor_position = 0;
00336 else if (cursor_position > m_text.length())
00337 cursor_position = m_text.length();
00338
00339 if (!m_does_cursor_overwrite)
00340 return Max(1, RenderFont()->PixelHeight()/6);
00341 else
00342 return (cursor_position == m_text.length()) ?
00343 RenderFont()->GlyphWidth("n") :
00344 RenderFont()->GlyphWidth(m_text.c_str()+cursor_position);
00345 }
00346
00347 void LineEdit::SetCursorPosition (Uint32 cursor_position)
00348 {
00349 if (cursor_position < 0)
00350 cursor_position = 0;
00351 else if (cursor_position > m_text.length())
00352 cursor_position = m_text.length();
00353
00354 ScreenCoordRect contents_rect(ContentsRect());
00355 ScreenCoord desired_cursor_offset = CursorOffset(cursor_position);
00356 ScreenCoord cursor_width = CursorWidth(cursor_position);
00357
00358 if (desired_cursor_offset + m_text_offset[Dim::X] < 0)
00359 m_text_offset[Dim::X] = -desired_cursor_offset;
00360 else if (desired_cursor_offset + m_text_offset[Dim::X] > contents_rect.Width() - cursor_width)
00361 m_text_offset[Dim::X] = -desired_cursor_offset + contents_rect.Width() - cursor_width;
00362
00363 m_cursor_position = cursor_position;
00364 }
00365
00366 void LineEdit::UpdateTextWidth ()
00367 {
00368 ScreenCoordRect contents_rect(ContentsRect());
00369 m_text_width = RenderFont()->StringRect(m_text.c_str()).Width();
00370 if (m_text_width <= contents_rect.Width())
00371 m_text_offset = ScreenCoordVector2::ms_zero;
00372 }
00373
00374 void LineEdit::MakeCursorVisible ()
00375 {
00376 m_is_cursor_visible = true;
00377 m_next_cursor_blink_time = MostRecentFrameTime() + 0.5f * m_cursor_blink_period;
00378 }
00379
00380 void LineEdit::MoveCursorLeft ()
00381 {
00382 if (m_cursor_position > 0)
00383 SetCursorPosition(m_cursor_position - 1);
00384 }
00385
00386 void LineEdit::MoveCursorRight ()
00387 {
00388 if (m_cursor_position < m_text.length())
00389 SetCursorPosition(m_cursor_position + 1);
00390 }
00391
00392 void LineEdit::TypeCharacter (char const c)
00393 {
00394 if (c == '\0' || c == '\n')
00395 return;
00396
00397 ASSERT1(m_cursor_position <= m_text.length());
00398
00399 if (m_does_cursor_overwrite)
00400 {
00401
00402
00403 if (m_cursor_position == m_text.length())
00404 {
00405 bool shift_text_worked = ShiftText(m_cursor_position, 1);
00406 if (shift_text_worked)
00407 {
00408 m_text[m_cursor_position] = c;
00409 MoveCursorRight();
00410 }
00411 }
00412
00413 else
00414 {
00415 m_text[m_cursor_position] = c;
00416 MoveCursorRight();
00417 }
00418 }
00419 else
00420 {
00421 bool shift_text_worked = ShiftText(m_cursor_position, 1);
00422 if (shift_text_worked)
00423 {
00424 m_text[m_cursor_position] = c;
00425 MoveCursorRight();
00426 }
00427 }
00428 }
00429
00430 bool LineEdit::ShiftText (Uint32 const position, Sint32 const offset)
00431 {
00432 ASSERT1(offset != 0);
00433 ASSERT1(position <= m_text.length());
00434
00435 if (m_text.length() + offset > m_character_limit)
00436 return false;
00437
00438 if (offset < 0)
00439 m_text.erase(position + offset, -offset);
00440 else
00441 m_text.insert(position, offset, '~');
00442
00443 return true;
00444 }
00445
00446 void LineEdit::AssignFilteredString (std::string const &string)
00447 {
00448 Uint32 text_size = 0;
00449 Uint32 string_index = 0;
00450
00451 m_text.clear();
00452 while (string_index < string.length() && text_size < m_character_limit)
00453 {
00454 char filtered_char = GetCharacterFilter().FilteredCharacter(string[string_index]);
00455
00456 if (filtered_char != '\0' && filtered_char != '\n')
00457 m_text += filtered_char;
00458
00459 ++text_size;
00460 ++string_index;
00461 }
00462 UpdateTextWidth();
00463 }
00464
00465 }