302 lines
9.3 KiB
C++
302 lines
9.3 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include <float.h>
|
|
#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 <Script::Object> 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 <int> 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 *> ::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 <int> 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 <int> 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;
|
|
}
|
|
//------------------------------------------------------------------------------
|