123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- #include "LineInput.hxx"
- #include <cassert>
- namespace {
- SDL_Rect const TEXTURE_RECTS[] = {
- {.x = 0, .y = 0, .w = 8, .h = 8}, // UPPER_LEFT
- {.x = 8, .y = 0, .w = 179, .h = 8}, // UPPER_EDGE
- {.x = 187, .y = 0, .w = 8, .h = 8}, // UPPER_RIGHT
- {.x = 0, .y = 8, .w = 8, .h = 34}, // LEFT_EDGE
- {.x = 8, .y = 8, .w = 179, .h = 34}, // CENTER
- {.x = 187, .y = 8, .w = 8, .h = 34}, // RIGHT_EDGE
- {.x = 0, .y = 42, .w = 8, .h = 7}, // LOWER_LEFT
- {.x = 8, .y = 42, .w = 179, .h = 7}, // LOWER_EDGE
- {.x = 187, .y = 42, .w = 8, .h = 7}, // 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);
- // TODO: the height value is based on the default font height, therefor it should not be hard coded
- // this hack here is a solution for when the text is empty,
- // because in that case we cannot determine the height from the rendered texture
- text_h = 32;
- int const w = std::min(text_w, area.w-LineInput::MIN_WIDTH-8);
- int const h = std::min(text_h, area.h-LineInput::MIN_HEIGHT-8);
- return {
- .x = area.x+12,
- .y = area.y+(area.h-h)/2,
- .w = area.w>=(w+LineInput::MIN_WIDTH+8) ? w : 0,
- .h = area.h>=(h+LineInput::MIN_HEIGHT+8) ? h : 0,
- };
- }
- }
- LineInput::LineInput(int const x, int const y, int const w, int const h, std::string value)
- :value_{std::move(value)}, x_{x}, y_{y}, w_{w}, h_{h}, focus_{false},
- visible_{true},
- texture_{"line_input.png"},
- font_{"kenney_pixel.ttf"}
- {
- assert(w_>=MIN_WIDTH);
- assert(h_>=MIN_HEIGHT);
- }
- void LineInput::move(int const x, int const y)
- {
- x_ = x;
- y_ = y;
- }
- void LineInput::resize(int const w, int const h)
- {
- assert(w>=MIN_WIDTH);
- assert(h>=MIN_HEIGHT);
- w_ = w;
- h_ = h;
- }
- SDL_Rect LineInput::get_bounding_box() const
- {
- return {x_, y_, w_, h_};
- }
- void LineInput::set_focus(bool const focus)
- {
- focus_ = focus;
- if (!focus_)
- SDL_StopTextInput();
- }
- bool LineInput::has_focus() const
- {
- return focus_;
- }
- void LineInput::on_event(SDL_Event const& evt)
- {
- if (!focus_ || !visible_)
- return;
- if (evt.type==SDL_TEXTINPUT) {
- value_ += evt.text.text;
- }
- else if (evt.type==SDL_KEYDOWN && evt.key.keysym.sym==SDLK_BACKSPACE) {
- if (!value_.empty()) {
- auto const begin = value_.begin();
- auto const end = value_.end();
- auto it = value_.end()-1;
- // For UTF-8 multibyte characters
- while (it!=begin && ((*it & 0xC0)==0x80)) {
- --it;
- }
- value_.erase(it, end);
- }
- }
- }
- void LineInput::update(std::chrono::milliseconds const delta_time)
- {
- if (!visible_)
- return;
- if (focus_ && !SDL_IsTextInputActive())
- SDL_StartTextInput();
- blink_timer_ = (blink_timer_+delta_time)%1'000u;
- auto const rect = get_bounding_box();
- SDL_SetTextInputRect(&rect);
- }
- void LineInput::render(SDLRenderer& renderer)
- {
- if (!visible_)
- return;
- using namespace std::chrono_literals;
- SDL_Texture* const background = texture_;
- SDL_Rect const target_rects[9] = {
- {.x = x_, .y = y_, .w = 8, .h = 8},
- {.x = x_+8, .y = y_, .w = w_-16, .h = 8},
- {.x = x_+w_-8, .y = y_, .w = 8, .h = 8},
- {.x = x_, .y = y_+8, .w = 8, .h = h_-15},
- {.x = x_+8, .y = y_+8, .w = w_-16, .h = h_-15},
- {.x = x_+w_-8, .y = y_+8, .w = 8, .h = h_-15},
- {.x = x_, .y = y_+h_-7, .w = 8, .h = 7},
- {.x = x_+8, .y = y_+h_-7, .w = w_-16, .h = 7},
- {.x = x_+w_-8, .y = y_+h_-7, .w = 8, .h = 7},
- };
- for (int n = 0; n<9; ++n)
- SDL_RenderCopy(renderer, background, ::TEXTURE_RECTS+n, target_rects+n);
- auto const text = TTF_RenderUTF8_Solid(font_, value_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});
- auto const text_ure = SDL_CreateTextureFromSurface(renderer, text);
- SDL_FreeSurface(text);
- auto const text_rect = ::calculate_text_rect(target_rects[4], text_ure);
- SDL_Rect const text_texture_rect{
- .x = 0, .y = 0, .w = text_rect.w, .h = text_rect.h,
- };
- SDL_RenderCopy(renderer, text_ure, &text_texture_rect, &text_rect);
- SDL_DestroyTexture(text_ure);
- if (focus_) {
- SDL_Rect cursor_rect{
- .x = text_rect.x+text_rect.w+1, .y = text_rect.y, .w = 2, .h = text_rect.h,
- };
- SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
- if (blink_timer_<500ms) {
- SDL_RenderFillRect(renderer, &cursor_rect);
- }
- SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
- }
- }
- char const* LineInput::value() const
- {
- return value_.c_str();
- }
- void LineInput::set_value(std::string value)
- {
- value_ = std::move(value);
- }
- bool LineInput::is_visible() const
- {
- return visible_;
- }
- void LineInput::set_visible(bool visible)
- {
- visible_ = visible;
- }
|