123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- /* MegaZeux
- *
- * Copyright (C) 2018 Alice Rowan <petrifiedrowan@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- #include <math.h>
- #include "core.h"
- #include "event.h"
- #include "graphics.h"
- #include "intake_num.h"
- #include "util.h"
- #include "window.h"
- struct intake_num_context
- {
- context ctx;
- int x;
- int y;
- int w;
- int color;
- int value;
- int min_val;
- int max_val;
- boolean leading_minus;
- boolean empty;
- boolean save;
- context *callback_ctx;
- void (*callback_fn)(context *, int);
- };
- /**
- * Change the input number and bound it.
- */
- static void change_value(struct intake_num_context *intk, int value)
- {
- if(intk->leading_minus && value)
- value *= -1;
- intk->value = CLAMP(value, intk->min_val, intk->max_val);
- intk->empty = false;
- intk->leading_minus = false;
- }
- /**
- * Expand draw area to encompass maximum/minimum value if necessary.
- * The minus is drawn on the left border char, so abs() is used to ignore it.
- */
- static void fix_draw_area(struct intake_num_context *intk)
- {
- char buffer[12];
- int buf_len;
- snprintf(buffer, 12, "%d", abs(intk->max_val));
- buf_len = strlen(buffer);
- if(buf_len > intk->w)
- intk->w = buf_len;
- snprintf(buffer, 12, "%d", abs(intk->min_val));
- buf_len = strlen(buffer);
- if(buf_len > intk->w)
- intk->w = buf_len;
- }
- /**
- * Draw the number input to the screen.
- */
- static void intake_num_draw(context *ctx)
- {
- struct intake_num_context *intk = (struct intake_num_context *)ctx;
- int write_pos = intk->x + intk->w + 1;
- char buffer[12] = { 0 };
- int i;
- snprintf(buffer, 12, "%d", intk->value);
- write_pos -= strlen(buffer);
- for(i = 0; i < intk->w + 2; i++)
- draw_char(0, intk->color, intk->x + i, intk->y);
- if(intk->leading_minus)
- draw_char('-', intk->color, write_pos, intk->y);
- if(!intk->empty)
- write_string(buffer, write_pos, intk->y, intk->color, false);
- }
- /**
- * Keyhandler for number input.
- */
- static boolean intake_num_key(context *ctx, int *key)
- {
- struct intake_num_context *intk = (struct intake_num_context *)ctx;
- int increment_value = 0;
- if(get_exit_status())
- *key = IKEY_ESCAPE;
- if((*key >= IKEY_0) && (*key <= IKEY_9))
- {
- // At exactly maximum/minimum, typing a number should wrap
- if((intk->value == intk->min_val && intk->min_val < 0)
- || (intk->value == intk->max_val && intk->max_val > 0))
- {
- change_value(intk, 0);
- }
- else
- {
- int add_value = (*key - IKEY_0);
- if(intk->value < 0)
- add_value *= -1;
- change_value(intk, intk->value * 10 + add_value);
- }
- return true;
- }
- switch(*key)
- {
- case IKEY_ESCAPE:
- {
- // Exit
- intk->save = false;
- destroy_context(ctx);
- return true;
- }
- case IKEY_RETURN:
- case IKEY_TAB:
- {
- // Set component and exit
- destroy_context(ctx);
- return true;
- }
- case IKEY_DELETE:
- case IKEY_PERIOD:
- {
- // Set to zero and exit
- change_value(intk, 0);
- destroy_context(ctx);
- return true;
- }
- case IKEY_BACKSPACE:
- {
- int new_value = intk->value / 10;
- if(intk->leading_minus)
- {
- intk->leading_minus = false;
- }
- else
- if(new_value != 0)
- {
- change_value(intk, new_value);
- }
- else
- {
- if(intk->value < 0)
- intk->leading_minus = true;
- intk->value = 0;
- intk->empty = true;
- }
- return true;
- }
- case IKEY_MINUS:
- {
- if(intk->min_val < 0)
- {
- // Add/remove leading '-'
- if(intk->empty || intk->value == 0)
- {
- intk->empty = 1;
- intk->leading_minus ^= 1;
- }
- else
- // Negate the existing number
- if(-intk->value >= intk->min_val && -intk->value <= intk->max_val)
- {
- change_value(intk, intk->value * -1);
- }
- }
- return true;
- }
- case IKEY_HOME:
- {
- intk->value = intk->min_val;
- return true;
- }
- case IKEY_END:
- {
- intk->value = intk->max_val;
- return true;
- }
- case IKEY_RIGHT:
- case IKEY_UP:
- {
- if(get_alt_status(keycode_internal) || get_ctrl_status(keycode_internal))
- {
- increment_value = 10;
- }
- else
- {
- increment_value = 1;
- }
- break;
- }
- case IKEY_PAGEUP:
- {
- increment_value = 100;
- break;
- }
- case IKEY_LEFT:
- case IKEY_DOWN:
- {
- if(get_alt_status(keycode_internal) || get_ctrl_status(keycode_internal))
- {
- increment_value = -10;
- }
- else
- {
- increment_value = -1;
- }
- break;
- }
- case IKEY_PAGEDOWN:
- {
- increment_value = -100;
- break;
- }
- }
- if(increment_value)
- {
- change_value(intk, intk->value + increment_value);
- return true;
- }
- return false;
- }
- /**
- * New mouse clicks exit and save the new value.
- */
- static boolean intake_num_click(context *ctx, int *key, int button,
- int x, int y)
- {
- if(button && !get_mouse_drag())
- {
- destroy_context(ctx);
- return true;
- }
- return false;
- }
- /**
- * On exit, get the final value and execute the callback if necessary.
- */
- static void intake_num_destroy(context *ctx)
- {
- struct intake_num_context *intk = (struct intake_num_context *)ctx;
- if(intk->save)
- {
- int value = CLAMP(intk->value, intk->min_val, intk->max_val);
- if(intk->callback_fn)
- intk->callback_fn(intk->callback_ctx, value);
- }
- }
- /**
- * Create a context for number entry. On exit if the number was changed, the
- * callback will be executed using the parent context and the final value as
- * inputs.
- */
- context *intake_num(context *parent, int value, int min_val, int max_val,
- int x, int y, int min_width, int color, void (*callback)(context *, int))
- {
- struct intake_num_context *intk = cmalloc(sizeof(struct intake_num_context));
- struct context_spec spec;
- intk->x = x;
- intk->y = y;
- intk->w = min_width;
- intk->color = color;
- intk->value = value;
- intk->min_val = min_val;
- intk->max_val = max_val;
- intk->leading_minus = false;
- intk->empty = false;
- intk->save = true;
- intk->callback_ctx = parent;
- intk->callback_fn = callback;
- fix_draw_area(intk);
- memset(&spec, 0, sizeof(struct context_spec));
- spec.draw = intake_num_draw;
- spec.key = intake_num_key;
- spec.click = intake_num_click;
- spec.destroy = intake_num_destroy;
- spec.framerate_mode = FRAMERATE_UI_INTERRUPT;
- create_context((context *)intk, parent, &spec, CTX_INTAKE_NUM);
- return (context *)intk;
- }
|