527 lines
No EOL
22 KiB
D
527 lines
No EOL
22 KiB
D
module gui.manager;
|
|
|
|
import app;
|
|
|
|
import cimgui.cimgui;
|
|
|
|
import bubel.ecs.entity;
|
|
import bubel.ecs.manager;
|
|
import bubel.ecs.std;
|
|
import bubel.ecs.system;
|
|
import bubel.ecs.vector;
|
|
|
|
import ecs_utils.math.vector;
|
|
|
|
import gui.attributes;
|
|
import gui.component;
|
|
import gui.system;
|
|
import gui.template_;
|
|
|
|
import std.traits;
|
|
|
|
extern(C):
|
|
|
|
struct GUIManager
|
|
{
|
|
Vector!SystemGUI systems;
|
|
Vector!ComponentGUI components;
|
|
Vector!TemplateGUI templates;
|
|
Vector!ComponentEditGUI edit_components;
|
|
Vector!bool filter;
|
|
Vector!ushort filter_list;
|
|
|
|
int selected_template = 0;
|
|
int selected_component = 0;
|
|
|
|
~this()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void selectTemplate(int id)
|
|
{
|
|
if(templates.length == 0)return;
|
|
selected_template = id;
|
|
while(selected_template < 0)selected_template += cast(int)templates.length;
|
|
while(selected_template >= templates.length)selected_template -= cast(int)templates.length;
|
|
}
|
|
|
|
void selectComponent(int id)
|
|
{
|
|
if(components.length == 0)return;
|
|
selected_component = id;
|
|
while(selected_component < 0)selected_component += cast(int)components.length;
|
|
while(selected_component >= components.length)selected_component -= cast(int)components.length;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
foreach(tmpl; templates)
|
|
{
|
|
launcher.manager.freeTemplate(tmpl.tmpl);
|
|
}
|
|
foreach(comp; components)
|
|
{
|
|
free(comp.data);
|
|
}
|
|
foreach(ref comp; edit_components)
|
|
{
|
|
if(comp.variables)Mallocator.dispose(comp.variables);
|
|
comp.variables = null;
|
|
comp.used = 0;
|
|
comp.name = null;
|
|
}
|
|
foreach(ref comp; filter)
|
|
{
|
|
comp = false;
|
|
}
|
|
|
|
filter_list.clear();
|
|
systems.clear();
|
|
templates.clear();
|
|
components.clear();
|
|
selected_template = 0;
|
|
selected_component = 0;
|
|
}
|
|
|
|
EntityTemplate* getSelectedTemplate()
|
|
{
|
|
if(templates.length > selected_template)return templates[selected_template].tmpl;
|
|
else return null;
|
|
}
|
|
|
|
ComponentRef getSelectedComponent()
|
|
{
|
|
if(components.length > selected_component)return ComponentRef(components[selected_component].data, components[selected_component].component_id);
|
|
else return ComponentRef(null, ushort.max);
|
|
}
|
|
|
|
void addSystem(ushort id, const (char)* name, bool enabled = true)
|
|
{
|
|
foreach(ref sys; systems)
|
|
{
|
|
if(sys.id == id)return;
|
|
}
|
|
System* system = launcher.manager.getSystem(id);
|
|
//const (char)* name =
|
|
systems.add(SystemGUI(name,id,enabled));
|
|
}
|
|
|
|
void addTemplate(ushort[] components, const (char)* name)
|
|
{
|
|
templates.add(TemplateGUI(name, launcher.manager.allocateTemplate(components)));
|
|
}
|
|
|
|
void addTemplate(EntityTemplate* tmpl, const (char)* name)
|
|
{
|
|
templates.add(TemplateGUI(name, tmpl));
|
|
}
|
|
|
|
// void addComponent(ComponentRef comp, const (char)* name)
|
|
// {
|
|
// uint size = gEntityManager.components[becsID(comp)].size;
|
|
// void* data = malloc(size);
|
|
// memcpy(data, comp.ptr, size);
|
|
// components.add(ComponentGUI(name, data, becsID(comp)));
|
|
// }
|
|
|
|
void addComponent(T)(T comp, const (char)* name)
|
|
{
|
|
uint size = gEntityManager.components[becsID(comp)].size;
|
|
void* data = malloc(size);
|
|
memcpy(data, &comp, size);
|
|
components.add(ComponentGUI(name, data, becsID(comp)));
|
|
|
|
if(edit_components.length <= becsID(comp))
|
|
{
|
|
edit_components.length = becsID(comp)+1;//.extend(becsID(comp) + 1);
|
|
}
|
|
//edit_components[becsID(comp)] = ComponentEditGUI(name);
|
|
if(edit_components[becsID(comp)].variables)return;
|
|
ComponentEditGUI comp_edit;
|
|
comp_edit.name = T.stringof;
|
|
//enum fields = __traits(allMembers, T);
|
|
alias fields = FieldNameTuple!T;
|
|
//pragma(msg,fields);
|
|
comp_edit.variables = Mallocator.makeArray!VariableGUI(fields.length);
|
|
foreach(member_str; fields)
|
|
{
|
|
alias member = __traits(getMember, T, member_str);
|
|
alias member_type = typeof(member);
|
|
//pragma(msg,member);
|
|
//pragma(msg,member_type);
|
|
//pragma(msg,__traits(getMember, T, member).offsetof);
|
|
ushort offset = member.offsetof;//cast(ushort)__traits(getMember, T, member).offsetof;
|
|
static if(is(member_type == vec2))
|
|
{
|
|
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.vec2,member_str,offset);
|
|
}
|
|
else static if(is(member_type == ivec2))
|
|
{
|
|
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.ivec2,member_str,offset);
|
|
}
|
|
else static if(is(member_type == vec4))
|
|
{
|
|
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.vec4,member_str,offset);
|
|
}
|
|
else static if(is(member_type == ivec4))
|
|
{
|
|
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.ivec4,member_str,offset);
|
|
}
|
|
else static if(__traits(isIntegral,member_type))
|
|
{
|
|
static if(__traits(isUnsigned, member_type))
|
|
{
|
|
static if(hasUDA!(member,GUIColor))
|
|
{
|
|
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.color,member_str,offset);
|
|
}
|
|
else switch(member_type.sizeof)
|
|
{
|
|
case 1: comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.ubyte_,member_str,offset);break;
|
|
case 2: comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.ushort_,member_str,offset);break;
|
|
case 4: comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.uint_,member_str,offset);break;
|
|
default:break;
|
|
}
|
|
static if(hasUDA!(member,GUIRange))
|
|
{
|
|
comp_edit.variables[comp_edit.used-1].int_.min = getUDAs!(member,GUIRange)[0].min;
|
|
comp_edit.variables[comp_edit.used-1].int_.max = getUDAs!(member,GUIRange)[0].max;
|
|
}
|
|
else
|
|
{
|
|
comp_edit.variables[comp_edit.used-1].int_.min = 0;
|
|
comp_edit.variables[comp_edit.used-1].int_.max = int.max;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(member_type.sizeof)
|
|
{
|
|
case 1:
|
|
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.byte_,member_str,offset);
|
|
comp_edit.variables[comp_edit.used-1].int_.min = byte.min;
|
|
comp_edit.variables[comp_edit.used-1].int_.max = byte.max;
|
|
break;
|
|
case 2:
|
|
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.short_,member_str,offset);
|
|
comp_edit.variables[comp_edit.used-1].int_.min = short.min;
|
|
comp_edit.variables[comp_edit.used-1].int_.max = short.max;
|
|
break;
|
|
case 4:
|
|
comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.int_,member_str,offset);
|
|
comp_edit.variables[comp_edit.used-1].int_.min = int.min;
|
|
comp_edit.variables[comp_edit.used-1].int_.max = int.max;
|
|
break;
|
|
default:break;
|
|
}
|
|
static if(hasUDA!(member,GUIRange))
|
|
{
|
|
comp_edit.variables[comp_edit.used-1].int_.min = getUDAs!(member,GUIRange)[0].min;
|
|
comp_edit.variables[comp_edit.used-1].int_.max = getUDAs!(member,GUIRange)[0].max;
|
|
}
|
|
/*{
|
|
comp_edit.variables[comp_edit.used-1].int_.min = int.min;
|
|
comp_edit.variables[comp_edit.used-1].int_.max = int.max;
|
|
}*/
|
|
}
|
|
}
|
|
else static if(__traits(isScalar,member_type))
|
|
{
|
|
switch(member_type.sizeof)
|
|
{
|
|
case 4:comp_edit.variables[comp_edit.used++] = VariableGUI(VariableGUI.Type.float_,member_str,offset);break;
|
|
case 8:
|
|
default:break;
|
|
}
|
|
static if(hasUDA!(member,GUIRange))
|
|
{
|
|
comp_edit.variables[comp_edit.used-1].float_.min = getUDAs!(member,GUIRange)[0].minf;
|
|
comp_edit.variables[comp_edit.used-1].float_.max = getUDAs!(member,GUIRange)[1].maxf;
|
|
}
|
|
{
|
|
comp_edit.variables[comp_edit.used-1].float_.min = -float.max;
|
|
comp_edit.variables[comp_edit.used-1].float_.max = float.max;
|
|
}
|
|
}
|
|
}
|
|
edit_components[becsID(comp)] = comp_edit;
|
|
}
|
|
|
|
void gui()
|
|
{
|
|
if(igCollapsingHeader("Systems", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
//bool true_ = true;
|
|
igIndent(8);
|
|
foreach(ref SystemGUI system;systems)
|
|
{
|
|
if(igCheckbox(system.name,&system.enabled))
|
|
{
|
|
System* sys = launcher.manager.getSystem(system.id);
|
|
if(system.enabled)sys.enable();
|
|
else sys.disable();
|
|
}
|
|
}
|
|
igUnindent(8);
|
|
}
|
|
}
|
|
|
|
static vec4 colorUintToVec4(uint color)
|
|
{
|
|
// color = *cast(uint*)(comp_ptr+var.offset);
|
|
return vec4(cast(float)(color & 0xFF) / 255,
|
|
cast(float)(color >> 8 & 0xFF) / 255,
|
|
cast(float)(color >> 16 & 0xFF) / 255,
|
|
cast(float)(color >> 24 & 0xFF) / 255);
|
|
}
|
|
|
|
static uint colorVec4ToUint(vec4 color)
|
|
{
|
|
return cast(uint)(color.x * 255) |
|
|
cast(uint)(color.y * 255) << 8 |
|
|
cast(uint)(color.z * 255) << 16 |
|
|
cast(uint)(color.w * 255) << 24;
|
|
}
|
|
|
|
static bool igDragScalarClamp(const(char)* label, ImGuiDataType data_type, void* v, float v_speed, const(void)* v_min, const(void)* v_max, const(char)* format, float power)
|
|
{
|
|
ubyte[8] v_backup;// = *v;
|
|
final switch(data_type)
|
|
{
|
|
case ImGuiDataType_S8:memcpy(v_backup.ptr, v, 1);break;
|
|
case ImGuiDataType_S16:memcpy(v_backup.ptr, v, 2);break;
|
|
case ImGuiDataType_S32:memcpy(v_backup.ptr, v, 4);break;
|
|
case ImGuiDataType_U8:memcpy(v_backup.ptr, v, 1);break;
|
|
case ImGuiDataType_U16:memcpy(v_backup.ptr, v, 2);break;
|
|
case ImGuiDataType_U32:memcpy(v_backup.ptr, v, 4);break;
|
|
case ImGuiDataType_Float:memcpy(v_backup.ptr, v, 4);break;
|
|
}
|
|
if (!igDragScalar(label, data_type, v, v_speed, v_min, v_max, format, power))
|
|
return false;
|
|
|
|
final switch(data_type)
|
|
{
|
|
case ImGuiDataType_S8:
|
|
if(*cast(byte*)v < *cast(byte*)v_min)*cast(byte*)v = *cast(byte*)v_min;
|
|
else if(*cast(byte*)v > *cast(byte*)v_max)*cast(byte*)v = *cast(byte*)v_max;
|
|
return *cast(byte*)v != *cast(byte*)v_backup.ptr;
|
|
case ImGuiDataType_S16:
|
|
if(*cast(short*)v < *cast(short*)v_min)*cast(short*)v = *cast(short*)v_min;
|
|
else if(*cast(short*)v > *cast(short*)v_max)*cast(short*)v = *cast(short*)v_max;
|
|
return *cast(short*)v != *cast(short*)v_backup.ptr;
|
|
case ImGuiDataType_S32:
|
|
if(*cast(int*)v < *cast(int*)v_min)*cast(int*)v = *cast(int*)v_min;
|
|
else if(*cast(int*)v > *cast(int*)v_max)*cast(int*)v = *cast(int*)v_max;
|
|
return *cast(int*)v != *cast(int*)v_backup.ptr;
|
|
case ImGuiDataType_U8:
|
|
if(*cast(ubyte*)v < *cast(ubyte*)v_min)*cast(ubyte*)v = *cast(ubyte*)v_min;
|
|
else if(*cast(ubyte*)v > *cast(ubyte*)v_max)*cast(ubyte*)v = *cast(ubyte*)v_max;
|
|
return *cast(ubyte*)v != *cast(ubyte*)v_backup.ptr;
|
|
case ImGuiDataType_U16:
|
|
if(*cast(ushort*)v < *cast(ushort*)v_min)*cast(ushort*)v = *cast(ushort*)v_min;
|
|
else if(*cast(ushort*)v > *cast(ushort*)v_max)*cast(ushort*)v = *cast(ushort*)v_max;
|
|
return *cast(ushort*)v != *cast(ushort*)v_backup.ptr;
|
|
case ImGuiDataType_U32:
|
|
if(*cast(uint*)v < *cast(uint*)v_min)*cast(uint*)v = *cast(uint*)v_min;
|
|
else if(*cast(uint*)v > *cast(uint*)v_max)*cast(uint*)v = *cast(uint*)v_max;
|
|
return *cast(uint*)v != *cast(uint*)v_backup.ptr;
|
|
case ImGuiDataType_Float:
|
|
if(*cast(float*)v < *cast(float*)v_min)*cast(float*)v = *cast(float*)v_min;
|
|
else if(*cast(float*)v > *cast(float*)v_max)*cast(float*)v = *cast(float*)v_max;
|
|
return *cast(float*)v != *cast(float*)v_backup.ptr;
|
|
}
|
|
}
|
|
|
|
void componentGUI(ushort comp_id, void* data_ptr)
|
|
{
|
|
vec4 color;
|
|
if(comp_id >= edit_components.length)return;
|
|
if(edit_components[comp_id].used)
|
|
{
|
|
if(igCollapsingHeader(edit_components[comp_id].name, ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen))
|
|
{
|
|
igIndent(8);
|
|
foreach(ref VariableGUI var;edit_components[comp_id].variables[0 .. edit_components[comp_id].used])
|
|
{
|
|
igPushIDPtr(&var);
|
|
switch(var.type)
|
|
{
|
|
case VariableGUI.Type.byte_:
|
|
igDragScalarClamp(var.name, ImGuiDataType_S8, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
|
|
break;
|
|
case VariableGUI.Type.ubyte_:
|
|
igDragScalarClamp(var.name, ImGuiDataType_U8, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
|
|
break;
|
|
case VariableGUI.Type.short_:
|
|
igDragScalarClamp(var.name, ImGuiDataType_S16, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
|
|
break;
|
|
case VariableGUI.Type.ushort_:
|
|
igDragScalarClamp(var.name, ImGuiDataType_U16, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
|
|
break;
|
|
case VariableGUI.Type.int_:
|
|
igDragScalarClamp(var.name, ImGuiDataType_S32, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
|
|
break;
|
|
case VariableGUI.Type.uint_:
|
|
igDragScalarClamp(var.name, ImGuiDataType_U32, data_ptr+var.offset, 0.1, cast(void*)&var.int_.min, cast(void*)&var.int_.max, null, 1);
|
|
break;
|
|
case VariableGUI.Type.float_:
|
|
igDragScalarClamp(var.name, ImGuiDataType_Float, data_ptr+var.offset, 0.1, cast(void*)&var.float_.min, cast(void*)&var.float_.max, "%2.2f", 1);
|
|
break;
|
|
case VariableGUI.Type.color:
|
|
color = colorUintToVec4(*cast(uint*)(data_ptr+var.offset));
|
|
if(igColorEdit4(var.name, color.data, ImGuiColorEditFlags_None))
|
|
*cast(uint*)(data_ptr+var.offset) = colorVec4ToUint(color);
|
|
break;
|
|
case VariableGUI.Type.vec2:
|
|
igDragFloat2(var.name, (cast(float*)(data_ptr+var.offset))[0..2], 0.1, -float.max, float.max, null, 1);
|
|
break;
|
|
case VariableGUI.Type.ivec2:
|
|
igDragInt2(var.name, (cast(int*)(data_ptr+var.offset))[0..2], 0.1, int.min, int.max, null);
|
|
break;
|
|
case VariableGUI.Type.vec4:
|
|
igDragFloat4(var.name, (cast(float*)(data_ptr+var.offset))[0..4], 0.1, -float.max, float.max, null, 1);
|
|
break;
|
|
case VariableGUI.Type.ivec4:
|
|
igDragInt4(var.name, (cast(int*)(data_ptr+var.offset))[0..4], 0.1, int.min, int.max, null);
|
|
break;
|
|
default:break;
|
|
}
|
|
igPopID();
|
|
}
|
|
//igPopID();
|
|
igUnindent(8);
|
|
}
|
|
}
|
|
}
|
|
|
|
void templateComponentsGUI()
|
|
{
|
|
if(selected_template >= templates.length)return;
|
|
EntityTemplate* tmpl = templates[selected_template].tmpl;
|
|
EntityManager.EntityInfo* info = tmpl.info;
|
|
foreach(comp_id; info.components)
|
|
{
|
|
void* data_ptr = tmpl.entity_data.ptr;
|
|
void* comp_ptr = data_ptr + info.tmpl_deltas[comp_id];
|
|
componentGUI(comp_id, comp_ptr);
|
|
}
|
|
}
|
|
|
|
void entityComponentsGUI(Entity* entity)
|
|
{
|
|
if(!entity)return;
|
|
EntityMeta meta = entity.getMeta();
|
|
EntityManager.EntityInfo* info = meta.block.type_info;
|
|
foreach(comp_id; info.components)
|
|
{
|
|
// void* data_ptr = tmpl.entity_data.ptr;
|
|
void* comp_ptr = meta.getComponent(comp_id);//data_ptr + info.tmpl_deltas[comp_id];
|
|
componentGUI(comp_id, comp_ptr);
|
|
}
|
|
}
|
|
|
|
void toolGui()
|
|
{
|
|
ImGuiStyle * style = igGetStyle();
|
|
ImVec4 col = style.Colors[ImGuiCol_Header];
|
|
style.Colors[ImGuiCol_Header] = style.Colors[ImGuiCol_TextSelectedBg];
|
|
//style.
|
|
//ImDrawList* draw_list = igGetWindowDrawList();
|
|
final switch(launcher.used_tool)
|
|
{
|
|
case Tool.entity_spawner:
|
|
if(templates.length)
|
|
{
|
|
{
|
|
if(igListBoxHeaderInt("Template",cast(int)templates.length,cast(int)templates.length))
|
|
{
|
|
foreach(i, tmpl; templates)
|
|
{
|
|
if(igSelectable(tmpl.name,selected_template == i,ImGuiSelectableFlags_AllowDoubleClick,ImVec2(0,0)))
|
|
{
|
|
selected_template = cast(uint)i;
|
|
}
|
|
}
|
|
igListBoxFooter();
|
|
}
|
|
}
|
|
if(igIsItemHovered(0))igSetTooltip("Select entity to spawn (SHIFT + Scroll)");
|
|
}
|
|
style.Colors[ImGuiCol_Header] = col;
|
|
templateComponentsGUI();
|
|
break;
|
|
case Tool.component_manipulator:
|
|
if(components.length)
|
|
{
|
|
if(igListBoxHeaderInt("Components",cast(int)components.length,cast(int)components.length))
|
|
{
|
|
foreach(i, comp; components)
|
|
{
|
|
if(igSelectable(comp.name,selected_component == i,0,ImVec2(0,0)))
|
|
{
|
|
selected_component = cast(uint)i;
|
|
}
|
|
}
|
|
igListBoxFooter();
|
|
}
|
|
if(igIsItemHovered(0))igSetTooltip("Select component to add/remove (SHIFT + Scroll)");
|
|
}
|
|
style.Colors[ImGuiCol_Header] = col;
|
|
if(selected_component < components.length)componentGUI(components[selected_component].component_id, components[selected_component].data);
|
|
break;
|
|
case Tool.selector:
|
|
{
|
|
Entity* entity = gEntityManager.getEntity(launcher.selected_entity);
|
|
style.Colors[ImGuiCol_Header] = col;
|
|
entityComponentsGUI(entity);
|
|
}
|
|
break;
|
|
}
|
|
|
|
style.Colors[ImGuiCol_Header] = col;
|
|
}
|
|
|
|
private void genFilterList()
|
|
{
|
|
filter_list.clear();
|
|
foreach(i, comp; filter)
|
|
{
|
|
if(comp)
|
|
{
|
|
filter_list.add(cast(ushort)i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void filterGUI()
|
|
{
|
|
ImGuiStyle * style = igGetStyle();
|
|
ImVec4 col = style.Colors[ImGuiCol_Header];
|
|
style.Colors[ImGuiCol_Header] = style.Colors[ImGuiCol_TextSelectedBg];
|
|
|
|
if(filter.length < edit_components.length)filter.length(edit_components.length);
|
|
|
|
int length = 0;
|
|
foreach(comp; edit_components)
|
|
{
|
|
if(comp.name !is null)length++;
|
|
}
|
|
|
|
if(length && igListBoxHeaderInt("Components##FilterComponents",cast(int)length,cast(int)length))
|
|
{
|
|
foreach(i, ref comp; edit_components)
|
|
{
|
|
if(comp.name is null)continue;
|
|
if(igSelectableBoolPtr(comp.name,&filter[i],0,ImVec2(0,0)))
|
|
{
|
|
genFilterList();
|
|
}
|
|
}
|
|
igListBoxFooter();
|
|
}
|
|
if(igIsItemHovered(0))igSetTooltip("Select components to filter while tool work.\nComponents are only changed for filtered entities.");
|
|
|
|
style.Colors[ImGuiCol_Header] = col;
|
|
}
|
|
} |