Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions base/sources/iron_ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -2339,6 +2339,31 @@ int ui_combo(ui_handle_t *handle, string_array_t *texts, char *label, bool show_
return handle->i;
}

static char *ui_normalize_numeric_text(char *text) {
static char normalized[1024];
bool comma = false;
bool digits = false;
bool invalid = false;
for (int i = 0; text[i] != 0 && i < (int)sizeof(normalized) - 1; ++i) {
char c = text[i];
if (c >= '0' && c <= '9') {
digits = true;
}
else if (c == ',') {
if (comma) {
invalid = true;
}
comma = true;
}
else if (c != ' ' && c != '\t' && !((c == '+' || c == '-') && i == 0)) {
invalid = true;
}
normalized[i] = c == ',' ? '.' : c;
normalized[i + 1] = 0;
}
return comma && digits && !invalid ? normalized : text;
}

float ui_slider(ui_handle_t *handle, char *text, float from, float to, bool filled, float precision, bool display_value, int align, bool text_edit) {
static char temp[1024];
if (!ui_is_visible(UI_ELEMENT_H())) {
Expand Down Expand Up @@ -2399,15 +2424,16 @@ float ui_slider(ui_handle_t *handle, char *text, float from, float to, bool fill
}
if (current->submit_text_handle == handle) {
ui_submit_text_edit();
char *numeric_text = ui_normalize_numeric_text(handle->text);
#ifdef WITH_EVAL
if (handle->text[0] == '.') {
handle->text = string("0%s", handle->text);
if (numeric_text[0] == '.') {
numeric_text = string("0%s", numeric_text);
}
minic_ctx_t *_ctx = minic_eval(string("float main() { return %s; }", handle->text));
minic_ctx_t *_ctx = minic_eval(string("float main() { return %s; }", numeric_text));
handle->f = minic_ctx_result(_ctx);
minic_ctx_free(_ctx);
#else
handle->f = atof(handle->text);
handle->f = atof(numeric_text);
#endif
handle->changed = current->changed = true;
}
Expand Down
10 changes: 10 additions & 0 deletions paint/sources/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ typedef struct version {

bool config_loaded = false;

f32 config_validate_window_scale(f32 scale) {
if (!(scale >= 1.0 && scale <= 4.0)) {
return 1.0;
}
return scale;
}

void config_load() {
char *path = "";
if (path_is_protected()) {
Expand Down Expand Up @@ -36,6 +43,7 @@ void config_load() {
gc_unroot(g_config);
g_config = json_parse(config_string);
gc_root(g_config);
g_config->window_scale = config_validate_window_scale(g_config->window_scale);
}
}
}
Expand All @@ -44,6 +52,7 @@ void config_save() {
if (g_config->workspace == WORKSPACE_PLAYER) {
return;
}
g_config->window_scale = config_validate_window_scale(g_config->window_scale);

// Use system application data folder
// when running from protected path like "Program Files"
Expand Down Expand Up @@ -395,6 +404,7 @@ void config_import_from(config_t *from) {
gc_root(g_config);
g_config->sha = string_copy(_sha);
g_config->version = string_copy(_version);
g_config->window_scale = config_validate_window_scale(g_config->window_scale);
gc_unroot(ui_children);
ui_children = any_map_create(); // Reset ui handles
gc_root(ui_children);
Expand Down
1 change: 1 addition & 0 deletions paint/sources/functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ void render_envsphere();
void render_pathsphere();
void config_load();
void config_save();
f32 config_validate_window_scale(f32 scale);
void config_init();
void config_init_layout();
char *config_get_sha();
Expand Down
3 changes: 2 additions & 1 deletion paint/sources/ui/box_preferences.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ ui_handle_t *_box_preferences_h;
i32 _box_preferences_i;

void box_preferences_set_scale() {
f32 scale = g_config->window_scale;
g_config->window_scale = config_validate_window_scale(g_config->window_scale);
f32 scale = g_config->window_scale;
ui_set_scale(scale);
ui_header_h = math_floor(ui_header_default_h * scale);
g_config->layout->buffer[LAYOUT_SIZE_STATUS_H] = math_floor(ui_statusbar_default_h * scale);
Expand Down
30 changes: 30 additions & 0 deletions paint/tests/test_issue_2082.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Regression checks for locale-safe UI scale input."""

from pathlib import Path


ROOT = Path(__file__).resolve().parents[2]


def read(*parts: str) -> str:
return ROOT.joinpath(*parts).read_text(encoding="utf-8")


def test_slider_normalizes_a_decimal_comma_before_evaluation() -> None:
ui = read("base", "sources", "iron_ui.c")
assert "ui_normalize_numeric_text" in ui
assert "ui_normalize_numeric_text(handle->text)" in ui


def test_window_scale_is_validated_when_loaded_applied_and_saved() -> None:
config = read("paint", "sources", "config.c")
preferences = read("paint", "sources", "ui", "box_preferences.c")
assert "f32 config_validate_window_scale" in config
assert config.count("config_validate_window_scale") >= 4
assert "g_config->window_scale = config_validate_window_scale(g_config->window_scale);" in preferences


if __name__ == "__main__":
test_slider_normalizes_a_decimal_comma_before_evaluation()
test_window_scale_is_validated_when_loaded_applied_and_saved()
print("2 regression checks passed")