/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #include "ui/ui_cursor.h" #include "ui/ui.h" #include "ui/ui_window.h" #include "script/script_engine_types.h" #include "script/script_variant.h" #include "log/log.h" using namespace GS; using namespace GS::S2D; //------------------------------------------------------------------------------ bool Scene::CallEventHandler(Cursor *cursor, EventCode event_code, EventTable *event_table) { EventHandler *handler = event_table ? event_table->GetHandler(event_code) : NULL; if (!handler) return false; if (vm->SetupFunctionCall(NULL, handler->GetHandler(), handler->GetContext())) { AutoPtr table_object(vm->CreateTable()); table_object->Set("sprite", Script::Variant(cursor->current_state.item, Script::typetag_Window)); table_object->Set("window", Script::Variant(cursor->current_state.item, Script::typetag_Window)); table_object->Set("widget", Script::Variant(cursor->current_state.widget, Script::typetag_Widget)); if (cursor->current_state.widget) table_object->Set("id", Script::Variant(cursor->current_state.widget->GetId())); table_object->Set("cursor_id", cursor->id); table_object->Set("x", cursor->current_state.x); table_object->Set("y", cursor->current_state.y); table_object->Set("dx", cursor->current_state.x - cursor->previous_state.x); table_object->Set("dy", cursor->current_state.y - cursor->previous_state.y); table_object->Set("down", cursor->current_state.down); // Cursor position in sprite. if (cursor->current_state.item) { Vector2 c_pos = cursor->current_state.item->ScreenToLocal(Vector2(cursor->current_state.x, cursor->current_state.y)); table_object->Set("local_x", c_pos.x); table_object->Set("local_y", c_pos.y); if (cursor->previous_state.item == cursor->current_state.item) { Vector2 p_pos = cursor->previous_state.item->ScreenToLocal(Vector2(cursor->previous_state.x, cursor->previous_state.y)); table_object->Set("local_dx", c_pos.x - p_pos.x); table_object->Set("local_dy", c_pos.y - p_pos.y); } else { table_object->Set("local_dx", 0); table_object->Set("local_dy", 0); } } vm->PushArgument(event_code); vm->PushArgument(table_object.c_ptr()); if (!vm->DoFunctionCall()) __ERR__(__LOG_E__ << "Event handler (" << event_code << ") call failed. The handler parameters should be (event, table).\n", false) return true; } return false; } void Scene::CallEventHandler(Cursor *cursor, Widget *widget, EventCode event_code, bool can_propagate) { for (Widget *base = widget; base; base = base->GetParent()) { if (CallEventHandler(cursor, event_code, base->GetEventTable())) return; if (!can_propagate) break; } // Try the sprite event handler. if (can_propagate && cursor->current_state.item.IsValid()) CallEventHandler(cursor, event_code, cursor->current_state.item->GetEventTable()); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Widget *Scene::RecurseWidgetBelowCursor(Window *window, Widget *widget, int mx, int my) { if (!window || !widget) return NULL; ListForeachPtr(Widget *, c, widget->GetChildren()) { Rect rect(c->GetRect()); if (!c->IsHidden() && window->LocalToScreen(rect).Inside(mx, my)) { Widget *sub_w = RecurseWidgetBelowCursor(window, c, mx, my); return sub_w ? sub_w : c; } } return widget; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Scene::ProcessCursorEnterLeaveWidget(Cursor *cursor, Window *window, Widget *widget) { if (widget && !widget->IsHidden()) { // Process on widget. iRect screen_rect = window->LocalToScreen(widget->GetRect()); if (screen_rect.Inside((int)cursor->current_state.x, (int)cursor->current_state.y)) { if (!screen_rect.Inside((int)cursor->previous_state.x, (int)cursor->previous_state.y)) CallEventHandler(cursor, widget, Event_CursorEnter, false); } else if (screen_rect.Inside((int)cursor->previous_state.x, (int)cursor->previous_state.y)) CallEventHandler(cursor, widget, Event_CursorLeave, false); // Process on widget children. ListForeachPtr(Widget *, c, widget->GetChildren()) ProcessCursorEnterLeaveWidget(cursor, window, c); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Scene::UpdateCursor(Cursor *cursor) { Cursor::State &p_state = cursor->previous_state, &c_state = cursor->current_state, &down_state = cursor->down_state; // Assert lock sprite validity. if (lock && !item_list.Find(lock)) lock = NULL; // Move cursor position in system reference. Vector4 c_ui = screen_to_ui * Vector4(c_state.x, c_state.y, 1); // __LOG__ << "x: " << c_ui.x << ", y: " << c_ui.y << "\n"; c_state.x = c_ui.x; c_state.y = c_ui.y; // Determine the current sprite under the cursor. if (lock) c_state.item = lock; else { float min_z_order = FLT_MAX; c_state.item = NULL; for (List ::Item *e = item_list.GetLast(); e; e = e->Previous()) { Item *i = e->Object(); switch (i->GetItemType()) { case Item::Type_Sprite: case Item::Type_Window: { Sprite *s = (Sprite *)i; if (s->opacity == 0.0) break; if (s->sprite_flags.IsSet(Sprite::FlagNonSensitive)) break; Rect wrect(s->GetRect()); if (s->LocalToScreen(wrect).Inside((int)c_state.x, (int)c_state.y) && (min_z_order > s->GetZOrder())) { min_z_order = s->GetZOrder(); c_state.item = s; } } break; } } } // Determine the current widget under the cursor. c_state.widget = NULL; if (c_state.item && (c_state.item->GetItemType() == Item::Type_Window)) { Window *w = (Window *)c_state.item.c_ptr(); c_state.widget = RecurseWidgetBelowCursor(w, w->GetBaseWidget(), (int)c_state.x, (int)c_state.y); } // State must be complete from here on. // Store down state. if (c_state.down && !p_state.down) down_state = c_state; // Handle cursor up event (done here so that the down_state widget/sprite will always catch the event). if (p_state.down && !c_state.down) { if (down_state.widget) CallEventHandler(cursor, down_state.widget, Event_CursorUp); else if (down_state.item) CallEventHandler(cursor, Event_CursorUp, down_state.item->GetEventTable()); } // Handle hit/down/move cursor events. if (c_state.widget) { if (p_state.down && !c_state.down) { if (c_state.widget == down_state.widget) CallEventHandler(cursor, c_state.widget, Event_CursorHit); } if (!p_state.down && c_state.down) CallEventHandler(cursor, c_state.widget, Event_CursorDown); if ((c_state.widget == p_state.widget) && ((c_state.x != p_state.x) || (c_state.y != p_state.y))) CallEventHandler(cursor, c_state.widget, Event_CursorMove); } else if (c_state.item) { if (p_state.down && !c_state.down) { if (c_state.item == down_state.item) CallEventHandler(cursor, Event_CursorHit, c_state.item->GetEventTable()); } if (!p_state.down && c_state.down) CallEventHandler(cursor, Event_CursorDown, c_state.item->GetEventTable()); if ((c_state.item == p_state.item) && ((c_state.x != p_state.x) || (c_state.y != p_state.y))) CallEventHandler(cursor, Event_CursorMove, c_state.item->GetEventTable()); } // Handle enter/leave cursor events. if (c_state.item) { if (c_state.item->GetItemType() == Item::Type_Window) { Window *w = (Window *)c_state.item.c_ptr(); ProcessCursorEnterLeaveWidget(cursor, w, w->GetBaseWidget()); } if (c_state.item != p_state.item) CallEventHandler(cursor, Event_CursorEnter, c_state.item->GetEventTable()); } if (p_state.item) { if (p_state.item->GetItemType() == Item::Type_Window) { Window *w = (Window *)p_state.item.c_ptr(); ProcessCursorEnterLeaveWidget(cursor, w, w->GetBaseWidget()); } if (p_state.item != c_state.item) CallEventHandler(cursor, Event_CursorLeave, p_state.item->GetEventTable()); } #if 0 if ((c_state.x != p_state.x) || (c_state.y != p_state.y)) { __LOG__ << "CURSOR ID = " << cursor->id << "\n"; __LOG__ << "\n"; __LOG__ << "X = " << c_state.x << ", Y = " << c_state.y << "\n"; if (c_state.item) __LOG__ << "PX = " << c_state.item->GetPosition().x << ", PY = " << c_state.item->GetPosition().y << "\n"; if (Sprite *sprite = (Sprite *)c_state.item) { __LOG__ << "SPRITE = "; if (sprite) __LOG__ << (int)sprite; else __LOG__ << "None"; } __LOG__ << ", WIDGET = "; if (c_state.widget) { __LOG__ << (int)c_state.widget << " (Id = " << c_state.widget->GetId() << "), type = " << c_state.widget->GetType() << "\n"; nRect rect = c_state.widget->GetRect(); __LOG__ << "SX = " << rect.sx << ", SY = " << rect.sy << ", W = " << rect.GetWidth() << ", EY = " << rect.GetHeight() << "\n"; } else __LOG__ << "None"; __LOG__ << "\n"; } #endif p_state = c_state; } //------------------------------------------------------------------------------