LineInput.cxx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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. texture_{"line_input.png"},
  36. font_{"kenney_pixel.ttf"}
  37. {
  38. assert(w_>=MIN_WIDTH);
  39. assert(h_>=MIN_HEIGHT);
  40. }
  41. void LineInput::move(int const x, int const y)
  42. {
  43. x_ = x;
  44. y_ = y;
  45. }
  46. void LineInput::resize(int const w, int const h)
  47. {
  48. assert(w>=MIN_WIDTH);
  49. assert(h>=MIN_HEIGHT);
  50. w_ = w;
  51. h_ = h;
  52. }
  53. SDL_Rect LineInput::get_bounding_box() const
  54. {
  55. return {x_, y_, w_, h_};
  56. }
  57. void LineInput::set_focus(bool const focus)
  58. {
  59. focus_ = focus;
  60. if (!focus_)
  61. SDL_StopTextInput();
  62. }
  63. bool LineInput::has_focus() const
  64. {
  65. return focus_;
  66. }
  67. void LineInput::on_event(SDL_Event const& evt)
  68. {
  69. if (!focus_)
  70. return;
  71. if (evt.type==SDL_TEXTINPUT) {
  72. value_ += evt.text.text;
  73. }
  74. else if (evt.type==SDL_KEYDOWN && evt.key.keysym.sym==SDLK_BACKSPACE) {
  75. if (!value_.empty()) {
  76. auto const begin = value_.begin();
  77. auto const end = value_.end();
  78. auto it = value_.end()-1;
  79. // For UTF-8 multibyte characters
  80. while (it!=begin && ((*it & 0xC0)==0x80)) {
  81. --it;
  82. }
  83. value_.erase(it, end);
  84. }
  85. }
  86. }
  87. void LineInput::update(std::chrono::milliseconds const delta_time)
  88. {
  89. if (focus_ && !SDL_IsTextInputActive())
  90. SDL_StartTextInput();
  91. blink_timer_ = (blink_timer_+delta_time)%1'000u;
  92. auto const rect = get_bounding_box();
  93. SDL_SetTextInputRect(&rect);
  94. }
  95. void LineInput::render(SDLRenderer& renderer)
  96. {
  97. using namespace std::chrono_literals;
  98. SDL_Texture* const background = texture_;
  99. SDL_Rect const target_rects[9] = {
  100. {.x = x_, .y = y_, .w = 8, .h = 8},
  101. {.x = x_+8, .y = y_, .w = w_-16, .h = 8},
  102. {.x = x_+w_-8, .y = y_, .w = 8, .h = 8},
  103. {.x = x_, .y = y_+8, .w = 8, .h = h_-15},
  104. {.x = x_+8, .y = y_+8, .w = w_-16, .h = h_-15},
  105. {.x = x_+w_-8, .y = y_+8, .w = 8, .h = h_-15},
  106. {.x = x_, .y = y_+h_-7, .w = 8, .h = 7},
  107. {.x = x_+8, .y = y_+h_-7, .w = w_-16, .h = 7},
  108. {.x = x_+w_-8, .y = y_+h_-7, .w = 8, .h = 7},
  109. };
  110. for (int n = 0; n<9; ++n)
  111. SDL_RenderCopy(renderer, background, ::TEXTURE_RECTS+n, target_rects+n);
  112. auto const text = TTF_RenderUTF8_Solid(font_, value_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});
  113. auto const text_ure = SDL_CreateTextureFromSurface(renderer, text);
  114. SDL_FreeSurface(text);
  115. auto const text_rect = ::calculate_text_rect(target_rects[4], text_ure);
  116. SDL_Rect const text_texture_rect{
  117. .x = 0, .y = 0, .w = text_rect.w, .h = text_rect.h,
  118. };
  119. SDL_RenderCopy(renderer, text_ure, &text_texture_rect, &text_rect);
  120. SDL_DestroyTexture(text_ure);
  121. if (focus_) {
  122. SDL_Rect cursor_rect{
  123. .x = text_rect.x+text_rect.w+1, .y = text_rect.y, .w = 2, .h = text_rect.h,
  124. };
  125. SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
  126. if (blink_timer_<500ms) {
  127. SDL_RenderFillRect(renderer, &cursor_rect);
  128. }
  129. SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
  130. }
  131. }
  132. char const* LineInput::value() const
  133. {
  134. return value_.c_str();
  135. }
  136. void LineInput::set_value(std::string value)
  137. {
  138. value_ = std::move(value);
  139. }