#include #include "event.h" #include "uiLibrary.h" #include "widget/widget.h" #include #define Min(a, b) ((a) < (b) ? (a) : (b)) #define Max(a, b) ((a) > (b) ? (a) : (b)) /// Return true if the rectangle contains the point. static bool RectContains(uiRect rect, uiPoint point) { return (rect.x <= point.x) && (point.x <= (rect.x + rect.width)) && (rect.y <= point.y) && (point.y <= (rect.y + rect.height)); } /// Get the bottom-most widget under the given mouse position. static uiWidget* GetWidgetUnderMouse(uiWidget* parent, uiPoint mouse) { assert(parent); // First check the children so that the selection is from "most specific" to // "less specific" from the user's perspective. list_foreach(parent->children, child, { uiWidget* target = GetWidgetUnderMouse(child, mouse); if (target != 0) { return target; } }); if (RectContains(parent->rect, mouse)) { return parent; } return 0; } /// Get the table row at the given pixel position. static void GetTableRowColAtXy( const uiTable* table, uiPoint p, int* out_row, int* out_col) { assert(table); assert(out_row); assert(out_col); const uiWidget* widget = (uiWidget*)table; int col = -1; int row = -1; if (RectContains(widget->rect, p)) { int x = p.x - widget->rect.x; for (col = 0; (col < table->cols) && (x > table->widths[col]); ++col) { x -= table->widths[col]; } // 0 is the header and we want to map the first row to 0, so -1. row = table->offset + ((p.y - widget->rect.y) / g_ui.font->header.glyph_height) - 1; // Out-of-bounds check. if ((col >= table->cols) || (row >= table->rows)) { col = row = -1; } } *out_col = col; *out_row = row; } /// Process a table click event. static void ClickTable(uiTable* table, const uiMouseClickEvent* event) { assert(table); assert(event); int row, col; GetTableRowColAtXy(table, event->mouse_position, &row, &col); if ((row != -1) && (col != -1)) { PushWidgetEvent(&(uiWidgetEvent){ .type = uiWidgetEventClick, .widget = uiMakeTablePtr(table), .table_click = (uiTableClickEvent){.row = row, .col = col} }); } } /// Process a table scroll event. static void ScrollTable(uiTable* table, const uiMouseScrollEvent* event) { assert(table); assert(event); table->offset = Min(table->rows - table->num_visible_rows, Max(0, table->offset - event->scroll_offset)); } /// Process a scroll event. static bool ProcessScrollEvent( uiWidget* widget, const uiMouseScrollEvent* event) { assert(widget); assert(event); bool processed = false; switch (widget->type) { case uiTypeTable: ScrollTable((uiTable*)widget, event); processed = true; break; default: break; } return processed; } /// Process a click event. static bool ProcessClickEvent( uiWidget* widget, const uiMouseClickEvent* event) { assert(widget); assert(event); bool processed = false; switch (widget->type) { case uiTypeTable: ClickTable((uiTable*)widget, event); processed = true; break; default: break; } return processed; } bool uiSendEvent(uiFrame* frame, const uiInputEvent* event) { assert(frame); assert(event); uiWidget* widget = (uiWidget*)frame; bool processed = false; switch (event->type) { case uiEventMouseButton: { const uiMouseButtonEvent* ev = &event->mouse_button; uiMouseButtonState* prev_state = &g_ui.mouse_button_state[ev->button]; if ((*prev_state == uiMouseDown) && (ev->state == uiMouseUp)) { // Click. uiSendEvent( frame, &(uiInputEvent){ .type = uiEventMouseClick, .mouse_click = (uiMouseClickEvent){ .button = ev->button, .mouse_position = ev->mouse_position} }); } *prev_state = ev->state; break; } case uiEventMouseClick: { const uiMouseClickEvent* ev = &event->mouse_click; uiWidget* target = GetWidgetUnderMouse(widget, ev->mouse_position); if (target) { processed = ProcessClickEvent(target, ev); } break; } case uiEventMouseScroll: { const uiMouseScrollEvent* ev = &event->mouse_scroll; uiWidget* target = GetWidgetUnderMouse(widget, ev->mouse_position); if (target) { processed = ProcessScrollEvent(target, ev); } break; } } return processed; }