cOMS/ui/UIInput.h
Dennis Eichhorn 39fbcf4300
Some checks are pending
CodeQL / Analyze (${{ matrix.language }}) (autobuild, c-cpp) (push) Waiting to run
Microsoft C++ Code Analysis / Analyze (push) Waiting to run
linux bug fixes
2025-03-22 01:10:19 +00:00

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