Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gamedata/scripts/lua_help_ex.script
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@
void AddHeader(string str) // Adds a non-selectable, colored title row (used to group items in a merged context menu)
CUIPropertiesBox AddSubmenu(string label) // Adds a "label >" row whose submenu expands on hover; returns the child box to populate (AddItem/AddHeader/nested AddSubmenu)
bool AddItem(string str, u32 id) // Adds a selectable item row carrying a u32 id (read back via GetSelectedItem():GetTAG() / map_spot_menu_property_clicked prop_id); lets a menu carry duplicate labels
void ShowAt(Frect parent_rect, vector2 point) // Opens the box at `point`, kept inside `parent_rect`; both in the box's PARENT space. AttachChild and AutoUpdateSize() first (submenus too)
}

class CUIListBoxItem : CUIFrameLineWnd {
Expand Down
1 change: 0 additions & 1 deletion src/xrGame/ui/UIActorMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,6 @@ void CUIActorMenu::CallMessageBoxOK(LPCSTR text)
void CUIActorMenu::ResetMode()
{
ClearAllLists();
m_pMouseCapturer = NULL;
m_UIPropertiesBox->Hide();
SetCurrentItem(NULL);
}
Expand Down
1 change: 0 additions & 1 deletion src/xrGame/ui/UIActorMenuInventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,6 @@ void CUIActorMenu::InitCellForSlot(u16 slot_idx)
void CUIActorMenu::InitInventoryContents(CUIDragDropListEx* pBagList)
{
ClearAllLists();
m_pMouseCapturer = NULL;
m_UIPropertiesBox->Hide();
SetCurrentItem(NULL);

Expand Down
4 changes: 2 additions & 2 deletions src/xrGame/ui/UIDragDropListEx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ void CUIDragDropListEx::DestroyDragItem()
{
if (m_selected_item && m_drag_item && m_drag_item->ParentItem() == m_selected_item)
{
VERIFY(GetParent()->GetMouseCapturer()==m_drag_item);
GetParent()->SetCapture(NULL, false);
VERIFY(m_drag_item->IsMouseCapturer());
GetParent()->SetCapture(m_drag_item, false);

delete_data(m_drag_item);
}
Expand Down
2 changes: 1 addition & 1 deletion src/xrGame/ui/UIFixedScrollBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ bool CUIFixedScrollBar::OnMouseAction(float x, float y, EUIMessages mouse_action
return true;
case WINDOW_MOUSE_MOVE:
{
bool im_capturer = (GetMouseCapturer() == m_ScrollBox);
bool im_capturer = m_ScrollBox->IsMouseCapturer();
bool cursor_over = false;
Fvector2 cursor_pos = GetUICursor().GetCursorPosition();
Frect box_rect;
Expand Down
2 changes: 0 additions & 2 deletions src/xrGame/ui/UIMpTradeWnd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,6 @@ void CUIMpTradeWnd::Show(bool status)

if (status)
{
m_pMouseCapturer = NULL;

m_static_information->SetText("");
m_static_money_change->SetText("");
}
Expand Down
86 changes: 35 additions & 51 deletions src/xrGame/ui/UIPropertiesBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,15 @@ void CUIPropertiesBox::HideActiveSubmenu()
m_submenu_close_at = 0;
}

bool CUIPropertiesBox::CursorOverTree()
CUIPropertiesBox* CUIPropertiesBox::BoxUnderCursor()
{
if (m_active_submenu && m_active_submenu->IsShown())
if (CUIPropertiesBox* deeper = m_active_submenu->BoxUnderCursor())
return deeper;

Frect r;
GetAbsoluteRect(r);
if (r.in(GetUICursor().GetCursorPosition()))
return true;
if (m_active_submenu && m_active_submenu->IsShown())
return m_active_submenu->CursorOverTree();
return false;
return r.in(GetUICursor().GetCursorPosition()) ? this : NULL;
}

void CUIPropertiesBox::ClearSubmenus()
Expand Down Expand Up @@ -251,7 +251,8 @@ void CUIPropertiesBox::Show(const Frect& parent_rect, const Fvector2& point)

ResetAll();

GetParent()->SetCapture(this, true);
if (!m_parent_menu)
GetParent()->SetCapture(this, true);
m_UIListWnd.Reset();

float pad_h = m_UIListWnd.GetPadSize().y;
Expand All @@ -264,66 +265,49 @@ void CUIPropertiesBox::Hide()
CUIWindow::Show(false);
CUIWindow::Enable(false);

m_pMouseCapturer = NULL;

if (GetParent()->GetMouseCapturer() == this)
{
if (m_parent_menu && m_parent_menu->IsShown())
GetParent()->SetCapture(m_parent_menu, true);
else
GetParent()->SetCapture(this, false);
}
ReleaseMouseCapture();

HideActiveSubmenu();
}

bool CUIPropertiesBox::OnMouseAction(float x, float y, EUIMessages mouse_action)
{
bool cursor_on_box;
if (m_parent_menu)
return HandleMouse(x, y, mouse_action);

CUIPropertiesBox* target = BoxUnderCursor();

if (x >= 0 && x < GetWidth() && y >= 0 && y < GetHeight())
cursor_on_box = true;
else
cursor_on_box = false;


CUIPropertiesBox* root = this;
while (root->m_parent_menu)
root = root->m_parent_menu;

if (mouse_action == WINDOW_LBUTTON_DOWN && !cursor_on_box)
{
root->Hide();
return true;
}
if (mouse_action == WINDOW_RBUTTON_DOWN && !cursor_on_box)
if (target && target != this)
{
root->Hide();
Frect r;
target->GetAbsoluteRect(r);
Fvector2 cur = GetUICursor().GetCursorPosition();
return target->HandleMouse(cur.x - r.x1, cur.y - r.y1, mouse_action);
}
if (mouse_action == WINDOW_MOUSE_WHEEL_DOWN || mouse_action == WINDOW_MOUSE_WHEEL_UP)

if (!target) // outside the whole tree
{
Fvector2 cur = GetUICursor().GetCursorPosition();
CUIPropertiesBox* target = this;
for (CUIPropertiesBox* b = root; b;
b = (b->m_active_submenu && b->m_active_submenu->IsShown()) ? b->m_active_submenu : NULL)
if (mouse_action == WINDOW_LBUTTON_DOWN || mouse_action == WINDOW_RBUTTON_DOWN)
{
Frect r;
b->GetAbsoluteRect(r);
if (r.in(cur))
target = b;
Hide();
return true;
}
if (target->m_UIListWnd.GetPadSize().y > target->m_UIListWnd.GetHeight())
target->m_UIListWnd.OnMouseAction(x, y, mouse_action);
return true;
}

bool res = inherited::OnMouseAction(x, y, mouse_action);
return HandleMouse(x, y, mouse_action);
}

if (IsShown() && GetParent() && GetParent()->GetMouseCapturer() != this)
GetParent()->SetCapture(this, true);
// Input handling for one box, after routing. Coordinates are relative to this box.
bool CUIPropertiesBox::HandleMouse(float x, float y, EUIMessages mouse_action)
{
if (mouse_action == WINDOW_MOUSE_WHEEL_DOWN || mouse_action == WINDOW_MOUSE_WHEEL_UP)
{
if (m_UIListWnd.GetPadSize().y > m_UIListWnd.GetHeight())
m_UIListWnd.OnMouseAction(x, y, mouse_action);
return true;
}

return res;
return inherited::OnMouseAction(x, y, mouse_action);
}

void CUIPropertiesBox::AutoUpdateSize()
Expand Down Expand Up @@ -370,7 +354,7 @@ void CUIPropertiesBox::Update()
{
Frect ir;
m_active_sub_item->GetAbsoluteRect(ir);
bool inside = ir.in(GetUICursor().GetCursorPosition()) || m_active_submenu->CursorOverTree();
bool inside = ir.in(GetUICursor().GetCursorPosition()) || m_active_submenu->BoxUnderCursor();
if (inside)
{
m_submenu_close_at = 0;
Expand Down
4 changes: 3 additions & 1 deletion src/xrGame/ui/UIPropertiesBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ class CUIPropertiesBox :
void ShowSubMenuForItem(CUIListBoxItem* item);
void HideActiveSubmenu();
void ClearSubmenus();
bool CursorOverTree();

CUIPropertiesBox* BoxUnderCursor();
bool HandleMouse(float x, float y, EUIMessages mouse_action);

DECLARE_SCRIPT_REGISTER_FUNCTION
};
1 change: 1 addition & 0 deletions src/xrGame/ui/UIPropertiesBox_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ void CUIPropertiesBox::script_register(lua_State* L)
.def("RemoveItem", &CUIPropertiesBox::RemoveItemByTAG)
.def("RemoveAll", &CUIPropertiesBox::RemoveAll)
.def("Show", (void(CUIPropertiesBox::*)(int, int))&CUIPropertiesBox::Show)
.def("ShowAt", (void(CUIPropertiesBox::*)(const Frect&, const Fvector2&))&CUIPropertiesBox::Show)
.def("Hide", &CUIPropertiesBox::Hide)
.def("GetSelectedItem", &CUIPropertiesBox::GetClickedItem)
.def("AutoUpdateSize", &CUIPropertiesBox::AutoUpdateSize)
Expand Down
2 changes: 1 addition & 1 deletion src/xrGame/ui/UIScrollBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ bool CUIScrollBox::OnMouseAction(float x, float y, EUIMessages mouse_action)
cursor_over = true;
}

bool im_capturer = (GetParent()->GetMouseCapturer() == this);
bool im_capturer = IsMouseCapturer();

if (mouse_action == WINDOW_LBUTTON_DOWN || mouse_action == WINDOW_LBUTTON_DB_CLICK)
{
Expand Down
91 changes: 67 additions & 24 deletions src/xrGame/ui/UIWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,36 @@ void CUIWindow::ResetPPMode()
}
}

// Windows that have taken the mouse, oldest first; the top one receives all mouse input. Nesting is
// a transient grab (a scrollbar drag) on top of a lasting one (an open popup), so a release restores
// the previous holder instead of leaving nobody in charge.
static xr_vector<CUIWindow*> s_capture_stack;

static void erase_capture(CUIWindow* w)
{
auto it = std::find(s_capture_stack.begin(), s_capture_stack.end(), w);
if (it != s_capture_stack.end())
s_capture_stack.erase(it);
}

static bool capture_inside(CUIWindow* c, CUIWindow* w)
{
for (CUIWindow* p = c; p; p = p->GetParent())
if (p == w)
return true;
return false;
}

static void erase_capture_subtree(CUIWindow* w)
{
s_capture_stack.erase(
std::remove_if(s_capture_stack.begin(), s_capture_stack.end(),
[w](CUIWindow* c) { return capture_inside(c, w); }),
s_capture_stack.end());
}

CUIWindow::CUIWindow()
: m_pParentWnd(NULL),
m_pMouseCapturer(NULL),
m_pMessageTarget(NULL),
m_pKeyboardCapturer(NULL),
m_bAutoDelete(false),
Expand Down Expand Up @@ -135,6 +162,8 @@ CUIWindow::~CUIWindow()
{
VERIFY(!(GetParent()&&IsAutoDelete()));

erase_capture_subtree(this);

//if (m_pHint)
// xr_delete(m_pHint);

Expand Down Expand Up @@ -252,8 +281,7 @@ void CUIWindow::DetachChild(CUIWindow* pChild)
if (NULL == pChild)
return;

if (m_pMouseCapturer == pChild)
SetCapture(pChild, false);
erase_capture_subtree(pChild);

//. SafeRemoveChild (pChild);
WINDOW_LIST_it it = std::find(m_ChildWndList.begin(), m_ChildWndList.end(), pChild);
Expand Down Expand Up @@ -321,24 +349,31 @@ bool CUIWindow::OnMouseAction(float x, float y, EUIMessages mouse_action)

if (GetParent() == NULL)
{
// the window that captured the mouse gets every message, wherever the cursor is
CUIWindow* cap = MouseCapturer();
if (cap && cap != this)
{
CUIWindow* cap_root = cap;
while (cap_root->GetParent())
cap_root = cap_root->GetParent();

if (cap_root == this || cap_root == cap) // our tree, or an unattached capturer
{
Frect cap_rect;
cap->GetAbsoluteRect(cap_rect);
Fvector2 cur = GetUICursor().GetCursorPosition();
cap->OnMouseAction(cur.x - cap_rect.left, cur.y - cap_rect.top, mouse_action);
return true;
}
}

if (!wndRect.in(cursor_pos))
return false;
//получить координаты относительно окна
cursor_pos.x -= wndRect.left;
cursor_pos.y -= wndRect.top;
}


//если есть дочернее окно,захватившее мышь, то
//сообщение направляем ему сразу
if (m_pMouseCapturer)
{
m_pMouseCapturer->OnMouseAction(cursor_pos.x - m_pMouseCapturer->GetWndRect().left,
cursor_pos.y - m_pMouseCapturer->GetWndRect().top,
mouse_action);
return true;
}

// handle any action
switch (mouse_action)
{
Expand Down Expand Up @@ -452,25 +487,34 @@ void CUIWindow::OnFocusLost()
//ему в независимости от того где мышь
void CUIWindow::SetCapture(CUIWindow* pChildWindow, bool capture_status)
{
if (GetParent())
{
GetParent()->SetCapture(this, capture_status);
}

if (capture_status)
{
if (!pChildWindow)
return;

//оповестить дочернее окно о потере фокуса мыши
if (NULL != m_pMouseCapturer)
m_pMouseCapturer->SendMessage(this, WINDOW_MOUSE_CAPTURE_LOST);
if (!s_capture_stack.empty() && s_capture_stack.back() != pChildWindow)
s_capture_stack.back()->SendMessage(this, WINDOW_MOUSE_CAPTURE_LOST);

m_pMouseCapturer = pChildWindow;
erase_capture(pChildWindow); // re-capture moves it to the top rather than stacking twice
s_capture_stack.push_back(pChildWindow);
}
else
{
m_pMouseCapturer = NULL;
erase_capture(pChildWindow);
}
}

CUIWindow* CUIWindow::MouseCapturer()
{
return s_capture_stack.empty() ? NULL : s_capture_stack.back();
}

void CUIWindow::ReleaseMouseCapture()
{
erase_capture_subtree(this);
}


//реакция на клавиатуру
bool CUIWindow::OnKeyboardAction(int dik, EUIMessages keyboard_action)
Expand Down Expand Up @@ -589,7 +633,6 @@ CUIWindow* CUIWindow::GetChildMouseHandler()
//для перевода окна и потомков в исходное состояние
void CUIWindow::Reset()
{
m_pMouseCapturer = NULL;
}

void CUIWindow::ResetAll()
Expand Down
11 changes: 7 additions & 4 deletions src/xrGame/ui/UIWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,14 @@ class CUIWindow : public CUISimpleWindow

//захватить/освободить мышь окном
//сообщение посылается дочерним окном родительскому
// Capture is global, not per-window: one stack of windows that have taken the mouse, topmost
// wins, and releasing restores the one underneath (a popup keeps its capture across a scrollbar
// drag inside it). A capturer need not be a child of the receiver, or attached at all --
// CUIDragItem has no parent.
void SetCapture(CUIWindow* pChildWindow, bool capture_status);
CUIWindow* GetMouseCapturer() { return m_pMouseCapturer; }
static CUIWindow* MouseCapturer();
bool IsMouseCapturer() { return MouseCapturer() == this; }
void ReleaseMouseCapture();

//окошко, которому пересылаются сообщения,
//если NULL, то шлем на GetParent()
Expand Down Expand Up @@ -307,9 +313,6 @@ class CUIWindow : public CUISimpleWindow
//указатель на родительское окно
CUIWindow* m_pParentWnd;

//дочернее окно которое, захватило ввод мыши
CUIWindow* m_pMouseCapturer;

//дочернее окно которое, захватило ввод клавиатуры
CUIWindow* m_pKeyboardCapturer;

Expand Down
Loading