Button.cxx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include "Button.hxx"
  2. #include "../../SDLRenderer.hxx"
  3. #include <cassert>
  4. namespace {
  5. SDL_Rect const TEXTURE_RECTS_UP[] = {
  6. {.x = 0, .y = 0, .w = 6, .h = 5}, // UPPER_LEFT
  7. {.x = 6, .y = 0, .w = 37, .h = 5}, // UPPER_EDGE
  8. {.x = 43, .y = 0, .w = 6, .h = 5}, // UPPER_RIGHT
  9. {.x = 0, .y = 5, .w = 6, .h = 35}, // LEFT_EDGE
  10. {.x = 6, .y = 5, .w = 37, .h = 35}, // CENTER
  11. {.x = 43, .y = 5, .w = 6, .h = 35}, // RIGHT_EDGE
  12. {.x = 0, .y = 40, .w = 6, .h = 9}, // LOWER_LEFT
  13. {.x = 6, .y = 40, .w = 37, .h = 9}, // LOWER_EDGE
  14. {.x = 43, .y = 40, .w = 6, .h = 9}, // LOWER_RIGHT
  15. };
  16. SDL_Rect const TEXTURE_RECTS_DOWN[] = {
  17. {.x = 0, .y = 0, .w = 6, .h = 5}, // UPPER_LEFT
  18. {.x = 6, .y = 0, .w = 37, .h = 5}, // UPPER_EDGE
  19. {.x = 43, .y = 0, .w = 6, .h = 5}, // UPPER_RIGHT
  20. {.x = 0, .y = 5, .w = 6, .h = 35}, // LEFT_EDGE
  21. {.x = 6, .y = 5, .w = 37, .h = 35}, // CENTER
  22. {.x = 43, .y = 5, .w = 6, .h = 35}, // RIGHT_EDGE
  23. {.x = 0, .y = 40, .w = 6, .h = 5}, // LOWER_LEFT
  24. {.x = 6, .y = 40, .w = 37, .h = 5}, // LOWER_EDGE
  25. {.x = 43, .y = 40, .w = 6, .h = 5}, // LOWER_RIGHT
  26. };
  27. SDL_Rect calculate_text_rect(SDL_Rect const& area, SDL_Texture* texture)
  28. {
  29. int text_w, text_h;
  30. SDL_QueryTexture(texture, nullptr, nullptr, &text_w, &text_h);
  31. float const text_aspect = static_cast<float>(text_w)/static_cast<float>(text_h);
  32. float const area_aspect = static_cast<float>(area.w)/static_cast<float>(area.h);
  33. int put_w, put_h;
  34. if (text_aspect>area_aspect) {
  35. put_w = area.w;
  36. put_h = static_cast<int>(static_cast<float>(put_w)/text_aspect);
  37. }
  38. else {
  39. put_h = area.h;
  40. put_w = static_cast<int>(static_cast<float>(put_h)*text_aspect);
  41. }
  42. return {
  43. .x = area.x+(area.w-put_w)/2,
  44. .y = area.y+(area.h-put_h)/2,
  45. .w = put_w,
  46. .h = put_h,
  47. };
  48. }
  49. }
  50. Button::Button(int const x, int const y, int const w, int const h, UiColor const color)
  51. :x_{x}, y_{y}, w_{w}, h_{h}, pressed_{false},
  52. visible_{true},
  53. up_{ui_image("button_up", color)},
  54. down_{ui_image("button_down", color)},
  55. font_{"kenney_pixel.ttf"}
  56. {
  57. assert(w_>=MIN_WIDTH);
  58. assert(h_>=MIN_HEIGHT);
  59. on_click_ = [] { };
  60. }
  61. void Button::set_title(std::string const& title)
  62. {
  63. title_ = title;
  64. }
  65. std::string const& Button::title() const
  66. {
  67. return title_;
  68. }
  69. void Button::set_pressed(bool const pressed)
  70. {
  71. pressed_ = pressed;
  72. }
  73. bool Button::is_pressed() const
  74. {
  75. return pressed_;
  76. }
  77. void Button::render(SDLRenderer& renderer)
  78. {
  79. if (!visible_)
  80. return;
  81. auto const text = TTF_RenderUTF8_Solid(font_, title_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});
  82. auto const text_ure = SDL_CreateTextureFromSurface(renderer, text);
  83. SDL_FreeSurface(text);
  84. SDL_Texture* texture;
  85. SDL_Rect const* texture_rects;
  86. SDL_Rect target_rects[9];
  87. if (pressed_) {
  88. texture = down_;
  89. texture_rects = ::TEXTURE_RECTS_DOWN;
  90. target_rects[0] = {.x = x_, .y = y_+4, .w = 6, .h = 5};
  91. target_rects[1] = {.x = x_+6, .y = y_+4, .w = w_-12, .h = 5};
  92. target_rects[2] = {.x = x_+w_-6, .y = y_+4, .w = 6, .h = 5};
  93. target_rects[3] = {.x = x_, .y = y_+9, .w = 6, .h = h_-14};
  94. target_rects[4] = {.x = x_+6, .y = y_+9, .w = w_-12, .h = h_-14};
  95. target_rects[5] = {.x = x_+w_-6, .y = y_+9, .w = 6, .h = h_-14};
  96. target_rects[6] = {.x = x_, .y = y_+h_-5, .w = 6, .h = 5};
  97. target_rects[7] = {.x = x_+6, .y = y_+h_-5, .w = w_-12, .h = 5};
  98. target_rects[8] = {.x = x_+w_-6, .y = y_+h_-5, .w = 6, .h = 5};
  99. }
  100. else {
  101. texture = up_;
  102. texture_rects = ::TEXTURE_RECTS_UP;
  103. target_rects[0] = {.x = x_, .y = y_, .w = 6, .h = 5};
  104. target_rects[1] = {.x = x_+6, .y = y_, .w = w_-12, .h = 5};
  105. target_rects[2] = {.x = x_+w_-6, .y = y_, .w = 6, .h = 5};
  106. target_rects[3] = {.x = x_, .y = y_+5, .w = 6, .h = h_-14};
  107. target_rects[4] = {.x = x_+6, .y = y_+5, .w = w_-12, .h = h_-14};
  108. target_rects[5] = {.x = x_+w_-6, .y = y_+5, .w = 6, .h = h_-14};
  109. target_rects[6] = {.x = x_, .y = y_+h_-9, .w = 6, .h = 9};
  110. target_rects[7] = {.x = x_+6, .y = y_+h_-9, .w = w_-12, .h = 9};
  111. target_rects[8] = {.x = x_+w_-6, .y = y_+h_-9, .w = 6, .h = 9};
  112. }
  113. for (int n = 0; n<9; ++n)
  114. SDL_RenderCopy(renderer, texture, texture_rects+n, target_rects+n);
  115. SDL_Rect const text_rect = ::calculate_text_rect(target_rects[4], text_ure);
  116. SDL_RenderCopy(renderer, text_ure, nullptr, &text_rect);
  117. SDL_DestroyTexture(text_ure);
  118. }
  119. void Button::update()
  120. {
  121. if (!visible_)
  122. return;
  123. int mouse_x, mouse_y;
  124. auto const mouse_button = SDL_GetMouseState(&mouse_x, &mouse_y);
  125. if ((mouse_x>=x_ && mouse_x<=(x_+w_) && mouse_y>=y_ && mouse_y<=(y_+h_))) {
  126. if (mouse_button & SDL_BUTTON(SDL_BUTTON_LEFT)) {
  127. pressed_ = true;
  128. }
  129. else {
  130. if (pressed_) {
  131. trigger();
  132. }
  133. pressed_ = false;
  134. }
  135. }
  136. else {
  137. pressed_ = false;
  138. }
  139. }
  140. void Button::trigger()
  141. {
  142. on_click_();
  143. }
  144. void Button::set_on_click(std::function<void()> handler)
  145. {
  146. on_click_ = std::move(handler);
  147. }
  148. void Button::move(int const x, int const y)
  149. {
  150. x_ = x;
  151. y_ = y;
  152. }
  153. void Button::resize(int const w, int const h)
  154. {
  155. assert(w>=MIN_WIDTH);
  156. assert(h>=MIN_HEIGHT);
  157. w_ = w;
  158. h_ = h;
  159. }
  160. SDL_Rect Button::get_bounding_box() const
  161. {
  162. return {x_, y_, w_, h_};
  163. }
  164. void Button::set_visible(bool const visible)
  165. {
  166. visible_ = visible;
  167. }
  168. bool Button::is_visible() const
  169. {
  170. return visible_;
  171. }