GTextBox.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #include "GTextBox.h"
  2. #include <AK/StdLibExtras.h>
  3. #include <SharedGraphics/CharacterBitmap.h>
  4. #include <SharedGraphics/Font.h>
  5. #include <LibGUI/GPainter.h>
  6. #include <Kernel/KeyCode.h>
  7. GTextBox::GTextBox(GWidget* parent)
  8. : GWidget(parent)
  9. {
  10. set_background_color(Color::White);
  11. }
  12. GTextBox::~GTextBox()
  13. {
  14. }
  15. void GTextBox::set_text(const String& text)
  16. {
  17. if (m_text == text)
  18. return;
  19. m_text = text;
  20. m_cursor_position = m_text.length();
  21. scroll_cursor_into_view(HorizontalDirection::Right);
  22. update();
  23. }
  24. void GTextBox::scroll_cursor_into_view(HorizontalDirection direction)
  25. {
  26. if (visible_content_rect().contains(cursor_content_position()))
  27. return;
  28. int total_text_width = font().width(m_text);
  29. dbgprintf("total_text_width = %d, visible_content width = %d\n", total_text_width, visible_content_rect().width());
  30. if (total_text_width < visible_content_rect().width()) {
  31. m_scroll_offset = 0;
  32. return;
  33. }
  34. if (direction == HorizontalDirection::Left) {
  35. dbgprintf("Left, orig offset = %d\n", m_scroll_offset);
  36. m_scroll_offset = cursor_content_position().x();
  37. int offset_into_visible = m_scroll_offset - visible_content_rect().x();
  38. if (offset_into_visible < font().glyph_width(' '))
  39. m_scroll_offset -= width() / 2;
  40. } else {
  41. m_scroll_offset = cursor_content_position().x() - visible_content_rect().width();
  42. dbgprintf("Right, orig offset = %d\n", m_scroll_offset);
  43. int offset_into_visible = m_scroll_offset - visible_content_rect().x();
  44. if (offset_into_visible > width() / 4) {
  45. dbgprintf("Right, adjust offset = %d\n", m_scroll_offset);
  46. m_scroll_offset += width() / 2;
  47. }
  48. }
  49. if (m_scroll_offset < 0)
  50. m_scroll_offset = 0;
  51. if (m_scroll_offset > total_text_width)dbgprintf("Right, adjust offset = %d\n", m_scroll_offset);
  52. m_scroll_offset = total_text_width - width();
  53. }
  54. Rect GTextBox::visible_content_rect() const
  55. {
  56. if (rect().is_empty())
  57. return { };
  58. return { m_scroll_offset, 0, rect().shrunken(6, 6).width(), rect().shrunken(6, 6).height() };
  59. }
  60. Point GTextBox::cursor_content_position() const
  61. {
  62. int x = 0;
  63. for (int i = 0; i < m_cursor_position; ++i)
  64. x += font().glyph_width(m_text[i]) + font().glyph_spacing();
  65. return { x, 0 };
  66. }
  67. void GTextBox::paint_event(GPaintEvent& event)
  68. {
  69. GPainter painter(*this);
  70. painter.add_clip_rect(event.rect());
  71. painter.fill_rect(rect().shrunken(2, 2), background_color());
  72. painter.draw_rect(rect(), foreground_color());
  73. if (is_focused())
  74. painter.draw_focus_rect(rect());
  75. Rect inner_rect = rect();
  76. inner_rect.shrink(6, 6);
  77. painter.add_clip_rect(inner_rect);
  78. painter.translate(-m_scroll_offset, 0);
  79. int space_width = font().glyph_width(' ') + font().glyph_spacing();
  80. int x = inner_rect.x();
  81. int y = inner_rect.center().y() - font().glyph_height() / 2;
  82. for (int i = 0; i < m_text.length(); ++i) {
  83. char ch = m_text[i];
  84. if (ch == ' ') {
  85. x += space_width;
  86. continue;
  87. }
  88. painter.draw_glyph({x, y}, ch, Color::Black);
  89. x += font().glyph_width(ch) + font().glyph_spacing();
  90. }
  91. if (is_focused() && m_cursor_blink_state) {
  92. Rect cursor_rect {
  93. inner_rect.x() + cursor_content_position().x(),
  94. inner_rect.y(),
  95. 1,
  96. inner_rect.height()
  97. };
  98. painter.fill_rect(cursor_rect, foreground_color());
  99. }
  100. }
  101. void GTextBox::mousedown_event(GMouseEvent&)
  102. {
  103. }
  104. void GTextBox::handle_backspace()
  105. {
  106. if (m_cursor_position == 0)
  107. return;
  108. if (m_text.length() == 1) {
  109. m_text = String::empty();
  110. m_cursor_position = 0;
  111. m_scroll_offset = 0;
  112. if (on_change)
  113. on_change(*this);
  114. update();
  115. return;
  116. }
  117. char* buffer;
  118. auto new_text = StringImpl::create_uninitialized(m_text.length() - 1, buffer);
  119. memcpy(buffer, m_text.characters(), m_cursor_position - 1);
  120. memcpy(buffer + m_cursor_position - 1, m_text.characters() + m_cursor_position, m_text.length() - (m_cursor_position - 1));
  121. m_text = move(new_text);
  122. --m_cursor_position;
  123. scroll_cursor_into_view(HorizontalDirection::Left);
  124. if (on_change)
  125. on_change(*this);
  126. update();
  127. }
  128. void GTextBox::keydown_event(GKeyEvent& event)
  129. {
  130. switch (event.key()) {
  131. case KeyCode::Key_Left:
  132. if (m_cursor_position) {
  133. --m_cursor_position;
  134. scroll_cursor_into_view(HorizontalDirection::Left);
  135. }
  136. m_cursor_blink_state = true;
  137. update();
  138. return;
  139. case KeyCode::Key_Right:
  140. if (m_cursor_position < m_text.length()) {
  141. ++m_cursor_position;
  142. scroll_cursor_into_view(HorizontalDirection::Right);
  143. }
  144. m_cursor_blink_state = true;
  145. update();
  146. return;
  147. case KeyCode::Key_Backspace:
  148. return handle_backspace();
  149. case KeyCode::Key_Return:
  150. if (on_return_pressed)
  151. on_return_pressed(*this);
  152. return;
  153. }
  154. if (!event.text().is_empty()) {
  155. ASSERT(event.text().length() == 1);
  156. char* buffer;
  157. auto new_text = StringImpl::create_uninitialized(m_text.length() + 1, buffer);
  158. memcpy(buffer, m_text.characters(), m_cursor_position);
  159. buffer[m_cursor_position] = event.text()[0];
  160. memcpy(buffer + m_cursor_position + 1, m_text.characters() + m_cursor_position, m_text.length() - m_cursor_position);
  161. m_text = move(new_text);
  162. ++m_cursor_position;
  163. scroll_cursor_into_view(HorizontalDirection::Right);
  164. if (on_change)
  165. on_change(*this);
  166. update();
  167. return;
  168. }
  169. }
  170. void GTextBox::timer_event(GTimerEvent&)
  171. {
  172. // FIXME: Disable the timer when not focused.
  173. if (!is_focused())
  174. return;
  175. m_cursor_blink_state = !m_cursor_blink_state;
  176. update();
  177. }
  178. void GTextBox::focusin_event(GEvent&)
  179. {
  180. start_timer(500);
  181. }
  182. void GTextBox::focusout_event(GEvent&)
  183. {
  184. stop_timer();
  185. }
  186. void GTextBox::resize_event(GResizeEvent&)
  187. {
  188. scroll_cursor_into_view(HorizontalDirection::Right);
  189. }