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 }