| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 | #include "Button.hxx"#include "../../SDLRenderer.hxx"#include <cassert>namespace {  SDL_Rect const TEXTURE_RECTS_UP[] = {      {.x = 0, .y = 0, .w = 6, .h = 5}, // UPPER_LEFT      {.x = 6, .y = 0, .w = 37, .h = 5}, // UPPER_EDGE      {.x = 43, .y = 0, .w = 6, .h = 5}, // UPPER_RIGHT      {.x = 0, .y = 5, .w = 6, .h = 35}, // LEFT_EDGE      {.x = 6, .y = 5, .w = 37, .h = 35}, // CENTER      {.x = 43, .y = 5, .w = 6, .h = 35}, // RIGHT_EDGE      {.x = 0, .y = 40, .w = 6, .h = 9}, // LOWER_LEFT      {.x = 6, .y = 40, .w = 37, .h = 9}, // LOWER_EDGE      {.x = 43, .y = 40, .w = 6, .h = 9}, // LOWER_RIGHT  };  SDL_Rect const TEXTURE_RECTS_DOWN[] = {      {.x = 0, .y = 0, .w = 6, .h = 5}, // UPPER_LEFT      {.x = 6, .y = 0, .w = 37, .h = 5}, // UPPER_EDGE      {.x = 43, .y = 0, .w = 6, .h = 5}, // UPPER_RIGHT      {.x = 0, .y = 5, .w = 6, .h = 35}, // LEFT_EDGE      {.x = 6, .y = 5, .w = 37, .h = 35}, // CENTER      {.x = 43, .y = 5, .w = 6, .h = 35}, // RIGHT_EDGE      {.x = 0, .y = 40, .w = 6, .h = 5}, // LOWER_LEFT      {.x = 6, .y = 40, .w = 37, .h = 5}, // LOWER_EDGE      {.x = 43, .y = 40, .w = 6, .h = 5}, // LOWER_RIGHT  };  SDL_Rect calculate_text_rect(SDL_Rect const& area, SDL_Texture* texture)  {    int text_w, text_h;    SDL_QueryTexture(texture, nullptr, nullptr, &text_w, &text_h);    float const text_aspect = static_cast<float>(text_w)/static_cast<float>(text_h);    float const area_aspect = static_cast<float>(area.w)/static_cast<float>(area.h);    int put_w, put_h;    if (text_aspect>area_aspect) {      put_w = area.w;      put_h = static_cast<int>(static_cast<float>(put_w)/text_aspect);    }    else {      put_h = area.h;      put_w = static_cast<int>(static_cast<float>(put_h)*text_aspect);    }    return {        .x = area.x+(area.w-put_w)/2,        .y = area.y+(area.h-put_h)/2,        .w = put_w,        .h = put_h,    };  }}Button::Button(int const x, int const y, int const w, int const h, UiColor const color)    :x_{x}, y_{y}, w_{w}, h_{h}, pressed_{false},     visible_{true},     up_{ui_image("button_up", color)},     down_{ui_image("button_down", color)},     font_{"kenney_pixel.ttf"}{  assert(w_>=MIN_WIDTH);  assert(h_>=MIN_HEIGHT);  on_click_ = [] { };}void Button::set_title(std::string const& title){  title_ = title;}std::string const& Button::title() const{  return title_;}void Button::set_pressed(bool const pressed){  pressed_ = pressed;}bool Button::is_pressed() const{  return pressed_;}void Button::render(SDLRenderer& renderer){  if (!visible_)    return;  auto const text = TTF_RenderUTF8_Solid(font_, title_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});  auto const text_ure = SDL_CreateTextureFromSurface(renderer, text);  SDL_FreeSurface(text);  SDL_Texture* texture;  SDL_Rect const* texture_rects;  SDL_Rect target_rects[9];  if (pressed_) {    texture = down_;    texture_rects = ::TEXTURE_RECTS_DOWN;    target_rects[0] = {.x = x_, .y = y_+4, .w = 6, .h = 5};    target_rects[1] = {.x = x_+6, .y = y_+4, .w = w_-12, .h = 5};    target_rects[2] = {.x = x_+w_-6, .y = y_+4, .w = 6, .h = 5};    target_rects[3] = {.x = x_, .y = y_+9, .w = 6, .h = h_-14};    target_rects[4] = {.x = x_+6, .y = y_+9, .w = w_-12, .h = h_-14};    target_rects[5] = {.x = x_+w_-6, .y = y_+9, .w = 6, .h = h_-14};    target_rects[6] = {.x = x_, .y = y_+h_-5, .w = 6, .h = 5};    target_rects[7] = {.x = x_+6, .y = y_+h_-5, .w = w_-12, .h = 5};    target_rects[8] = {.x = x_+w_-6, .y = y_+h_-5, .w = 6, .h = 5};  }  else {    texture = up_;    texture_rects = ::TEXTURE_RECTS_UP;    target_rects[0] = {.x = x_, .y = y_, .w = 6, .h = 5};    target_rects[1] = {.x = x_+6, .y = y_, .w = w_-12, .h = 5};    target_rects[2] = {.x = x_+w_-6, .y = y_, .w = 6, .h = 5};    target_rects[3] = {.x = x_, .y = y_+5, .w = 6, .h = h_-14};    target_rects[4] = {.x = x_+6, .y = y_+5, .w = w_-12, .h = h_-14};    target_rects[5] = {.x = x_+w_-6, .y = y_+5, .w = 6, .h = h_-14};    target_rects[6] = {.x = x_, .y = y_+h_-9, .w = 6, .h = 9};    target_rects[7] = {.x = x_+6, .y = y_+h_-9, .w = w_-12, .h = 9};    target_rects[8] = {.x = x_+w_-6, .y = y_+h_-9, .w = 6, .h = 9};  }  for (int n = 0; n<9; ++n)    SDL_RenderCopy(renderer, texture, texture_rects+n, target_rects+n);  SDL_Rect const text_rect = ::calculate_text_rect(target_rects[4], text_ure);  SDL_RenderCopy(renderer, text_ure, nullptr, &text_rect);  SDL_DestroyTexture(text_ure);}void Button::update(){  if (!visible_)    return;  int mouse_x, mouse_y;  auto const mouse_button = SDL_GetMouseState(&mouse_x, &mouse_y);  if ((mouse_x>=x_ && mouse_x<=(x_+w_) && mouse_y>=y_ && mouse_y<=(y_+h_))) {    if (mouse_button & SDL_BUTTON(SDL_BUTTON_LEFT)) {      pressed_ = true;    }    else {      if (pressed_) {        trigger();      }      pressed_ = false;    }  }  else {    pressed_ = false;  }}void Button::trigger(){  on_click_();}void Button::set_on_click(std::function<void()> handler){  on_click_ = std::move(handler);}void Button::move(int const x, int const y){  x_ = x;  y_ = y;}void Button::resize(int const w, int const h){  assert(w>=MIN_WIDTH);  assert(h>=MIN_HEIGHT);  w_ = w;  h_ = h;}SDL_Rect Button::get_bounding_box() const{  return {x_, y_, w_, h_};}void Button::set_visible(bool const visible){  visible_ = visible;}bool Button::is_visible() const{  return visible_;}
 |