#include "panel.h"
#include "panel.inl"
#include "component.inl"
#include "gui.inl"
#include "layout.inl"
#include "window.inl"
#include <draw2d/guictx.h>
#include <geom2d/s2d.h>
#include <geom2d/v2d.h>
#include <core/arrpt.h>
#include <core/objh.h>
#include <core/strings.h>
#include <sewer/cassert.h>
#include <sewer/ptr.h>
struct _panel_t
{
GuiComponent component;
Window *window;
ArrPt(Layout) *layouts;
uint32_t visible_layout;
uint32_t active_layout;
uint32_t flags;
S2Df natural_size;
S2Df control_size;
S2Df content_size;
void *data;
FPtr_destroy func_destroy_data;
ArrPt(GuiComponent) *children;
};
void _panel_destroy(Panel **panel)
{
GuiComponent *component;
cassert_no_null(panel);
cassert_no_null(*panel);
component = &(*panel)->component;
_component_destroy(&component);
}
void _panel_destroy_all(Panel **panel)
{
cassert_no_null(panel);
cassert_no_null(*panel);
if ((*panel)->data != NULL)
{
if ((*panel)->func_destroy_data != NULL)
(*panel)->func_destroy_data(&(*panel)->data);
}
arrpt_destroy(&(*panel)->layouts, _layout_destroy, Layout);
cassert(arrpt_size((*panel)->children, GuiComponent) == 0);
arrpt_destroy(&(*panel)->children, NULL, GuiComponent);
_component_destroy_imp(&(*panel)->component);
obj_delete(panel, Panel);
}
static Panel *i_create(const uint32_t flags)
{
const GuiCtx *context = guictx_get_current();
Panel *panel = obj_new0(Panel);
void *ositem = context->func_create[ekGUI_TYPE_PANEL](flags);
_component_init(&panel->component, context, PARAM(type, ekGUI_TYPE_PANEL), &ositem);
panel->layouts = arrpt_create(Layout);
panel->visible_layout = UINT32_MAX;
panel->active_layout = UINT32_MAX;
panel->natural_size = s2df(-1.f, -1.f);
panel->control_size = s2df((flags & ekVIEW_HSCROLL) ? 256.f : -1.f, (flags & ekVIEW_VSCROLL) ? 256.f : -1.f);
panel->content_size = s2df(-1.f, -1.f);
panel->flags = flags;
panel->children = arrpt_create(GuiComponent);
return panel;
}
Panel *panel_create(void)
{
return i_create(0);
}
Panel *panel_scroll(const bool_t hscroll, const bool_t vscroll)
{
uint32_t flags = 0;
flags |= (hscroll == TRUE) ? ekVIEW_HSCROLL : 0;
flags |= (vscroll == TRUE) ? ekVIEW_VSCROLL : 0;
return i_create(flags);
}
Panel *panel_custom(const bool_t hscroll, const bool_t vscroll, const bool_t border)
{
uint32_t flags = 0;
flags |= (hscroll == TRUE) ? ekVIEW_HSCROLL : 0;
flags |= (vscroll == TRUE) ? ekVIEW_VSCROLL : 0;
flags |= (border == TRUE) ? ekVIEW_BORDER : 0;
return i_create(flags);
}
void panel_data_imp(Panel *panel, void **data, FPtr_destroy func_destroy_data)
{
cassert_no_null(panel);
cassert(panel->data == NULL);
cassert(panel->func_destroy_data == NULL);
panel->data = ptr_dget_no_null(data, void);
panel->func_destroy_data = func_destroy_data;
}
void *panel_get_data_imp(const Panel *panel)
{
cassert_no_null(panel);
return panel->data;
}
void panel_size(Panel *panel, const S2Df size)
{
cassert_no_null(panel);
panel->control_size = size;
}
uint32_t panel_layout(Panel *panel, Layout *layout)
{
uint32_t layout_index;
cassert_no_null(panel);
cassert_no_null(layout);
layout_index = arrpt_size(panel->layouts, Layout);
arrpt_append(panel->layouts, layout, Layout);
_layout_attach_to_panel(layout, panel);
if (layout_index == 0)
{
cassert(panel->active_layout == UINT32_MAX);
panel->active_layout = 0;
}
else
{
cassert(panel->active_layout != UINT32_MAX);
}
return layout_index;
}
Layout *panel_get_layout(Panel *panel, const uint32_t index)
{
cassert_no_null(panel);
return arrpt_get(panel->layouts, index, Layout);
}
void panel_visible_layout(Panel *panel, const uint32_t index)
{
cassert_no_null(panel);
cassert(index < arrpt_size(panel->layouts, Layout));
if (panel->active_layout != index)
panel->active_layout = index;
}
void panel_update(Panel *panel)
{
cassert_no_null(panel);
cassert_no_null(panel->component.context);
cassert_no_nullf(panel->component.context->func_panel_set_need_display);
if (panel->window != NULL)
{
panel->component.context->func_panel_set_need_display(panel->component.ositem);
_window_update(panel->window);
}
}
real32_t panel_scroll_width(const Panel *panel)
{
real32_t v = 0;
cassert_no_null(panel);
cassert_no_null(panel->component.context);
cassert_no_nullf(panel->component.context->func_panel_scroller_size);
panel->component.context->func_panel_scroller_size(panel->component.ositem, &v, NULL);
return v;
}
real32_t panel_scroll_height(const Panel *panel)
{
real32_t v = 0;
cassert_no_null(panel);
cassert_no_null(panel->component.context);
cassert_no_nullf(panel->component.context->func_panel_scroller_size);
panel->component.context->func_panel_scroller_size(panel->component.ositem, NULL, &v);
return v;
}
void _panel_attach_component(Panel *panel, GuiComponent *component)
{
cassert_no_null(panel);
cassert_no_null(component);
if (arrpt_find(panel->children, component, GuiComponent) == UINT32_MAX)
{
arrpt_append(panel->children, component, GuiComponent);
_component_attach_to_panel(cast(panel, GuiComponent), component);
}
}
static void i_hide_component(GuiComponent *component)
{
Panel *panels[GUI_COMPONENT_MAX_PANELS];
uint32_t i, n;
_component_visible(component, FALSE);
_component_panels(component, &n, panels);
for (i = 0; i < n; ++i)
{
arrpt_foreach(child, panels[i]->children, GuiComponent)
i_hide_component(child);
arrpt_end()
}
}
void _panel_dettach_component(Panel *panel, GuiComponent *component)
{
uint32_t index = UINT32_MAX;
cassert_no_null(panel);
cassert_no_null(component);
#if defined(__WINDOWS__)
i_hide_component(component);
#endif
_component_set_parent_window(component, NULL);
_component_detach_from_panel(&panel->component, component);
index = arrpt_find(panel->children, component, GuiComponent);
arrpt_delete(panel->children, index, NULL, GuiComponent);
}
void _panel_destroy_component(Panel *panel, GuiComponent *component)
{
bool_t exists = FALSE;
cassert_no_null(panel);
cassert_no_null(component);
arrpt_foreach(layout, panel->layouts, Layout)
if (layout != NULL)
{
if (_layout_search_component(layout, component, NULL, FALSE) != NULL)
{
exists = TRUE;
break;
}
}
arrpt_end()
if (exists == FALSE)
{
uint32_t index;
#if defined(__WINDOWS__)
i_hide_component(component);
#endif
_component_set_parent_window(component, NULL);
_component_detach_from_panel(&panel->component, component);
index = arrpt_find(panel->children, component, GuiComponent);
arrpt_delete(panel->children, index, _component_destroy, GuiComponent);
}
}
void _panel_hide_all(Panel *panel)
{
cassert_no_null(panel);
i_hide_component(&panel->component);
}
void _panel_invalidate_layout(Panel *panel, Layout *layout)
{
cassert_no_null(panel);
cassert_no_null(panel->component.context);
cassert_no_nullf(panel->component.context->func_panel_set_need_display);
if (panel->visible_layout != UINT32_MAX)
{
Layout *main_layout = arrpt_get(panel->layouts, panel->visible_layout, Layout);
if (_layout_search_layout(main_layout, layout) == TRUE)
panel->visible_layout = UINT32_MAX;
}
}
GuiComponent *_panel_get_component(Panel *panel)
{
cassert_no_null(panel);
return &(panel)->component;
}
GuiComponent *_panel_find_component(Panel *panel, void *ositem)
{
cassert_no_null(panel);
arrpt_foreach(component, panel->children, GuiComponent)
if (component->ositem == ositem)
return component;
if (component->type == ekGUI_TYPE_PANEL)
{
GuiComponent *child = _panel_find_component(cast(component, Panel), ositem);
if (child != NULL)
return child;
}
arrpt_end()
return NULL;
}
bool_t _panel_in_active_layout(const Panel *panel, const GuiComponent *component)
{
Layout *layout = NULL;
cassert_no_null(panel);
cassert(panel->active_layout == panel->visible_layout);
layout = arrpt_get(panel->layouts, panel->active_layout, Layout);
return (bool_t)(_layout_search_component(layout, component, NULL, TRUE) != NULL);
}
Layout *_panel_active_layout(const Panel *panel)
{
cassert_no_null(panel);
cassert(panel->active_layout == panel->visible_layout);
return arrpt_get(panel->layouts, panel->active_layout, Layout);
}
void _panel_panels(const Panel *panel, uint32_t *num_panels, Panel **panels)
{
cassert_no_null(panel);
cassert_no_null(num_panels);
cassert_no_null(panels);
*num_panels = 1;
panels[0] = cast(panel, Panel);
}
void _panel_window(Panel *panel, Window *window)
{
cassert_no_null(panel);
if (window != NULL)
{
cassert(panel->window == NULL);
panel->window = obj_retain(window, Window);
}
else
{
if (panel->window != NULL)
obj_release(&panel->window, Window);
}
{
GuiComponent **child;
uint32_t i, num_elems;
child = arrpt_all(panel->children, GuiComponent);
num_elems = arrpt_size(panel->children, GuiComponent);
for (i = 0; i < num_elems; ++i, ++child)
{
cassert_no_null(child);
_component_set_parent_window(*child, panel->window);
}
}
}
Window *_panel_get_window(Panel *panel)
{
cassert_no_null(panel);
return panel->window;
}
Cell *_panel_get_component_cell(Panel *panel, const GuiComponent *component)
{
Cell *cell = NULL;
cassert_no_null(panel);
cassert(arrpt_find(panel->children, component, GuiComponent) != UINT32_MAX);
if (panel->active_layout != UINT32_MAX)
{
Layout *layout = arrpt_get(panel->layouts, panel->active_layout, Layout);
_layout_search_component(layout, component, &cell, FALSE);
}
return cell;
}
static void i_activate_layout(const ArrPt(Layout) *layouts, ArrPt(GuiComponent) *children, const uint32_t active_layout)
{
const Layout *layout = arrpt_get_const(layouts, active_layout, Layout);
ArrPt(GuiComponent) *layout_components = arrpt_create(GuiComponent);
_layout_components(layout, layout_components);
arrpt_foreach(component, children, GuiComponent)
if (arrpt_find(layout_components, component, GuiComponent) != UINT32_MAX)
_component_visible(component, TRUE);
else
_component_visible(component, FALSE);
arrpt_end()
arrpt_destroy(&layout_components, NULL, GuiComponent);
}
void _panel_compose(Panel *panel, const S2Df *required_size, S2Df *final_size)
{
Layout *layout = NULL;
cassert_no_null(panel);
if (panel->visible_layout != panel->active_layout)
{
i_activate_layout(panel->layouts, panel->children, panel->active_layout);
panel->visible_layout = panel->active_layout;
}
layout = arrpt_get(panel->layouts, panel->active_layout, Layout);
_layout_compose(layout, required_size, final_size);
cassert(panel->control_size.width <= 0 || final_size->width <= panel->control_size.width);
cassert(panel->control_size.height <= 0 || final_size->height <= panel->control_size.height);
}
void _panel_natural(Panel *panel, const uint32_t di, real32_t *dim0, real32_t *dim1)
{
Layout *layout = NULL;
cassert_no_null(panel);
if (panel->visible_layout != panel->active_layout)
{
i_activate_layout(panel->layouts, panel->children, panel->active_layout);
panel->visible_layout = panel->active_layout;
}
layout = arrpt_get(panel->layouts, panel->active_layout, Layout);
_layout_natural(layout, di, dim0, dim1);
if (di == 0)
{
panel->natural_size.width = *dim0;
panel->content_size.width = *dim0;
if (panel->control_size.width > 0)
*dim0 = panel->control_size.width;
}
else
{
cassert(di == 1);
panel->natural_size.height = *dim1;
panel->content_size.height = *dim1;
if (panel->control_size.height > 0)
*dim1 = panel->control_size.height;
}
}
void _panel_expand(Panel *panel, const uint32_t di, const real32_t current_size, const real32_t required_size, real32_t *final_size)
{
Layout *layout = NULL;
real32_t cursize = 0;
real32_t reqsize = 0;
real32_t laysize = 0;
cassert_no_null(panel);
cassert_no_null(final_size);
cassert(panel->active_layout == panel->visible_layout);
layout = arrpt_get(panel->layouts, panel->active_layout, Layout);
if (di == 0)
{
cursize = panel->natural_size.width;
if (panel->control_size.width > 0)
{
cassert_unref(current_size == panel->control_size.width, current_size);
if (required_size > panel->control_size.width)
reqsize = required_size;
else
reqsize = panel->control_size.width;
}
else
{
reqsize = required_size;
}
}
else
{
cassert(di == 1);
cursize = panel->natural_size.height;
if (panel->control_size.height > 0)
{
if (required_size > panel->control_size.height)
reqsize = required_size;
else
reqsize = panel->control_size.height;
}
else
{
reqsize = required_size;
}
}
_layout_expand(layout, di, cursize, reqsize, &laysize);
if (di == 0 && panel->control_size.width > 0)
*final_size = required_size;
else if (di == 1 && panel->control_size.height > 0)
*final_size = required_size;
else
*final_size = laysize;
if (panel->flags & ekVIEW_HSCROLL || panel->flags & ekVIEW_VSCROLL)
{
if (di == 0)
{
panel->content_size.width = laysize;
}
else
{
cassert(di == 1);
panel->content_size.height = laysize;
}
}
}
void _panel_OnResize(Panel *panel, const S2Df *size)
{
cassert_no_null(panel);
unref(size);
if (panel->flags & ekVIEW_HSCROLL || panel->flags & ekVIEW_VSCROLL)
panel->component.context->func_panel_content_size(panel->component.ositem, panel->content_size.width, panel->content_size.height, 10, 10);
}
void _panel_taborder(const Panel *panel, Window *window)
{
Layout *layout = NULL;
cassert_no_null(panel);
cassert(panel->visible_layout == panel->active_layout);
cassert_unref(panel->window == window, window);
layout = arrpt_get(panel->layouts, panel->active_layout, Layout);
_layout_taborder(layout, panel->window);
}
void _panel_locate(Panel *panel)
{
Layout *layout = NULL;
cassert_no_null(panel);
cassert(panel->visible_layout == panel->active_layout);
layout = arrpt_get(panel->layouts, panel->active_layout, Layout);
_layout_locate(layout);
}
void _panel_locale(Panel *panel)
{
cassert_no_null(panel);
arrpt_foreach(child, panel->children, GuiComponent)
_component_locale(child);
arrpt_end()
}
ArrPt(Layout) *_panel_layouts(const Panel *panel)
{
cassert_no_null(panel);
return panel->layouts;
}
void _panel_content_size(Panel *panel, const real32_t width, const real32_t height)
{
cassert_no_null(panel);
panel->component.context->func_panel_content_size(panel->component.ositem, width, height, 10, 10);
}