#include "osscrolls.inl"
#include "osscroll.inl"
#include "oscontrol.inl"
#include <core/event.h>
#include <core/heap.h>
#include <sewer/cassert.h>
#include <sewer/ptr.h>
struct _osscrolls_t
{
OSControl *control;
Listener *OnScroll;
OSScroll *hscroll;
OSScroll *vscroll;
bool_t hvisible;
bool_t vvisible;
uint32_t view_width;
uint32_t view_height;
uint32_t content_width;
uint32_t content_height;
uint32_t line_width;
uint32_t line_height;
};
OSScrolls *_osscrolls_create(OSControl *control, const bool_t horizontal, const bool_t vertical)
{
OSScrolls *scroll = heap_new0(OSScrolls);
cassert_no_null(control);
cassert(horizontal == TRUE || vertical == TRUE);
scroll->control = control;
if (horizontal == TRUE)
scroll->hscroll = _osscroll_horizontal(control);
if (vertical == TRUE)
scroll->vscroll = _osscroll_vertical(control);
scroll->hvisible = TRUE;
scroll->vvisible = TRUE;
return scroll;
}
void _osscrolls_destroy(OSScrolls **scroll)
{
cassert_no_null(scroll);
cassert_no_null(*scroll);
if ((*scroll)->hscroll != NULL)
_osscroll_destroy(&(*scroll)->hscroll, (*scroll)->control);
if ((*scroll)->vscroll != NULL)
_osscroll_destroy(&(*scroll)->vscroll, (*scroll)->control);
listener_destroy(&(*scroll)->OnScroll);
heap_delete(scroll, OSScrolls);
}
void _osscrolls_OnScroll(OSScrolls *scroll, Listener *listener)
{
cassert_no_null(scroll);
listener_update(&scroll->OnScroll, listener);
}
void _osscrolls_visible_area(OSScrolls *scroll, uint32_t *x, uint32_t *y, uint32_t *width, uint32_t *height, uint32_t *total_width, uint32_t *total_height)
{
cassert_no_null(scroll);
if (scroll->hscroll != NULL)
{
uint32_t pos = _osscroll_pos(scroll->hscroll);
ptr_assign(x, pos);
ptr_assign(total_width, scroll->content_width);
}
else
{
ptr_assign(x, 0);
ptr_assign(total_width, scroll->view_width);
}
if (scroll->vscroll != NULL)
{
uint32_t pos = _osscroll_pos(scroll->vscroll);
ptr_assign(y, pos);
ptr_assign(total_height, scroll->content_height);
}
else
{
ptr_assign(y, 0);
ptr_assign(total_height, scroll->view_height);
}
ptr_assign(width, scroll->view_width);
ptr_assign(height, scroll->view_height);
}
uint32_t _osscrolls_x_pos(const OSScrolls *scroll)
{
cassert_no_null(scroll);
if (scroll->hscroll != NULL)
return _osscroll_pos(scroll->hscroll);
return 0;
}
uint32_t _osscrolls_y_pos(const OSScrolls *scroll)
{
cassert_no_null(scroll);
if (scroll->vscroll != NULL)
return _osscroll_pos(scroll->vscroll);
return 0;
}
uint32_t _osscrolls_bar_width(const OSScrolls *scroll, const bool_t check_if_visible)
{
if (check_if_visible == TRUE)
{
cassert_no_null(scroll);
if (scroll->vscroll != NULL)
{
if (scroll->vvisible == TRUE && scroll->content_height > scroll->view_height)
return _osscroll_bar_width(scroll->vscroll);
}
return 0;
}
else
{
return _osscroll_bar_width(scroll->vscroll);
}
}
uint32_t _osscrolls_bar_height(const OSScrolls *scroll, const bool_t check_if_visible)
{
if (check_if_visible == TRUE)
{
cassert_no_null(scroll);
if (scroll->hscroll != NULL)
{
if (scroll->hvisible == TRUE && scroll->content_width > scroll->view_width)
return _osscroll_bar_height(scroll->hscroll);
}
return 0;
}
else
{
return _osscroll_bar_height(scroll->hscroll);
}
}
bool_t _osscrolls_event(OSScrolls *scroll, const gui_orient_t orient, const gui_scroll_t event, const bool_t update_children)
{
OSScroll *sbar = NULL;
uint32_t step = 0;
uint32_t page = 0;
uint32_t max = 0;
cassert_no_null(scroll);
cassert_no_null(scroll->control);
switch (orient)
{
case ekGUI_HORIZONTAL:
sbar = scroll->hscroll;
step = scroll->line_width;
page = scroll->view_width;
max = scroll->content_width - scroll->view_width;
break;
case ekGUI_VERTICAL:
sbar = scroll->vscroll;
step = scroll->line_height;
page = scroll->view_height;
max = scroll->content_height - scroll->view_height;
break;
cassert_default();
}
if (sbar != NULL)
{
uint32_t curpos = _osscroll_pos(sbar);
uint32_t pos = curpos;
switch (event)
{
case ekGUI_SCROLL_BEGIN:
pos = 0;
break;
case ekGUI_SCROLL_END:
pos = max;
break;
case ekGUI_SCROLL_STEP_LEFT:
if (pos > step)
pos -= step;
else
pos = 0;
break;
case ekGUI_SCROLL_STEP_RIGHT:
pos += step;
if (pos > max)
pos = max;
break;
case ekGUI_SCROLL_PAGE_LEFT:
if (pos > page)
pos -= page;
else
pos = 0;
break;
case ekGUI_SCROLL_PAGE_RIGHT:
pos += page;
if (pos > max)
pos = max;
break;
case ekGUI_SCROLL_THUMB:
pos = _osscroll_trackpos(sbar);
break;
cassert_default();
}
if (scroll->OnScroll != NULL)
{
EvScroll p;
real32_t r = (real32_t)pos;
gui_type_t type = _oscontrol_type(scroll->control);
p.orient = orient;
p.scroll = event;
p.cpos = (real32_t)curpos;
if (type == ekGUI_TYPE_PANEL)
{
listener_event(scroll->OnScroll, ekGUI_EVENT_SCROLL, cast(scroll->control, OSPanel), &p, &r, OSPanel, EvScroll, real32_t);
}
else
{
cassert(type == ekGUI_TYPE_CUSTOMVIEW);
listener_event(scroll->OnScroll, ekGUI_EVENT_SCROLL, cast(scroll->control, OSView), &p, &r, OSView, EvScroll, real32_t);
}
pos = (uint32_t)r;
}
if (curpos != pos)
{
_osscroll_set_pos(sbar, pos);
if (update_children == TRUE)
{
int32_t incr_x = 0;
int32_t incr_y = 0;
switch (orient)
{
case ekGUI_HORIZONTAL:
incr_x = (int32_t)curpos - (int32_t)pos;
break;
case ekGUI_VERTICAL:
incr_y = (int32_t)curpos - (int32_t)pos;
break;
cassert_default();
}
_osscroll_control_scroll(scroll->control, incr_x, incr_y);
}
return TRUE;
}
}
return FALSE;
}
void _osscrolls_set(OSScrolls *scroll, const uint32_t x, const uint32_t y, const bool_t update_children)
{
uint32_t px = x;
uint32_t py = y;
int32_t incr_x = 0;
int32_t incr_y = 0;
cassert_no_null(scroll);
if (px != UINT32_MAX)
{
uint32_t max = scroll->content_width - scroll->view_width;
max += _osscrolls_bar_width(scroll, TRUE);
if (px > max)
px = max;
}
if (py != UINT32_MAX)
{
if (py > scroll->content_height - scroll->view_height)
py = scroll->content_height - scroll->view_height;
}
if (px != UINT32_MAX && scroll->hscroll != NULL)
{
uint32_t pos = _osscroll_pos(scroll->hscroll);
if (pos != px)
{
_osscroll_set_pos(scroll->hscroll, px);
incr_x = (int32_t)pos - (int32_t)px;
}
}
if (py != UINT32_MAX && scroll->vscroll != NULL)
{
uint32_t pos = _osscroll_pos(scroll->vscroll);
if (pos != py)
{
_osscroll_set_pos(scroll->vscroll, py);
incr_y = (int32_t)pos - (int32_t)py;
}
}
if (update_children == TRUE)
{
if (incr_x != 0 || incr_y != 0)
_osscroll_control_scroll(scroll->control, incr_x, incr_y);
}
}
static bool_t i_limits(OSScroll *scroll, const bool_t visible, const uint32_t visible_size, const uint32_t total_size)
{
if (visible == TRUE && visible_size > 0 && visible_size < total_size)
{
uint32_t max = total_size;
uint32_t pos = _osscroll_pos(scroll);
uint32_t page = visible_size;
uint32_t max_pos = total_size - visible_size;
if (pos > max_pos)
pos = max_pos;
_osscroll_config(scroll, pos, max, page);
return TRUE;
}
else
{
return FALSE;
}
}
static void i_update_bars(OSScrolls *scroll)
{
bool_t with_hscroll = FALSE;
bool_t with_vscroll = FALSE;
uint32_t bw = 0;
uint32_t bh = 0;
cassert_no_null(scroll);
if (scroll->hscroll != NULL)
{
with_hscroll = i_limits(scroll->hscroll, scroll->hvisible, scroll->view_width, scroll->content_width);
bh = _osscroll_bar_height(scroll->hscroll);
}
if (scroll->vscroll != NULL)
{
with_vscroll = i_limits(scroll->vscroll, scroll->vvisible, scroll->view_height, scroll->content_height);
bw = _osscroll_bar_width(scroll->vscroll);
}
if (with_hscroll == TRUE)
{
uint32_t x = 0;
uint32_t y = scroll->view_height - bh;
uint32_t width = scroll->view_width;
uint32_t height = bh;
if (with_vscroll == TRUE)
{
width -= bw;
i_limits(scroll->hscroll, scroll->hvisible, width, scroll->content_width);
}
_osscroll_frame(scroll->hscroll, x, y, width, height);
_osscroll_visible(scroll->hscroll, TRUE);
}
else
{
if (scroll->hscroll != NULL)
_osscroll_visible(scroll->hscroll, FALSE);
}
if (with_vscroll == TRUE)
{
uint32_t x = scroll->view_width - bw;
uint32_t y = 0;
uint32_t width = bw;
uint32_t height = scroll->view_height;
_osscroll_frame(scroll->vscroll, x, y, width, height);
_osscroll_visible(scroll->vscroll, TRUE);
}
else
{
if (scroll->vscroll != NULL)
_osscroll_visible(scroll->vscroll, FALSE);
}
}
void _osscrolls_content_size(OSScrolls *scroll, const uint32_t width, const uint32_t height, const uint32_t line_width, const uint32_t line_height)
{
cassert_no_null(scroll);
scroll->content_width = width;
scroll->content_height = height;
scroll->line_width = line_width;
scroll->line_height = line_height;
i_update_bars(scroll);
}
void _osscrolls_control_size(OSScrolls *scroll, const uint32_t width, const uint32_t height)
{
cassert_no_null(scroll);
scroll->view_width = width;
scroll->view_height = height;
i_update_bars(scroll);
}
void _osscrolls_visible(OSScrolls *scroll, const bool_t horizontal, const bool_t vertical)
{
cassert_no_null(scroll);
scroll->hvisible = horizontal;
scroll->vvisible = vertical;
i_update_bars(scroll);
}