LineInput.cxx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #include "LineInput.hxx"
  2. #include <cassert>
  3. namespace {
  4. SDL_Rect const TEXTURE_RECTS[] = {
  5. {.x = 0, .y = 0, .w = 8, .h = 8}, // UPPER_LEFT
  6. {.x = 8, .y = 0, .w = 179, .h = 8}, // UPPER_EDGE
  7. {.x = 187, .y = 0, .w = 8, .h = 8}, // UPPER_RIGHT
  8. {.x = 0, .y = 8, .w = 8, .h = 34}, // LEFT_EDGE
  9. {.x = 8, .y = 8, .w = 179, .h = 34}, // CENTER
  10. {.x = 187, .y = 8, .w = 8, .h = 34}, // RIGHT_EDGE
  11. {.x = 0, .y = 42, .w = 8, .h = 7}, // LOWER_LEFT
  12. {.x = 8, .y = 42, .w = 179, .h = 7}, // LOWER_EDGE
  13. {.x = 187, .y = 42, .w = 8, .h = 7}, // LOWER_RIGHT
  14. };
  15. SDL_Rect calculate_text_rect(SDL_Rect const& area, SDL_Texture* texture)
  16. {
  17. int text_w, text_h;
  18. SDL_QueryTexture(texture, nullptr, nullptr, &text_w, &text_h);
  19. // TODO: the height value is based on the default font height, therefor it should not be hard coded
  20. // this hack here is a solution for when the text is empty,
  21. // because in that case we cannot determine the height from the rendered texture
  22. text_h = 32;
  23. int const w = std::min(text_w, area.w-LineInput::MIN_WIDTH-8);
  24. int const h = std::min(text_h, area.h-LineInput::MIN_HEIGHT-8);
  25. return {
  26. .x = area.x+12,
  27. .y = area.y+(area.h-h)/2,
  28. .w = area.w>=(w+LineInput::MIN_WIDTH+8) ? w : 0,
  29. .h = area.h>=(h+LineInput::MIN_HEIGHT+8) ? h : 0,
  30. };
  31. }
  32. }
  33. LineInput::LineInput(int const x, int const y, int const w, int const h, std::string value)
  34. :value_{std::move(value)}, x_{x}, y_{y}, w_{w}, h_{h}, focus_{false},
  35. visible_{true},
  36. texture_{"line_input.png"},
  37. font_{"kenney_pixel.ttf"}
  38. {
  39. assert(w_>=MIN_WIDTH);
  40. assert(h_>=MIN_HEIGHT);
  41. }
  42. void LineInput::move(int const x, int const y)
  43. {
  44. x_ = x;
  45. y_ = y;
  46. }
  47. void LineInput::resize(int const w, int const h)
  48. {
  49. assert(w>=MIN_WIDTH);
  50. assert(h>=MIN_HEIGHT);
  51. w_ = w;
  52. h_ = h;
  53. }
  54. SDL_Rect LineInput::get_bounding_box() const
  55. {
  56. return {x_, y_, w_, h_};
  57. }
  58. void LineInput::set_focus(bool const focus)
  59. {
  60. focus_ = focus;
  61. if (!focus_)
  62. SDL_StopTextInput();
  63. }
  64. bool LineInput::has_focus() const
  65. {
  66. return focus_;
  67. }
  68. void LineInput::on_event(SDL_Event const& evt)
  69. {
  70. if (!focus_ || !visible_)
  71. return;
  72. if (evt.type==SDL_TEXTINPUT) {
  73. value_ += evt.text.text;
  74. auto it = value_.begin();
  75. auto const end = value_.end();
  76. int len = 0;
  77. // we assume valid UTF-8 here
  78. while (it!=end && len<MAX_CHARACTERS) {
  79. ++len;
  80. if ((*it & 0x80)==0)
  81. ++it;
  82. else if ((*it & 0xE0)==0xC0)
  83. std::advance(it, 2);
  84. else if ((*it & 0xF0)==0xE0)
  85. std::advance(it, 3);
  86. else if ((*it & 0xF8)==0xF0)
  87. std::advance(it, 4);
  88. }
  89. value_.erase(it, end);
  90. }
  91. else if (evt.type==SDL_KEYDOWN && evt.key.keysym.sym==SDLK_BACKSPACE) {
  92. if (!value_.empty()) {
  93. auto const begin = value_.begin();
  94. auto const end = value_.end();
  95. auto it = value_.end()-1;
  96. // For UTF-8 multibyte characters
  97. while (it!=begin && ((*it & 0xC0)==0x80)) {
  98. --it;
  99. }
  100. value_.erase(it, end);
  101. }
  102. }
  103. }
  104. void LineInput::update(std::chrono::milliseconds const delta_time)
  105. {
  106. if (!visible_)
  107. return;
  108. if (focus_ && !SDL_IsTextInputActive())
  109. SDL_StartTextInput();
  110. blink_timer_ = (blink_timer_+delta_time)%1'000u;
  111. auto const rect = get_bounding_box();
  112. SDL_SetTextInputRect(&rect);
  113. }
  114. void LineInput::render(SDLRenderer& renderer)
  115. {
  116. if (!visible_)
  117. return;
  118. using namespace std::chrono_literals;
  119. SDL_Texture* const background = texture_;
  120. SDL_Rect const target_rects[9] = {
  121. {.x = x_, .y = y_, .w = 8, .h = 8},
  122. {.x = x_+8, .y = y_, .w = w_-16, .h = 8},
  123. {.x = x_+w_-8, .y = y_, .w = 8, .h = 8},
  124. {.x = x_, .y = y_+8, .w = 8, .h = h_-15},
  125. {.x = x_+8, .y = y_+8, .w = w_-16, .h = h_-15},
  126. {.x = x_+w_-8, .y = y_+8, .w = 8, .h = h_-15},
  127. {.x = x_, .y = y_+h_-7, .w = 8, .h = 7},
  128. {.x = x_+8, .y = y_+h_-7, .w = w_-16, .h = 7},
  129. {.x = x_+w_-8, .y = y_+h_-7, .w = 8, .h = 7},
  130. };
  131. for (int n = 0; n<9; ++n)
  132. SDL_RenderCopy(renderer, background, ::TEXTURE_RECTS+n, target_rects+n);
  133. auto const text = TTF_RenderUTF8_Solid(font_, value_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});
  134. auto const text_ure = SDL_CreateTextureFromSurface(renderer, text);
  135. SDL_FreeSurface(text);
  136. auto const text_rect = ::calculate_text_rect(target_rects[4], text_ure);
  137. SDL_Rect const text_texture_rect{
  138. .x = 0, .y = 0, .w = text_rect.w, .h = text_rect.h,
  139. };
  140. SDL_RenderCopy(renderer, text_ure, &text_texture_rect, &text_rect);
  141. SDL_DestroyTexture(text_ure);
  142. if (focus_) {
  143. SDL_Rect cursor_rect{
  144. .x = text_rect.x+text_rect.w+1, .y = text_rect.y, .w = 2, .h = text_rect.h,
  145. };
  146. SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
  147. if (blink_timer_<500ms) {
  148. SDL_RenderFillRect(renderer, &cursor_rect);
  149. }
  150. SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
  151. }
  152. }
  153. char const* LineInput::value() const
  154. {
  155. return value_.c_str();
  156. }
  157. void LineInput::set_value(std::string value)
  158. {
  159. value_ = std::move(value);
  160. }
  161. bool LineInput::is_visible() const
  162. {
  163. return visible_;
  164. }
  165. void LineInput::set_visible(bool visible)
  166. {
  167. visible_ = visible;
  168. }