mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-10 19:08:39 +00:00
256 lines
8.8 KiB
C
Executable File
256 lines
8.8 KiB
C
Executable File
#ifndef COMS_UI_INPUT_H
|
|
#define COMS_UI_INPUT_H
|
|
|
|
#include "../stdlib/Types.h"
|
|
#include "../camera/Camera.h"
|
|
#include "attribute/UIAttribute.h"
|
|
#include "attribute/UIAttributeBorder.h"
|
|
#include "attribute/UIAttributeShadow.h"
|
|
#include "attribute/UIAttributeBackground.h"
|
|
#include "attribute/UIAttributeDimension.h"
|
|
#include "UIAnimation.h"
|
|
#include "UIStyleType.h"
|
|
#include "UIElement.h"
|
|
#include "UICursor.h"
|
|
#include "UIWindow.h"
|
|
#include "UIPanel.h"
|
|
#include "UILayout.h"
|
|
#include "../math/Evaluator.h"
|
|
#include "../gpuapi/RenderUtils.h"
|
|
#include "../object/Vertex.h"
|
|
|
|
enum UIInputType : byte {
|
|
UI_INPUT_TYPE_TEXT,
|
|
UI_INPUT_TYPE_NUMERIC,
|
|
UI_INPUT_TYPE_DATE,
|
|
UI_INPUT_TYPE_DATE_TIME,
|
|
UI_INPUT_TYPE_TIME,
|
|
};
|
|
|
|
struct UIInputState {
|
|
uint16 cursor_pos_x;
|
|
uint16 cursor_pos_y;
|
|
UIInputType type;
|
|
int32 min_value;
|
|
int32 max_value;
|
|
uint16 max_input_length;
|
|
};
|
|
|
|
struct UIInput {
|
|
UIAttributeDimension dimension;
|
|
byte opacity; // 1 byte alpha channel
|
|
byte padding;
|
|
|
|
UIAttributeBackground background;
|
|
UIAttributeBorder border;
|
|
UIAttributeShadow shadow_outer;
|
|
UIAttributeShadow shadow_inner;
|
|
};
|
|
|
|
void ui_input_state_serialize(const UIInputState* __restrict state, byte** __restrict pos)
|
|
{
|
|
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(state->cursor_pos_x);
|
|
*pos += sizeof(state->cursor_pos_x);
|
|
|
|
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(state->cursor_pos_y);
|
|
*pos += sizeof(state->cursor_pos_y);
|
|
|
|
**pos = state->type;
|
|
*pos += sizeof(state->type);
|
|
|
|
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(state->min_value);
|
|
*pos += sizeof(state->min_value);
|
|
|
|
*((int32 *) *pos) = SWAP_ENDIAN_LITTLE(state->max_value);
|
|
*pos += sizeof(state->max_value);
|
|
|
|
*((uint16 *) *pos) = SWAP_ENDIAN_LITTLE(state->max_input_length);
|
|
*pos += sizeof(state->max_input_length);
|
|
}
|
|
|
|
void ui_input_state_unserialize(UIInputState* __restrict state, const byte** __restrict pos)
|
|
{
|
|
state->cursor_pos_x = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
|
*pos += sizeof(state->cursor_pos_x);
|
|
|
|
state->cursor_pos_y = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
|
*pos += sizeof(state->cursor_pos_y);
|
|
|
|
state->type = (UIInputType) **pos;
|
|
*pos += sizeof(state->type);
|
|
|
|
state->min_value = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
|
*pos += sizeof(state->min_value);
|
|
|
|
state->max_value = SWAP_ENDIAN_LITTLE(*((int32 *) *pos));
|
|
*pos += sizeof(state->max_value);
|
|
|
|
state->max_input_length = SWAP_ENDIAN_LITTLE(*((uint16 *) *pos));
|
|
*pos += sizeof(state->max_input_length);
|
|
}
|
|
|
|
void ui_input_state_populate(const UIAttributeGroup* __restrict group, UIInputState* __restrict state) {
|
|
|
|
UIAttribute* attributes = (UIAttribute *) (group + 1);
|
|
for (uint32 i = 0; i < group->attribute_count; ++i) {
|
|
switch (attributes[i].attribute_id) {
|
|
case UI_ATTRIBUTE_TYPE_TYPE: {
|
|
state->type = (UIInputType) attributes[i].value_int;
|
|
} break;
|
|
case UI_ATTRIBUTE_TYPE_MIN_VALUE: {
|
|
state->min_value = attributes[i].value_int;
|
|
} break;
|
|
case UI_ATTRIBUTE_TYPE_MAX_VALUE: {
|
|
state->max_value = attributes[i].value_int;
|
|
} break;
|
|
case UI_ATTRIBUTE_TYPE_MAX_INPUT_LENGTH: {
|
|
state->max_input_length = (uint16) attributes[i].value_int;
|
|
} break;
|
|
default: {}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ui_input_element_serialize(const UIInput* __restrict details, byte** __restrict pos)
|
|
{
|
|
ui_attr_dimension_serialize(&details->dimension, pos);
|
|
|
|
**pos = details->opacity;
|
|
*pos += sizeof(details->opacity);
|
|
|
|
**pos = details->padding;
|
|
*pos += sizeof(details->padding);
|
|
|
|
ui_attr_background_serialize(&details->background, pos);
|
|
ui_attr_border_serialize(&details->border, pos);
|
|
ui_attr_shadow_serialize(&details->shadow_outer, pos);
|
|
ui_attr_shadow_serialize(&details->shadow_inner, pos);
|
|
}
|
|
|
|
void ui_input_element_unserialize(UIInput* __restrict details, const byte** __restrict pos)
|
|
{
|
|
ui_attr_dimension_unserialize(&details->dimension, pos);
|
|
|
|
details->opacity = **pos;
|
|
*pos += sizeof(details->opacity);
|
|
|
|
details->padding = **pos;
|
|
*pos += sizeof(details->padding);
|
|
|
|
ui_attr_background_unserialize(&details->background, pos);
|
|
ui_attr_border_unserialize(&details->border, pos);
|
|
ui_attr_shadow_unserialize(&details->shadow_outer, pos);
|
|
ui_attr_shadow_unserialize(&details->shadow_inner, pos);
|
|
}
|
|
|
|
void ui_input_element_populate(
|
|
UILayout* layout,
|
|
UIElement* element,
|
|
const UIAttributeGroup* __restrict group,
|
|
UIInput* __restrict input
|
|
) {
|
|
v4_f32 parent_dimension = {};
|
|
if (element->parent) {
|
|
UIElement* parent = element->parent ? (UIElement *) (layout->data + element->parent) : NULL;
|
|
// @bug How to ensure that the parent is initialized before the child element
|
|
// Currently the order of the initialization depends on the theme file, NOT the layout file
|
|
// We could fix it by loading the style based on the layout order but this would result in many misses when looking up styles
|
|
// The reason for these misses are, that often only 1-2 style_types exist per element
|
|
switch (parent->type) {
|
|
case UI_ELEMENT_TYPE_VIEW_WINDOW: {
|
|
UIWindow* parent_window = (UIWindow *) (layout->data + parent->style_types[UI_STYLE_TYPE_ACTIVE]);
|
|
parent_dimension = parent_window->dimension.dimension;
|
|
} break;
|
|
case UI_ELEMENT_TYPE_VIEW_PANEL: {
|
|
UIPanel* parent_window = (UIPanel *) (layout->data + parent->style_types[UI_STYLE_TYPE_ACTIVE]);
|
|
parent_dimension = parent_window->dimension.dimension;
|
|
} break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
if (!element->vertices_active_offset && !element->vertex_count_max) {
|
|
element->vertices_active_offset = layout->active_vertex_offset;
|
|
UIAttribute* vertex_attr = ui_attribute_from_group(group, UI_ATTRIBUTE_TYPE_VERTEX_COUNT);
|
|
element->vertex_count_max = (uint16) (vertex_attr ? vertex_attr->value_int : 8);
|
|
|
|
layout->active_vertex_offset += element->vertex_count_max;
|
|
}
|
|
|
|
UIAttribute* attributes = (UIAttribute *) (group + 1);
|
|
|
|
// First set all values, which we can set immediately
|
|
for (uint32 i = 0; i < group->attribute_count; ++i) {
|
|
switch (attributes[i].attribute_id) {
|
|
case UI_ATTRIBUTE_TYPE_POSITION_X:
|
|
case UI_ATTRIBUTE_TYPE_DIMENSION_WIDTH:
|
|
case UI_ATTRIBUTE_TYPE_POSITION_Y:
|
|
case UI_ATTRIBUTE_TYPE_DIMENSION_HEIGHT: {
|
|
ui_theme_assign_dimension(&input->dimension, &attributes[i]);
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 ui_input_element_update(UILayout* layout, UIElement* element)
|
|
{
|
|
UIInput* input = (UIInput *) (layout->data + element->style_types[element->style_new]);
|
|
//UIInputState* state = (UIInputState *) (layout->data + element->state);
|
|
|
|
int32 idx = 0;
|
|
f32 zindex = element->zindex;
|
|
|
|
v4_f32 dimension = input->dimension.dimension;
|
|
|
|
// Border
|
|
if (input->border.thickness) {
|
|
idx += vertex_rect_create(
|
|
layout->vertices_active + element->vertices_active_offset, zindex, -1,
|
|
dimension, input->dimension.alignment,
|
|
input->border.color
|
|
);
|
|
|
|
// Adjusting dimension based on border
|
|
// border is part of width/height
|
|
dimension.x += input->border.thickness;
|
|
dimension.y += input->border.thickness;
|
|
dimension.width -= input->border.thickness;
|
|
dimension.height -= input->border.thickness;
|
|
|
|
// @bug change to camera_step_closer()
|
|
zindex = nextafterf(zindex, INFINITY);
|
|
}
|
|
|
|
// Background
|
|
if (input->background.background_color) {
|
|
idx += vertex_rect_create(
|
|
layout->vertices_active + element->vertices_active_offset + idx, zindex, -1,
|
|
dimension, input->dimension.alignment,
|
|
input->background.background_color
|
|
);
|
|
|
|
// @bug change to camera_step_closer()
|
|
zindex = nextafterf(zindex, INFINITY);
|
|
}
|
|
|
|
// Cursor
|
|
HashEntryInt32* cursor_entry = (HashEntryInt32 *) hashmap_get_entry(&layout->hash_map, "cursor");
|
|
if (cursor_entry) {
|
|
UIElement* cursor_element = (UIElement *) (layout->data + cursor_entry->value);
|
|
// This requires the cursor position is already updated wherever we handle the input update (on_enter, on_leave)
|
|
idx += ui_cursor_element_update(layout, cursor_element);
|
|
|
|
memcpy(
|
|
layout->vertices_active + element->vertices_active_offset + idx,
|
|
layout->vertices_active + cursor_element->vertices_active_offset,
|
|
cursor_element->vertex_count_active * sizeof(*(layout->vertices_active))
|
|
);
|
|
}
|
|
|
|
element->vertex_count_active = (uint16) idx;
|
|
|
|
return idx;
|
|
}
|
|
|
|
#endif |