| Line |
Branch |
Exec |
Source |
| 1 |
|
|
#include "eu/mrgui/widgets.h" |
| 2 |
|
|
|
| 3 |
|
|
#include <SDL_timer.h> |
| 4 |
|
|
|
| 5 |
|
|
#include "eu/render/canvas.h" |
| 6 |
|
|
#include "eu/render/font.h" |
| 7 |
|
|
|
| 8 |
|
|
namespace eu::mrgui |
| 9 |
|
|
{ |
| 10 |
|
|
namespace |
| 11 |
|
|
{ |
| 12 |
|
|
/// djb2 initial hash |
| 13 |
|
|
constexpr u32 initial_hash = 5381; |
| 14 |
|
|
|
| 15 |
|
|
/// djb2 hash function |
| 16 |
|
✗ |
u32 hash(const unsigned char* str, std::size_t length, u32 seed) |
| 17 |
|
|
{ |
| 18 |
|
✗ |
u32 hash = seed; |
| 19 |
|
|
|
| 20 |
0/2
✗ Branch 4 → 3 not taken.
✗ Branch 4 → 5 not taken.
|
✗ |
for (std::size_t index = 0; index < length; index += 1) |
| 21 |
|
|
{ |
| 22 |
|
✗ |
auto c = str[index]; |
| 23 |
|
✗ |
hash = ((hash << 5) + hash) + c; |
| 24 |
|
|
} |
| 25 |
|
|
|
| 26 |
|
✗ |
return hash; |
| 27 |
|
|
} |
| 28 |
|
|
} |
| 29 |
|
|
|
| 30 |
|
✗ |
[[nodiscard]] u32 IdStack::get(const std::string& str) const |
| 31 |
|
|
{ |
| 32 |
|
✗ |
return hash(reinterpret_cast<const unsigned char*>(str.data()), str.size(), last_id()); |
| 33 |
|
|
} |
| 34 |
|
|
|
| 35 |
|
✗ |
[[nodiscard]] u32 IdStack::get(int id) const |
| 36 |
|
|
{ |
| 37 |
|
✗ |
return hash(reinterpret_cast<unsigned char*>(&id), sizeof(int), last_id()); |
| 38 |
|
|
} |
| 39 |
|
✗ |
void IdStack::push(const std::string& str) |
| 40 |
|
|
{ |
| 41 |
0/4
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 5 not taken.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
|
✗ |
stack.push_back(get(str)); |
| 42 |
|
✗ |
} |
| 43 |
|
✗ |
void IdStack::push(int id) |
| 44 |
|
|
{ |
| 45 |
0/4
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 5 not taken.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
|
✗ |
stack.push_back(get(id)); |
| 46 |
|
✗ |
} |
| 47 |
|
✗ |
void IdStack::pop() |
| 48 |
|
|
{ |
| 49 |
|
✗ |
stack.pop_back(); |
| 50 |
|
✗ |
} |
| 51 |
|
✗ |
[[nodiscard]] u32 IdStack::last_id() const |
| 52 |
|
|
{ |
| 53 |
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
|
✗ |
return stack.empty() ? initial_hash : *stack.rbegin(); |
| 54 |
|
|
} |
| 55 |
|
|
|
| 56 |
|
|
|
| 57 |
|
|
|
| 58 |
|
✗ |
void UiState::begin() |
| 59 |
|
|
{ |
| 60 |
|
✗ |
hot_item = std::nullopt; |
| 61 |
|
✗ |
} |
| 62 |
|
|
|
| 63 |
|
✗ |
void UiState::end() |
| 64 |
|
|
{ |
| 65 |
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 7 not taken.
|
✗ |
if (mousedown) |
| 66 |
|
|
{ |
| 67 |
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 9 not taken.
|
✗ |
if (active_item == std::nullopt) |
| 68 |
|
|
{ |
| 69 |
|
✗ |
active_item = std::nullopt; |
| 70 |
|
✗ |
active_item_locked = true; |
| 71 |
|
|
} |
| 72 |
|
|
} |
| 73 |
|
|
else |
| 74 |
|
|
{ |
| 75 |
|
✗ |
active_item = std::nullopt; |
| 76 |
|
✗ |
active_item_locked = false; |
| 77 |
|
|
} |
| 78 |
|
|
|
| 79 |
0/6
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 14 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 14 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
|
✗ |
if (key && key->key == SDLK_TAB) |
| 80 |
|
|
{ |
| 81 |
|
✗ |
kbd_item = std::nullopt; |
| 82 |
|
|
} |
| 83 |
|
✗ |
key = std::nullopt; |
| 84 |
|
✗ |
keychar = std::nullopt; |
| 85 |
|
✗ |
} |
| 86 |
|
|
|
| 87 |
|
✗ |
[[nodiscard]] bool UiState::is_active_free() const |
| 88 |
|
|
{ |
| 89 |
0/4
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 6 not taken.
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
|
✗ |
return active_item_locked == false && active_item.has_value() == false; |
| 90 |
|
|
} |
| 91 |
|
|
|
| 92 |
|
|
// Simple button IMGUI widget |
| 93 |
|
✗ |
bool button_logic(u32 id, const Rect& rect, UiState& uistate) |
| 94 |
|
|
{ |
| 95 |
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 13 not taken.
|
✗ |
if (is_within(uistate.mouse, rect)) |
| 96 |
|
|
{ |
| 97 |
|
✗ |
uistate.hot_item = id; |
| 98 |
0/6
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 9 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 9 not taken.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 13 not taken.
|
✗ |
if (uistate.is_active_free() && uistate.mousedown) |
| 99 |
|
|
{ |
| 100 |
|
✗ |
uistate.active_item = id; |
| 101 |
|
|
} |
| 102 |
|
|
} |
| 103 |
|
|
|
| 104 |
0/2
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 17 not taken.
|
✗ |
if (uistate.kbd_item == std::nullopt) |
| 105 |
|
|
{ |
| 106 |
|
✗ |
uistate.kbd_item = id; |
| 107 |
|
|
} |
| 108 |
|
|
|
| 109 |
|
|
// keyboard interaction |
| 110 |
0/2
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 31 not taken.
|
✗ |
if (uistate.kbd_item == id) |
| 111 |
|
|
{ |
| 112 |
0/2
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 31 not taken.
|
✗ |
if (uistate.key.has_value()) |
| 113 |
|
|
{ |
| 114 |
|
✗ |
const auto k = *uistate.key; |
| 115 |
|
✗ |
uistate.key = std::nullopt; |
| 116 |
0/3
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 28 not taken.
✗ Branch 23 → 29 not taken.
|
✗ |
switch (k.key) |
| 117 |
|
|
{ |
| 118 |
|
✗ |
case SDLK_TAB: |
| 119 |
|
✗ |
uistate.kbd_item = std::nullopt; |
| 120 |
0/2
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
|
✗ |
if ((k.mod & SDL_KMOD_SHIFT) != 0) |
| 121 |
|
|
{ |
| 122 |
|
✗ |
uistate.kbd_item = uistate.last_widget; |
| 123 |
|
|
} |
| 124 |
|
✗ |
break; |
| 125 |
|
✗ |
case SDLK_RETURN: |
| 126 |
|
✗ |
return true; |
| 127 |
|
✗ |
default: |
| 128 |
|
|
// ignore |
| 129 |
|
✗ |
break; |
| 130 |
|
|
} |
| 131 |
|
|
} |
| 132 |
|
|
} |
| 133 |
|
|
|
| 134 |
|
✗ |
uistate.last_widget = id; |
| 135 |
|
|
|
| 136 |
|
|
// If button is hot and active, but mouse button is not |
| 137 |
|
|
// down, the user must have clicked the button. |
| 138 |
0/2
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 38 not taken.
|
✗ |
if (uistate.mousedown == false && |
| 139 |
0/6
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 38 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 38 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
|
✗ |
uistate.hot_item == id && |
| 140 |
|
✗ |
uistate.active_item == id) |
| 141 |
|
|
{ |
| 142 |
|
✗ |
return true; |
| 143 |
|
|
} |
| 144 |
|
|
|
| 145 |
|
|
// Otherwise, no clicky. |
| 146 |
|
✗ |
return false; |
| 147 |
|
|
} |
| 148 |
|
|
|
| 149 |
|
✗ |
bool basic_button(u32 id, const Rect& rect, UiState& uistate, render::SpriteBatch* batch) |
| 150 |
|
|
{ |
| 151 |
|
✗ |
const auto ret = button_logic(id, rect, uistate); |
| 152 |
|
|
|
| 153 |
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 14 not taken.
|
✗ |
if (uistate.kbd_item == id) |
| 154 |
|
|
{ |
| 155 |
|
✗ |
render::Quad{ |
| 156 |
|
|
.tint = colors::red_vermillion |
| 157 |
0/6
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 44 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 43 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 43 not taken.
|
✗ |
}.draw(batch, rect.with_inset(Lrtb{ -4 }).with_offset({ 8, -8 })); |
| 158 |
|
|
} |
| 159 |
|
|
|
| 160 |
|
|
// shadow |
| 161 |
|
✗ |
render::Quad{ |
| 162 |
|
|
.tint = colors::black |
| 163 |
0/4
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 48 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 48 not taken.
|
✗ |
}.draw(batch, rect.with_offset({ 8.0f, -8.0f })); |
| 164 |
|
|
|
| 165 |
0/2
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 36 not taken.
|
✗ |
if (uistate.hot_item == id) |
| 166 |
|
|
{ |
| 167 |
0/2
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 31 not taken.
|
✗ |
if (uistate.active_item == id) |
| 168 |
|
|
{ |
| 169 |
|
|
// Button is both 'hot' and 'active' |
| 170 |
0/4
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 51 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 51 not taken.
|
✗ |
render::Quad{ .tint = colors::white }.draw(batch, rect.with_offset({ 2.0f, -2.0f })); |
| 171 |
|
|
} |
| 172 |
|
|
else |
| 173 |
|
|
{ |
| 174 |
|
|
// Button is merely 'hot' |
| 175 |
0/2
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 54 not taken.
|
✗ |
render::Quad{ .tint = colors::white }.draw(batch, rect); |
| 176 |
|
|
} |
| 177 |
|
|
} |
| 178 |
|
|
else |
| 179 |
|
|
{ |
| 180 |
|
|
// button is not hot, but it may be active |
| 181 |
0/2
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 55 not taken.
|
✗ |
render::Quad{ .tint = colors::orange }.draw(batch, rect); |
| 182 |
|
|
// todo(Gustav): add grays |
| 183 |
|
|
} |
| 184 |
|
|
|
| 185 |
|
✗ |
return ret; |
| 186 |
|
|
} |
| 187 |
|
|
|
| 188 |
|
✗ |
bool button_texture(u32 id, const Rect& rect, UiState& uistate, render::SpriteBatch* batch, const render::Texture2d* texture, const Rect& active, const Rect& hot, const Rect& normal) |
| 189 |
|
|
{ |
| 190 |
|
✗ |
const auto ret = button_logic(id, rect, uistate); |
| 191 |
|
|
|
| 192 |
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 14 not taken.
|
✗ |
if (uistate.kbd_item == id) |
| 193 |
|
|
{ |
| 194 |
|
|
// keep? |
| 195 |
|
✗ |
render::Quad{ |
| 196 |
|
|
.tint = colors::red_vermillion |
| 197 |
0/6
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 39 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 38 not taken.
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 38 not taken.
|
✗ |
}.draw(batch, rect.with_inset(Lrtb{ -4 }).with_offset({ 8, -8 })); |
| 198 |
|
|
} |
| 199 |
|
|
|
| 200 |
0/2
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 30 not taken.
|
✗ |
if (uistate.hot_item == id) |
| 201 |
|
|
{ |
| 202 |
0/2
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 24 not taken.
|
✗ |
if (uistate.active_item == id) |
| 203 |
|
|
{ |
| 204 |
|
|
// Button is both 'hot' and 'active' |
| 205 |
0/2
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 43 not taken.
|
✗ |
render::Quad{ .texture = texture, .texturecoord = active }.draw(batch, rect); |
| 206 |
|
|
} |
| 207 |
|
|
else |
| 208 |
|
|
{ |
| 209 |
|
|
// Button is merely 'hot' |
| 210 |
0/2
✗ Branch 28 → 29 not taken.
✗ Branch 28 → 44 not taken.
|
✗ |
render::Quad{ .texture = texture, .texturecoord = hot }.draw(batch, rect); |
| 211 |
|
|
} |
| 212 |
|
|
} |
| 213 |
|
|
else |
| 214 |
|
|
{ |
| 215 |
|
|
// button is not hot, but it may be active |
| 216 |
0/2
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 45 not taken.
|
✗ |
render::Quad{ .texture = texture, .texturecoord = normal }.draw(batch, rect); |
| 217 |
|
|
// todo(Gustav): add grays |
| 218 |
|
|
} |
| 219 |
|
|
|
| 220 |
|
✗ |
return ret; |
| 221 |
|
|
} |
| 222 |
|
|
|
| 223 |
|
✗ |
bool slider(u32 id, float* val, const Rect& rect, UiState& uistate, render::SpriteBatch* batch) |
| 224 |
|
|
{ |
| 225 |
|
✗ |
const auto grab_size = 16.0f; |
| 226 |
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 82 not taken.
|
✗ |
const auto inner = rect.with_inset(Lrtb{ 3.0f }); |
| 227 |
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 92 not taken.
|
✗ |
const auto avail = inner.get_size().x - grab_size; |
| 228 |
|
✗ |
const auto pos = avail * *val; |
| 229 |
|
|
|
| 230 |
0/6
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 84 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 84 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 83 not taken.
|
✗ |
const auto grabber_rect = Rect::from_size({ grab_size, inner.get_size().y }).with_bottom_left_at({ inner.left + pos, inner.bottom }); |
| 231 |
|
|
|
| 232 |
0/4
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 92 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 21 not taken.
|
✗ |
if (is_within(uistate.mouse, inner)) |
| 233 |
|
|
{ |
| 234 |
|
✗ |
uistate.hot_item = id; |
| 235 |
0/6
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 17 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 21 not taken.
|
✗ |
if (uistate.is_active_free() && uistate.mousedown) |
| 236 |
|
|
{ |
| 237 |
|
✗ |
uistate.active_item = id; |
| 238 |
|
|
} |
| 239 |
|
|
} |
| 240 |
|
|
|
| 241 |
0/2
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 25 not taken.
|
✗ |
if (uistate.kbd_item == std::nullopt) |
| 242 |
|
|
{ |
| 243 |
|
✗ |
uistate.kbd_item = id; |
| 244 |
|
|
} |
| 245 |
|
|
|
| 246 |
0/2
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 34 not taken.
|
✗ |
if (uistate.kbd_item == id) |
| 247 |
|
|
{ |
| 248 |
0/4
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 86 not taken.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 86 not taken.
|
✗ |
render::Quad{ .tint = colors::red_vermillion }.draw(batch, rect.with_inset(Lrtb{ -6 })); |
| 249 |
|
|
} |
| 250 |
|
|
|
| 251 |
0/2
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 89 not taken.
|
✗ |
render::Quad{ .tint = colors::orange }.draw(batch, rect); |
| 252 |
0/6
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 45 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 45 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 90 not taken.
|
✗ |
render::Quad{ .tint = uistate.active_item == id && uistate.hot_item == id ? colors::white : colors::green_bluish }.draw(batch, grabber_rect); |
| 253 |
0/2
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 71 not taken.
|
✗ |
if (uistate.kbd_item == id) |
| 254 |
|
|
{ |
| 255 |
0/2
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 71 not taken.
|
✗ |
if (uistate.key.has_value()) |
| 256 |
|
|
{ |
| 257 |
|
✗ |
const auto k = *uistate.key; |
| 258 |
|
✗ |
uistate.key = std::nullopt; |
| 259 |
|
✗ |
constexpr float change = 0.1f; |
| 260 |
|
|
|
| 261 |
0/4
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 60 not taken.
✗ Branch 55 → 64 not taken.
✗ Branch 55 → 68 not taken.
|
✗ |
switch (k.key) |
| 262 |
|
|
{ |
| 263 |
|
✗ |
case SDLK_TAB: |
| 264 |
|
✗ |
uistate.kbd_item = std::nullopt; |
| 265 |
0/2
✗ Branch 57 → 58 not taken.
✗ Branch 57 → 59 not taken.
|
✗ |
if ((k.mod & SDL_KMOD_SHIFT) != 0) |
| 266 |
|
|
{ |
| 267 |
|
✗ |
uistate.kbd_item = uistate.last_widget; |
| 268 |
|
|
} |
| 269 |
|
✗ |
break; |
| 270 |
|
✗ |
case SDLK_LEFT: |
| 271 |
0/2
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 63 not taken.
|
✗ |
if (*val > 0) |
| 272 |
|
|
{ |
| 273 |
|
✗ |
*val = std::max(0.0f, *val - change); |
| 274 |
|
✗ |
return true; |
| 275 |
|
|
} |
| 276 |
|
✗ |
break; |
| 277 |
|
✗ |
case SDLK_RIGHT: |
| 278 |
0/2
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 67 not taken.
|
✗ |
if (*val < 1) |
| 279 |
|
|
{ |
| 280 |
|
✗ |
*val = std::min(1.0f, *val + change); |
| 281 |
|
✗ |
return true; |
| 282 |
|
|
} |
| 283 |
|
✗ |
break; |
| 284 |
|
✗ |
default: |
| 285 |
|
|
// ignore |
| 286 |
|
✗ |
break; |
| 287 |
|
|
} |
| 288 |
|
|
} |
| 289 |
|
|
} |
| 290 |
|
|
|
| 291 |
|
✗ |
uistate.last_widget = id; |
| 292 |
|
|
|
| 293 |
0/2
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 79 not taken.
|
✗ |
if (uistate.active_item == id) |
| 294 |
|
|
{ |
| 295 |
0/2
✗ Branch 74 → 75 not taken.
✗ Branch 74 → 91 not taken.
|
✗ |
const auto new_range = to_01(inner, uistate.mouse); |
| 296 |
|
✗ |
const auto new_value = keep_within01(new_range.x); |
| 297 |
0/2
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 78 not taken.
|
✗ |
if (*val != new_value) |
| 298 |
|
|
{ |
| 299 |
|
✗ |
*val = new_value; |
| 300 |
|
✗ |
return true; |
| 301 |
|
|
} |
| 302 |
|
|
} |
| 303 |
|
|
|
| 304 |
|
✗ |
return false; |
| 305 |
|
|
} |
| 306 |
|
|
|
| 307 |
|
|
|
| 308 |
|
✗ |
void draw_text(render::SpriteBatch* batch, render::DrawableFont* font, const std::string& str, float size, const v2& pos, const Rgb& color) |
| 309 |
|
|
{ |
| 310 |
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 11 not taken.
|
✗ |
render::DrawableText text{ font }; |
| 311 |
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 9 not taken.
|
✗ |
text.set_text(str); |
| 312 |
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 9 not taken.
|
✗ |
text.set_size(size); |
| 313 |
0/2
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 9 not taken.
|
✗ |
text.compile(); |
| 314 |
0/2
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 9 not taken.
|
✗ |
text.draw(batch, pos, color, color); |
| 315 |
|
✗ |
} |
| 316 |
|
|
|
| 317 |
|
✗ |
bool textfield(u32 id, std::string* val, render::DrawableFont* font, float size, const v2& pos, UiState& uistate, render::SpriteBatch* batch) |
| 318 |
|
|
{ |
| 319 |
|
|
using namespace std; |
| 320 |
|
✗ |
bool changed = false; |
| 321 |
|
|
|
| 322 |
|
✗ |
auto& text_state = uistate.text_state; |
| 323 |
|
|
|
| 324 |
|
|
// Layout |
| 325 |
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 174 not taken.
|
✗ |
render::DrawableText text{ font }; |
| 326 |
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 172 not taken.
|
✗ |
text.set_text(*val); |
| 327 |
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 172 not taken.
|
✗ |
text.set_size(size); |
| 328 |
0/2
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 172 not taken.
|
✗ |
text.compile(); |
| 329 |
0/6
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 145 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 145 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 144 not taken.
|
✗ |
const auto rect = text.get_extents().with_offset(pos).with_inset(Lrtb{ -6 }); |
| 330 |
|
|
|
| 331 |
0/4
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 172 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 21 not taken.
|
✗ |
if (is_within(uistate.mouse, rect)) { |
| 332 |
|
✗ |
uistate.hot_item = id; |
| 333 |
0/6
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 17 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 17 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 21 not taken.
|
✗ |
if (uistate.active_item == std::nullopt && uistate.mousedown) |
| 334 |
|
|
{ |
| 335 |
|
✗ |
uistate.active_item = id; |
| 336 |
0/2
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 172 not taken.
|
✗ |
text_state.focus(val); |
| 337 |
|
|
} |
| 338 |
|
|
} |
| 339 |
0/2
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 25 not taken.
|
✗ |
if (uistate.kbd_item == std::nullopt) |
| 340 |
|
|
{ |
| 341 |
|
✗ |
uistate.kbd_item = id; |
| 342 |
|
|
} |
| 343 |
|
|
|
| 344 |
0/2
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 34 not taken.
|
✗ |
if (uistate.kbd_item == id) { |
| 345 |
0/4
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 147 not taken.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 147 not taken.
|
✗ |
render::Quad{ .tint = colors::red_vermillion }.draw(batch, rect.with_inset(Lrtb{ -6 })); |
| 346 |
|
|
} |
| 347 |
0/6
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 38 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 48 not taken.
|
✗ |
if (uistate.active_item == id || uistate.hot_item == id) { |
| 348 |
0/4
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 150 not taken.
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 150 not taken.
|
✗ |
render::Quad{ .tint = colors::white }.draw(batch, rect.with_inset(Lrtb{ -6 })); |
| 349 |
|
|
} |
| 350 |
|
|
else { |
| 351 |
0/4
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 153 not taken.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 153 not taken.
|
✗ |
render::Quad{ .tint = colors::green_bluish }.draw(batch, rect.with_inset(Lrtb{ -6 })); |
| 352 |
|
|
} |
| 353 |
|
|
|
| 354 |
|
|
// Handle input |
| 355 |
0/2
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 92 not taken.
|
✗ |
if (uistate.kbd_item == id) { |
| 356 |
0/2
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 80 not taken.
|
✗ |
if (uistate.key.has_value()) { |
| 357 |
0/2
✗ Branch 59 → 60 not taken.
✗ Branch 59 → 156 not taken.
|
✗ |
auto k = uistate.key.value(); |
| 358 |
|
✗ |
const auto shift = k.mod & SDL_KMOD_SHIFT; |
| 359 |
0/8
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 63 not taken.
✗ Branch 60 → 65 not taken.
✗ Branch 60 → 67 not taken.
✗ Branch 60 → 69 not taken.
✗ Branch 60 → 71 not taken.
✗ Branch 60 → 73 not taken.
✗ Branch 60 → 78 not taken.
|
✗ |
switch (k.key) { |
| 360 |
0/2
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 156 not taken.
|
✗ |
case SDLK_LEFT: text_state.onKeyLeft(shift != 0, val); changed = true; break; |
| 361 |
0/2
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 156 not taken.
|
✗ |
case SDLK_RIGHT: text_state.onKeyRight(shift != 0, val); changed = true; break; |
| 362 |
0/2
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 156 not taken.
|
✗ |
case SDLK_HOME: text_state.onKeyLineStart(shift != 0, val); changed = true; break; |
| 363 |
0/2
✗ Branch 67 → 68 not taken.
✗ Branch 67 → 156 not taken.
|
✗ |
case SDLK_END: text_state.onKeyLineEnd(shift != 0, val); changed = true; break; |
| 364 |
0/2
✗ Branch 69 → 70 not taken.
✗ Branch 69 → 156 not taken.
|
✗ |
case SDLK_BACKSPACE: text_state.onKeyBackspace(shift != 0, val); changed = true; break; |
| 365 |
0/2
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 156 not taken.
|
✗ |
case SDLK_DELETE: text_state.onKeyDelete(shift != 0, val); changed = true; break; |
| 366 |
|
✗ |
case SDLK_RETURN: // ignore |
| 367 |
|
|
case SDLK_TAB: |
| 368 |
|
✗ |
uistate.kbd_item = std::nullopt; |
| 369 |
0/2
✗ Branch 74 → 75 not taken.
✗ Branch 74 → 76 not taken.
|
✗ |
if (shift != 0) |
| 370 |
|
|
{ |
| 371 |
|
✗ |
uistate.kbd_item = uistate.last_widget; |
| 372 |
|
|
} |
| 373 |
|
✗ |
uistate.key = std::nullopt; |
| 374 |
|
✗ |
break; |
| 375 |
|
✗ |
default: break; |
| 376 |
|
|
} |
| 377 |
|
|
} |
| 378 |
0/8
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 87 not taken.
✗ Branch 83 → 84 not taken.
✗ Branch 83 → 87 not taken.
✗ Branch 85 → 86 not taken.
✗ Branch 85 → 87 not taken.
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 92 not taken.
|
✗ |
if (uistate.keychar.has_value() && *uistate.keychar >= 32 && *uistate.keychar < 127) { |
| 379 |
|
✗ |
const char c = *uistate.keychar; |
| 380 |
0/2
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 172 not taken.
|
✗ |
text_state.onChar(c, val); |
| 381 |
|
✗ |
changed = true; |
| 382 |
|
|
} |
| 383 |
|
|
} |
| 384 |
|
|
|
| 385 |
|
|
// Mouse click to set cursor |
| 386 |
0/8
✗ Branch 92 → 93 not taken.
✗ Branch 92 → 98 not taken.
✗ Branch 94 → 95 not taken.
✗ Branch 94 → 98 not taken.
✗ Branch 96 → 97 not taken.
✗ Branch 96 → 98 not taken.
✗ Branch 99 → 100 not taken.
✗ Branch 99 → 103 not taken.
|
✗ |
if (uistate.mousedown == false && uistate.hot_item == id && uistate.active_item == id) { |
| 387 |
|
|
// Map mouse x to character index |
| 388 |
0/2
✗ Branch 100 → 101 not taken.
✗ Branch 100 → 172 not taken.
|
✗ |
text_state.click(val, uistate.mouse.x - rect.left, uistate.mouse.y - rect.top); |
| 389 |
|
✗ |
uistate.kbd_item = id; |
| 390 |
|
|
} |
| 391 |
|
|
|
| 392 |
|
✗ |
uistate.last_widget = id; |
| 393 |
|
|
|
| 394 |
|
|
static int last_cursor = -10; |
| 395 |
|
✗ |
const auto current_cursor = text_state.state.cursor; |
| 396 |
0/2
✗ Branch 104 → 105 not taken.
✗ Branch 104 → 110 not taken.
|
✗ |
if (last_cursor != current_cursor) |
| 397 |
|
|
{ |
| 398 |
0/6
✗ Branch 105 → 106 not taken.
✗ Branch 105 → 172 not taken.
✗ Branch 106 → 107 not taken.
✗ Branch 106 → 159 not taken.
✗ Branch 107 → 108 not taken.
✗ Branch 107 → 157 not taken.
|
✗ |
LOG_INFO("Cursor position: {}", current_cursor); |
| 399 |
|
✗ |
last_cursor = current_cursor; |
| 400 |
|
|
} |
| 401 |
|
|
|
| 402 |
|
|
// Draw text |
| 403 |
0/2
✗ Branch 110 → 111 not taken.
✗ Branch 110 → 172 not taken.
|
✗ |
text.draw(batch, pos, colors::black, colors::black); |
| 404 |
|
|
|
| 405 |
|
|
// Draw cursor |
| 406 |
0/2
✗ Branch 112 → 113 not taken.
✗ Branch 112 → 141 not taken.
|
✗ |
if (uistate.kbd_item == id) { |
| 407 |
0/2
✗ Branch 113 → 114 not taken.
✗ Branch 113 → 172 not taken.
|
✗ |
const auto is_cursor_blink_visible = (SDL_GetTicks() >> 8) & 1; |
| 408 |
0/2
✗ Branch 114 → 115 not taken.
✗ Branch 114 → 141 not taken.
|
✗ |
if (is_cursor_blink_visible != 0) { |
| 409 |
|
|
// Compute cursor x |
| 410 |
|
✗ |
float cursor_x = rect.left; |
| 411 |
0/6
✗ Branch 115 → 116 not taken.
✗ Branch 115 → 120 not taken.
✗ Branch 118 → 119 not taken.
✗ Branch 118 → 120 not taken.
✗ Branch 121 → 122 not taken.
✗ Branch 121 → 132 not taken.
|
✗ |
if (text_state.state.cursor > 0 && std::cmp_less_equal(text_state.state.cursor, val->size())) { |
| 412 |
0/2
✗ Branch 122 → 123 not taken.
✗ Branch 122 → 166 not taken.
|
✗ |
const std::string substr = val->substr(0, text_state.state.cursor); |
| 413 |
0/2
✗ Branch 123 → 124 not taken.
✗ Branch 123 → 164 not taken.
|
✗ |
render::DrawableText t{ font }; |
| 414 |
0/2
✗ Branch 124 → 125 not taken.
✗ Branch 124 → 162 not taken.
|
✗ |
t.set_text(substr); |
| 415 |
0/2
✗ Branch 125 → 126 not taken.
✗ Branch 125 → 162 not taken.
|
✗ |
t.set_size(size); |
| 416 |
0/2
✗ Branch 126 → 127 not taken.
✗ Branch 126 → 162 not taken.
|
✗ |
t.compile(); |
| 417 |
0/4
✗ Branch 127 → 128 not taken.
✗ Branch 127 → 161 not taken.
✗ Branch 128 → 129 not taken.
✗ Branch 128 → 161 not taken.
|
✗ |
cursor_x = rect.left + t.get_extents().get_size().x; |
| 418 |
|
✗ |
} |
| 419 |
|
✗ |
const auto ascent = font->ascent * size; |
| 420 |
|
✗ |
const auto descent = font->descent * size; |
| 421 |
0/6
✗ Branch 136 → 137 not taken.
✗ Branch 136 → 168 not taken.
✗ Branch 138 → 139 not taken.
✗ Branch 138 → 167 not taken.
✗ Branch 139 → 140 not taken.
✗ Branch 139 → 167 not taken.
|
✗ |
render::Quad{ .tint = colors::black }.draw(batch, Rect::from_size({ 1, ascent + -descent }).with_bottom_left_at({ cursor_x, pos.y + descent })); |
| 422 |
|
|
} |
| 423 |
|
|
} |
| 424 |
|
|
|
| 425 |
|
✗ |
return changed; |
| 426 |
|
✗ |
} |
| 427 |
|
|
|
| 428 |
|
|
|
| 429 |
|
|
} |
| 430 |
|
|
|