LineInput.cxx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. }
  75. else if (evt.type==SDL_KEYDOWN && evt.key.keysym.sym==SDLK_BACKSPACE) {
  76. if (!value_.empty()) {
  77. auto const begin = value_.begin();
  78. auto const end = value_.end();
  79. auto it = value_.end()-1;
  80. // For UTF-8 multibyte characters
  81. while (it!=begin && ((*it & 0xC0)==0x80)) {
  82. --it;
  83. }
  84. value_.erase(it, end);
  85. }
  86. }
  87. }
  88. void LineInput::update(std::chrono::milliseconds const delta_time)
  89. {
  90. if (!visible_)
  91. return;
  92. if (focus_ && !SDL_IsTextInputActive())
  93. SDL_StartTextInput();
  94. blink_timer_ = (blink_timer_+delta_time)%1'000u;
  95. auto const rect = get_bounding_box();
  96. SDL_SetTextInputRect(&rect);
  97. }
  98. void LineInput::render(SDLRenderer& renderer)
  99. {
  100. if (!visible_)
  101. return;
  102. using namespace std::chrono_literals;
  103. SDL_Texture* const background = texture_;
  104. SDL_Rect const target_rects[9] = {
  105. {.x = x_, .y = y_, .w = 8, .h = 8},
  106. {.x = x_+8, .y = y_, .w = w_-16, .h = 8},
  107. {.x = x_+w_-8, .y = y_, .w = 8, .h = 8},
  108. {.x = x_, .y = y_+8, .w = 8, .h = h_-15},
  109. {.x = x_+8, .y = y_+8, .w = w_-16, .h = h_-15},
  110. {.x = x_+w_-8, .y = y_+8, .w = 8, .h = h_-15},
  111. {.x = x_, .y = y_+h_-7, .w = 8, .h = 7},
  112. {.x = x_+8, .y = y_+h_-7, .w = w_-16, .h = 7},
  113. {.x = x_+w_-8, .y = y_+h_-7, .w = 8, .h = 7},
  114. };
  115. for (int n = 0; n<9; ++n)
  116. SDL_RenderCopy(renderer, background, ::TEXTURE_RECTS+n, target_rects+n);
  117. auto const text = TTF_RenderUTF8_Solid(font_, value_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});
  118. auto const text_ure = SDL_CreateTextureFromSurface(renderer, text);
  119. SDL_FreeSurface(text);
  120. auto const text_rect = ::calculate_text_rect(target_rects[4], text_ure);
  121. SDL_Rect const text_texture_rect{
  122. .x = 0, .y = 0, .w = text_rect.w, .h = text_rect.h,
  123. };
  124. SDL_RenderCopy(renderer, text_ure, &text_texture_rect, &text_rect);
  125. SDL_DestroyTexture(text_ure);
  126. if (focus_) {
  127. SDL_Rect cursor_rect{
  128. .x = text_rect.x+text_rect.w+1, .y = text_rect.y, .w = 2, .h = text_rect.h,
  129. };
  130. SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
  131. if (blink_timer_<500ms) {
  132. SDL_RenderFillRect(renderer, &cursor_rect);
  133. }
  134. SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
  135. }
  136. }
  137. char const* LineInput::value() const
  138. {
  139. return value_.c_str();
  140. }
  141. void LineInput::set_value(std::string value)
  142. {
  143. value_ = std::move(value);
  144. }
  145. bool LineInput::is_visible() const
  146. {
  147. return visible_;
  148. }
  149. void LineInput::set_visible(bool visible)
  150. {
  151. visible_ = visible;
  152. }